ocaml-book-1.0/0000755000000000000000000000000007740234274010274 5ustar ocaml-book-1.0/debian/0000755000000000000000000000000011644116343011510 5ustar ocaml-book-1.0/debian/ocaml-book-fr.links0000644000000000000000000000007511644114464015206 0ustar usr/share/doc/ocaml-book/fr usr/share/doc/ocaml-book-fr/book ocaml-book-1.0/debian/ocaml-book-en.links0000644000000000000000000000007511644114451015175 0ustar usr/share/doc/ocaml-book/en usr/share/doc/ocaml-book-en/book ocaml-book-1.0/debian/ocaml-book-fr.install0000644000000000000000000000003611644110027015520 0ustar fr/ usr/share/doc/ocaml-book/ ocaml-book-1.0/debian/ocaml-book-en.install0000644000000000000000000000003611644110042015510 0ustar en/ usr/share/doc/ocaml-book/ ocaml-book-1.0/debian/rules0000755000000000000000000000012511644114703012564 0ustar #!/usr/bin/make -f %: dh $@ override_dh_compress: dh_compress -X.pdf -X.css -X.js ocaml-book-1.0/debian/ocaml-book-en.doc-base0000644000000000000000000000074311644115216015534 0ustar Document: ocaml-book-en Title: Developing applications with Objective Caml Author: Emmanuel Chailloux, Pascal Manoury, Bruno Pagano Abstract: This book described the Objective CAML (OCaml) programming language. This is the English translation of the O'Reilly's OCaml French book Section: Programming/OCaml Format: HTML Index: /usr/share/doc/ocaml-book/en/html/index.html Files: /usr/share/doc/ocaml-book/en/html/* Format: PDF Files: /usr/share/doc/ocaml-book/en/ocaml-ora-book.pdf ocaml-book-1.0/debian/changelog0000644000000000000000000000571511644116317013373 0ustar ocaml-book (1.0-5) unstable; urgency=low * orphan package * acknowledge NMU, thanks Jakub Wilk for taking care of it! * debian/rules: port to minimal dh style and debhelper 8 * debian/control: - bump Standards-Version to 3.9.2 (no changes needed) - add missing ${misc:Depends} * add symlinks /usr/share/doc/ocaml-book-{fr,en}/book -> /usr/share/doc/ocaml-book-{fr,en} * doc-base: change section to Programming/OCaml * utf8 sanitization for: ocaml-book-fr.doc-base, copyright -- Stefano Zacchiroli Sat, 08 Oct 2011 19:40:50 +0200 ocaml-book (1.0-4.1) unstable; urgency=low * Non-maintainer upload. * Add binary-arch target to debian/rules (closes: #640877). Thanks to Niels Thykier for the bug report. * Move debhelper from Build-Depends-Indep to Build-Depends, as it's needed for the clean target. -- Jakub Wilk Wed, 21 Sep 2011 00:53:13 +0200 ocaml-book (1.0-4) unstable; urgency=low * debian/control - removed non-ASCII characters from debian/control - use debhelper 4 - bumped Standards-Version to 3.6.1.0 * debian/rules - use debian/compat istead of DH_COMPAT -- Stefano Zacchiroli Wed, 15 Oct 2003 16:09:50 +0200 ocaml-book (1.0-3) unstable; urgency=low * debian/control - bumped Standards-Version to 3.5.9 - recommends www-browser and/or pdf-viewer - specified that French version is HTML only * debian/ocaml-book-{en,fr}.doc-base - improved doc-base's abstracts for both the original and translated versions * debian/* - fixed some spelling errors reported by lintian * debian/rules - avoid compressing CSS and JavaScript files * {fr,en}/html/videoc.js - hacked JavaScript's {open,close}PopUp so that they output
and
tags around exercises' solutions. This avoid pop up mass on mozilla-like browsers. Solutions aren't popped up on request but at least they are available and exercises aren't hidden behind them (Closes: Bug#180799, Bug#185266) * debian/TODO - added with one entry: figure out how to make pop-up works also on mozilla-like browsers :-) -- Stefano Zacchiroli Mon, 2 Jun 2003 17:30:45 +0200 ocaml-book (1.0-2) unstable; urgency=low * Added ocaml-book-en package which contains a preliminary version of the english translation of the book. * Moved installed version of the book from /usr/share/doc/ocaml-book-fr from /usr/share/doc/ocaml-book which now contains also the english translation. * Removed from the README.Debian mention of the doubt on the non-free-ness of the book, this book is surely non compliant with the DFSG. * Added to the README.Debian mention of where the real book is now installed. -- Stefano Zacchiroli Sat, 6 Apr 2002 11:22:18 +0200 ocaml-book (1.0-1) unstable; urgency=low * Initial Release. -- Stefano Zacchiroli Wed, 16 Jan 2002 17:02:12 +0100 ocaml-book-1.0/debian/ocaml-book-fr.doc-base0000644000000000000000000000054111644115662015542 0ustar Document: ocaml-book-fr Title: Développement d'applications avec Objective Caml Author: Emmanuel Chailloux, Pascal Manoury, Bruno Pagano Abstract: This book describes the Objective CAML (OCaml) programming language Section: Programming/OCaml Format: HTML Index: /usr/share/doc/ocaml-book/fr/html/index.html Files: /usr/share/doc/ocaml-book/fr/html/* ocaml-book-1.0/debian/copyright0000644000000000000000000001114611644116221013441 0ustar This package was debianized by Stefano Zacchiroli on Wed, 16 Jan 2002 17:02:12 +0100. The original French version was downloaded from: http://www.pps.jussieu.fr/Livres/ora/DA-OCAML/ The translated version was downloaded from: http://caml.inria.fr/oreilly-book/ === AUTHORS === French version authors: Emmanuel Chailloux Pascal Manoury Bruno Pagano Translators from the French version to the English one: Francisco Valverde Albacete Joshua D. Guttman Mark Andrew Theo Honohan Martin Anlauf Jerzy Karczmarczuk Christopher B. Browne Xavier Leroy David Casperson Markus Mottl Gang Chen Charles Neveu Harry Chomsky Tim Perkis Ruchira Datta Alan Schmitt Seth Delackner Paul Steckler Patrick Doane Perdita Stevens Andreas Eder François Thomasset Manuel Fahndrich Hans-Joerg Tiede Translation project coordinators: Ruchira Datta Joshua D. Guttman Benjamin C. Pierce Proofreaders for the translated version: Julian Assange Christopher League Robert Bauer Ronald Legere Will Benton Matthew W. Markland Antony Courtney Markus Mottl Thomas Valentino Crimi Eric Merritt Scott Cyphers William D. Neumann Laura Dean John Prevost Ken Dyck Brian Rogoff Miles Egan Ken Rose Jeremy Fincher George Richard Russell Eric Frias Anders Selander Brent Fulgham Rafael 'Dido' Sevilla Jason Gibson Mark Shure Dan Grossman John Max Skaller John Heron Matt Sottile Garry Hodgson Jason Voegele Samin Ishtiaq Don Wakefield Jeffrey Katcher Russle Brock Wilcox Doug Landauer System administration for the translation project: Xavier Leroy === COPYRIGHT === There are two different copyright notes: one for the French version of the book and the other for the corresponding English translation. Copyright for the French version: =========================== This document is the official (1.0) electronic version of the book "Développement d'applications avec Objective Caml", written by Emmanuel Chailloux, Pascal Manoury, and Bruno Pagano, and published by Éditions O'Reilly under ISBN 2-84177-121-0. Authors and publishers are willing to make this document freely available on any medium, provided that - the content of the official version is not modified without expressed agreement of the authors or the publisher. - unofficial modifications expressely state so, while always providing users with a live pointer to the corresponding official version. - commercial products that include this document are themselves compliant with the DSFG and don't consist of this document only. - this very notice is made easily available to any user of the document =========================== Copyright for the English translation: =========================== This document is the first released draft (0.9) of the English translation of the book "Développement d'applications avec Objective Caml", written by Emmanuel Chailloux, Pascal Manoury, and Bruno Pagano, and published by Éditions O'Reilly under ISBN 2-84177-121-0. Authors, translators and publisher are willing to make this document freely available on any medium, provided that - the content of the official version is not modified without expressed agreement of the authors or the publisher. - unofficial modifications expressely state so, while always providing users with a live pointer to the corresponding official version at http://caml.inria.fr/oreilly-book/. - commercial products that include this document are themselves compliant with the DFSG and don't consist of this document only. - this very notice is made easily available to any user of the document =========================== ocaml-book-1.0/debian/control0000644000000000000000000000163311644116343013116 0ustar Source: ocaml-book Section: non-free/doc Priority: optional Maintainer: Debian QA Group Build-Depends: debhelper (>> 8.0.0) Standards-Version: 3.9.2 Package: ocaml-book-fr Architecture: all Depends: ${misc:Depends} Recommends: www-browser Description: French book: "Developpement d'applications avec Objective Caml" HTML version of the French book: "Developpement d'applications avec Objective Caml" published by O'Reilly. . This package contains the HTML version of the book. Package: ocaml-book-en Architecture: all Depends: ${misc:Depends} Recommends: www-browser | pdf-viewer Description: English book: "Developing applications with Objective Caml" This is the English translation of the O'Reilly's OCaml French book "Developpement d'applications avec Objective Caml" that can be found in the ocaml-book-fr package. . This package contains both the HTML and PDF version of the book. ocaml-book-1.0/debian/TODO0000644000000000000000000000031707666671710012216 0ustar - figure out how to make exercises' solutions pop up on click on mozilla-like browsers too. Actually it seems to work only on IE Mon, 02 Jun 2003 17:56:20 +0200 zack ocaml-book-1.0/debian/compat0000644000000000000000000000000211644107173012707 0ustar 8 ocaml-book-1.0/fr/0000755000000000000000000000000007453537737010716 5ustar ocaml-book-1.0/fr/html/0000755000000000000000000000000007666671601011655 5ustar ocaml-book-1.0/fr/html/book-ora218.html0000644000000000000000000010634207421273603014502 0ustar Index des lments du langage Index Suivant

Index des lments du langage

  • & , 2
  • && , 2
  • !, 3
  • [<, B
  • [>, B
  • (), 2
  • **, 2
  • *., 2
  • *, 2, 2
  • +., 2
  • +, 2
  • -., 2
  • ->, 2
  • -, 2
  • /., 2
  • /, 2
  • ::, 2
  • :=, 3
  • :>, 15
  • :, 2, B
  • ;, 3
  • <-, 3, 3, 3
  • <=, 2
  • <>, 2
  • <, 2
  • ==, 2
  • =, 2
  • >=, 2
  • >}, 15
  • >, 2
  • ?, B
  • @, 2
  • [], 2
  • #, 15, 15
  • %, 8
  • ^, 2
  • _, 2
  • {<, 15
  • `, B
  • ||, 2

  • Arg (module), 8
  • Arith_status (module), 8
  • Array (module), 3, 8, 8, 8
  • accept, 20
  • acos, 2
  • add_available_units, 8
  • add_interfaces, 8
  • alarm, 18
  • alloc.h, 12
  • allow_unsafe_modules, 8
  • and (mot-cl), 2, 2
  • append, 8, 8
  • argv, 8
  • array (type), 3
  • as (mot-cl), 2, 15, A
  • asin, 2
  • assoc, 6, 8
  • assq, 8
  • atan, 2

  • Buffer (module), 8
  • background, 5
  • big_int (type du module Num), 8
  • bind, 20, 20
  • blit, 8
  • blit_image, 5
  • bool (type), 2
  • bprintf, 8
  • broadcast, 19
  • button_down, 5

  • Callback (module), 12
  • Condition (module), 19
  • catch, 8
  • ceil, 2
  • char (type), 2
  • char_of_int, 2
  • chdir, 8
  • check, 9
  • class (mot-cl), 15
  • clear_available_units, 8
  • clear_graph, 5
  • close, 18, 20, 20
  • close_graph, 5
  • close_in, 3
  • close_out, 3
  • close_process, 18
  • color (type), 5
  • combine, 6, 8
  • command, 8
  • compact, 9
  • compilateur
    • -modern, B
  • concat, 8, 8
  • connect, 20, 20
  • constraint (mot-cl), 15
  • copy, 8, 15
  • cos, 2
  • create, 3, 8, 9, 19, 19, 19
  • create_image, 5
  • create_process, 18
  • current_point, 5

  • Delayed, 4
  • Digest (module), 8, 8
  • Dynlink (module), 8
  • delay, 19
  • descr_of_in_channel, 18
  • descr_of_out_channel, 18
  • do (mot-cl), 3
  • done (mot-cl), 3
  • downto (mot-cl), 3
  • draw_arc, 5
  • draw_circle, 5
  • draw_ellipse, 5
  • draw_image, 5
  • dump_image, 5
  • dup, 18
  • dup2, 18

  • End_of_file, 3
  • Event (module), 19
  • else (mot-cl), 2
  • end (mot-cl), 14, 15
  • eprintf, 8
  • error, 8
  • error (type du module Unix), 18
  • error_message, 18
  • establish_server, 20
  • event, 5
  • exception (mot-cl), 2
  • exists, 2, 8
  • exit, 19
  • exn (type du module Pervasives), 2
  • exp, 2
  • external (mot-cl), 12

  • Filename (module), 8
  • Format (module), 8
  • failwith, 2
  • false, 2
  • file, 8
  • file_exists, 8
  • fill, 8
  • fill_poly, 5
  • fill_rect, 5
  • filter, 8
  • find, 8
  • find_all, 8
  • flatten, 8
  • float (type), 2
  • float_of_string, 2
  • floor, 2
  • fold_left, 2, 8, 8
  • fold_right, 8, 8
  • for (mot-cl), 3
  • for_all, 2, 8
  • force, 4
  • foreground, 5
  • format (type), 8, 8
  • fprintf, 8
  • from_channel, 8
  • from_string, 8
  • fst, 2
  • full_major, 9
  • fun (mot-cl), 2
  • function (mot-cl), 2
  • functor (mot-cl), 14

  • Gc (module), 9
  • Genlex (module), 11
  • Graphics (module), 5
  • get, 8, 9, 9
  • get_image, 5
  • getcwd, 8
  • getenv, 8
  • gethostbyaddr, 20
  • gethostbyname, 20
  • gethostname, 20
  • getservbyname, 20
  • getservbyport, 20
  • global_replace, 11

  • Hashtbl (module), 8, 8
  • handle_error, 18
  • hd, 2, 8
  • host_entry (type du module Unix), 20

  • if (mot-cl), 2
  • ignore, 3
  • image, 5
  • in (mot-cl), 2
  • in_channel, 3
  • in_channel_of_descr, 18
  • inet_addr (type du module Unix), 20
  • inet_addr_of_string, 20
  • init, 6, 8
  • initializer (mot-cl), 15
  • input, 3
  • input_line, 3
  • int, 6
  • int (type), 2
  • int_of_char, 2
  • int_of_string, 2
  • interactive, 8
  • iter, 8, 8
  • iter2, 8
  • iteri, 8

  • key_pressed, 5
  • kill, 18, 18, 19

  • Lazy (module), 4
  • Lexing (module), 11
  • List (module), 2, 8, 8, 8
  • labltk (commande), B
  • labltklink (commande), B
  • labltkopt (commande), B
  • lazy (mot-cl), 4
  • length, 8, 8
  • let (mot-cl), 2, 2
  • lexbuf (type du module Lexing), 11
  • lineto, 5
  • list (type), 2
  • listen, 20, 20
  • loadfile, 8
  • loadfile_private, 8
  • lock, 19
  • log, 2
  • log10, 2
  • lseek, 18

  • Map (module), 14
  • Marshal (module), 8, 8
  • Match_Failure, 2
  • Mutex (module), 19
  • major, 9
  • make, 8
  • make_image, 5
  • make_lexer, 11
  • make_matrix, 8
  • map, 2, 2, 8, 8
  • map2, 8
  • mapi, 8
  • match (mot-cl), 2, 4
  • matched_string, 11
  • max_array_length, 8, 8
  • mem, 2, 8
  • mem_assoc, 8
  • mem_assq, 8
  • memory.h, 12
  • memq, 2, 8
  • method (mot-cl), 15
  • minor, 9
  • mkfifo, 18
  • mlvalues.h, 12
  • mod, 2
  • #modern (directive), B
  • module (mot-cl), 14
  • module type (mot-cl), 14
  • mouse_pos, 5
  • moveto, 5
  • mutable (mot-cl), 3

  • None, 9
  • Num (module), 8
  • new (mot-cl), 15
  • next, 4
  • not, 2, 2
  • nth, 8
  • num (type du module Num), 8

  • OS_type, 8
  • object (mot-cl), 15
  • ocaml (commande), 7, 7
  • ocamlbrowser (commande), B
  • ocamlc (commande), 7, 7, 7
  • ocamlc.opt (commande), 7
  • ocamldebug (commande), 10
  • ocamldep (commande), 10
  • ocamllex (commande), 11, 11
  • ocamlmktop (commande), 5, 7, 7
  • ocamlopt (commande), 7
  • ocamlopt.opt (commande), 7
  • ocamlrun (commande), 7, 7
  • ocamlyacc (commande), 11, 11
  • of (mot-cl), 2
  • of_channel, 4
  • of_list, 6, 8
  • of_string, 4
  • open (mot-cl), 8, 14
  • open_connection, 20
  • open_flag (type du module Unix), 18
  • open_graph, 5
  • open_in, 3
  • open_out, 3
  • open_process, 18
  • openfile, 18
  • option (type du module Pervasives), 9
  • or, 2
  • out_channel, 3
  • out_channel_of_descr, 18
  • output, 3

  • Pervasives (module), 8
  • Printexc (module), 8
  • Printf (module), 8
  • parse, 8
  • parser (mot-cl), 4, 11
  • partition, 8
  • pipe, 18
  • plot, 5
  • point_color, 5
  • print, 8
  • print_newline, 3
  • print_stat, 9
  • print_string, 3
  • printf, 8
  • private (mot-cl), 15
  • process_status (type du module Unix), 18

  • Queue (module), 8

  • Random (module), 8
  • raise (mot-cl), 2
  • ratio (type du module Num), 8
  • read, 18
  • read_key, 5
  • read_line, 3
  • rec (mot-cl), 2
  • receive, 19
  • -rectypes, A
  • ref (type), 3
  • regexp, 11
  • register, 12
  • remove, 8
  • remove_assoc, 8
  • remove_assq, 8
  • rename, 8
  • rev, 8
  • rev_append, 8
  • rgb (type), 5

  • Set (module), 14
  • SOCK_STREAM, 20
  • Some, 9
  • Sort (module), 8
  • Stack (module), 8, 14
  • Stack_overflow (exception), 4
  • Str (module), 11
  • Stream (module), 4
  • String (module), 8
  • Sys (module), 8
  • Sys_error, 3
  • search_forward, 11
  • seek_command (type du module Unix), 18
  • send, 19
  • service_entry (type du module Unix), 20
  • set, 8, 9, 9
  • set_binary_mode_in, 18
  • set_binary_mode_out, 18
  • set_color, 5
  • set_font, 5
  • set_line, 5
  • set_signal, 18
  • set_text_size, 5
  • shutdown_connection, 20
  • sig (mot-cl), 14
  • sigalrm, 18
  • sigchld, 18
  • sigint, 18
  • signal, 18, 19
  • signal_behavior (type du module Unix), 18
  • sigusr1, 18
  • sigusr2, 18
  • sin, 2
  • sleep, 18
  • snd, 2
  • sockaddr (type du module Unix), 20
  • socket, 20
  • socket (type du module Unix), 20
  • socket_domain (type du module Unix), 20
  • socket_type (type du module Unix), 20
  • split, 8
  • sprintf, 8
  • sqrt, 2
  • stat, 9
  • status, 5
  • stderr, 3, 18
  • stdin, 3, 18
  • stdout, 3, 18
  • stream (type), 4
  • string, 8
  • string (type), 2
  • string_of_float, 2
  • string_of_inet_addr, 20
  • string_of_int, 2
  • struct (mot-cl), 14
  • sub, 8
  • sync, 19

  • Thread (module), 19
  • ThreadUnix (module), 20
  • tan, 2
  • then (mot-cl), 2
  • time, 6, 8
  • tl, 2, 8
  • to (mot-cl), 3
  • to_buffer, 8
  • to_channel, 8
  • to_list, 8
  • to_string, 8, 8
  • token (type du module Genlex), 11
  • #trace (directive), 10
  • true, 2
  • try (mot-cl), 2
  • try_lock, 19
  • type (mot-cl), 2

  • Unix (module), 18
  • Unix_error, 18
  • unit (type), 2
  • unlock, 19
  • #untrace (directive), 10
  • #untrace_all (directive), 10

  • Value, 4
  • val (mot-cl), 14, 15
  • val mutable (mot-cl), 15
  • value, 12, 12
  • virtual (mot-cl), 15

  • Weak (module), 8, 9
  • wait, 18, 19
  • wait_next_event, 5
  • wait_time_read, 20
  • wait_time_write, 20
  • waitpid, 18
  • when (mot-cl), 2
  • while (mot-cl), 3
  • with (mot-cl), 2, 2, 2, 4, 14
  • word_size, 8
  • write, 18

Index Suivant ocaml-book-1.0/fr/html/book-ora161.html0000644000000000000000000047501607421273602014505 0ustar Jeux deux joueurs Prcdent Index Suivant

Jeux deux joueurs

L'application prsente dans cette section poursuit un double objectif. D'une part elle s'attaque une rsolution de problmes dont la complexit empche une exploration systmatique de toutes les solutions, mettant ainsi en valeur la bonne adquation du langage Objective CAML pour le traitement d'applications symboliques. D'autre part, elle permet grce l'utilisation de modules paramtrs, de dfinir un cadre gnral pour la construction de jeux deux joueurs, permettant ainsi de factoriser une partie de la recherche et de rendre facilement modifiable des composants comme la fonction d'valuation d'une position d'un jeu ou son affichage.

On prsente tout d'abord la problmatique des jeux deux joueurs, puis nous dcrivons l'algorithme minimax-ab permettant un lagage rapide de l'arbre des coups possibles. Nous [ralisons un module paramtr implantant cet algorithme pour dcrire ensuite le module paramtr gnral des applications jeux 2 joueurs. Enfin, nous appliquons ces foncteurs pour raliser deux applications jeux : un puissance 4 (un morpion gravitationnel), et Stone Henge (un jeu de construction de lignes de force).

Problmatique des jeux 2 joueurs

Les jeux deux joueurs font partie des applications classiques en programmation symbolique. C'est un bon exemple de rsolution de problmes pour au moins deux raisons :
  • le nombre de solutions analyser pour obtenir le meilleur coup ncessite des mthodes autres que la force brute : aux checs le nombre de coups possibles est en moyenne de 30 et une partie se joue en une quarantaine de coups pour chaque joueur; ce qui donne quelques 3080 positions pour explorer l'arbre complet d'une partie!
  • la qualit d'une solution est facilement vrifiable, en particulier il est possible de tester la qualit de la solution propose par un programme en utilisant un autre programme.
Supposons que l'on puisse utiliser une exploration totale de toutes les parties possibles partir d'une position lgale du jeu. Un tel programme a besoin d'une fonction de gnration des coups lgaux partir de cette position et d'une fonction d'valuation donnant un score la position donne. La fonction d'valuation donne un score maximal une position gagnante et un score minimal une position perdante. partir de la position initiale, on peut donc construire l'arbre de toutes les variantes de la partie, o chaque noeud correspond une position, ses fils aux positions suivantes obtenues en ayant jou un coup et les feuilles aux positions gagnantes, perdantes ou nulles. Une fois cet arbre construit, son exploration permet de dterminer s'il existe un chemin menant la victoire, ou une position nulle le cas chant. Le chemin de plus petite longueur peut alors tre choisi pour amener au rsultat voulu.

Comme la taille d'un tel arbre est en rgle gnrale trop grande pour tre envisageable, il est ncessaire d'en limiter sa construction. La premire possibilit est de limiter la profondeur de recherche, c'est--dire le nombre de coups et de rponses valuer. On rduit ainsi la profondeur de l'arbre donc sa taille. Dans ce cas on atteint rarement des feuilles moins d'tre en fin de partie.

D'autre part, nous pouvons essayer de limiter le nombre de coups engendrs pour pouvoir les valuer plus finement. Pour cela, nous tentons de n'engendrer que les coups semblant les plus favorables et de les examiner en commenant par les meilleurs. Cela permet ainsi d'laguer rapidement des branches entires de l'arbre. C'est justement ce que propose l'algorithme minimax-ab qui est prsent dans la section suivante.

Minimax ab

On prsente la recherche minimax puis on dcrit sa variante optimise par les coupures ab. L'implantation de cet algorithme utilise un module FAlphabeta paramtr par la reprsentation du jeu et la fonction d'valuation. On distingue les deux joueurs en les nommant A et B.

Minimax

L'algorithme minimax est un algorithme de recherche en profondeur, avec une profondeur limite. Il ncessite d'utiliser :
  • une fonction de gnration des coups lgaux partir d'une position,
  • et une fonction d'valuation d'une position de jeu.
partir d'une position du jeu, l'algorithme explore l'arbre de tous les coups lgaux jusqu' la profondeur demande. Les scores des feuilles de l'arbre sont alors calculs par la fonction d'valuation. Un score positif indique une bonne position pour le joueur A, un score ngatif une mauvaise position pour le joueur A donc une bonne position pour le joueur B. Selon le joueur qui joue, le passage d'une position une autre est maximisante (pour le joueur A) ou minimisante (pour le joueur B). Les joueurs essaient de jouer les coups les plus profitables pour eux-mmes. En cherchant le meilleur coup pour le joueur A, la recherche en profondeur 1 cherchera dterminer le coup immdiat qui maximise le score de la nouvelle position.



Figure 17.1 : recherche maximisante un niveau


Sur la figure 17.1, le joueur A part de la position O, dtermine quatre coups lgaux, construit ces nouvelles configurations et les value. De ces scores, sa meilleur position est P2 (de score 8). Il propage cette valeur la position O, indiquant par l que cette position amne en un coup une nouvelle position de score 8 en jouant le coup C2. L'exploration en profondeur 1 est en rgle gnrale insuffisante, car on ne tient pas compte de la rponse de l'adversaire. Cela produit des programmes cherchant le gain immdiat de matriel (comme la prise d'une reine aux checs), sans s'apercevoir que les pices sont protges ou que la position devient perdante (gambit de la reine pour faire mat). Une exploration de profondeur 2 permet de s'apercevoir du contrecoup.

La figure 17.2 montre un niveau supplmentaire de dveloppement de l'arbre en tenant compte de la rponse du joueur B. Celui-ci recherche aussi son meilleur coup. Pour cela l'algorithme minimax minimisera les scores des noeuds de profondeur 2.


Figure 17.2 : recherche maximisante et minimisante deux niveaux


Le coup P2 qui amenait une position immdiate de score 8, va en fait amener la position du jeu un score de -3. En effet si B joue le coup D5, alors le score de la position Q5 vaut -3. On s'aperoit alors que le coup C1 limite les dgats avec un score de -1. Il sera donc prfr.

Dans la plupart des jeux, il est possible de faire lanterner son adversaire, en le faisant jouer coups forcs, dans le but d'embrouiller la situation en esprant qu'il commette une faute. Pour cela la recherche de profondeur 2 est trs insuffisante pour le ct tactique du jeu. Le ct stratgique est rarement bien exploit par un programme car il n'a pas la vision de la probable volution de la position en fin de partie. La difficult de profondeur plus grande provient de l'explosion combinatoire. Par exemple aux checs, l'exploration de 2 profondeurs supplmentaires apporte un facteur d'environ mille fois plus de combinaisons (30 * 30). Donc si on cherche calculer une profondeur de 10, on obtiendra environ 514 positions, ce qui est bien entendu trop. Pour cela on essaie d'laguer l'arbre de recherche. On peut noter que dans la figure 17.2, il n'est pas forcment utile d'explorer la branche P3 dans la mesure o le score de cette position la profondeur 1 (voir figure 17.1) est dj moins bon que celui trouv dans la branche P1. De mme la branche P4 n'a pas besoin d'tre compltement explore. Ds le calcul de Q7, on obtient un score infrieur celui de P1 (toujours compltement explore). Les calculs de Q8 et Q9 ne pourront pas amliorer cette situation mme si leur score respectif est meilleur que Q7. Dans une tape minimisante, le score le plus faible est remont. On sait dj qu'ils n'apporteront rien de nouveau. La variante ab du minimax utilise cet lagage pour diminuer le nombre de branches explorer.

Minimax-ab

On appelle coupure a la borne infrieure d'un noeud maximisant, et coupure b la borne suprieure d'un noeud minimisant. La figure 17.3 montre les coupures effectues dans les branches P3 et P4 par la connaissance de la borne infrieure -1 de P1.


Figure 17.3 : coupure a dans une tape max-min


Ds que l'arbre est plus large ou plus profond le nombre de coupures augmente, lagant ainsi de grands sous-arbres.

Module paramtr pour le minimax-ab

On dsire raliser un module paramtr FAlphabeta qui implante cet algorithme, rutilisable pour tout jeu deux joueurs. Ses paramtres correspondent d'une part toutes les informations d'arbitrage du jeu et d'autre part la fonction d'valuation.

Interfaces
On dclare les signatures REPRESENTATION, pour la reprsentation du jeu, et EVAL, pour l'valuation d'une position.

# module type REPRESENTATION = sig
type jeu
type coup
val jeu_depart : unit -> jeu
val coups_legaux: bool -> jeu -> coup list
val jouer: bool -> coup -> jeu -> jeu
end;;

# module type EVAL =
sig
type jeu
val evaluer: bool -> jeu -> int
val plusI : int
val moinsI: int
val est_feuille: bool -> jeu -> bool
val est_stable: bool -> jeu -> bool
type etat = G | P | N | C
val etat_de : bool -> jeu -> etat
end;;


Les types jeu et coup sont abstraits. Un joueur sera reprsent par un boolen. La fonction coups_legaux prend un joueur, une position et retourne la liste des coups possibles. La fonction jouer prend un joueur, un coup, une position et retourne une nouvelle position. Les valeurs plusI et moinsI sont les bornes des valeurs retournes par la fonction evaluer. Le prdicat est_feuille vrifie si un joueur dans une position donne peut jouer. Le prdicat est_stable indique si la position pour le joueur est dans une situation stable. Les rsultats des ces fonctions influencent la poursuite de l'exploration de coups quand on atteint la profondeur prvue.

La signature ALPHABETA correspond la signature rsultant de l'application totale du module paramtr que l'on dsire raliser. Celle-ci masque les diffrentes fonctions auxiliaires que nous utiliserons pour l'implantation de l'algorithme.

# module type ALPHABETA = sig
type jeu
type coup
val alphabeta : int -> bool -> jeu -> coup
end ;;
La fonction alphabeta prend en paramtre la profondeur de la recherche, le joueur et la position du jeu, elle retourne le coup dtermin.

On dfinit enfin la signature fonctionnelle FALPHABETA que devra respecter l'implantation du foncteur.

# module type FALPHABETA =
functor (Rep : REPRESENTATION) ->
functor (Eval : EVAL with type jeu = Rep.jeu) ->
ALPHABETA with type jeu = Rep.jeu and type coup = Rep.coup ;;


Implantation
Le module paramtr FAlphabetaO explicite le partage du type jeu entre ses deux paramtres Rep et Eval. Ce module ne comprend que six fonctions et deux exceptions. Le joueur true cherche maximiser son score alors que le joueur false le minimise. La fonction maxmin_iter calcule le maximum entre le meilleur score des branches partir d'un coup du joueur true et la borne a. La fonction maxmin prend quatre paramtres : prof qui indique la profondeur actuelle du calcul, noeud une position du jeu, alpha et beta les bornes des coupures. Si le noeud est une feuille de l'arbre ou si la profondeur maximale est atteinte alors la fonction retourne l'valuation de la position. Si ce n'est pas le cas, elle applique tous les coups lgaux du joueur true la fonction maxmin_iter en lui passant la fonction de poursuite de calcul dont la profondeur est dcrmente (minmax). Cette dernire cherchera minimiser le score de la rponse du joueur false. Les coupures sont implantes par des exceptions. Si la coupure b est dclenche dans l'itration sur les coups lgaux de la fonction maxmin, alors celle-ci retourne immdiatement avec la valeur propage par l'exception. Les fonctions minmax_iter et minmax effectuent le travail pour l'autre joueur. La fonction cherche dtermine le coup jouer partir du meilleur score en parcourant les listes des scores et des coups. La fonction principale alphabeta de ce module calcule les coups lgaux d'une position pour un joueur donn et lance les recherches la profondeur indique puis retourne le meilleur coup.

# module FAlphabetaO 
(Rep : REPRESENTATION) (Eval : EVAL with type jeu = Rep.jeu) =
struct
type jeu = Rep.jeu
type coup = Rep.coup
exception AlphaCoupure of int
exception BetaCoupure of int

let maxmin_iter noeud minmax_cur beta alpha cp =
let alpha_resu =
max alpha (minmax_cur (Rep.jouer true cp noeud) beta alpha)
in if alpha_resu >= beta then raise (BetaCoupure alpha_resu)
else alpha_resu

let minmax_iter noeud maxmin_cur alpha beta cp =
let beta_resu =
min beta (maxmin_cur (Rep.jouer false cp noeud) alpha beta)
in if beta_resu <= alpha then raise (AlphaCoupure beta_resu)
else beta_resu

let rec maxmin prof noeud alpha beta =
if (prof < 1 & Eval.est_stable true noeud)
or Eval.est_feuille true noeud
then Eval.evaluer true noeud
else
try let prev = maxmin_iter noeud (minmax (prof - 1)) beta
in List.fold_left prev alpha (Rep.coups_legaux true noeud)
with BetaCoupure a -> a

and minmax prof noeud beta alpha =
if (prof < 1 & Eval.est_stable false noeud)
or Eval.est_feuille false noeud
then Eval.evaluer false noeud
else
try let prev = minmax_iter noeud (maxmin (prof - 1)) alpha
in List.fold_left prev beta (Rep.coups_legaux false noeud)
with AlphaCoupure b -> b

let rec cherche a l1 l2 = match (l1,l2) with
(h1::q1, h2::q2) -> if a = h1 then h2 else cherche a q1 q2
| ([], []) -> failwith ("AB: "^(string_of_int a)^" non trouve'")
| (_ , _) -> failwith "AB: longueur diff"

(* val alphabeta : int -> bool -> Rep.jeu -> Rep.coup *)
let alphabeta prof joueur racine =
let alpha = ref Eval.moinsI and beta = ref Eval.plusI in
let l = ref [] in
let cpl = Rep.coups_legaux joueur racine in
let eval =
try
for i = 0 to (List.length cpl) - 1 do
if joueur then
let b = Rep.jouer joueur (List.nth cpl i) racine in
let a = minmax (prof-1) b !beta !alpha
in l := a :: !l ;
alpha := max !alpha a ;
(if !alpha >= !beta then raise (BetaCoupure !alpha) )
else
let a = Rep.jouer joueur (List.nth cpl i) racine in
let b = maxmin (prof-1) a !alpha !beta
in l := b :: !l ;
beta := min !beta b ;
(if !beta <= !alpha then raise (AlphaCoupure !beta))
done ;
if joueur then !alpha else !beta
with
BetaCoupure a -> a
| AlphaCoupure b -> b
in
l := List.rev !l ;
cherche eval !l cpl
end ;;


On peut alors fermer le module FAlphabetaO en lui associant cette signature.

# module FAlphabeta = (FAlphabetaO : FALPHABETA);;
module FAlphabeta : FALPHABETA


C'est ce dernier module qui est ensuite utilis sur diffrentes reprsentations et fonctions d'valuation de jeu.

Organisation d'un programme de jeux

L'organisation d'un programme de jeu 2 joueurs doit bien sparer la partie spcifique un jeu donn de la partie commune toutes les applications de jeux. Pour cela on se propose d'utiliser plusieurs modules paramtrs par les modules spcifiques, permettant ainsi d'viter de rcrire chaque fois les parties communes. La figure 17.4 montre l'organisation choisie.



Figure 17.4 : organisation d'une application de jeux


Les modules sur fond blanc correspondent aux parties communes de l'application. Ce sont des modules paramtrs. On y retrouve le foncteur FAlphabeta dcrit prcdemment. Les modules sur fond gris sont les modules spcifiques au jeu. Les trois principaux sont la reprsentation du jeu (J_Repr), l'affichage du jeu (J_Aff) et la fonction d'valuation (J_Eval). Les modules aux bords gras arrondis sont obtenus par l'application des modules paramtrs sur les modules simples spcifiques au jeu.

Le module FAlphabeta vient d'tre dcrit. Les deux autres modules communs sont FMain qui contient la boucle principale et FSquelette pour la gestion des joueurs.

Module FMain

Le module Fmain contient le point d'entre de l'excution d'un programme de jeux. Il est paramtr par un module de signature SQUELETTE de description de l'interaction avec un joueur dont voici la dfinition.

# module type SQUELETTE = sig
val accueil: unit -> unit
val init: unit -> ((unit -> unit) * (unit -> unit))
val encore: unit -> bool
val fin: unit -> unit
exception Gagne
exception Perd
exception Nul
val gagne: unit -> unit
val perd: unit -> unit
val nul: unit -> unit
end ;;


La fonction init construit un couple des fonctions d'action de chaque joueur. Les autres fonctions servent grer l'interaction d'une partie. Le module FMain contient deux fonctions : ca_joue qui fait alterner les joueurs et main qui gre la boucle principale.

# module FMain (P : SQUELETTE) =
struct
let ca_joue tours = while true do (fst tours) () ; (snd tours) () done

let main () = let fini = ref false
in P.accueil ();
while not !fini do
( try ca_joue (P.init ())
with P.Gagne -> P.gagne ()
| P.Perd -> P.perd ()
| P.Nul -> P.nul () );
fini := not (P.encore ())
done ;
P.fin ()
end ;;
module FMain :
functor(P : SQUELETTE) ->
sig
val ca_joue : (unit -> 'a) * (unit -> 'b) -> unit
val main : unit -> unit
end


Module FSquelette

Le module paramtr FSquelette gre les tours de chaque joueur selon les indications fournies en dbut de partie comme la nature des joueurs (programme ou non) et l'ordre des joueurs. Il a besoin de plusieurs paramtres pour la reprsentation du jeu, son affichage, sa fonction d'valuation et la recherche ab comme cela est dcrit la figure 17.4.

On donne d'abord la signature manquante pour l'affichage.

# module type AFFICHAGE = sig
type jeu
type coup
val accueil: unit -> unit
val fin: unit -> unit
val gagne: unit -> unit
val perd: unit -> unit
val nul: unit -> unit
val init: unit -> unit
val affiche : bool -> coup -> jeu -> jeu -> unit
val choix : bool -> jeu -> coup
val q_jouer : unit -> bool
val q_commencer : unit -> bool
val q_continuer : unit -> bool
end ;;


Il est noter que la reprsentation du jeu et celle des coups doivent tre partages par tous les modules paramtres, d'o les contraintes sur les types. Les deux fonctions principales sont tourH et tourM, elles grent respectivement le tour d'un joueur humain (utilisant la fonction Aff.choix) et celui d'un programme. La fonction init dtermine la nature des joueurs suite aux rponses des appels Aff.q_jouer

# module FSquelette
(Rep : REPRESENTATION)
(Aff : AFFICHAGE with type jeu = Rep.jeu and type coup = Rep.coup)
(Eval : EVAL with type jeu = Rep.jeu)
(Alpha : ALPHABETA with type jeu = Rep.jeu and type coup = Rep.coup) =
struct
let prof = ref 4
exception Gagne
exception Perd
exception Nul
let gagne = Aff.gagne
let perd = Aff.perd
let nul = Aff.nul
let encore = Aff.q_continuer
let le_jeu = ref (Rep.jeu_depart())
let fin = Aff.fin
let accueil = Aff.accueil

let tourH joueur () =
let choix = Aff.choix joueur !le_jeu in
let ancien_jeu = !le_jeu
in le_jeu := Rep.jouer joueur choix !le_jeu ;
Aff.affiche joueur choix ancien_jeu !le_jeu ;
match Eval.etat_de joueur !le_jeu with
Eval.P -> raise Perd
| Eval.G -> raise Gagne
| Eval.N -> raise Nul
| _ -> ()

let tourM joueur () =
let choix = Alpha.alphabeta !prof joueur !le_jeu in
let ancien_jeu = !le_jeu
in le_jeu := Rep.jouer joueur choix !le_jeu ;
Aff.affiche joueur choix ancien_jeu !le_jeu ;
match Eval.etat_de joueur !le_jeu with
Eval.G -> raise Gagne
| Eval.P -> raise Perd
| Eval.N -> raise Nul
| _ -> ()

let init () =
let a = Aff.q_jouer () in
let b = Aff.q_jouer()
in le_jeu :=Rep.jeu_depart () ;
Aff.init () ;
match (a,b) with
true,true -> tourM true, tourM false
| true,false -> tourM true, tourH false
| false,true -> tourH true, tourM false
| false,false -> tourH true, tourH false
end ;;


Les parties indpendantes du jeu sont maintenant ralises. On peut donc commencer la programmation de diffrents jeux. Cette organisation modulaire facilite les modifications de l'affichage ou de la fonction d'valuation d'un jeu comme nous allons le voir.

Puissance 4

On s'intresse tout d'abord un premier jeu simple, un morpion gravitationnel, appel habituellement Puissance 4. Le jeu est reprsent par sept colonnes de six lignes. chaque tour un joueur pose dans une colonne un jeton de sa couleur, celui-ci tombe jusqu'au premier emplacement libre de sa colonne. Si une colonne est compltement remplie, aucun joueur ne peut y jouer. La partie se termine quand un des joueurs a reli une ligne de quatre pices (horizontale, verticale, ou diagonale), dans ce cas il a gagn, ou quand toutes les colonnes sont pleines et dans ce cas la partie est nulle. La figure 17.5 montre une partie finie.



Figure 17.5 : Une partie de Puissance 4


On remarque une ligne de quatre jetons gris en diagonale partant du coin en bas droite.

Reprsentation du jeu : module P4_rep
On choisit pour ce jeu une reprsentation matricielle. Chaque case de la matrice est vide ou bien contient un jeton d'un des deux joueurs. Un coup est le numro d'une colonne. Les coups lgaux sont les colonnes dont la dernire case n'est pas remplie.

# module P4_rep = struct
type cell = A | B | Vide
type jeu = cell array array
type coup = int
let col = 7 and lig = 6
let jeu_depart () = Array.create_matrix lig col Vide

let coups_legaux b m =
let l = ref [] in
for c = 0 to col-1 do if m.(lig-1).(c) = Vide then l := (c+1) :: !l done;
!l

let ajoute mat c =
let l = ref lig
in while !l > 0 & mat.(!l-1).(c-1) = Vide do decr l done ; !l + 1

let jouer_gen cp m e =
let mj = Array.map Array.copy m
in mj.((ajoute mj cp)-1).(cp-1) <- e ; mj

let jouer b cp m = if b then jouer_gen cp m A else jouer_gen cp m B
end ;;


On peut facilement vrifier que ce module accepte les contraintes de la signature REPRESENTATION.

# module P4_rep_T = (P4_rep : REPRESENTATION) ;;
module P4_rep_T : REPRESENTATION


Affichage du jeu : module P4_text
Le module P4_text dcrit une interface textuelle pour le jeu Puissance 4 compatible avec la signature AFFICHAGE. Il ne prsente pas de grande difficult, mais permet de bien comprendre ensuite l'assemblage des modules.

# module P4_text = struct
open P4_rep
type jeu = P4_rep.jeu
type coup = P4_rep.coup

let print_jeu mat =
for l = lig - 1 downto 0 do
for c = 0 to col - 1 do
match mat.(l).(c) with
A -> print_string "X "
| B -> print_string "O "
| Vide -> print_string ". "
done;
print_newline ()
done ;
print_newline ()

let accueil () = print_string "P4 ...\n"
let fin () = print_string "A bientt ... \n"
let question s = print_string s; print_string " o/n ? " ; read_line() = "o"
let q_commencer () = question "Voulez-vous commencer"
let q_continuer () = question "Encore une partie"
let q_jouer () = question "Est-ce une machine qui joue ?"

let gagne ()= print_string "Le 1er joueur gagne" ; print_newline ()
let perd () = print_string "Le 1er joueur perd" ; print_newline ()
let nul () = print_string "Partie nulle" ; print_newline ()

let init () = print_string "X: 1er joueur O: 2eme joueur";
print_newline () ; print_newline () ;
print_jeu (jeu_depart ()) ; print_newline()

let affiche b c aj j = print_jeu j

let est_coup = function '1'..'7' -> true | _ -> false

exception Coup of int
let rec choix joueur jeu =
print_string ("Choix joueur" ^ (if joueur then "1" else "2") ^ " : ") ;
let l = coups_legaux joueur jeu
in try while true do
let i = read_line()
in ( if (String.length i > 0) && (est_coup i.[0])
then let c = (int_of_char i.[0]) - (int_of_char '0')
in if List.mem c l then raise (Coup c) );
print_string "coup non valable, essayez encore : "
done ;
List.hd l
with Coup i -> i
| _ -> List.hd l
end ;;


On vrifie immdiatement qu'il respecte bien les contraintes de la signature AFFICHAGE.

# module P4_text_T = (P4_text : AFFICHAGE) ;;
module P4_text_T : AFFICHAGE


Fonction d'valuation : module P4_eval
La qualit d'un jeu dpend principalement de la fonction d'valuation d'une position. Le module P4_eval dfinit la fonction evaluer qui calcule la valeur d'une position pour un joueur donn. Cette fonction appelle la fonction eval_bloc, dans les quatre directions en spcialisant les diagonales, qui elle-mme appelle la fonction eval_quatre pour calculer le nombre de jetons dans la ligne demande. Le tableau valeur donne la valeur d'un bloc contenant 0, 1, 2 ou 3 jetons de la mme couleur. L'exception Quatre est dclenche quand 4 jetons sont aligns.

# module P4_eval = struct
open P4_rep
type jeu = P4_rep.jeu
let valeur = Array.of_list [0; 2; 10; 50]
exception Quatre of int
exception Valeur_nulle
exception Arg_invalid
let moinsI = -10000
let plusI = 10000

let eval_quatre m l_dep c_dep delta_l delta_c =
let n = ref 0 and e = ref Vide
and x = ref c_dep and y = ref l_dep
in try
for i = 1 to 4 do
if !y<0 or !y>=lig or !x<0 or !x>=col then raise Arg_invalid ;
( match m.(!y).(!x) with
A -> if !e = B then raise Valeur_nulle ;
incr n ;
if !n = 4 then raise (Quatre plusI) ;
e := A
| B -> if !e = A then raise Valeur_nulle ;
incr n ;
if !n = 4 then raise (Quatre moinsI);
e := B;
| Vide -> () ) ;
x := !x + delta_c ;
y := !y + delta_l
done ;
valeur.(!n) * (if !e=A then 1 else -1)
with
Valeur_nulle | Arg_invalid -> 0

let eval_bloc m e cmin cmax lmin lmax dx dy =
for c=cmin to cmax do for l=lmin to lmax do
e := !e + eval_quatre m l c dx dy
done done

let evaluer b m =
try let evaluation = ref 0
in (* evaluation des lignes *)
eval_bloc m evaluation 0 (lig-1) 0 (col-4) 0 1 ;
(* evaluation des colonnes *)
eval_bloc m evaluation 0 (col-1) 0 (lig-4) 1 0 ;
(* diagonales partant de la premiere ligne ( droite) *)
eval_bloc m evaluation 0 (col-4) 0 (lig-4) 1 1 ;
(* diagonales partant de la premiere colonne ( droite) *)
eval_bloc m evaluation 1 (lig-4) 0 (col-4) 1 1 ;
(* diagonales partant de la premiere ligne ( gauche) *)
eval_bloc m evaluation 3 (col-1) 0 (lig-4) 1 (-1) ;
(* diagonales partant de la derniere colonne ( gauche) *)
eval_bloc m evaluation 1 (lig-4) 3 (col-1) 1 (-1) ;
!evaluation
with Quatre v -> v

let est_feuille b m = let v = evaluer b m
in v=plusI or v=moinsI or coups_legaux b m = []

let est_stable b j = true

type etat = G | P | N | C

let etat_de joueur m =
let v = evaluer joueur m
in if v = plusI then if joueur then G else P
else if v = moinsI then if joueur then P else G
else if coups_legaux joueur m = [] then N else C
end ;;


Le module P4_eval est compatible avec les contraintes de la signature EVAL

# module P4_eval_T = (P4_eval : EVAL) ;;
module P4_eval_T : EVAL


Pour faire jouer deux fonctions d'valuation l'une contre l'autre, il faut modifier la fonction evaluer en aiguillant chaque joueur sur sa fonction d'valuation propre.

Assemblage des modules
Toutes les briques ncessaires la ralisation du jeu puissance 4 sont maintenant ralises. Il ne reste plus qu' les assembler selon le schma de la figure 17.4. On construit tout d'abord le squelette P4_Squelette qui est l'application du module paramtr FSquelette aux modules P4_rep, P4_text, P4_eval et au rsultat de l'application du module paramtr FAlphabeta sur P4_rep et P4_eval.

# module P4_squelette = 
FSquelette (P4_rep) (P4_text) (P4_eval) (FAlphabeta (P4_rep) (P4_eval)) ;;
module P4_squelette :
sig
val prof : int ref
exception Gagne
exception Perd
exception Nul
val gagne : unit -> unit
val perd : unit -> unit
val nul : unit -> unit
val encore : unit -> bool
val le_jeu : P4_text.jeu ref
val fin : unit -> unit
val accueil : unit -> unit
val tourH : bool -> unit -> unit
val tourM : bool -> unit -> unit
val init : unit -> (unit -> unit) * (unit -> unit)
end


On obtient alors le module principal P4_main de l'application du module paramtr FMain sur le rsultat de l'application prcdente P4_squelette.

# module P4_main = FMain(P4_squelette) ;;
module P4_main :
sig
val ca_joue : (unit -> 'a) * (unit -> 'b) -> unit
val main : unit -> unit
end
Le lancement du jeu s'effectue par l'application de la fonction P4_main.main sur ().

Test du jeu
Tel que le squelette gnral des jeux a t crit, il est possible de faire jouer deux personnes entre elles (le programme ne sert que d'arbitre), une personne contre le programme ou le programme contre lui-mme. Bien que cette dernire possibilit ne prsente que peu d'intrt pour un joueur, elle permet de lancer des tests plus facilement sans devoir attendre la rponse d'un joueur. Le jeu suivant choisit cette option.
# P4_main.main ();;
P4 ...
Est-ce une machine qui joue ? o/n ? o
Est-ce une machine qui joue ? o/n ? o
X: 1er joueur   O: 2eme joueur

. . . . . . . 
. . . . . . . 
. . . . . . . 
. . . . . . . 
. . . . . . . 
. . . . . . . 
Une fois la position initiale affiche, le joueur 1 (jou par le programme) calcule son coup qui est ensuite affich.
. . . . . . . 
. . . . . . . 
. . . . . . . 
. . . . . . . 
. . . . . . . 
. . . . . X . 
Le joueur 2 (toujours jou par le programme) calcule son propre coup et le jeu s'affiche. Le jeu se poursuit ainsi de suite jusqu' une position de fin de partie. Dans le cas prsent le joueur 1 gagne la partie sur la position finale suivante :
. O O O . O . 
. X X X . X . 
X O O X . O . 
X X X O . X . 
X O O X X O . 
X O O O X X O 
Le 1er joueur gagne
Encore une partie o/n ? n
A bientot ... 
- : unit = ()
Interface graphique
Comme le plaisir de jouer provient aussi de l'interface graphique du programme, on dfinit un nouveau module P4_graph compatible avec la signature AFFICHAGE qui ouvre une fentre graphique et gre les clics souris. On retrouve le texte de ce module dans le sous-catalogue Applications du CD-ROM (voir page ??).

On cre alors un nouveau squelette (P4_squeletteG) qui rsulte de l'application du module paramtr FSquelette.

# module P4_squeletteG =
FSquelette (P4_rep) (P4_graph) (P4_eval) (FAlphabeta (P4_rep) (P4_eval)) ;;


Seul le paramtre du module d'affichage diffre de la version texte dans l'application du module paramtr FSquelette. On cre alors le module principal de l'application Puissance 4 avec interface graphique.

# module P4_mainG = FMain(P4_squeletteG) ;;
module P4_mainG :
sig
val ca_joue : (unit -> 'a) * (unit -> 'b) -> unit
val main : unit -> unit
end


L'valuation de l'expression P4.mainG.main() affiche la fentre graphique de la figure 17.5 et gre l'interaction avec le joueur.

Stone Henge

Le jeu Stone Henge, d Reiner Knizia, est un jeu de construction de lignes de force. Les rgles sont simples apprendre, mais l'intrt du jeu reste grand mme aprs de nombreuses parties. La rgle en ligne peut tre consulte au lien suivant :

Lien


http://www.gamecabinet.com/frenchRules/Stonehenge.html
La position initiale du jeu est reprsente la figure 17.6.

Prsentation du jeu

Le but du jeu est de gagner au moins 8 lignes de force (lignes claires) sur les 15 existantes. Le gain d'une ligne est visualis par la pose d'un menhir (ou mgalithe) sur une case gris fonc associe une ligne de force.



Figure 17.6 : Position initiale de Stone Henge


son tour chaque joueur pose une pierre (sur les 9 qu'il possde au dbut) numrote (de 1 6) sur une des 18 cases centrales grises. On ne peut pas poser une pierre sur une case occupe. Une fois une pierre pose, une ou plusieurs lignes de force peuvent tre perdues ou gagnes. Une ligne de force est gagne par un joueur si l'autre joueur ne peut pas dpasser le score (total des pierres dj poses d'une couleur) du premier, mme en remplissant les cases encore vides de cette ligne par ses meilleures pierres. Par exemple, la figure 17.7, le joueur noir commence par poser la pierre de valeur 3, le joueur rouge sa pierre 2, puis le joueur noir joue sa pierre 6 et gagne une ligne. Le joueur rouge pose alors sa pierre 4 et gagne aussi une ligne de force. Celle-ci n'est pas encore compltement remplie, mais le joueur noir ne pourra jamais dpasser le score de son adversaire. On peut noter que le joueur rouge aurait pu jouer une pierre 3 la place de sa pierre 4. En effet il reste une seule case libre sur cette ligne de force et la pierre la plus forte de noir vaut 5, donc noir ne peut plus battre rouge sur cette ligne.



Figure 17.7 : Position aprs 4 coups


Dans un cas d'galit sur une ligne pleine, celui qui a pos la dernire pierre et qui n'a pas pu battre le score de son adversaire perd cette ligne. La figure 17.8 montre un tel cas. Le dernier coup de rouge est la pierre 4. Sur la ligne pleine o le 4 est pos il y a galit. Comme rouge est le dernier joueur a y avoir pos une pierre, il n'a pas pu battre son adversaire et perd la ligne, d'o le menhir noir qui indique le gain de noir. On s'aperoit que la fonction jouer qui effectue le rle d'arbitre doit tenir compte de ces subtilits de pose de menhirs.



Figure 17.8 : Position aprs 6 coups


Il n'y a jamais de partie nulle ce jeu. Il y a 15 lignes et toutes sont prises un moment de la partie, donc un des deux joueurs atteint le gain d'au moins 8 lignes.

Complexit de la recherche

Avant toute implantation d'un nouveau jeu, il est ncessaire de calculer le nombre de coups lgaux entre deux tours de jeu ainsi que le nombre de coups d'une partie. On dtermine alors la profondeur raisonnable de l'algorithme minimax-ab. Dans le jeu StoneHenge le nombre de coups d'une partie est born par le nombre de jetons des deux joueurs, c'est--dire 18. Le nombre de coups possibles diminue au fur et mesure de la partie. Au premier coup de la partie, le joueur a 6 jetons diffrents et 18 cases libres, ce qui lui fait 108 coups lgaux. Au deuxime coup, le deuxime joueur a aussi 6 jetons diffrents pour 17 cases libre (102 coups lgaux). Le fait de passer d'une profondeur de 2 4 pour les premiers coups de la partie fait passer d'environ 104 coups 108 coups. Par contre en fin de partie, disons sur les 8 derniers coups, la complexit se rduit fortement. Si on calcule le pire des cas (tous les jetons sont diffrents) on obtient environ 23 millions de possibilits :

4*8 * 4*7 * 3*6 * 3*5 * 2*4 * 2*3 * 1*2 * 1*1 = 23224320

Il semble dlicat de calculer au del d'une profondeur 2 pour les premiers coups. Cela dpend de la fonction d'valuation, et de sa capacit valuer les positions de dbut de partie, du moins tant qu'il y a peu de menhirs poss. Par contre en fin de partie la profondeur peut augmenter 4 ou 6, mais cela est probablement trop tard pour rcuprer une position affaiblie.

Implantation

La ralisation de ce jeu suit l'organisation utilise pour le jeu Puissance 4 et dcrite la figure 17.4. Les deux principales difficults viennent du respect des rgles du jeu pour la pose des menhirs et de la fonction d'valuation qui doit pouvoir valuer une situation le plus rapidement possible tout en tant pertinente.

On dcrit tout d'abord la reprsentation du jeu et l'arbitre de jeu, pour s'intresser par la suite la fonction d'valuation.

Reprsentation du jeu
Il y a quatre donnes particulires pour ce jeu :
  • les pierres (ou jetons) des joueurs (type pierre)
  • les menhirs (type menhir)
  • les 15 lignes de force
  • les 18 cases o poser les pierres
On donne un numro unique chaque case.
              1---2
             / \ / \
           3---4---5
          / \ / \ / \
         6---7---8---9
        / \ / \ / \ / \
       10--11--12--13--14
        \ / \ / \ / \ /
         15--16--17--18 
Par chaque case passe 3 lignes de force. On numrote aussi chaque ligne de force. Cette description se retrouve dans la dclaration de la liste lignes, que l'on convertit en vecteur (vecteur_l). Une case est soit vide, soit contient la pierre pose et son propritaire. On conserve d'autre part pour chaque case le numro des lignes qui y passent. Ce tableau est calcul par la fonction lignes_par_case et est nomm num_ligne_par_case.

Le jeu est reprsent par le vecteur des 18 cases, le vecteur des 15 lignes de force contenant ou non un menhir, et des listes de jetons pour les deux joueurs. La fonction jeu_depart cre ces quatre lments. Le calcul des coups lgaux pour un joueur revient un produit cartsien des jetons restants avec les cases libres. Plusieurs fonctions utilitaires permettent de compter le score d'un joueur dans une ligne, de calculer le nombre de cases libres d'une ligne et de vrifier si une ligne a dj un menhir. Il ne reste plus qu' crire la fonction jouer qui joue un coup et dcide des menhirs poser. Nous la dcrivons la fin du listing suivant du module Stone_rep.

# module Stone_rep = struct
type joueur = bool
type pierre = P of int
let int_of_pierre = function P x -> x

type menhir = Rien | M of joueur

type case = Vide | Occup of joueur*pierre
let value_on_case = function
Vide -> 0
| Occup (j, x) -> int_of_pierre x

type jeu = J of case array * menhir array * pierre list * pierre list
type coup = int * pierre

let lignes = [
[0;1]; [2;3;4]; [5; 6; 7; 8;]; [9; 10; 11; 12; 13]; [14; 15; 16; 17];
[0; 2; 5; 9]; [1; 3; 6; 10; 14]; [4; 7; 11; 15]; [8; 12; 16]; [13; 17];
[9; 14]; [5; 10; 15]; [2; 6; 11; 16]; [0; 3; 7; 12; 17]; [1; 4; 8; 13] ]

let vecteur_l = Array.of_list lignes

let lignes_par_case v =
let t = Array.length v in
let r = Array.create 18 [||] in
for i = 0 to 17 do
let w = Array.create 3 0
and p = ref 0 in
for j=0 to t-1 do if List.mem i v.(j) then (w.(!p) <- j; incr p)
done;
r.(i) <- w
done;
r

let num_ligne_par_case = lignes_par_case vecteur_l
let rec lignes_de_i i l = List.filter (fun t -> List.mem i t) l

let lignes_des_cases l =
let a = Array.create 18 l in
for i=0 to 17 do
a.(i) <- (lignes_de_i i l)
done; a

let ldc = lignes_des_cases lignes

let jeu_depart ()= let lp = [6; 5; 4; 3; 3; 2; 2; 1; 1] in
J ( Array.create 18 Vide, Array.create 15 Rien,
List.map (fun x -> P x) lp, List.map (fun x -> P x) lp )

let rec unicite l = match l with
[] -> []
| h::t -> if List.mem h t then unicite t else h:: (unicite t)

let coups_legaux joueur (J (ca, m, r1, r2)) =
let r = if joueur then r1 else r2 in
if r = [] then []
else
let l = ref [] in
for i = 0 to 17 do
if value_on_case ca.(i) = 0 then l:= i:: !l
done;
let l2 = List.map (fun x->
List.map (fun y-> x,y) (List.rev(unicite r)) ) !l in
List.flatten l2
let copie_plateau p = Array.copy p

let copie_carnac m = Array.copy m
let rec joue_pierre caillou l = match l with
[] -> []
| x::q -> if x=caillou then q
else x::(joue_pierre caillou q)

let compte_case joueur case = match case with
Vide -> 0
| Occup (j,p) -> if j = joueur then (int_of_pierre p) else 0

let compte_ligne joueur ligne pos =
List.fold_left (fun x y -> x + compte_case joueur pos.(y)) 0 ligne

let rec compte_max n = function
[] -> 0
| t::q ->
if (n>0) then
(int_of_pierre t) + compte_max (n-1) q
else
0

let rec nbr_cases_libres ca l = match l with
[] -> 0
| t::q -> let c = ca.(t) in
match c with
Vide -> 1 + nbr_cases_libres ca q
|_ -> nbr_cases_libres ca q

let a_menhir i ma =
match ma.(i) with
Rien -> false
| _ -> true

let quel_menhir i ma =
match ma.(i) with
Rien -> failwith "quel_menhir"
| M j -> j

let est_pleine l ca = nbr_cases_libres ca l = 0

(* fonction jouer : arbitre du jeu *)
let jouer joueur coup jeu =
let (c, i) = coup in
let J (p, m, r1, r2) = jeu in
let nr1,nr2 = if joueur then joue_pierre i r1,r2
else r1, joue_pierre i r2 in
let np = copie_plateau p in
let nm = copie_carnac m in
np.(c)<-Occup(joueur,i); (* on joue le coup *)
let lignes_de_la_case = num_ligne_par_case.(c) in

(* calcul des menhirs des 3 lignes de la case *)
for k=0 to 2 do
let l = lignes_de_la_case.(k) in
if not (a_menhir l nm) then (
if est_pleine vecteur_l.(l) np then (
let c1 = compte_ligne joueur vecteur_l.(l) np
and c2 = compte_ligne (not joueur) vecteur_l.(l) np in
if (c1 > c2) then nm.(l) <- M joueur
else ( if c2 > c1 then nm.(l) <- M (not joueur)
else nm.(l) <- M (not joueur ))))
done;

(* calcul des autres menhirs *)
for k=0 to 14 do
if not (a_menhir k nm ) then
if est_pleine vecteur_l.(k) np then failwith "joueur"
else
let c1 = compte_ligne joueur vecteur_l.(k) np
and c2 = compte_ligne (not joueur) vecteur_l.(k) np in
let cases_libres = nbr_cases_libres np vecteur_l.(k) in
let max1 = compte_max cases_libres
(if joueur then nr1 else nr2)
and max2 = compte_max cases_libres
(if joueur then nr2 else nr1) in
if c1 >= c2 + max2 then nm.(k) <- M joueur
else if c2 >= c1 + max1 then nm.(k) <- M (not joueur)
done;
J(np,nm,nr1,nr2)
end;;


La fonction jouer se dcoupe en 3 tapes :
  1. la recopie du jeu et la pose du coup;
  2. la dtermination de la pose d'un menhir sur une des trois lignes de la case joue;
  3. le traitement des autres lignes de force.
La deuxime tape vrifie pour chacune des 3 lignes qui passent par cette case si elle n'a pas dj un menhir, puis si elle est pleine. Dans ce cas elle fait le compte pour chaque joueur et dtermine lequel est le plus grand strictement et attribue le menhir ce joueur l. En cas d'galit, la ligne va au joueur adverse de celui qui vient de jouer. En effet, il n'existe pas de ligne avec une seule case. Une ligne pleine a au moins deux pierres. Donc le joueur qui vient de jouer a juste galis le score de son adversaire. Il ne peut pas prtendre gagner cette ligne qui va son adversaire. Si la ligne traite n'est pas pleine, elle sera analyse par l'tape 3.

La troisime tape vrifie pour chaque ligne non encore attribue, ce qui indique par l que la ligne n'est pas pleine, si un joueur ne peut plus tre battu par son adversaire. Dans ce cas, la ligne lui est immdiatement donne. Pour raliser ce test, il est ncessaire de calculer le score maximal potentiel d'un joueur sur une ligne (c'est dire en utilisant ses meilleurs pierres). Si la ligne est encore en discussion rien ne se passe.

valuation
La fonction d'valuation doit rester simple devant le nombre de cas traiter en dbut de jeu. L'ide est de ne pas trop simplifier le jeu en jouant immdiatement ses pierres les plus fortes, ce qui laisserait le champ libre l'adversaire. Deux critres sont utiliss : le nombre de menhirs gagns et la potentialit des futurs coups en calculant la valeur des pierres restantes. On utilise la formule suivante pour le joueur 1 :
score   =   50 * (c1 - c2) + 10 * (pr1 - pr2)
o ci est le nombre de menhirs et pri la somme des pierres restantes du joueur i. La formule retourne un rsultat positif si les diffrences des menhirs (c1 - c2) et des potentialits (pr1 - pr2) sont l'avantage du joueur 1. On s'aperoit alors que la pose de la pierre 6 n'est effectue que si elle rapporte au moins deux lignes. Le gain d'une ligne donne 50, mais le fait de jouer 6 fait perdre 10*6 points, on prfre alors jouer 1 qui calcule le mme score (perte de 10 points).

# module Stone_eval = struct
open Stone_rep
type jeu = Stone_rep.jeu

exception Fini of bool
let plusI = 1000 and moinsI = -1000

let nbr_lignes_gagnees (J(ca, m,r1,r2)) =
let c1,c2 = ref 0,ref 0 in
for i=0 to 14 do
if a_menhir i m then if quel_menhir i m then incr c1 else incr c2
done;
!c1,!c2

let rec nbr_points_restant lig = match lig with
[] -> 0
| t::q -> (int_of_pierre t) + nbr_points_restant q

let evaluer joueur jeu =
let (J (ca,ma,r1,r2)) = jeu in
let c1,c2 = nbr_lignes_gagnees jeu in
let pr1,pr2 = nbr_points_restant r1, nbr_points_restant r2 in
match joueur with
true -> if c1 > 7 then plusI else 50 * (c1 - c2) + 10 * (pr1 - pr2)
| false -> if c2 > 7 then moinsI else 50 * (c1 - c2) + 10 * (pr1 - pr2)

let est_feuille joueur jeu =
let v = evaluer joueur jeu in
v = plusI or v = moinsI or coups_legaux joueur jeu = []

let est_stable joueur jeu = true

type etat = G | P | N | C
let etat_de joueur m =
let v = evaluer joueur m in
if v = plusI then if joueur then G else P
else
if v = moinsI
then if joueur then P else G
else
if coups_legaux joueur m = [] then N else C
end;;


Assemblage
On crit par ailleurs le module Stone_graph qui dcrit une interface graphique compatible avec la signature AFFICHAGE. On construit Stone_squeletteG la manire de P4_squeletteG en passant les arguments propres au jeu Stone Henge l'application du module paramtre FSquelette.

# module Stone_squeletteG = FSquelette (Stone_rep)
(Stone_graph)
(Stone_eval)
(FAlphabeta (Stone_rep) (Stone_eval)) ;;
module Stone_squeletteG :
sig
val prof : int ref
exception Gagne
exception Perd
exception Nul
val gagne : unit -> unit
val perd : unit -> unit
val nul : unit -> unit
val encore : unit -> bool
val le_jeu : Stone_graph.jeu ref
val fin : unit -> unit
val accueil : unit -> unit
val tourH : bool -> unit -> unit
val tourM : bool -> unit -> unit
val init : unit -> (unit -> unit) * (unit -> unit)
end


On construit alors le module principal Stone_mainG.

# module Stone_mainG = FMain(Stone_squeletteG);;
module Stone_mainG :
sig
val ca_joue : (unit -> 'a) * (unit -> 'b) -> unit
val main : unit -> unit
end


Le lancement de Stone_mainG.main () ouvre la fentre de la figure 17.6. Aprs le dialogue pour savoir qui joue, la partie commence. Un joueur humain slectionne d'abord la pierre jouer puis la case o la poser.

Pour en faire plus

L'organisation de ces applications utilisant plusieurs modules paramtrs permet de rutiliser pleinement le module FAlphabeta et le module FSquelette pour les deux jeux que nous avons dcrits. Nanmoins pour le jeu Stone Henge certaines fonctions du module Stone_rep, ncessaires pour la fonction jouer, qui n'apparaissent pas dans la signature REPRESENTATION ont t utilises par la fonction d'valuation. C'est pour cela que le module Stone_rep n'a pas t immdiatement ferm par la signature REPRESENTATION. Cette ouverture entre modules pour la partie spcifique des jeux permet un dveloppement plus incrmentiel sans fragiliser le schma de dpendances des modules prsent la figure 17.4.

Une premire amlioration concerne les jeux o d'une position et d'un coup il est facile de dterminer la position prcdente. Dans ces cas l, il peut tre plus efficace de ne pas effectuer une copie du jeu par la fonction jouer, mais de conserver un historique des coups jous pour pouvoir revenir en arrire (backtraking). C'est le cas de Puissance 4 mais pas de Stone Henge.

Une deuxime amlioration est de profiter du temps d'attente de la rponse d'un joueur pour commencer l'valuation des futures positions. Pour cela on peut utiliser des processus lgers (voir chapitre 19) qui lancent le calcul pendant cette attente. Si la rponse fait partie du dbut de l'exploration des positions, alors le gain de temps est immdiat, sinon on repart avec la nouvelle position.

Une troisime amlioration est de constituer et d'exploiter des bibliothques d'ouvertures. Nous avons pu constater avec Stone Henge, mais c'est aussi le cas pour beaucoup d'autres jeux, que l'ventail des possibilits et donc le nombre des coups lgaux explorer est plus important en dbut de partie. Il y a donc tout gagner prcalculer les meilleurs coups des positions de dpart et les sauver dans un fichier. On peut enrichir l'intrt du jeu, en faisant intervenir le hasard pour choisir parmi un certain nombre de variantes menant des positions dont les valeurs sont proches.

Une quatrime voie est de ne pas borner la profondeur de la recherche par une valeur fixe, mais par un temps de calcul ne pas dpasser. De la sorte, le programme devient plus efficace en fin de partie quand l'ventail des choix se retrouve limit. Cette modification demande de modifier quelque peu l'implantation de minmax afin de pouvoir reprendre un arbre pour augmenter sa profondeur. Une heuristique dpendant du jeu, donc paramtre de minmax pourrait choisir quelles sont les branches dont l'exploration doit tre pousse et quelles sont celles qui peuvent tre abandonnes.

Il existe d'autre part de nombreux autres jeux qui ne demandent qu' tre implants ou rimplants. On peut citer plusieurs classiques : Dames, Othello, Abalone, ..., mais aussi de nombreux jeux moins connus et nanmoins fort jouables. On trouve au lien suivant quelques projets d'tudiants comme le jeu de Dames ou le jeu Nuba.

Lien


http://www.pps.jussieu.fr/~emmanuel/Public/enseignement/


Les jeux avec tirage alatoire (pioche des jeux de cartes, lancement de ds) ncessitent une modification de l'algorithme minimax-ab pour tenir compte de la probabilit de ce tirage.

Nous reviendrons sur les interfaces des jeux au chapitre 21 en construisant des interfaces pour les navigateurs WWW apportant sans frais la fonction retour au dernier coup. On apprciera d'autant plus l'organisation par module, qui permet de ne modifier qu'un lment, ici l'affichage et l'interaction, d'une application de jeux 2 joueurs.




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora177.html0000644000000000000000000010666307421273602014513 0ustar Synchronisation des processus Prcdent Index Suivant

Synchronisation des processus

C'est dans le cadre de processus partageant une mme zone mmoire que le mot << concurrence >> prend tout son sens : les divers processus impliqus sont en concurrence pour l'accs cette unique ressource qu'est la mmoire2. Au problme du partage des ressources vient s'ajouter celui du manque de matrise de l'alternance et du temps d'excution des processus concurrents.

Le systme qui gre l'ensemble des processus peut tout moment interrompre un calcul en cours. Ainsi lorsque deux processus cooprent, ils doivent pouvoir garantir l'intgrit des manipulations de certaines donnes partages. Pour cela, un processus doit pouvoir rester matre de ces donnes tant qu'il n'a pas achev un calcul ou toute autre opration (par exemple, une acquisition sur un priphrique). Pour garantir l'exclusivit d'accs aux donnes un seul processus, on met en oeuvre un mcanisme dit d'exclusion mutuelle.

Section critique et exclusion mutuelle

Les mcanismes d'exclusion mutuelle sont implants l'aide de donnes particulires appeles verrous. Les oprations sur les verrous sont limites leur cration, leur pose et leur libration. Un verrou est la plus petite donne partage par un ensemble de processus concurrents. Sa manipulation est toujours exclusive. la notion d'exclusivit de manipulation d'un verrou s'ajoute celle d'exclusivit de dtention : seul le processus qui a pris un verrou peut le librer ; si d'autres processus veulent leur tour disposer de ce verrou, ils doivent en attendre la libration par le processus dtenteur.

Module Mutex

Le module Mutex est utilis pour crer des verrous entre processus en relation d'exclusion mutuelle sur une zone mmoire. Nous allons illustrer leur utilisation par deux petits exemples classiques de concurrence.

Les fonctions de cration, prise et libration des verrous sont :

# Mutex.create ;;
- : unit -> Mutex.t = <fun>
# Mutex.lock ;;
- : Mutex.t -> unit = <fun>
# Mutex.unlock ;;
- : Mutex.t -> unit = <fun>


Il existe une variante la prise de verrou qui n'est pas bloquante :

# Mutex.try_lock;;
- : Mutex.t -> bool = <fun>
Si le verrou est dj pris, la fonction retourne false. Dans l'autre cas, la fonction prend le verrou et retourne true.

Les philosophes orientaux

Cette petite histoire, due Dijkstra, illustre un pur problme de partage de ressources. Elle se raconte ainsi : << Cinq philosophes orientaux partagent leur temps entre l'tude et la venue au rfectoire pour manger un bol de riz. La salle affecte la sustentation des philosophes ne comprend qu'une seule table ronde sur laquelle sont disposs un grand plat de riz (toujours plein) cinq bols et cinq baguettes. >>




Figure 19.1 : la table des philosophes orientaux


Comme on le voit sur la figure 19.1, un philosophe qui prend les deux baguettes autour de son bol empche ses voisins d'en faire autant. Ds qu'il dpose l'une de ses baguettes, son voisin, affam, peut s'en emparer. Le cas chant, ce dernier devra attendre que l'autre baguette soit disponible. Les baguettes sont ici les ressources partages.

Pour simplifier les choses, on supposera que chaque philosophe a des habitudes et vient toujours la mme place. On modlise les cinq baguettes par autant de verrous stocks dans un vecteur b.

# let b =
let b0 = Array.create 5 (Mutex.create()) in
for i=1 to 4 do b0.(i) <- Mutex.create() done;
b0 ;;
val b : Mutex.t array = [|<abstr>; <abstr>; <abstr>; <abstr>; <abstr>|]


Manger et mditer sont simuls par une suspension des processus.

# let mediter = Thread.delay
and manger = Thread.delay ;;
val mediter : float -> unit = <fun>
val manger : float -> unit = <fun>


On modlise un philosophe par une fonction qui excute l'infini la squence des actions de la petite histoire de Dijkstra. La prise d'une baguette est simule par l'obtention d'un verrou, ainsi un seul philosophe la fois peut dtenir une baguette. On introduit un petit temps de rflexion entre la prise et le dpt de chacune des deux baguettes ainsi que quelques affichages traant l'activit du philosophe.

# let philosophe i =
let ii = (i+1) mod 5
in while true do
mediter 3. ;
Mutex.lock b.(i);
Printf.printf "Le philosophe (%d) prend sa baguette gauche" i ;
Printf.printf " et mdite encore un peu\n";
mediter 0.2;
Mutex.lock b.(ii);
Printf.printf "Le philosophe (%d) prend sa baguette droite\n" i;
manger 0.5;
Mutex.unlock b.(i);
Printf.printf "Le philosophe (%d) repose sa baguette gauche" i;
Printf.printf " et commence dj mditer\n";
mediter 0.15;
Mutex.unlock b.(ii);
Printf.printf "Le philosophe (%d) repose sa baguette droite\n" i
done ;;
val philosophe : int -> unit = <fun>


On pourra tester ce petit programme en excutant :

for i=0 to 4 do ignore (Thread.create philosophe i) done ;
while true do Thread.delay 5. done ;;


On suspend, dans la boucle infinie while, le processus principal de faon augmenter les chances des processus philosophes d'entrer en action. On utilise dans la boucle d'activit des philosophes des dlais d'attente dont la dure est choisie alatoirement dans le but de crer un peu de disparit dans l'excution parallle des processus.

Problmes de la solution nave
Il peut arriver une chose terrible nos philosophes : ils arrivent tous en mme temps et se saisissent de leur baguette gauche. Dans ce cas nous sommes dans une situation d'inter-blocage. Plus aucun philosophe ne pourra manger ! Nous sommes dans un cas de famine.

Pour viter cela, les philosophes peuvent reposer une baguette s'il n'arrivent pas prendre la deuxime. Cela est fort courtois, mais permet deux philosophes de se liguer contre un troisime pour l'empcher de se sustenter en ne relchant les baguettes que si l'autre voisin les a prises. Il existe diverses solutions ce problme. L'une d'elle fait l'objet de l'exercice page ??.

Producteurs et consommateurs I

Le couple producteurs-consommateurs est un exemple classique de la programmation concurrente. Un groupe de processus, dsigns comme les producteurs, est charg d'emmagasiner des donnes dans une file d'attente ; un second groupe, les consommateurs, est charg de les dstocker. Chaque intervenant exclut les autres.

Nous implantons ce schma en utilisant une file d'attente partage entre les producteurs et les consommateurs. Pour garantir le bon fonctionnement de l'ensemble, la file d'attente est manipule en exclusion mutuelle afin de garantir l'intgrit des oprations d'ajout et de retrait.

f est la file d'attente partage, et m  le verrou d'exclusion mutuelle.

# let f = Queue.create () and m = Mutex.create () ;;
val f : '_a Queue.t = <abstr>
val m : Mutex.t = <abstr>


Nous divisons l'activit d'un producteur en deux parties : crer un produit (fonction produire) et stocker un produit (fonction stocker). Seule l'opration de stockage a besoin du verrou.

# let produire i p d =
incr p ;
Thread.delay d ;
Printf.printf "Le producteur (%d) a produit %d\n" i !p ;
flush stdout ;;
val produire : int -> int ref -> float -> unit = <fun>

# let stocker i p =
Mutex.lock m ;
Queue.add (i,!p) f ;
Printf.printf "Le producteur (%d) a ajout son %d-ime produit\n" i !p ;
flush stdout ;
Mutex.unlock m ;;
val stocker : int -> int ref -> unit = <fun>


Le code du producteur est une boucle sans fin de cration et de stockage. Nous introduisons un dlai alatoire d'attente la fin de chaque itration afin d'obtenir un effet de dsynchronisation dans l'excution.

# let producteur i =
let p = ref 0 and d = Random.float 2.
in while true do
produire i p d ;
stocker i p ;
Thread.delay (Random.float 2.5)
done ;;
val producteur : int -> unit = <fun>


La seule opration du consommateur est le retrait d'un lment de la file en prenant garde l'existence effective d'un produit.

# let consommateur i =
while true do
Mutex.lock m ;
( try
let ip, p = Queue.take f
in Printf.printf "Le consommateur(%d) " i ;
Printf.printf "a retir le produit (%d,%d)\n" ip p ;
flush stdout ;
with
Queue.Empty ->
Printf.printf "Le consommateur(%d) " i ;
print_string "est reparti les mains vides\n" ) ;
Mutex.unlock m ;
Thread.delay (Random.float 2.5)
done ;;
val consommateur : int -> unit = <fun>


Le programme de test suivant cre quatre producteurs et quatre consommateurs.

for i = 0 to 3 do
ignore (Thread.create producteur i);
ignore (Thread.create consommateur i)
done ;
while true do Thread.delay 5. done ;;


Attente et synchronisation

La relation d'exclusion mutuelle n'est pas assez fine pour dcrire la synchronisation entre processus. Il n'est pas rare que le travail d'un processus dpende de la ralisation d'une action par un autre processus, modifiant par l une certaine condition. Il est alors souhaitable que les processus puissent communiquer le fait que cette condition a pu changer, indiquant aux processus en attente de la tester de nouveau. Les diffrents processus sont alors en relation d'exclusion mutuelle avec communication.

Dans l'exemple prcdent, un consommateur, plutt que repartir les mains vides pourrait attendre qu'un producteur vienne rapprovisionner le stock. Ce dernier pourra signaler au consommateur en attente qu'il y a quelque chose emporter. Le modle de l'attente sur condition d'une prise de verrou est connu sous le nom de smaphore.

Smaphores
Un smaphore est une variable entire s ne pouvant prendre que des valeurs positives (ou nulles). Une fois s initialise, les seules oprations admises sont : wait(s) et signal(s), notes respectivement P(s) et V(s). Elles sont dfinies ainsi, s correspond au nombre de ressources d'un type donn.
  • wait(s) : si s > 0 alors s := s -1, sinon l'excution du processus ayant appel wait(s) est suspendue.
  • signal(s) : si un processus a t suspendu lors d'une excution antrieure d'un wait(s) alors le rveiller, sinon s := s + 1.
Un smaphore ne prenant que les valeurs 0 ou 1 est appel smaphore binaire.

Module Condition

Les fonctions du module Condition implantent des primitives de mise en sommeil et de rveil de processus sur un signal. Un signal, dans ce cadre, est une variable partage par l'ensemble des processus. Son type est abstrait et les fonctions de manipulation sont :
create
: unit -> Condition.t qui cre un nouveau signal.
signal
: Condition.t -> unit qui rveille l'un des processus en attente du signal.
broadcast
: Condition.t -> unit qui rveille l'ensemble des processus en attente du signal.
wait
: Condition.t -> Mutex.t -> unit qui suspend le processus appelant sur le signal pass en premier argument. Le second argument est un verrou utilis pour protger la manipulation du signal. Il est libr, puis repositionn chaque excution de la fonction.

Producteurs et consommateurs (2)

Nous reprenons l'exemple des producteurs et consommateurs en utilisant le mcanisme des variables de condition pour mettre en attente un consommateur arrivant alors que le magasin est vide.

Pour raliser la synchronisation entre attente d'un consommateur et production, on dclare :

# let c = Condition.create () ;;
val c : Condition.t = <abstr>


On modifie la fonction de stockage du producteur en lui ajoutant l'mission d'un signal :

# let stocker2 i p =
Mutex.lock m ;
Queue.add (i,!p) f ;
Printf.printf "Le producteur (%d) a ajoute son %d-ime produit\n" i !p ;
flush stdout ;
Condition.signal c ;
Mutex.unlock m ;;
val stocker2 : int -> int ref -> unit = <fun>
# let producteur2 i =
let p = ref 0 in
let d = Random.float 2.
in while true do
produire i p d;
stocker2 i p;
Thread.delay (Random.float 2.5)
done ;;
val producteur2 : int -> unit = <fun>


L'activit du consommateur se fait alors en deux temps : attendre qu'un produit soit disponible puis emporter le produit. Le verrou est pris quand l'attente prend fin et il est rendu ds que le consommateur a emport son produit. L'attente se fait sur la variable c.

# let attendre2 i =
Mutex.lock m ;
while Queue.length f = 0 do
Printf.printf "Le consommateur (%d) attend\n" i ;
Condition.wait c m
done ;;
val attendre2 : int -> unit = <fun>
# let emporter2 i =
let ip, p = Queue.take f in
Printf.printf "Le consommateur (%d) " i ;
Printf.printf "emporte le produit (%d, %d)\n" ip p ;
flush stdout ;
Mutex.unlock m ;;
val emporter2 : int -> unit = <fun>
# let consommateur2 i =
while true do
attendre2 i;
emporter2 i;
Thread.delay (Random.float 2.5)
done ;;
val consommateur2 : int -> unit = <fun>
Notons qu'il n'est plus besoin de vrifier l'existence effective d'un produit puisqu'un consommateur doit attendre l'existence d'un produit dans la file d'attente pour que sa suspension prenne fin. Vu que la fin de son attente correspond la prise du verrou, il ne court pas le risque de se faire drober le nouveau produit avant de l'avoir emport.

Lecteurs et crivain

Voici un autre exemple classique de processus concurrents dans lequel les agents n'ont pas le mme comportement vis--vis de la donne partage.

Un crivain et des lecteurs oprent sur une donne partage. L'action du premier est susceptible de rendre momentanment les donnes incohrentes alors que les seconds n'ont qu'une action passive. La difficult vient du fait que nous ne souhaitons pas interdire plusieurs lecteurs de consulter la donne simultanment. Une solution ce problme est de grer un compteur dnombrant les lecteurs en train d'accder aux donnes. L'criture ne sera acceptable que si le nombre de lecteurs est nul.

La donne est symbolise par l'entier data qui prendra la valeur 0 ou 1. La valeur 0 signifie que la donne est prte pour la lecture :

# let data = ref 0 ;;
val data : int ref = {contents=0}


Les oprations sur le compteur n sont protges par le verrou m :

# let n = ref 0 ;;
val n : int ref = {contents=0}
# let m = Mutex.create () ;;
val m : Mutex.t = <abstr>
# let cpt_incr () = Mutex.lock m ; incr n ; Mutex.unlock m ;;
val cpt_incr : unit -> unit = <fun>
# let cpt_decr () = Mutex.lock m ; decr n ; Mutex.unlock m ;;
val cpt_decr : unit -> unit = <fun>
# let cpt_signal () = Mutex.lock m ;
if !n=0 then Condition.signal c ;
Mutex.unlock m ;;
val cpt_signal : unit -> unit = <fun>


Les lecteurs mettent jour le compteur et mettent le signal c lorsque plus aucun lecteur n'est prsent. Ils indiquent par l l'crivain qu'il peut entrer en action.

# let c = Condition.create () ;;
val c : Condition.t = <abstr>
# let lire i =
cpt_incr () ;
Printf.printf "Le lecteur (%d) lit (donne=%d)\n" i !data ;
Thread.delay (Random.float 1.5) ;
Printf.printf "Le lecteur (%d) a fini sa lecture\n" i ;
cpt_decr () ;
cpt_signal () ;;
val lire : int -> unit = <fun>

# let lecteur i = while true do lire i ; Thread.delay (Random.float 1.5) done ;;
val lecteur : int -> unit = <fun>


L'crivain tente de bloquer le compteur pour interdire aux lecteurs d'accder la donne partage. Mais il ne peut le faire que si le compteur est nul, sinon il attend le signal lui indiquant que c'est le cas.

# let ecrire () =
Mutex.lock m ;
while !n<>0 do Condition.wait c m done ;
print_string "L'crivain crit\n" ; flush stdout ;
data := 1 ; Thread.delay (Random.float 1.) ; data := 0 ;
Mutex.unlock m ;;
val ecrire : unit -> unit = <fun>

# let ecrivain () =
while true do ecrire () ; Thread.delay (Random.float 1.5) done ;;
val ecrivain : unit -> unit = <fun>


On cre pour tester ces fonctions un crivain et six lecteurs.

ignore (Thread.create ecrivain ());
for i=0 to 5 do ignore(Thread.create lecteur i) done;
while true do Thread.delay 5. done ;;


Cette solution garantit que ni l'crivain ni les lecteurs n'ont accs aux donnes en mme temps. Par contre, rien ne garantit que l'crivain puisse un jour remplir son office, nous sommes confronts l encore un cas de famine.


Prcdent Index Suivant ocaml-book-1.0/fr/html/videoc.js0000644000000000000000000004446307666671600013476 0ustar /* $Id: videoc.js,v 1.1.1.1 1999/12/23 19:07:56 emmanuel Exp $ * The JavaScript runtime library for VideoC. * * FUTURE adapt messages to the preferred language. * Make a hint popup above the nailed hints. * Make a popup with the usage if not used for some duration. */ window.videoc_version = ' $Id: videoc.js,v 1.1.1.1 1999/12/23 19:07:56 emmanuel Exp $ '; // Check if the browser is known and at least at version 4 for Communicator and // IExplorer. The results are stored in the window object. { var ua = window.navigator.userAgent; // document.write("" + ua + ""); // For IE4.0, this gives Mozilla/4.0(compatible; MSIE 4.0;Windows 95) // For Communicator 4 on Windows95: Mozilla/4.02 [en] (Win95; I) // and on linux: Mozilla/4.04 [en] (X11; I; Linux 2.0.30 i586) // for Netscape on DECalpha: Mozilla/3.0 (X11; I; OSF1 V4.0 alpha) var tag = "MSIE "; var uai = ua.indexOf(tag); window.IExplorer = false; window.Mozilla = false; if ( uai > 0 ) { window.IExplorer = true; // This is IExplorer, fetch version number: window.version = parseInt(ua.substring(uai+tag.length, ua.indexOf(".", uai))); // document.write("\n\n IExplorer " + window.version + "\n\n"); // alert("\n\n IExplorer " + window.version + "\n\n"); } else { tag = "ozilla"; uai = ua.indexOf(tag); if ( uai > 0 ) { // This is Mozilla, fetch version number: window.Mozilla = true; window.version = parseInt(ua.substring(uai+tag.length+1, ua.indexOf(".", uai))); // document.write("\n\n Mozilla " + window.version + "\n\n"); // alert("\n\n Mozilla " + window.version + "\n\n"); } else { // Unknown browser alert("Votre butineur (" + ua + ") m'est inconnu. " + "Ce document souffrira de ne pas tre vu avec Communicator " + "ou IExplorer (version >= 4 tous les deux)."); } } } //{{{ // MOCHE // This height must be set up accordingly to the nav bar: var navBarHeight = 130; //}}} //{{{ Common stuff // To inspect an object: confirm(object_to_string(...)) function object_to_string(obj) { var result = ""; for (var i in obj) { result += "[." + i + " = " + obj[i] + "]"; if ( window.Mozilla ) { result += "\n"; } } return result; } // It is possible to store information from page to page within the // browser in the navigator object. All the information is stored in // an object. if ( typeof navigator.VideoC == "undefined" ) { navigator.VideoC = new Object(); }; // This function should be called whenever a page containing a searched // word is displayed and we want the word to be hightlighted. function afterWordSearchSubmission() { alert("pre-ok"); addHook("alert(\"post-ok\")"); } function addHook(expression) { navigator.VideoC.hook = expression; } // This function toggles the state of all the hints of the page. It // chooses at random some sensitive anchors and display their // associated hints. This may be interesting when to make users aware // of that possibility or to awake them. function show_hints_at_random (delay) { if ( delay > 0 ) { // sleep delay seconds } var i = random(document.hints.length); var anchor = document.hints[i][0]; // Create a pseudo event: use a method on hint instead ? var event = new Object; event.type = 'click'; event.target = new Object; event.target.hint = anchor; event.srcElement = event.target; toggleHint(event); } // a simple random function (see Knuth vol2, section 3.2.1.2 ex 1) var seed = 5772156648; function random (n) { var a = 3141592621; var c = 2718281829; var m = 10000000000; seed = (a * seed + c)%m; return seed%n; } // Make a message appear in the status bar. function showMessage (msg) { window.status = msg; } // Make a special message appear in the status bar. This is a // specialized function since a call to it appears in every // mouse-sensitive areas inserted by the annote tool. function ctsm() { showMessage("Click to see more"); } //}}} ------------------------------------------------------------------- //{{{ Netscape versions: // Makes the hint pop up until the mouse leaves the sensitive area. // A click makes the popup pinned down until another click. Show // the usage in the status zone of the window. function nc_toggleHint (e) { // receive the anchor as the target of the event then // retrieve the associated hint. var hint = e.target.hint; //confirm("Received event " + object_to_string(e.target.hint)); // DEBUG // Toggle the state of the hint: if ( hint.visibility == 'hidden' || hint.visibility == 'hide' ) { // Needed by netscape to prevent a right-ragged layer: //hint.bgColor = '#f5f5dc'; if ( ! hint.immotile ) { var hintX = e.layerX/2; var hintY = e.layerY+20; hint.moveTo(hintX, hintY); } hint.visibility = 'show'; window.status = 'Click to nail the hint'; } else { if ( hint.permanent ) { window.status = 'Click to remove the hint'; } else { hint.visibility = 'hidden'; } } // Click means: show/hide the hint permanently: if ( e.type == 'click' ) { hint.permanent = ! hint.permanent; if ( hint.permanent ) { window.status = 'Click to remove the hint'; } else { window.status = 'Removed!'; } } // Don't let these links be followed, they're not real anchors: return true; } // Link the sensitive area (ie the hint) from the anchor. A hint // may be reached from many anchors. function nc_initializeHint(hintId) { var hint = document.layers['l__' + hintId]; if ( hint == null ) return alert("Missing hint " + hintId); hint.visibility == 'hidden'; hint.permanent = false; hint.immotile = false; hint.name = hintId; var matches = 0; for (var i = 0 ; i < document.links.length ; i++ ) { var anchor = document.links[i]; //confirm("anchor[" + i + "]=" + object_to_string(anchor)); // DEBUG if ( anchor.protocol == 'javascript:' && anchor.pathname.indexOf(hintId)>=0 ) { matches++; nc_initializeHintWithAnchor(hint, anchor); }; } if ( matches == 0 ) { alert("No associated mouse-sensitive areas " + hintId); } return hint; } function nc_initializeHintWithAnchor(hint, anchor) { //confirm("hint= " + object_to_string(hint) + // ", anchor= " + object_to_string(anchor)); if ( anchor == null ) return alert("Missing sensitive anchor " + hint.name); anchor.hint = hint; var arr = new Array(anchor); if ( typeof hint.anchors == "undefined" ) { hint.anchors = arr; } else { hint.anchors = hint.anchors.concat(arr); } anchor.onMouseOver = nc_toggleHint; anchor.onMouseOut = nc_toggleHint; anchor.onClick = nc_toggleHint; return hint; } // NOTE: Related, credits and wordsearch appear with different widths // so I set their zIndexes with the smallest on top. // Prepare an already existing layer so it can receive the page // explaining related links (required/suggested). function nc_showRelated(url) { var layer = document.layers["RelatedLayer"]; if ( layer.visibility == "visible" || layer.visibility == "show" ) { //alert("nc_hideRelated"); // DEBUG layer.visibility = "hidden"; return false; } else { var newWidth = 2*getWindowWidth()/3; var xoffset = (getWindowWidth() - newWidth)/2; layer.moveToAbsolute(xoffset, navBarHeight); layer.clip.left = 2; layer.clip.top = 2; layer.clip.width = newWidth-2; if ( ! layer.alreadyLoaded ) { //layer.src = url; layer.load(url, newWidth); layer.zIndex = 2; layer.alreadyLoaded = true; //document.NCwordSearchForm.onSubmit = afterWordSearchSubmission; } layer.visibility = "show"; //alert(object_to_string(layer)); // DEBUG // Preempt the default href: return false; } } // Prepare an already existing layer so it can receive the page // that contains the credits of the current page. function nc_showCredits(url) { var layer = document.layers["CreditsLayer"]; if ( layer.visibility == "visible" || layer.visibility == "show" ) { layer.visibility = "hidden"; return false; } else { var newWidth = getWindowWidth()-40; var xoffset = (getWindowWidth() - newWidth)/2; layer.moveToAbsolute(xoffset, navBarHeight); layer.clip.left = 2; layer.clip.top = 2; layer.clip.width = newWidth-2; if ( ! layer.alreadyLoaded ) { layer.load(url, newWidth); layer.zIndex = 1; layer.alreadyLoaded = true; } layer.visibility = "show"; return false; } } // Prepare a layer to get a word and search for it. function nc_getWordAndSearch() { var layer = document.layers["WordSearchLayer"]; //confirm("nc_getWordAndSearch" + object_to_string(layer)); // DEBUG if ( layer.visibility == "visible" || layer.visibility == "show" ) { layer.visibility = "hidden"; return false; } else { var newWidth = 300; var xoffset = (getWindowWidth() - newWidth)/2; layer.moveToAbsolute(xoffset, navBarHeight); layer.clip.left = 0; layer.clip.top = 0; //layer.clip.width = newWidth; layer.visibility = "show"; layer.zIndex = 3; //alert(object_to_string(layer)); return false; } } //}}} ------------------------------------------------------------------- // {{{ Explorer versions: // Makes the hint pop up until the mouse leaves the sensitive area. // A click makes the popup pinned down until another click. It seems // there is a race condition where hint.style is reported not to be an // object !? function ie_toggleHint () { // receive the anchor as target. var anchor = event.srcElement; while ( anchor && anchor.tagName != "A" ) { anchor = anchor.parentElement; //confirm("event on " + object_to_string(anchor)); // DEBUG } // Don't let these links be followed, they're not real anchors: event.cancelBubble = true; if ( anchor ) { //confirm("event on " + object_to_string(anchor)); // DEBUG var hint = anchor.hint; //confirm("event on " + object_to_string(hint)); // DEBUG //confirm("event on " + object_to_string(hint.style)); // DEBUG // Toggle the state of the hint: if ( hint.style.visibility == 'hidden' || hint.style.visibility == "" ) { if ( ! hint.immotile ) { hint.style.pixelLeft = event.offsetX/2; hint.style.pixelTop = event.offsetY + 20; //confirm("Event is " + object_to_string(event)); // DEBUG }; hint.style.display = "block"; hint.style.visibility = 'visible'; hint.style.zIndex = 5; window.status = 'Click to nail the hint'; } else { if ( hint.permanent ) { window.status = 'Click (at the same place) to remove the hint'; } else { hint.style.visibility = 'hidden'; } } // Click means: show/hide the hint permanently: if ( event.type == 'click' ) { hint.permanent = ! hint.permanent; if ( hint.permanent ) { window.status = 'Click to remove the hint'; } else { window.status = 'Removed!'; } } return false; } else { return false; } } // Link the mouse-sensitive areas to their hint. function ie_initializeHint(hintId) { var hint = document.all('d__' + hintId); if ( hint == null ) alert("Missing DIV " + hintId); hint.permanent = false; hint.immotile = false; hint.name = hintId; //hint.anchors = new Array(); var matches = 0; for (var i = 0 ; i < document.links.length ; i++ ) { var anchor = document.links[i]; // confirm("anchor[" + i + "]=" + object_to_string(anchor)); // DEBUG if ( anchor.id.indexOf(hintId)>=0 ) { //confirm("got anchor " + hintId); // DEBUG matches++; ie_initializeHintWithAnchor(hint, anchor); }; } if ( matches == 0 ) { alert("No mouse-sensitive anchors for " + hintId); } return hint; } function ie_initializeHintWithAnchor(hint, anchor) { //confirm("hint= " + object_to_string(hint) + // ", anchor= " + object_to_string(anchor)); if ( anchor == null ) alert("Missing ANCHOR " + hint.name); anchor.hint = hint; var arr = new Array(anchor); //KO: the next instruction makes IExplorer abort!? //KO: hint.anchors = hint.anchors.concat(arr); //confirm("hint= " + object_to_string(hint)); // DEBUG // Attention, don't write onMouseOver but onmouseover instead! anchor.onmouseover = ie_toggleHint; anchor.onmouseout = ie_toggleHint; anchor.onclick = ie_toggleHint; hint.className = 'hint'; hint.style.display = 'none'; return hint; } function ie_showRelated(url) { var newStyle = "status,resizable,scrollbars,height=" + (getWindowHeight()*2/3) + ",width=" + (getWindowWidth()*3/4); var newWindow = window.open(url, "relatedWindow", newStyle); newWindow.focus(); } function ie_showCredits(url) { var newStyle = "status,resizable,scrollbars,height=" + (getWindowHeight()/2) + ",width=" + (getWindowWidth()*2/3); var newWindow = window.open(url, "creditsWindow", newStyle); newWindow.focus(); } function ie_getWordAndSearch() { var frame = document.all.WordSearchFrame; //confirm(object_to_string(frame)); // DEBUG if ( event ) { event.returnValue = false; } if ( frame.style.display != "none" ) { frame.style.display = "none"; return false; } else { // Center the button: var width = document.wordSearchForm.children[0].clientWidth; var xoffset = (getWindowWidth() - width)/2; frame.style.left = xoffset; frame.style.top = navBarHeight; frame.style.width = Math.max(width, 200); frame.style.display = "block"; frame.className = "wordSearch"; frame.zIndex = 3; return false; } } // }}} ------------------------------------------------------------------- // {{{ Final versions (independent of the browser): // These untested functions propose to leave hooks in the persistent part // of the runtime of javascript (persistent means visible from page to // page). A page may leave a function to be run by the next page to load. function runPreHook () { if ( (typeof navigator.VideoC != "undefined") && (typeof navigator.VideoC.preHook != "undefined") ) { eval(navigator.VideoC.preHook); delete navigator.VideoC.preHook; }; } function runPostHook () { if ( (typeof navigator.VideoC != "undefined") && (typeof navigator.VideoC.postHook != "undefined") ) { eval(navigator.VideoC.postHook); delete navigator.VideoC.postHook; }; } // This function was used to popup a small window, ask for a word and // ask the VideoC server for pages containing that word. function initializeJSforThatPage () { if ( window.IExplorer ) { // USELESS now: //var t = '
tag, it resets fonts, margins and // many things. So now the generation of a layer or div tag is // conditionalized over the kind of browser. // Netscape does not like more than one document.write. // Netscape does not execute instructions after a document.write. // Netscape does not like a document.write opening a tag closed by // another subsequent document.write. // Netscape does not like a document.write generating a '; document.write(tmp); } function initializePreviousHints() { //confirm("initializePreviousHints: " + currentHintIds.length); // DEBUG for ( var i=0 ; i)zn+@T>pK y r|tcuvsyCeq?o3Ar ?PAv W>| uwt ʣ`er͑ϳXߝŷȫAq ̵?K/x#I͊gPax^Mŋϔ#ъH"<DH|L{E "-B#Ɵ@AѢH>4ѦPZxڃTTjݚu뛮^&+V ٲh-r\˶۷pʝKݻxrL7پY L0a$+vxqƎ#s,ʘgryg tMӨS^ͺװc˞M۸sͻ Nqz+c?)uTdջ臕ױ>O|evxZV .zH&U, Nh`GؓwiD`qxhVH/Ju<ңuc;.P@6c~5@/~$W,E!N#BI9UV=&6)&l|mƹOoʉ^Gi'wz' ' ~"':^e&f馜vrWU4ёѳԜti:)p`1O^@cl:*1%lZ˜Ѯbl (n*@dmm.[$GTcQ/%Lʪn r`8 [ܬ*ḻ5N]/@}#c2lm/7"([vPUlʽ! = ԙP{.Ao"U5dUr}2Q*LLg-̹ȽJx79{߁O%xSv L6ExY}Fyr_E//2V0vK<騧8|Iׂ^.`Y;{zcAXgyý;/nGgtV3?BAb ףͮ{O&xEے7xy^ t2Vui]`c*8+#?Z`f΃$YӴHEz0* S4@.k._`H@ \prDB~K .Da8.O"Cj Eω΋06GX#'q""FAFM}G#`YW帽B)`!PvV J~ @JЂͧ=9Ch e6aIQ-Rc HBBS!vL |(G稴(#IRTd0# Gh!5a ZR~&Sb*\ M*`TqʗT_L:v4?WE 'VSjˊ^ 3֧U=+W$t?\˴E]f36҃srJvB~=G,o,YNUF*1DZFzHJLڤNPR:TZVzXZ\ڥ^`b:dZfzhjlئq_p rhdpd|ֵ i6ɠtSge P*. =%%Ci}(2:di&F +AZ;ocaml-book-1.0/fr/html/book-ora091.html0000644000000000000000000005711707421273602014505 0ustar Exercices Prcdent Index Suivant

Exercices

Suivi de l'volution du tas

Pour suivre l'volution du tas, on se propose d'crire une fonction qui conserve les informations sur le tas sous la forme d'un enregistrement de la forme suivante :

# type tr_gc = {etat : Gc.stat;
temps : float; numero : int};;
Le temps correspond au nombre de millisecondes depuis le dbut de l'excution et le numro sert diffrencier les appels. On utilise la fonction Unix.time (voir chapitre 18, page ??) qui fournit le temps coul en millisecondes.
  1. crire une fonction trace_gc qui retourne un tel enregistrement.

    # type tr_gc = {etat : Gc.stat; temps : float; numero : int} ;;
    type tr_gc = { etat: Gc.stat; temps: float; numero: int }
    # let trace_gc =
    let num = ref 0 in
    function () -> incr num;
    { etat = Gc.stat (); temps = Unix.time(); numero = !num} ;;
    val trace_gc : unit -> tr_gc = <fun>


  2. Modifier cette fonction pour qu'elle puisse sauver la valeur de type tr_gc dans un fichier sous forme d'une valeur persistante. Cette nouvelle fonction a besoin d'un canal en sortie pour crire. On utilisera le module Marshal, dcrit la page ??, pour la sauvegarde des informations.

    # let trace_out_gc oc =
    let trgc = trace_gc in
    Marshal.to_channel oc trgc [];;
    val trace_out_gc : out_channel -> unit = <fun>


  3. crire un programme autonome, prenant en entre un nom de fichier de donnes de type tr_gc et affiche le nombre de GC mineurs et majeurs.

    type tr_gc = {etat : Gc.stat; temps : float; numero : int} ;;

    let rec last l = match l with
    [] -> failwith "last"
    | [e] -> e
    | t::q -> last q;;

    let visu ltgc =
    let fst = List.hd ltgc
    and lst = last ltgc in
    let nb_minor = lst.etat.Gc.minor_collections - fst.etat.Gc.minor_collections
    and nb_major = lst.etat.Gc.major_collections - fst.etat.Gc.major_collections in

    Printf.printf "Nombre de Gc: mineur = %d, majeur = %d\n" nb_minor nb_major ;;


    let rec read_trace ic =
    try
    let tgc = ( (Marshal.from_channel ic) : tr_gc) in
    tgc :: (read_trace ic)
    with
    End_of_file -> close_in ic; [];;

    let usage () =
    Printf.printf "usage: visu filename\n";;

    let main () =
    if Array.length (Sys.argv) < 2 then usage()
    else
    let ltgc = read_trace (open_in Sys.argv.(1)) in
    visu ltgc;;


    main();;









  4. Tester ce programme en crant un fichier de trace au niveau de la boucle d'interaction.

Allocation mmoire et styles de programmation

Cet exercice compare l'influence des styles de programmation sur l'volution du tas. Pour cela on reprend l'exercice sur les nombres premiers du chapitre 8 page ??. On cherche comparer deux versions, une rcursive terminale (voir page ??) et l'autre non, de la fonction du crible d'Eratosthne.

  1. crire la fonction rcursive terminale erart qui calcule les nombres entiers d'un intervalle donn. crire ensuite une fonction erart_go qui prend un entier et retourne la liste de nombres premiers infrieurs. fichier eras2.ml :

    (* fichier eras2.ml *)

    let erart l =
    let rec erart_aux l r = match l with
    [] -> List.rev r
    | p::q -> erart_aux (List.filter ( fun x -> x mod p <> 0) q) (p::r)
    in
    erart_aux l [] ;;

    let erart_go n =
    erart (Interval.interval (<) (fun x -> x + 1) 2 n) ;;





  2. crire un programme qui prend un nom de fichier et une liste de nombres sur la ligne de commande et calcule pour chaque nombre la liste de nombres premiers infrieurs celui-ci en utilisant la fonction prcdente. Cette fonction effectue une trace du GC dans le fichier indiqu. On regroupe dans le fichier trgc.ml les commandes de trace de l'exercice prcdent. fichier era2_main.ml :

    (* fichier : era2_main.ml *)

    open Trgc;;

    let usage () = Printf.printf "Usage: testgc filename n1 [n2 n3 ... np]n\n";;

    let main f =
    if Array.length (Sys.argv) < 3 then usage()
    else
    let fn = Sys.argv.(1)
    and vn = Array.map int_of_string
    (Array.sub Sys.argv 2 (Array.length Sys.argv - 2)) in
    let oc = open_out fn in
    Array.map (fun n ->
    let r = f n in
    trace_out_gc oc;
    r) vn ;
    close_out oc ;;







    fichier main_rt.ml :

    (* fichier main_rt.ml *)

    Era2_main.main Eras2.erart_go;;
    (* fichier trgc.ml *)

    type tr_gc = {etat : Gc.stat; temps : float; numero : int} ;;
    let trace_gc =
    let num = ref 0 in
    function () -> incr num;
    { etat = Gc.stat (); temps = Unix.time(); numero = !num} ;;

    let trace_out_gc oc =
    let trgc = trace_gc () in
    Marshal.to_channel oc trgc [];;


  3. Compiler ces fichiers en crant un excutable autonome et le tester sur l'appel suivant puis afficher la trace obtenue. .
    erart trace_rt 3000 4000 5000 6000
    
    Compilation pour Unix :
    $ ocamlc -custom -o erart unix.cma interval.ml trgc.ml eras2.ml era2_main.ml main_rt.ml -cclib -lunix
    
    
    Test :
    $ erart traceRT 3000 4000 5000 6000
    
    Rsultats :
    $ visu traceRT 
    Nombre de Gc: mineur = 130, majeur = 22
    


  4. Effectuer le mme travail pour la fonction non rcursive terminale. On reprend le fichier eras.ml, de la page ??, contenant une fonction de calcul non rcursive terminale.

    Puis on cre le fichier main_nrt.ml suivant :
    (* fichier main_nrt.ml *)

    Era2_main.main Eras.era_go;;


    Compilation :
    $  ocamlc -custom -o eranrt unix.cma interval.ml trgc.ml eras.ml era2_main.ml main_nrt.ml -cclib -lunix
    
    Test :
    $ eranrt traceNRT 3000 4000 5000 6000
    
    Rsultats (version 2.04/Unix) :
    $ visu traceNRT 
    Nombre de Gc: mineur = 130, majeur = 10
    


  5. Comparer les rsultats de la trace. Les deux programmes allouent les mmes donnes : ils ont le mme nombre de GC mineurs. La version rcursive terminale effectue plus de GC majeurs. En effet, l'accumulateur r de la fonction erart_rt est valu dans l'appel rcursif et sa tte de liste est range dans la zone des valeurs anciennes chaque GC mineur. Le filtrage des multiples d'un nombre peut entraner un GC mineur qui fait vieillir la liste filtre en cours de construction. Les anciennes listes filtres sont libres pendant un GC majeur. Ainsi l'espace mmoire de la gnration ancienne qui contient la liste des nombres premiers dj trouvs est plus petit ce qui augmente le nombre de GC majeurs.

    Ce cas est particulier

    L'criture d'une fonction rcursive sous forme rcursive terminale fait gagner en allocation de pile, mais pas forcment en nombre de GC.

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora039.gif0000644000000000000000000000701307073350152014275 0ustar GIF89aUUU!,ڋ޼H G@A hj J%" ` Ԯ 7VU k,|E`GXhxTuu)9Yax7@D *:JZjz +;K[k{ ,+aO߿߅| h !É8FXhsmHV/Aȕ|J;.˙\͝0)Ρ<"ѥ}&E4jˌJSX6,bU)6m 2GY0s3ԝɸ&uV'[ʼ9,Iq"7~7dā'qHv4ǦIs>mm8 }Tn9c;N܇pqkV;nöHK|Ԡ@ǂt >Eyk ߣ|AkGpWBycWI=5w`vaƀk>$6Fke'ic(-^ۋT#ah6j 錌$dJ.dN> %ZFXeBDH fl^]jID_iݕ"8&ptHx(X}DAvK  =?>j蟇MI|h b驀 )髚֊fSj:gRhp( j6jqb2l^J4 ^-䭂^-eV)n,Tz&~D2`E+ A-wFVH'[xѢzj$'𲄉M9x䥀ulbdpk2 xyl,I*, kC@35ʩtؼҚ5[WF$,#DWD3KP~IkR.΁f3y`?O$Kxjՠn,^k瞟  ,n̯hn:&W]~a95JP(l7Ds0Wz S|K..LQD4AGo8mr/w  0{#nfL7HH4BgHI-}!HH5}Jrv4zE&D[a y薹v/{7,ȾlXr$P|D?eKTC2dD 6Mj%w,J2g09qV8=걎G0Mk{*yߪ%;B}f&I5Yh_:ˌmR( b^sڄ f5b ed80/Hh8CB[X7]-LU5WHimu͆O8hV*ޜ5yhc {il%tdѬް4 N8u*G cB-fh6 Jz>R8Q,ܚ7hSbh(r$_֢Sp Z]ַO]v!57`kĚ:|:M=颱UhVi fK r㱄 A Tc0D˟戴_i 3Z~Y vòq\VQx!s& )w-.HXɕMApJSd2{^uSϒw.u!X,{P¨5/\xڕ]w%%;: 3^ի׶s̆TD'N$Z/c_ #qg37&OHNr36]J)CPueWs2y[A@/mǢ95Xt }6.LV sr(Wn<)-9p'/s'+M}pێܒݛ4swu}tyD%km=8:СL,]qO;xCqz㋠T;w=;W}Lo| !K[~{ўMxIM_qg?J[zW~,Mw|d{W}H&VsGug0dJrw}tW# Nw0 =Y 7@hn#ʡIv)h3\7A40VPŃ>swLfDe$s6Y!]#G7D{ #|=Xm Z$4p6-.(W⁝t|;yj'vr*s+<>5k\:qH0@!4z{0Ԓ1t@443@{&aE&h. Cd3l|O!4u#iA}dKl醋rk4who'}T]@&Ť<$qW@J8f7~XKDH!@LLtSmhv(#wrm8;5%ꈍٍ idH9إY| i-xI8\"ɒ *~3I5iHG~ܸ=)o@铼h'ͰyGw zƔ'|QٔIY}VYEUɕgw[+yuOyf[Ɩmtac"?fvЗImqiszgw),Yxrq 9GYљ.9tyw hIC"iF2vɓ"z9ȎIeF|ii ɜni's܉# $ *Z扞F01뙞:/Fݧ:ƹmVТ&q*<Q@ͦ1k= =0CfG/99@Hg=G#ش ExĝOwPA(q<@i0 ;s5B8Z5#@zD>=>>B=j+Rڛ@ < 4Gh0f @3@z shJ J ;ocaml-book-1.0/fr/html/book-ora054.html0000644000000000000000000000352307421273601014473 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a prsent les notions de base de la programmation graphique et de la programmation par vnements en utilisant la bibliothque Graphics de la distribution Objective CAML. Aprs avoir dtaill les lments graphiques de base (couleur, trac, remplissage, texte et bitmap) l'animation de ceux-ci a t aborde. Le mcanisme de gestion d'vnements de Graphics a t ensuite dtaill ce qui a permis d'introduire une mthode gnrale de gestion d'interaction avec l'utilisateur mettant en jeu un modle de programmation par vnements. Pour amliorer l'interaction et proposer au programmeur des composants graphiques interactifs, il a t dvelopp une nouvelle bibliothque, appele Upi, facilitant la construction d'interfaces graphiques. Cette bibliothque a t utilise pour crire l'interface de la calculatrice imprative.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora093.html0000644000000000000000000000616707421273602014506 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

Les techniques sur la rcupration mmoire sont tudies depuis quarante ans, en fait depuis les premires implantations du langage Lisp. Il y a donc une trs grande littrature dans le domaine. Le seul ouvrage, en franais, de prsentation de ces techniques est le livre Gestion Dynamique de la Mmoire dans les Langages de Programmation d'Eric Spir ([Spi90]). On pourra aussi lire le tutoriel de Paul Wilson publi IWMM92 (International Workshop on Memory Management) ([Wil92]) qui possde une importante bibliographie. De trs nombreux liens permettent de connatre l'tat de l'art du domaine.

Lien


ftp://ftp.netcom.com/pub/hb/hbaker/home.html
est une introduction aux GC squentiels.

Lien


http://www.cs.ukc.ac.uk/people/staff/rej/gc.html
contient la prsentation de [Jon98] et donne accs une imposante bibliographie.

Lien


http://www.cs.colorado.edu/~zorn/DSA.html
liste les diffrents outils de mise au point de GC.

Lien


http://reality.sgi.com/boehm_mti/
propose les sources en C d'un GC racines ambigus pour le langage C. Ce dernier remplace l'allocation classique malloc par une version spcialise GC_malloc. La rcupration explicite free est remplace par une nouvelle version qui ne fait plus rien.

Lien


http://www.harlequin.com/mm/reference/links.html
maintient une liste de liens sur le domaine.

Le chapitre 12 sur l'interface entre les langages C et Objective CAML reviendra sur la gestion mmoire.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora199.html0000644000000000000000000000614207421273602014506 0ustar Introduction Prcdent Index Suivant

Introduction

cette tape de sa lecture, le lecteur ne doit plus douter de la richesse d'Objective CAML. Ce langage repose sur un noyau fonctionnel et impratif et il intgre les deux grands modles d'organisation d'applications que sont les modules et les objets. Bien que prsents comme des bibliothques, les processus lgers sont partie prenante du langage. Les primitives systme, en majeure partie portables, compltent le langage avec toutes les possibilits offertes par la programmation rpartie. Ces diffrents paradigmes de programmation se moulent dans le cadre gnral du typage statique avec infrence. Pour autant, ces lments ne tranchent pas, eux seuls, la question de sa pertinence pour le dveloppement d'applications, ou plus prosaquement : << est-ce un bon langage ? >>.

On ne peut son propos utiliser l'un des arguments classiques suivants :
  • (tendance marketing) << c'est un bon langage car les clients l'achtent >> ;
  • (tendance historique) << c'est un bon langage car il existe des milliers de lignes dj crites avec >>;
  • (tendance systme) << c'est un bon langage car les systmes Unix ou Windows sont crits avec >> ;
  • (tendance application phare) << c'est un bon langage car telle ou telle application a t crite avec >> ;
  • (tendance normalisation) << c'est un bon langage car il possde une spcification ISO >>.
Nous allons passer en revue une dernire fois les divers traits du langage mais cette fois sous l'angle de la pertinence de la rponse qu'il peut apporter aux besoins d'une quipe de dveloppement. Les critres retenus pour formuler nos lments d'valuation prennent en compte les qualits intrinsques du langage, son environnement de dveloppement, les contributions de la communaut et les applications significatives ralises. Nous confrontons enfin Objective CAML avec plusieurs langages fonctionnels proches ainsi que le langage objets Java pour en souligner les principales diffrences.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora090.html0000644000000000000000000004575507421273602014511 0ustar Module Weak Prcdent Index Suivant

Module Weak

Un pointeur faible (weak pointer) est un pointeur dont la zone mmoire pointe est rcuprable tout moment par le GC. Il peut tre surprenant de parler d'une valeur qui peut disparatre tout instant. En fait il faut voir ces pointeurs faibles comme un rservoir de valeurs encore disponibles. Cela s'avre particulirement utile quand les ressources mmoire sont petites par rapport aux lments conserver. Le cas classique est la gestion d'un cache mmoire : une valeur peut tre perdue, mais elle reste directement accessible tant qu'elle existe.

En Objective CAML on ne peut pas manipuler directement des pointeurs faibles, mais uniquement des tableaux de pointeurs faibles. Le module Weak dfinit le type abstrait 'a Weak.t correspondant au type 'a option array, vecteur de pointeurs faibles de type 'a. Le type concret 'a option est dfini de la manire suivante :
type 'a option = None | Some of 'a;;
Les fonctions principales de ce module sont dfinies la figure 9.14.

fonction type
create int -> 'a t
set 'a t -> int -> 'a option -> unit
get 'a t -> int -> 'a option
check 'a t -> int -> bool

Figure 9.14 : fonctions principales du module Weak


La fonction create alloue l'espace d'un tableau de pointeurs faibles dont chaque lment est initialis par None. La fonction set range une valeur de type 'a option un indice donn. La fonction get retourne la valeur d'indice n contenue dans un tableau de pointeurs faibles. La valeur retourne est alors rfrence dans l'ensemble des racines du GC et n'est plus rcuprable tant que dure ce rfrencement. On vrifie l'existence effective d'une valeur soit en utilisant la fonction check, soit par filtrage sur les motifs du type 'a option. La premire solution est indpendante de la reprsentation des pointeurs faibles.

Les fonctions habituelles sur les structures linaires existent aussi : length, pour la longueur, fill et blit pour les copies de parties de tableaux.

Exemple : cache d'images

Dans une application de construction d'images, il n'est pas rare de travailler sur plusieurs images. Quand on passe d'une image une autre, la premire est sauvegarde dans un fichier et la suivante est charge partir d'un autre fichier. On ne conserve en gnral que les noms des dernires images traites. Pour viter des accs au disque trop frquents tout en n'occupant pas trop d'espace mmoire, on utilise un cache mmoire qui contient les dernires images charges. Le contenu du cache mmoire peut tre libr si ncessaire. On l'implante par un tableau de pointeurs faibles, laissant la charge du GC la dcision de libration de ces images. Le chargement d'une image cherche tout d'abord dans le cache si l'image y est, si oui elle devient l'image courante, sinon son fichier est lu.

On dfinit une table des images de la manire suivante :

# type table_of_images = {
size : int;
mutable ind : int;
mutable name : string;
mutable current : Graphics.color array array;
cache : ( string * Graphics.color array array) Weak.t } ;;
Le champ size donne la taille du tableau ; le champ ind, l'indice de l'image courante ; le champ name, le nom de de l'image courante ; le champ current, l'image courante et le champ cache contient le tableau de pointeurs faibles sur les images. Il contient les dernires images charges et leur nom.

La fonction init_table initialise la table avec une premire image.

# let open_image filename =
let ic = open_in filename
in let i = ((input_value ic) : Graphics.color array array)
in ( close_in ic ; i ) ;;
val open_image : string -> Graphics.color array array = <fun>

# let init_table n filename =
let i = open_image filename
in let c = Weak.create n
in Weak.set c 0 (Some (filename,i)) ;
{ size=n; ind=0; name = filename; current = i; cache = c } ;;
val init_table : int -> string -> table_of_images = <fun>


Le chargement d'une nouvelle image conservera l'image courante dans la table et chargera la nouvelle. Pour ce faire, il faut tout d'abord tenter de trouver l'image dans le cache.

# exception Found of int * Graphics.color array array ;;
# let search_table filename table =
try
for i=0 to table.size-1 do
if i<>table.ind then match Weak.get table.cache i with
Some (n,img) when n=filename -> raise (Found (i,img))
| _ -> ()
done ;
None
with Found (i,img) -> Some (i,img) ;;



# let load_table filename table =
if table.name = filename then () (* l'image est l'image courante *)
else
match search_table filename table with
Some (i,img) ->
(* l'image trouve devient l'image courante *)
table.current <- img ;
table.name <- filename ;
table.ind <- i
| None ->
(* l'image n'est pas dans le cache, il faut la charger *)
(* trouver une place vide dans le cache *)
let i = ref 0 in
while (!i<table.size && Weak.check table.cache !i) do incr i done ;
(* si aucune n'est libre, on en prend une pleine *)
( if !i=table.size then i:=(table.ind+1) mod table.size ) ;
(* on charge l'image cette place
et elle devient l'image courante *)
table.current <- open_image filename ;
table.ind <- !i ;
table.name <- filename ;
Weak.set table.cache table.ind (Some (filename,table.current)) ;;
val load_table : string -> table_of_images -> unit = <fun>
La fonction load_table teste si l'image demande est l'image courante, sinon elle vrifie dans le cache si celle-ci existe, si ce n'est pas le cas elle la charge du disque; dans les deux cas elle en fait l'image courante.

Pour tester ce programme, on utilise la fonction d'affichage du cache suivante :

# let print_table table =
for i = 0 to table.size-1 do
match Weak.get table.cache ((i+table.ind) mod table.size) with
None -> print_string "[] "
| Some (n,_) -> print_string n ; print_string " "
done ;;
val print_table : table_of_images -> unit = <fun>


Puis on teste le programme suivant :

# let t = init_table 10 "IMAGES/animfond.caa" ;;
val t : table_of_images =
{size=10; ind=0; name="IMAGES/animfond.caa";
current=
[|[|7437734; 7437734; 7437734; 7437734; 7437734; 7437734; 7437734;
7437734; 7437734; 7437734; 7437734; 7437734; 7440038; 7440038; ...|];
...|];
cache=...}
# load_table "IMAGES/anim.caa" t ;;
- : unit = ()
# print_table t ;;
IMAGES/anim.caa [] [] [] [] [] [] [] [] IMAGES/animfond.caa - : unit = ()


Cette technique de cache peut s'adapter diffrentes applications.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora055.gif0000644000000000000000000000440307073350152014273 0ustar GIF89a?>!,?>ڋH扦ʶ L׮a p¢L*%" JK'j"+ wޱN e:y`09HX6hȅ9IXs)::Jz*j ;(K{ h <&L|,fL =$M}d >^#N~'^?-On_O e_:/F@Pw97EGeIE\}2dJ;)@e̕\)S&x 5,i$%MfG KYMPUtvk_MXb(:Vڵjux]M;/߾*-0ÇSS- ÌAU1*&yiWSMҵH:UIM2qs^ֻdnj瞒eAJٍ>-Z|%\^a~>"K"-[ ȇIYdr_s5 Hkń!\ XR|T 1aybKA↺Ea:ވh5Hd9#xEŒ8%OSNHb -(%ba"bݚ@Zds FAWv\BP|̟ȹxf&BC4ɣ`i1 uj l%"j`*>2 D͠J +qY(,G$:;Ų&@m~bvm;;*n2Ǭ&힋ĻG  kBkkcB+ܪH 0t:q>$/q?EƑ1Lj )kd;Ī8W\7ҳRЄye"Es8C_{?>8Q{DU;tO_2j M  Y+oOv/M׽wCxk^s W+CTV3UYP$U&v pBsG餻;JAy^:ιV\OCƣB5g8۬X/_ $G/ Mxzu|*x^U4_ |h@V3LGŃA[Y뱇 SbY1EhA9Ya1͆_Pb %ĝЈG!?ˉO ŪhO( \T?$6jQP>mAX,Q](yeIAlnLd!%/ƩLj 4mJ:[&%&T1&-k"ԐOR XI[rͲ^+2d KVjrklfcLR>!LRdIaRD115))Erڄ'81LWFFJrIՕSxNǞ4w%~wb^)/K]W=}e#Mr=znp&lfMX{!Q(6 as0Ub׷\_MF-|?d&#`JSrle#c~[| DblP9v@37!rUB;+kz.`? ς =@# ^49C:v:m"`: 4k==MLRZf^5[G5!m[XZӺu}=c` Vb%m^#[^6oF[FNvgzfHVhoYܯ$7m;f#ݝIxWgryyo|aT&\gvp'k VpEk/k| ;ocaml-book-1.0/fr/html/book-ora189.html0000644000000000000000000003120407421273602014502 0ustar Protocoles de communication Prcdent Index Suivant

Protocoles de communication

Les diffrentes communications des client-serveur dcrits la section prcdente consistent envoyer une chane de caractres termine par un retour-chariot et en recevoir une autre. Bien que simples elles dfinissent un protocole. Ds que l'on veut communiquer des valeurs plus complexes : un flottant, une matrice de flottants, un arbre d'expressions arithmtiques, une fermeture ou un objet, se pose le problme du codage de ces valeurs. Plusieurs solutions existent selon la nature des programmes communicants que l'on caractrise par : le langage d'implantation, l'architecture machine et dans certains cas le systme d'exploitation. En effet selon l'architecture machine les entiers seront reprsents de diffrentes manires (bits de poids fort gauche, droite, bits de tag, taille du mot machine). Pour communiquer une valeur entire cohrente entre diffrents programmes, il est ncessaire d'avoir une reprsentation commune des valeurs que l'on appelle reprsentation externe1. Les valeurs plus structures, comme les enregistrements, doivent galement avoir une reprsentation externe. On rencontre nanmoins un problme lorsque certains langages autorisent des constructions, comme les champs de bits en C, qui n'existent pas dans les autres langages. Le passage de valeurs fonctionnelles ou d'objets, qui contiennent une partie code, pose une nouvelle difficult pour le chargement de ce code. Celui-ci est-il compatible (code-octet) entre l'envoyeur et le rcepteur, et existe-t-il un mcanisme de chargement dynamique de code? En rgle gnrale on simplifiera ce problme en supposant que le code existe des deux cts. Ce n'est pas lui qui est transmis, mais des informations permettant de le retrouver. Pour un objet on communique ses variables d'instance et son type ce qui permet d'en retrouver les mthodes. Pour une fermeture on envoie son environnement et l'adresse de son code. Ce qui implique que les deux programmes communicants sont le mme excutable.

Une deuxime difficult provient de la complexit des changes lie la ncessit de synchroniser les communications mettant en jeu plusieurs programmes.

On prsente tout d'abord des protocoles texte, pour ensuite discuter de l'acquittement et des limites de temps entre requtes et rponses. Nous voquons aussi la difficult de communiquer des valeurs internes d'un programme en particulier dans le cadre de l'interoprabilit entre programmes issus de diffrents langages.

Protocole texte

Les protocoles texte, c'est dire la communication en format ASCII, sont les plus courants car les plus simples mettre en oeuvre et les plus portables. Par contre quand le protocole se complique, il peut tre lourd implanter. Dans ce cadre l on dfinit une grammaire pour dcrire le format de communication. Celui-ci peut tre riche, mais ce sera aux programmes communicants de faire le travail de codage et d'interprtation des chanes reues.

En rgle gnrale, l'utilisation d'une application rseau ne permet pas de voir les diffrentes couches de protocoles utilises. C'est typiquement le cas du protocole HTTP permettant un navigateur de communiquer avec un site Web.

Protocole HTTP

On rencontre frquemment le terme <<HTTP>> dans les publicits. Il correspond au protocole de communication des applications du Web. Celui-ci est compltement dcrit sur la page du consortium W3 :

Lien


http://www.w3.org
Ce protocole est utilis pour transporter les requtes mises par les navigateurs (Communicator, Explorer, Opera, etc) et pour retourner le contenu de la page demande. Une requte effectue par un navigateur contient le nom du protocole (HTTP), le nom de la machine (www.ufr-info-p6.jussieu.fr), et l'arborescence de la page demande (/Public/Localisation/index.html). Cet ensemble de trois composants permet de dfinir une URL (Uniform Ressource Locators) :
http://www.ufr-info-p6.jussieu.fr/Public/Localisation/index.html
Quand une telle URL est demande partir d'un navigateur, une connexion via une socket est tablie entre le navigateur et le serveur s'excutant sur la machine indique, par dfaut sur le port 80. Puis le navigateur envoie une requte au format HTTP, proche de la suivante :
GET /index.html HTTP/1.0
Alors le serveur lui rpond dans le protocole HTTP, l'en-tte :
HTTP/1.1 200 OK
Date: Wed, 14 Jul 1999 22:07:48 GMT
Server: Apache/1.3.4 (Unix) PHP/3.0.6 AuthMySQL/2.20
Last-Modified: Thu, 10 Jun 1999 12:53:46 GMT
ETag: "4c801-e4f-375fb55a"
Accept-Ranges: bytes
Content-Length: 3663
Connection: close
Content-Type: text/html
Cet en-tte indique que la requte est accepte (code 200 OK), la nature du serveur, la date de modification de la page, la longueur de la page envoye et le type du contenu qui va suivre. Sans la prcision de la version de protocole (HTTP/1.0) de la commande GET, seule la page HTML est transfre. La connexion suivante avec telnet permet de voir ce qui est effectivement transmis :
$ telnet www.ufr-info-p6.jussieu.fr 80
Trying 132.227.68.44...
Connected to triton.ufr-info-p6.jussieu.fr.
Escape character is '^]'.
GET


<!-- index.html -->
<HTML>
<HEAD>
<TITLE>Serveur de l'UFR d'Informatique de Pierre et Marie Curie</TITLE>
</HEAD>
<BODY>

<IMG SRC="/Icons/upmc.gif" ALT="logo-P6" ALIGN=LEFT HSPACE=30>
Unit&eacute; de Formation et de Recherche 922 - Informatique<BR>
Universit&eacute; Pierre et Marie Curie<BR>
4, place Jussieu<BR>
75252 PARIS Cedex 05, France<BR><P> 
....
</BODY>
</HTML>
<!-- index.html -->

Connection closed by foreign host.
La connexion se referme ds que la page est copie. Le protocole de base est en mode texte ainsi que le langage interprter. On note que les images ne sont pas transmises avec la page. C'est au navigateur, lors de l'analyse syntaxique de la page HTML, de remarquer les ancres et les images (voir la balise IMG de la page transmise). ce moment l, le navigateur envoie une nouvelle requte pour chaque image rencontre dans le source HTML, il y a donc une nouvelle connexion par image. Ces images sont affiches ds qu'elles sont reues. C'est pour cette raison que les images s'affichent souvent en parallle.

Le protocole HTTP est assez simple, mais il vhicule une information dans le langage HTML qui lui est plus complexe.

Protocoles avec acquittement et limite de temps

Quand le protocole est complexe, il est utile que le rcepteur d'un message indique l'envoyeur qu'il a bien reu le message et que celui-ci est grammaticalement correct. En effet le client peut tre en attente bloquante de rponse avant de poursuivre ses tches. Si la partie du serveur traitant cette requte a une difficult d'interprtation du message, le serveur doit l'indiquer au client plutt que d'oublier cette requte. L'exemple du protocole HTTP a une gestion de code d'erreurs. Une requte correcte renvoie le code 200. Une requte mal forme ou la demande d'accs une page non autorise renvoie un code d'erreur 4xx ou 5xx selon la nature de l'erreur. Ces codes d'erreur permettent au client de savoir ce qu'il a faire et au serveur de tenir des fichiers de compte-rendu prcis sur la nature des incidents.

Lorsqu'un serveur est dans un tat incohrent, il peut toujours accepter une connexion d'un client, mais risque de ne jamais lui envoyer de rponse sur la socket. Pour viter ces attentes bloquantes, il est utile de fixer une limite de temps pour la transmission de la rponse. Pass ce dlai, le client suppose que le serveur ne rpondra plus. Alors le client peut fermer cette connexion pour passer la suite de son travail. C'est ainsi que les navigateurs WWW font. Quand une requte n'a pas de rponse au bout d'un certain temps, le navigateur dcide de l'indiquer l'utilisateur. Objective CAML possde des entres-sorties avec limite de temps. Dans le module Thread, les fonctions wait_time_read et wait_time_write suspendent l'excution de la tche jusqu'au moment o un caractre peut tre lu ou crit, cela dans un certain dlai. Elles prennent en entre un descripteur de fichier et un dlai exprim en secondes : Unix.file_descr -> float -> bool. Si le dlai a t dpass la fonction retourne false, sinon l'entre-sortie peut tre effectue.

Transmission de valeurs en reprsentation interne

L'intrt de la transmission de valeurs en reprsentation interne est de simplifier le protocole. Il n'y a plus besoin de coder ni de dcoder les donnes dans un format texte. Les difficults inhrentes l'envoi-rception de valeurs en reprsentation interne sont les mmes que celles rencontres pour les valeurs persistantes (voir le module Marshal page ??). En effet lire ou crire une valeur dans un fichier est quivalent recevoir ou mettre cette mme valeur sur une socket.

Valeurs fonctionnelles

Dans le cas de transmission d'une fermeture entre deux programmes Objective CAML, le code de la fermeture n'est pas envoy, seul son environnement et son pointeur de code (voir figure 12.9 page ??) le sont. Pour que cela fonctionne il est ncessaire que le serveur possde le mme code au mme emplacement. Ce qui implique que le mme programme tourne en tant que serveur et en tant que client. Rien n'empche cependant que ces deux programmes soient en train d'excuter des parties de code diffrentes. On adapte alors le service de calcul matriciel en envoyant la fermeture contenant dans son environnement les donnes calculer. la rception le serveur applique cette fermeture () et le calcul se dclenche.

Interoprer avec d'autres langages

L'intrt des protocoles texte est qu'ils sont indpendants des langages d'implantation des clients et des serveurs. En effet le code ASCII est toujours connu des langages de programmation. Ensuite c'est au client et au serveur de savoir analyser syntaxiquement les chanes de caractres transmises. Un exemple d'un tel protocole ouvert est la simulation de joueurs de football, appele RoboCup.

Robots footballeurs

Une quipe de football joue contre une autre quipe. Chaque membre d'une quipe est un client du serveur arbitre. Les joueurs d'une mme quipe ne peuvent pas communiquer directement entre eux. Ils doivent passer par le serveur, qui retransmet le dialogue. Le serveur indique selon la position du joueur une partie du terrain. Toutes ces communications s'effectuent selon un protocole texte. La page de prsentation du protocole, du serveur et de certains clients :

Lien


http://www.robocup.org/
Le serveur est crit en C. Les clients sont crits dans diffrents langages : C, C++, SmallTalk, Objective CAML, etc. Rien n'empche d'ailleurs une quipe de possder des joueurs issus de diffrents langages.

Ce protocole rpond aux besoins d'interoprabilit entre programmes de diffrents langages d'implantation. Il est relativement simple, mais il ncessite un analyseur syntaxique particulier pour chaque famille de langages.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora153.html0000644000000000000000000000333507421273602014475 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

La premire section compare le modle modulo-fonctionnel et le modle objet. Cette comparaison distingue les points particuliers de chaque modle, pour montrer ensuite comment plusieurs d'entre eux peuvent se traduire la main dans l'autre modle. On peut ainsi simuler la relation d'hritage dans les modules et utiliser les classes pour traduire des modules simples. Les limitations de chaque modle sont ensuite rcapitules. La deuxime section s'intresse au problme d'extensibilit pour les structures de donnes et les traitements, et propose une solution qui mixe les deux modles. La troisime section dcrit d'autres compositions des deux modles par l'utilisation des types abstraits des modules pour les objets.


Prcdent Index Suivant ocaml-book-1.0/fr/html/videoc-ocda.css0000644000000000000000000001250407073350147014535 0ustar /* $Id: videoc-ocda.css,v 1.1.1.1 1999/12/23 19:07:56 emmanuel Exp $ * The cascading style sheet for VideoC. * * ATTENTION: Do not modify videoc.css but videoc.css.cpp instead! * Colors and fonts are defined in videoc.colors.cpp. */ /* $Id: videoc-ocda.css,v 1.1.1.1 1999/12/23 19:07:56 emmanuel Exp $ * Colors for the style sheets of VideoC. * * They are used to regenerates the videoc*.css files. NOTE: Arial does not seem to use the ISO encoding: e' is correct but e` a` are incorrect. Revert to Times everywhere. */ /* end of videoc.colors.cpp */ BODY, .regularBody { font-family: "Times", serif ; margin-left: 20px; margin-right: 20px; color: black ; background-color: #ffffff ; } .topic, .subtopic, .subsubtopic, .lesson, .solution, .exotext { font-size: 80%; color: black ; font-size: medium; } A, .reference { color: seagreen; /*rgb(231,141,57) ;*/ font-weight: bold; } .bannerText { color: rgb(231,141,57) ; font-weight: bold; } .banner, .bannerSolution { color: rgb(231,141,57) ; font-weight: bold; align: center; border: solid 0 black ; padding: 0; } .tocAlphabet { align: center; font-size: 140%; } .detail { display: inline; } .relatedTable { align: center; margin-left: 10; margin-right: 10; margin-top: 5; } .requiredTable TH, .requiredTable TD { valign: top; align: center; } .suggestedTable TH, .suggestedTable TD { valign: top; align: center; } .related, .credits { font-family: "Times", serif ; display: block; font-size: 80%; align: center; width: 100%; margin-right: 50%; padding: 10px; border: solid 2px rgb(231,141,57) ; color: black ; background-color: rgb(53, 229, 175) ; } .related A, .credits A { color: black ; font-weight: bold; } .authorEmail, .careTakerEmail { width: 50%; align: right; font-family: "Times", serif ; font-size: 80%; color: red; } .suggested, .required { display: wrap; width: 100%; align: right; font-family: "Times", serif ; font-size: 100%; color: black ; } .wordSearch { border-width: 3px; border-color: blue; background-color: lightblue; } h1 { font-family: "Helvetica", serif ; display: block; align: center; width: 100%; padding: 10px; border-width: 3px; border-color: rgb(231,141,57) ; font-size: 200%; color: ivory; background-color: steelblue; /* PeMOC background rgb(231,141,57) ; color: #ffffff ; */ } .specialH1 { font-family: "Times", serif ; display: block; align: center; width: 100%; padding: 10px; border-width: 3px; border-color: rgb(231,141,57) ; font-size: 200%; color: black ; background-color: white; } h2, .topicHeader, .lessonHeader, .exerciseHeader, .answerHeader { font-family: "Helvetica", serif ; display: block; width: 100%; align: center; padding: 10px; font-size: 200%; color: ivory; /*gold; *//*steelblue; */ background-color: steelblue; /*rgb(213,141,57);*/ /*darkblue;*/ /* PeMOC rgb(231,141,57) ; color: #ffffff ; */ } h3, .subtopicHeader { display: block; align: center; width: 100%; padding: 5px; font-size: 140%; color: ivory; background-color: steelblue; /* color: rgb(231,141,57) ; */ /* background-color: #ffffff ;*/ } h4, .subsubtopicheader { display: block; align: center; width: 100%; padding: 1px; font-size: 100%; font-style: italic; color: ivory; background-color: steelblue; /* color: rgb(231,141,57) ; background-color: #ffffff ; */ } .message { align: center; padding: 10px; width: 66%; font-size: 130%; font-style: bolder; color: rgb(231,141,57) ; background-color: rgb(248,255,222) ; border: solid thick rgb(231,141,57) ; } /* The overall style for code excerpts. */ .codeInline { font-family: monospace; } .code, .c, .scheme, .tex, .perl, .sh, .asm, .makefile, .gdb, .shc, .syntax, .pascal, .texForAnnote, .texForSty, .schemeSlide, .vi { font-family: monospace; margin-left: 20; margin-right: 20; margin-top: 5; margin-bottom: 5; padding: 5px; color: red; background-color: rgb(248,255,222) ; border: solid thin rgb(231,141,57) ; } PRE { font-family: monospace; white-space: pre; margin-left: 20; margin-right: 20; margin-top: 5; margin-bottom: 5; padding: 5px; color: darkblue ; background-color: rgb(238,238,224); /*ivory;*/ /*beige;*/ /*sienna; *//*indianred; *//*orange;*/ /* PeMOC color: red; background-color: rgb(248,255,222) ; */ border: solid thin rgb(231,141,57) ; } .inclusion, .cInclusion, .texForAnnoteInclusion, .shInclusion, .perlInclusion, .texInclusion, .schemeSlideComment, .schemeInclusion, .schemeComment { color: purple; background-color: rgb(248,255,222) ; } /* Anchors that are mouse-sensitive should not be too emphasized * certainly not as a real anchor. */ .mousable { color: coral; /*seagreen; *//*steelblue;*/ /* PeMOC color: blue; */ text-decoration: none ! important; } /* The overall style for popups. Popups appear when moused over. * They stay or disappear, permanently, if clicked over. */ .hint { color: darkblue; padding: 5px; margin: 5px; font-size: medium; /* background-color: rgb(53, 229, 175) ;*/ background-color : orange; border: solid thin red; } /* For QCM */ .okChoice { color: green; margin-left: 20; } .koChoice { color: red; margin-left: 20; } /* end of videoc.css */ ocaml-book-1.0/fr/html/book-ora089.html0000644000000000000000000001525607421273602014512 0ustar Module Gc Prcdent Index Suivant

Module Gc

Le module Gc permet d'obtenir des statistiques sur le tas et fournit un contrle sur son volution ainsi que sur le dclenchement des diffrents GC. Deux types enregistrements concrets sont dfinis : stat et control. Les champs du type control sont modifiables. Ils permettent d'influer sur le comportement du GC. Les champs du type stat ne le sont pas. Ils refltent simplement l'tat du tas un instant donn.

Les champs d'un enregistrement stat contiennent principalement des compteurs indiquant :
  • nombres de GC : minor_collections, major_collections et compactions
  • nombre de mots allous et transfrs depuis le dbut d'excution du programme : minor_words, promoted_words, major_words
Les champs d'un enregistrement control sont :
  • minor_heap_size qui dfinit la taille de la zone dvolue la jeune gnration ;
  • major_heap_increment qui dfinit l'incrment appliqu l'accroissement de la zone de l'ancienne gnration ;
  • space_overhead qui dfinit le pourcentage de mmoire utilis au del duquel un GC majeur est dclench (la valeur par dfaut est 42) ;
  • max_overhead qui dfinit le rapport entre mmoire libre et occup partir duquel on dclenche la compaction. La valeur 0 provoque une compaction systmatique aprs chaque GC majeur, la valeur maximale 1000000 inhibe la compaction ;
  • verbose est un paramtre entier gouvernant l'mission d'une trace de l'activit du GC.
Les fonctions manipulant les types stat et control sont donnes la figure 9.13.

stat unit -> stat
print_stat out_channel -> unit
get unit control
set control -> unit

Figure 9.13 : fonctions de contrle et de statistiques de tas


Les fonction suivantes de type unit -> unit permettent de forcer l'excution d'un nombre d'tapes plus ou moins grand du GC d'Objective CAML : minor (tape 1), major (tapes 1 et 2), full_major (tapes 1,2 et 3) et compact (tapes 1,2,3 et 4).

Exemples

Voici ce qu'indique l'appel de GC.stat :

# Gc.stat();;
- : Gc.stat =
{Gc.minor_words=5828243; Gc.promoted_words=509102; Gc.major_words=1002421;
Gc.minor_collections=184; Gc.major_collections=9; Gc.heap_words=579584;
Gc.heap_chunks=8; Gc.live_words=137371; Gc.live_blocks=28552;
Gc.free_words=199502; Gc.free_blocks=1690; Gc.largest_free=72704;
Gc.fragments=89; Gc.compactions=0}


On remarque le nombre d'excutions de chaque phase : GC mineur, GC majeur, compaction, ainsi que le nombre de mots traits par les diffrents espaces mmoire. L'appel de compact force les quatre tapes du GC. Les informations sur le tas sont effectivement modifies (voir appel de Gc.stat).

# Gc.compact();;
- : unit = ()
# Gc.stat();;
- : Gc.stat =
{Gc.minor_words=5834712; Gc.promoted_words=509935; Gc.major_words=1003254;
Gc.minor_collections=185; Gc.major_collections=11; Gc.heap_words=344064;
Gc.heap_chunks=5; Gc.live_words=223345; Gc.live_blocks=57824;
Gc.free_words=120718; Gc.free_blocks=2; Gc.largest_free=90112;
Gc.fragments=1; Gc.compactions=1}


Les champs GC.minor_collections et compactions sont incrments de 1, alors que le champ Gc.major_collections est augment de 2. Tous les champs du type GC.control sont modifiables. Pour qu'ils puissent tre pris en compte, on utilisera la fonction Gc.set qui prend une valeur de type control et modifie le comportement du GC.

Par exemple, le champ verbose peut prendre des valeurs de 0 127 activant 7 indicateurs diffrents.

# c.Gc.verbose <- 31;;
Characters 1-2:
This expression has type int * int but is here used with type Gc.control
# Gc.set c;;
Characters 7-8:
This expression has type int * int but is here used with type Gc.control
# Gc.compact();;
- : unit = ()


Ce qui affichera :
<>Starting new major GC cycle
allocated_words = 329
extra_heap_memory = 0u
amount of work to do = 3285u
Marking 1274 words
!Starting new major GC cycle
Compacting heap...
done.
Les diffrentes phases du GC sont indiques ainsi que le nombre d'objets traits.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora109.html0000644000000000000000000010747607421273602014511 0ustar Exercices Prcdent Index Suivant

Exercices

Suppression de commentaires

Les commentaires en Objective CAML sont hirarchiques. On peut ainsi commenter des parties de texte, y compris celles contenant dj des commentaires. Un commentaire commence par les caractres (* et se termine par *). Voici un exemple :

(* dbut du commentaire
sur plusieurs
lignes *)

let succ x = (* fonction successeur *)
x + 1;;

(* niveau 1 texte comment
let old_succ y = (* niveau 2 fonction successeur niveau 2 *)
y +1;;
niveau 1 *)
succ 2;;
Le but de cet exercice est de crer un nouveau texte sans tous les commentaires. Le choix de l'outil d'analyse lexicale est libre.

  1. crire un analyseur lexical qui puisse reconnatre les commentaires d'Objective CAML. Ceux-ci commencent par un (* et se terminent par un *). Attention les commentaires sont bien balancs, c'est--dire que le nombre d'ouvertures est gal au nombre de fermetures.
    (** fichier comment1.mll **)

    {
    let ignore_lex lb = ignore (Lexing.lexeme lb)
    let traite_normal = ignore_lex
    let traite_comment = ignore_lex
    exception Commentaires_mal_balances
    }

    rule normal = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; normal lexbuf }
    | "*)" { raise Commentaires_mal_balances }
    | _ { traite_normal lexbuf ; normal lexbuf }
    | eof { () }

    and commentaire = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; commentaire lexbuf }
    | "*)" { ignore_lex lexbuf }
    | _ { traite_comment lexbuf ; commentaire lexbuf }
    | eof { raise Commentaires_mal_balances }



  2. crire un programme qui prend un fichier, le lit, retire les commentaires de son texte puis cre un nouveau fichier rsultat.
    (** fichier comment2.mll **)
    {
    let ignore_lex lb = ignore (Lexing.lexeme lb)
    let sortie = ref stdout
    let init_sortie f = sortie := f
    let traite_normal lb = output_string !sortie (Lexing.lexeme lb)
    let traite_comment = ignore_lex
    exception Commentaires_mal_balances
    }

    rule normal = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; normal lexbuf }
    | "*)" { raise Commentaires_mal_balances }
    | _ { traite_normal lexbuf ; normal lexbuf }
    | eof { () }

    and commentaire = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; commentaire lexbuf }
    | "*)" { ignore_lex lexbuf }
    | _ { traite_comment lexbuf ; commentaire lexbuf }
    | eof { raise Commentaires_mal_balances }


    {

    let decommente src dest =
    let file_in = open_in src in
    let lb = Lexing.from_channel file_in in
    let file_out = open_out dest in
    init_sortie file_out ;
    normal lb ;
    close_in file_in ;
    close_out file_out ;;

    let usage () =
    print_string "comment2 filein fileout";
    print_newline() ;;


    let main () =
    if Array.length (Sys.argv) <> 3 then usage ()
    else decommente Sys.argv.(1) Sys.argv.(2) ;;

    main ();;
    }











  3. En Objective CAML les chanes de caractres peuvent contenir n'importe quel caractres, y compris les suites (* ou *). Par exemple la chane de caractres "un te(*xte quelco*)nque" ne doit pas tre considr comme un commentaire. Modifier l'analyseur lexical pour prendre en compte les chanes de caractres.
    (** fichier comment3.mll **)
    {
    let ignore_lex lb = ignore (Lexing.lexeme lb)
    let sortie = ref stdout
    let init_sortie f = sortie := f
    let traite_normal lb = output_string !sortie (Lexing.lexeme lb)
    let traite_comment = ignore_lex
    exception Commentaires_mal_balances
    }

    rule normal = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; normal lexbuf }
    | "*)" { raise Commentaires_mal_balances }
    | '"' { traite_normal lexbuf ; chaine lexbuf ; normal lexbuf }
    | _ { traite_normal lexbuf ; normal lexbuf }
    | eof { () }

    and commentaire = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; commentaire lexbuf }
    | "*)" { ignore_lex lexbuf }
    | _ { traite_comment lexbuf ; commentaire lexbuf }
    | eof { raise Commentaires_mal_balances }

    and chaine = parse
    '"' { traite_normal lexbuf ; () }
    | "\\\"" { traite_normal lexbuf ; chaine lexbuf }
    | _ { traite_normal lexbuf ; chaine lexbuf }





  4. Utiliser ce nouvel analyseur pour enlever les commentaires d'un programme Objective CAML.
    (** fichier comment4.mll **)
    {
    let ignore_lex lb = ignore (Lexing.lexeme lb)
    let sortie = ref stdout
    let init_sortie f = sortie := f
    let traite_normal lb = output_string !sortie (Lexing.lexeme lb)
    let traite_comment = ignore_lex
    exception Commentaires_mal_balances
    }

    rule normal = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; normal lexbuf }
    | "*)" { raise Commentaires_mal_balances }
    | '"' { traite_normal lexbuf ; chaine lexbuf ; normal lexbuf }
    | _ { traite_normal lexbuf ; normal lexbuf }
    | eof { () }

    and commentaire = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; commentaire lexbuf }
    | "*)" { ignore_lex lexbuf }
    | _ { traite_comment lexbuf ; commentaire lexbuf }
    | eof { raise Commentaires_mal_balances }

    and chaine = parse
    '"' { traite_normal lexbuf ; () }
    | "\\\"" { traite_normal lexbuf ; chaine lexbuf }
    | _ { traite_normal lexbuf ; chaine lexbuf }


    {

    let decommente src dest =
    let file_in = open_in src in
    let lb = Lexing.from_channel file_in in
    let file_out = open_out dest in
    init_sortie file_out ;
    normal lb ;
    close_in file_in ;
    close_out file_out ;;

    let usage () =
    print_string "comment2 filein fileout";
    print_newline() ;;


    let main () =
    if Array.length (Sys.argv) <> 3 then usage ()
    else decommente Sys.argv.(1) Sys.argv.(2) ;;

    main ();;
    }

valuateur

On utilise ocamlyacc pour raliser un valuateur d'expressions. L'ide est d'associer directement l'valuation des expressions aux rgles de grammaire.

On choisit un langage d'expressions arithmtiques prfixes (et compltement parenthses) avec des oprateurs d'arit variable. Par exemple, l'expression (ADD e1 e2 .. en) est quivalente e1 + e2 + .. + en. Les oprateurs d'addition et de multiplication sont associatifs droite et ceux de soustraction et de division associatifs gauche.
  1. Dfinir dans un fichier opn_parser.mly l'analyse syntaxique et les rgles d'valuation d'une expression.

    %{
    let rec app_right f xs =
    match xs with
    [x] -> x
    | x::xs -> f x (app_right f xs)
    | _ -> failwith"missing argument" ;;
    let rec app_left f xs =
    match xs with
    [x] -> x
    | x1::x2::xs -> app_left f ((f x1 x2)::xs)
    | _ -> failwith"missing argument" ;;

    let t = Hashtbl.create 3 ;;

    (
    Hashtbl.add t "ADD" (app_right (+.));
    Hashtbl.add t "SUB" (app_left (-.));
    Hashtbl.add t "MUL" (app_right ( *.));
    Hashtbl.add t "DIV" (app_left (/.))
    ) ;;

    let apply o vs =
    try
    (Hashtbl.find t o) vs
    with
    Not_found -> (Printf.eprintf"Unknown operator %s\n" o; exit(1)) ;;
    %}

    %token Lpar Rpar
    %token <float> Num
    %token <string> Atom

    %start term
    %type <float> term
    %type <float list> terms

    %%
    term :
    Num { $1 }
    | Lpar Atom terms Rpar
    { (apply $2 $3) }
    ;
    terms :
    term { [$1] }
    | term terms { $1::$2 }
    ;
    %%


  2. Dfinir dans un fichier opn_lexer.mll l'analyse lexicale des expressions.

    {
    open Opn_parser
    }

    rule lexer = parse
    [' ' '\n'] { lexer lexbuf }
    | '(' { Lpar }
    | ')' { Rpar }
    | '-'?['0'-'9']*'.'?['0'-'9']*
    { Num (float_of_string (Lexing.lexeme lexbuf)) }
    | ['A'-'z']+ { Atom (Lexing.lexeme lexbuf) }


  3. crire un petit programme principal opn qui lit une ligne sur l'entre standard et affiche le rsultat de l'valuation.

    open Opn_lexer ;;
    open Opn_parser ;;

    Printf.printf"? "; flush stdout;
    let buf = Lexing.from_string (input_line stdin) in
    Printf.printf "= %f\n" (Opn_parser.term Opn_lexer.lexer buf) ;;

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora018.gif0000644000000000000000000000711307073350152014273 0ustar GIF89a aea!, 80I8{`(dihlp,ϬBx|臨pH, rl:ʧtJ֬v p+.(2z8-ۍ~˳|!~k h gf^{ebǨ|aΝ{`Քz_ w\݅z|Ż[AU᫵Xo "L")Av.& q"Y rǑ/, *Ht0_ő(.h HVy4h+s4&̓5J#ZǨL? ;:xEJ-V_6ڈD6Flୃ +47N]+#3>ɘi83pWaTґMZͺְټMڸͻNȅO|iic Gd1bB gV阙k0Y Z \sPwȧ!8(Tc$Bd*CȞ 7h#z#=*:)<kUg ++N;.y%7J瘙r*l*c&ࢡʨ* %J jz _cVΚz6 4*;̺Ֆz/ X/ K f/ Kyj8, r쫻^ ˹pL;2˫r{sn:Kn%\\fƅjĶI;}1UKY5\Kk6E_-4o0ҽpxBۯ)IyʙQ"M),.3f'rh턑K6)Ԣ9ӟWzkjhgi9CB9k9?,i N@Wo+/w=;o{}~/??fϿ$&b!pe剠#APԹ  2DCH8&Ya\Gy>p\Opx%HQz=c1O!!hϏ|#IL2%yl"BMsuSl52n? FKQp.l0ph')IsΕ > viM,;sjQA-Z9̛5P"XgLMG)-%KU((Ԁ`UКPzY'M#jӹ5@)6R j:X)bIXt⵱|7פU"m0cPQƒe=AEUIy޺Q?ɺEe=xD@ܬYӴfLAk?}օ$vWsro(u^w}=o2]WBqo' nk=*-{No`u 6X0"%LWV/J4 s1C@{p&>[C(8q< x_L_9"!LJ%2jAɟ e'gJFVn\]rP,r[xzu#fi0]fRkg*9ƽ\99g5Ɲm\4wS9ifehv5s_#塶s-ZrQ5mI;*gMn}fvT6mH;}U!K4ki336lokizΨhɱ̩y=Y]ک-(heɶ .ZΣNN}ndOڡ`Ǜnԫ3ܻ4߽Vu`w+Q6h[ټ%EV Ӫ& ~qn8}%[GmVw^ǼX`+6ywV-/?[qsoݹMy΋t@Gxr}-|׿ x0+~Gw>y$Wr<*[/u|cY<ՇŮGco s =?(ደ/!B^=F}lEnrld0{qۉY{'epGub7a&h3Gdw&p~JVw uwW[b&wxVu>XZWX\uw&mApm' :ǂW7q%t_xVoa׀ǃ?gOOG('+8WjKgvmWlXEi'k!WUhmf~6p&u' nӅE0(3hT[rkhdžUhke.6Ȁ/HEvv(XTTwu"\8VpgY8DJsun؄*5n(HX$w؈ g( *Uӹɠvɡ(Wr#%:<(Y"JtЙI)䉞y*ꢹWx$yJy҅lU٥j* i./fexxxj$I/_wZOI QY CFjSɨ:!j<9 f!Q0ZzJ ;ocaml-book-1.0/fr/html/book-ora068.gif0000644000000000000000000000574507073350152014311 0ustar GIF89aUUU!,ڋ޼Hp L/˨ P*̦syTDϪF :춆{<x0hxXSВ)9IYiyd * YjyAzʣ*;KC[b 0l|\wlbИLU-U-M>>[>κNɕ$m\o' >s Z7m*dQÈMϨ2zLtHHF"ɕvTa ˄b3f͛<9P rv<*:MEPԧTEěVWf 깠GÚ@*lr\eҝ^Xx o^a ~j+1N/~̸'F rd!:W.QH!ћ3x8GKozR$ YޓbϾO_wю0~F{f-]6=qޣ=uOۏG]oю|Qǯ:9pDkhLڧ}rWkrx7tB'`"+f8D݋أ+s ]w%X ) w߽8%w}a^Yc)IdZ~HH)pfAXzVޝ<~VT͡1=ʁiNJlm,Z jG*WjjHzn agZ+I!HʪzHK'Z2ʞl]<{mfaޅ@,``xێ[z^Y\pEq ˮc/oEہVDGXh /-xdQNL 0#kK2'KXQ (?EOS?Cw×{?ULfxyjHʔ!>g;"wI7`Ar;ΧE)!uI9עr b'-(8M$є3*+!\ʒZՂw: Zg&'V$@Hוt[-j4ŊGâD`CVѷ}n[uIah߭.oM&xW^jT9M .;]/{K1ob9XFpyC+;KheBGT*8_!` cJMSaW×Ĭ|AkK&_LPzPa w|]Oo_h> ߰S/rwU3WH#bcP^ A#?}9 h!6$}Dyy”O%D5b ClD*mLxEPn6:5v#(PeuD\EttAv>`vBXa1u)ƃCKS\Hdr+wrFr)$Wr-W֤kȆmoq(C:U3gqUwpL`JwdoU3Un$R/JJX"(,QQ7-Re2CI iPrQ!7/}+RM2l%7X1(8ڤӱM@Fj Ag0'OFeyg6+XTAC&16QL5vf8}| 4wP_XCx85$xp:S_[%I\ŐJs=P;ocaml-book-1.0/fr/html/book-ora159.html0000644000000000000000000000504507421273602014503 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

Le modle modulaire souffre d'une faible rutilisation de code et de difficults pour la programmation incrmentielle. L'article <<Programmation modulaire avec surcharge et liaison retarde>> ([AC96]) montre une extension simple du langage de modules, autorisant l'extension d'un module ainsi que la surcharge. Le choix du code d'un foncteur surcharg reprend les techniques de fonctions gnriques de CLOS. La correction du systme de types de ces modules tendus n'est pas tablie.

La problmatique du mlange des modles est bien argumente dans l'article <<Modular Object-Oriented Programming with Units and Mixing>> ([FF98]), dans le sens de la facilit de rutilisation du code. Le problme de l'extensibilit des composants est largement dcrit. Cet article est en version HTML l'adresse suivante :

Lien


http://www.cs.rice.edu/CS/PLT/Publications/icfp98-ff/paper.shtml


On s'aperoit dans ces propositions qu'il reste une part de typage dynamique pour la contrainte de type et/ou la rsolution de conflits de types. Il n'est probablement pas draisonnable d'ouvrir le typage statique pour obtenir des langages <<le plus souvent>> typs statiquement dans le but d'augmenter la rutilisabilit du code en autorisant un dveloppement incrmentiel de celui-ci.








Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora065.gif0000644000000000000000000000450607073350152014300 0ustar GIF89a_UUU!,_ڋ޼HBʶKL x¢Lʦ)HԪj )d|VGǗwhT6hp2c)8IY#BZs Z;$@{c{t; iˀ<&ܜ`=i ]- Ъ(#gNt~>lj6LU:Ǟ'@ތy{hJ8{޺`Z Sр-fD]%eJ<%=3(cDy#:D+Ę,s1"4CQNcMgrptS<(4gס:AG)T1j[g +O0șw^ڥPwSDzLU 0x.aDp 3w0IEӛRpj9%4 %\ a2etG> Bv5U 7> /yѡWJT&+D:bEC|bwZ`H`g` .v]"NH|UaZ{mbS&XVjb+[2' Έ}̗cc(dgdd$ NIeV^%Qx%@t)&bxui&Nmsq9gyuwzyʹ'}f9薅y"9|Rz,JzZJaf<ʧZj (:Zj$"R y, e> 8ZK"zFk7hN-+43\# w@H :<;,upx6Rq}{,Ss+fqac)ڼr \eJQݼݱ(S&I_,K:2U״ձ0.38Ra*uȩ&P,]<5r;gK _,P9A@ѕMtm8]L;O{6uT q4xIkѬ}8k4b:ߜ߄Gሼ{{P+:n5JmEB C}a_9Jer8V2ԐFf #&D8Qq4f+"P%I9H2ԈPrUq2-XDʅD(/^u&=*WW3gH]iH'zSs1zA`LmS ըJuTuDstZ7R9.7!vC%JnmKJ;t[ Vk`{X*v "c Y"(,UsMvr#TU&h'P mi yR{7gH؎ʧ@mmw>s} JyUh)VUms8Z& $ZV+W 9A.Rq e3`s/:\0wo_:*WZ/3Ll X`fw؀5 uĖ\L#,^ʧuzįڭfD§wLÝ)4w'iaӯ?U1qB127[6`:䡖5+0T0|fҦfiNIWns@ڱ.cDcVeh@:C4#'m#eJ5ifϡ,xE]ٴ΄YjSXֱr\kԹ$744sGL76%_͊G#kPj'n]޵PtKFٱi➰%Ov@Ɗĉ`6K;ŠvwU$-bx܃!wd%֖\ t&)!WQGrFkj[^ߊdn 9w5uc Bϳx+}Hoz ;ocaml-book-1.0/fr/html/book-ora099.html0000644000000000000000000003065207421273602014510 0ustar Profiling Prcdent Index Suivant

Profiling

Cet outil permet d'obtenir un certain nombre de mesures sur l'excution d'un programme : nombre de passages dans une fonction ou dans certaines structures de contrle (conditionnelles, filtrages et boucles). Les informations sont enregistres dans un fichier. De leur examen on peut dduire des erreurs algorithmiques ou les points cruciaux optimiser dans une application.

Pour pouvoir traiter le code, il est ncessaire de compiler l'excutable dans un mode spcial qui lui ajoute les instructions ncessaires aux divers dcomptes. Il existe deux modes de profiling, un pour le compilateur de code-octet et un autre pour le compilateur natif, ainsi que deux commandes pour la prsentation des rsultats. Dans l'analyse du code natif on rcupre aussi le temps pass dans chaque fonction.

Le profiling d'une application se droule donc en trois temps :
  1. compilation en mode profiling;
  2. excution du programme;
  3. prsentation des mesures.

Commandes de compilation

Les commandes de compilation en mode profiling sont les suivantes :
  • ocamlcp -p options pour le compilateur de code-octet ;
  • ocamlopt -p options pour le compilateur natif.
Ces compilateurs produisent le mme type de fichiers que les commandes habituelles (7). Les diffrentes options sont dcrites la figure 10.1.

f appel de fonctions
i branches du if
l boucles while et for
m branches du match
t branches du try
a toutes les options

Figure 10.1 : Options des commandes de profiling


Elles indiquent quelles structures de contrle doivent tre prises en compte. Par dfaut les options fm sont actives.

Excution d'un programme

Compilateur de code-octet

L'excution d'un programme compil dans un mode de profilage produira, si il termine, un fichier nomm ocamlprof.dump qui contient les diffrentes informations demandes.

On reprend l'exemple sur le produit des entiers d'une liste. On crit le fichier f1.ml suivant :

let rec interval a b =
if b < a then []
else a::(interval (a+1) b);;

exception Found_zero ;;

let mult_list l =
let rec mult_rec l = match l with
[] -> 1
| 0::_ -> raise Found_zero
| n::x -> n * (mult_rec x)
in
try mult_rec l with Found_zero -> 0
;;


et le fichier f2.ml qui utilise les fonctions de f1.ml :

let l1 = F1.interval 1 30;;
let l2 = F1.interval 31 60;;
let l3 = l1 @ (0::l2);;

print_int (F1.mult_list l1);;
print_newline();;

print_int (F1.mult_list l3);;
print_newline();;


La compilation de ces fichiers en mode profilage est la suivante :
ocamlcp -i -p a -c f1.ml
val profile_f1_ : int array
val interval : int -> int -> int list
exception Found_zero
val mult_list : int list -> int
Avec l'option -p, le compilateur ajoute une nouvelle fonction (profile_f1_) pour l'initialisation des compteurs du module F1. Il en est de mme pour le fichier f2.ml :
ocamlcp -i -p a -o f2.exe f1.cmo f2.ml
val profile_f2_ : int array
val l1 : int list
val l2 : int list
val l3 : int list

Compilateur natif

La compilation en code natif donne le rsultat suivant :
$   ocamlopt -i -p  -c f1.ml
val interval : int -> int -> int list
exception Found_zero
val mult_list : int list -> int
$  ocamlopt -i -p -o f2nat.exe f1.cmx f2.ml
Seule l'option -p sans argument est utilise. L'excution de f2nat.exe produit un fichier de nom gmon.out. Celui-ci possde un format traitable par les outils habituels Unix (voir page ??).

Prsentation des rsultats

Comme les informations recueillies par les deux modes de profiling sont diffrentes, leur prsentation s'en ressent. Dans le premier mode (code-octet), des commentaires sur le nombre de passages dans les structures de contrle sont ajouts au texte du programme. Dans le second (natif), on associe chaque fonction le temps pass dans son corps et son nombre d'appels.

Compilateur code-octet

La commande ocamlprof donne l'analyse des rsultats des mesures. Elle utilise les informations contenues dans le fichier camlprof.dump. Cette commande prend en entre le source du programme, lit le fichier de mesure et produit un nouveau texte de programme contenant en commentaires les compteurs demands.

Sur notre exemple cela donnera :
$ ocamlprof f1.ml

let rec interval a b = 
  (* 62 *) if b < a then (* 2 *) []
  else (* 60 *) a::(interval (a+1) b);;

exception Found_zero ;; 

let mult_list l = 
 (* 2 *) let rec mult_rec l = (* 62 *) match l with 
    [] -> (* 1 *) 1
  | 0::_ -> (* 1 *) raise Found_zero
  | n::x -> (* 60 *) n * (mult_rec x)
 in
  try mult_rec l with Found_zero -> (* 1 *) 0
;; 
Ces compteurs refltent bien les calculs demands dans F2. Il y a deux appels list_mult et 62 la fonction auxiliaire mult_rec. L'exploration des branches du filtrage match montre les 60 passages dans le cas courant, l'unique passage dans le filtre [] au premier calcul, et l'unique passage dans le filtre de tte gal 0, dclenchant une exception dont le filtrage se voit dans le compteur du try.

La commande camlprof accepte deux options. La premire -f fichier indique le nom du fichier contenant les mesures. La seconde -F chane spcifie une chane ajouter dans les commentaires associs aux structures de contrle traites.

Compilateur natif

Pour connatre le temps pass dans les appels des fonctions de multiplication des lments d'une liste, on crit le fichier f3.ml suivant :

let l1 = F1.interval 1 30;;
let l2 = F1.interval 31 60;;
let l3 = l1 @ (0::l2);;

for i=0 to 100000 do
F1.mult_list l1;
F1.mult_list l3
done;;

print_int (F1.mult_list l1);;
print_newline();;

print_int (F1.mult_list l3);;
print_newline();;
C'est le mme fichier que f2.ml avec une boucle de 100000 itrations.

L'excution du programme cre le fichier gmon.out. Celui-ci est dans un format lisible par la commande gprof que l'on trouve sur les systmes Unix. L'appel suivant de gprof affiche les informations de temps pass et le graphe des appels. Comme la sortie est assez longue, on ne montre que la premire page qui contient le nom des fonctions appeles au moins une fois et le temps pass dans chacune d'elles.
$ gprof  f3nat.exe 
Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  us/call  us/call  name    
 92.31      0.36     0.36   200004     1.80     1.80  F1_mult_rec_45
  7.69      0.39     0.03   200004     0.15     1.95  F1_mult_list_43
  0.00      0.39     0.00     2690     0.00     0.00  oldify
  0.00      0.39     0.00      302     0.00     0.00  darken
  0.00      0.39     0.00      188     0.00     0.00  gc_message
  0.00      0.39     0.00      174     0.00     0.00  aligned_malloc
  0.00      0.39     0.00      173     0.00     0.00  alloc_shr
  0.00      0.39     0.00      173     0.00     0.00  fl_allocate
  0.00      0.39     0.00       34     0.00     0.00  caml_alloc3
  0.00      0.39     0.00       30     0.00     0.00  caml_call_gc
  0.00      0.39     0.00       30     0.00     0.00  garbage_collection
...
Le premier enseignement est que presque tout le temps d'excution se droule dans la fonction F1_mult_rec_45, qui correspond la fonction F1.mult_rec du fichier f1.ml. On s'aperoit d'autre part que de nombreuses fonctions sont appeles. Les premires de la liste sont les fonctions de la bibliothque d'excution de gestion mmoire (9).






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora108.html0000644000000000000000000010064107421273602014473 0ustar Basic revisit Prcdent Index Suivant

Basic revisit

Nous voulons prsent utiliser le couple ocamllex et ocamlyacc afin de remplacer la fonction parse donne page ?? pour Basic, par des fonctions engendres partir des fichiers de spcification du lexique et de la syntaxe du langage.

Pour y arriver, nous ne pourrons pas reprendre tels quels le type des units lexicales que nous avions dfini. Nous allons devoir dfinir un type plus prcis permettant de distinguer les diffrents oprateurs, les commandes et les mots cls.

Nous aurons galement besoin d'isoler les dclarations de type concernant la syntaxe abstraite dans un fichier basic_types.mli. Il contiendra les dclarations du type phrases et de tous les types ncessaires celui-l.

Le fichier basic_parser.mly

L'en-tte
du fichier contient un appel aux dclarations de types pour la syntaxe abstraite ainsi que deux fonctions auxiliaires de conversion de chanes de caractres vers leur quivalent en syntaxe abstraite.

%{
open Basic_types ;;

let phrase_of_cmd c =
match c with
"RUN" -> Run
| "LIST" -> List
| "END" -> End
| _ -> failwith "line : unexpected command"
;;

let op_bin_of_rel r =
match r with
"=" -> EGAL
| "<" -> INF
| "<=" -> INFEQ
| ">" -> SUP
| ">=" -> SUPEQ
| "<>" -> DIFF
| _ -> failwith "line : unexpected relation symbol"
;;

%}


Les dclarations
contiennent trois parties : la dclaration des lexmes; la dclaration de leurs rgles d'associativit et de priorit; la dclaration de la rgle axiome line qui correspond l'analyse d'une ligne de programme ou de commande.

Les units lexicales sont les suivantes :


%token <int> Lint
%token <string> Lident
%token <string> Lstring
%token <string> Lcmd
%token Lplus Lmoins Lmult Ldiv Lmod
%token <string> Lrel
%token Land Lor Lneg
%token Lpar Rpar
%token <string> Lrem
%token Lrem Llet Lprint Linput Lif Lthen Lgoto
%token Legal
%token Leol
Leurs noms parlent d'eux-mmes et ils sont dcrits par le fichier basic_lexer.mll (voir page ??).

Les rgles de priorit entre oprateurs reprennent les valeurs donnes par les fonctions priority_ob et priority_ou dfinies lorsque nous avions donne la grammaire de notre Basic (voir page ??).


%right Lneg
%left Land Lor
%left Legal Lrel
%left Lmod
%left Lplus Lmoins
%left Lmult Ldiv
%nonassoc Lopp
Le symbole Lopp nous servira traiter le moins unaire. Ce n'est pas un terminal de la grammaire, mais un << pseudo non terminal >> permettant de grer la surcharge entre oprateurs lorsque deux utilisations d'un mme symbole ne doivent pas avoir la mme priorit selon le contexte. Et c'est le cas du symbole moins (-). Nous reviendrons sur ce point lorsque nous aurons nonc les rgles de la grammaire.

Le non terminal axiome est line. La fonction engendre retournera l'arbre de syntaxe abstraite de la ligne analyse.

 %start line
 %type <Basic_types.phrase> line
Les rgles
de grammaire se dcomposent en trois non terminaux : line pour une ligne; inst pour une instruction du langage; exp pour les expressions. L'action associe chacune des rgles se contente de construire le morceau de syntaxe abstraite correspondant.

%%
line :
Lint inst Leol { Ligne {num=$1; inst=$2} }
| Lcmd Leol { phrase_of_cmd $1 }
;

inst :
Lrem { Rem $1 }
| Lgoto Lint { Goto $2 }
| Lprint exp { Print $2 }
| Linput Lident { Input $2 }
| Lif exp Lthen Lint { If ($2, $4) }
| Llet Lident Legal exp { Let ($2, $4) }
;

exp :
Lint { ExpInt $1 }
| Lident { ExpVar $1 }
| Lstring { ExpStr $1 }
| Lneg exp { ExpUnr (NON, $2) }
| exp Lplus exp { ExpBin ($1, PLUS, $3) }
| exp Lmoins exp { ExpBin ($1, MOINS, $3) }
| exp Lmult exp { ExpBin ($1, MULT, $3) }
| exp Ldiv exp { ExpBin ($1, DIV, $3) }
| exp Lmod exp { ExpBin ($1, MOD, $3) }
| exp Legal exp { ExpBin ($1, EGAL, $3) }
| exp Lrel exp { ExpBin ($1, (op_bin_of_rel $2), $3) }
| exp Land exp { ExpBin ($1, ET, $3) }
| exp Lor exp { ExpBin ($1, OU, $3) }
| Lmoins exp %prec Lopp { ExpUnr(OPPOSE, $2) }
| Lpar exp Rpar { $2 }
;
%%
Ces rgles n'appellent pas de commentaire particulier sauf celle-ci :
exp :
 ...
 | Lmoins exp %prec Lopp { ExpUnr(OPPOSE, $2) }
Elle concerne l'utilisation unaire du symbole -. Le mot cl %prec que l'on trouve dans cette rgle indique que cette construction doit recevoir la priorit de Lopp (ici, la priorit maximale).

Le fichier basic_lexer.mll

L'analyse lexicale ne contient qu'un seul ensemble : lexer qui correspondra au type prs l'ancienne fonction lexer (voir page ??). L'action smantique associe la reconnaissance de chaque unit lexicale est simplement le renvoi du constructeur correspondant. Comme le type des units lexicales est dclar dans le fichier des rgles de syntaxe, il faut inclure ce dernier. On rajoute une petite fonction auxiliaire charge de supprimer les guillemets autour des chanes de caractres.

{
open Basic_parser ;;

let string_chars s = String.sub s 1 ((String.length s)-2) ;;
}

rule lexer = parse
[' ' '\t'] { lexer lexbuf }

| '\n' { Leol }

| '!' { Lneg }
| '&' { Land }
| '|' { Lor }
| '=' { Legal }
| '%' { Lmod }
| '+' { Lplus }
| '-' { Lmoins }
| '*' { Lmult }
| '/' { Ldiv }

| ['<' '>'] { Lrel (Lexing.lexeme lexbuf) }
| "<=" { Lrel (Lexing.lexeme lexbuf) }
| ">=" { Lrel (Lexing.lexeme lexbuf) }

| "REM" [^ '\n']* { Lrem (Lexing.lexeme lexbuf) }
| "LET" { Llet }
| "PRINT" { Lprint }
| "INPUT" { Linput }
| "IF" { Lif }
| "THEN" { Lthen }
| "GOTO" { Lgoto }

| "RUN" { Lcmd (Lexing.lexeme lexbuf) }
| "LIST" { Lcmd (Lexing.lexeme lexbuf) }
| "END" { Lcmd (Lexing.lexeme lexbuf) }

| ['0'-'9']+ { Lint (int_of_string (Lexing.lexeme lexbuf)) }
| ['A'-'z']+ { Lident (Lexing.lexeme lexbuf) }
| '"' [^ '"']* '"' { Lstring (string_chars (Lexing.lexeme lexbuf)) }
Remarquons que nous avons d isoler le symbole = qui sert la fois dans les expressions et dans l'instruction d'affectation.

Seules deux de ces expressions rationnelles ncessitent des explications complmentaires. La premire concerne les lignes de commentaires ("REM" [^ '\n']*). Elle reconnat le mot cl REM suivi par un nombre quelconque de caractres diffrents du retour chariot. La seconde concerne les chane de caractres ('"' [^ '"']* '"') considres comme une suite de caractres diffrents de " et encadre par des ".

Compilation, intgration

La compilation du couple analyseur lexical, analyseur syntaxique doit tre faite en suivant un certain ordre. Ceci est d la dpendance mutuelle dans la dclaration des lexmes. Ainsi, dans notre exemple, il faudra taper la squence de commandes :
ocamlc -c basic_types.mli
ocamlyacc basic_parser.mly
ocamllex basic_lexer.mll
ocamlc -c basic_parser.mli
ocamlc -c basic_lexer.ml
ocamlc -c basic_parser.ml
Ce qui engendrera les fichiers basic_lexer.cmo et basic_parser.cmo qui pourrons tre intgrs l'application.

Nous disposons maintenant de tout le matriel ncessaire pour reformuler notre application.

Nous supprimons tous les types et toutes les fonctions des paragraphes << analyse lexicale >> (page ??) et << analyse syntaxique >> (page ??) de l'application Basic et, dans la fonction une_commande (page ??), nous remplaons l'expression

match parse (input_line stdin) with
par

match line lexer (Lexing.from_string ((input_line stdin)^"\n")) with
Remarquons que nous devons remettre en fin de ligne le caractre '\n' que la fonction input_line avait supprim. Ceci est ncessaire, car nous utilisons ce caractre pour marquer la fin d'une ligne de commande (Leol).






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora074.gif0000644000000000000000000000611107073350152014272 0ustar GIF89aUUU!,ڋ޼Hʶ L4 !L*ťJjaܮ cNgT[^dt7t8'G2(A&7pPy9Ӹ8J(!9 i:`HyA[zfUۊ{[l+ܜL}L]=4;=e=N΅`iMVN_t~@yI_&~*᱇0|u*|B+*(42$E,e#ʕ,[: dI43cSKΛ%=RFhJq-k)ygVy֦x'XnYzmgn\{!߼Zjrګg\ :ju\bEܩ'6~藍;;t1Mtװ0 <ǦVI2LA a jN? LGw9uAı5*W]uIvDMT D-3- O}F~ڝp$Z7!'8wb wq7oS2督y7颷WD_꥿zwoz>w;z.9 C{?/u_jccGx:V fWOKboߜTKϼRq CK: @Q,踊`p3,5A*!WJ#R w悅)l qAp\a yA"(KS"J>'gEB 8#qQ#=mXQXA5dzSp| Ot=ҁ8$ 9 TC xHK$9@E*rh|% HrcvJM8VVTebq&wT.SKe&,bnrzP;PJn=S8X2ɉdێ2gFsgI#aY }xN6+Nrq w$ ,8 3#J!l\P&6IAŮ!x=rWy A4D9(MGϑ卄y,@:b(O\؄6$Nh\&G@ײCϰΧK19k܄cJ{=z& .2Ϡ(Bҹ%/FB2^R' Z_Ɇ*@Wfyx:f?#޷<шދp){_T)ZlTo kf?#}/w++GƟWCn}c+#rǃiQp~VP}v"o/wKh+A7o2SlsceO"(}TЀ2";Q B>fZb+ǁDZ, 6'7g['`udV.8g{XaGw1bV)v`6vO3#v1Cj]Zf(0 4t%8%zKa]ǀk'b&&yvnSbfok'1j.{Ux3/වG}zB1׃tYXU|+2|!*|(qG0S Introduction Prcdent Index Suivant

Introduction

La conception et la programmation modulaire permettent le dcoupage d'une application en units logicielles et, autant qu'il est possible, le dveloppement spar de ces units. Un module est compilable sparment des autres units constituant l'application. En consquence, le dveloppeur d'une application utilisant les ressources d'un module n'est pas oblig de disposer des sources du module, seul le code compil lui suffit pour obtenir l'excutable final de son application. Cependant, il doit connatre, pour construire le source de sa propre application, les identificateurs (types, fonctions, exceptions, voire modules) fournis par le module. Pour ce faire, un module possde une interface qui dcrit les types et les valeurs qu'il fournit.

L'explicitation d'une interface permet de rendre son utilisation indpendante de son implantation. L'utilisateur d'un module ne connat que les noms des fonctions fournies sans avoir accs leur implantation. Ainsi, s'il ne change rien l'ensemble des noms connus, l'administrateur d'un module peut changer du tout au tout son implantation sans que ses utilisateurs aient besoin de s'en apercevoir. Ce qui est un avantage pour la maintenance des applications. L'interface permet, l'instar de dclarations locales, de masquer certains lments d'implantation que le concepteur du module ne souhaite pas publier. La principale application de ce mcanisme est la ralisation de types de donnes abstraits.

Enfin, les mcanismes de modules volus (et celui d'Objective CAML en fait partie) permettent la dfinition de modules paramtrs ou gnriques. Ce sont des modules prenant d'autres modules en paramtre ce qui accrot les possibilits de rutilisation de code.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora061.gif0000644000000000000000000000350707073350152014274 0ustar GIF89a6 UUU!,6 ڋ޼jH扦ʦ LC@]~¢d% j׭ qyN\M;z^'4!T8h6#Іh9Hy(Y)rG80(ЉpJ2+kj ))K6),k YJ GDmkjz3}<~Zp <j{|:0x⭠B$\ۮ#ZHlOuB"ʕ,K|$47qœtsϡ6=(ҥ2e4ӧQgTj Xp}SBbR٘i| 7n۟k=ԝ.C:[/(0g0LKd<ұɐPvW/|.С7&(ΦWK]ֲŽEE< ^ALʑv$P#7|;Ot:@Ek@,3D  =9a'R 3z9#JoЩ6Nҳ~("2j#~|j" C1crRŭꜟjWXC/# ;˨Z Qa65cv=Po*sv$ 5\=^ON˰+._6q mݮy8~"+͹Fh- eSİ=ƮW:R'n 1۩r"ӳ-z絺<п/̏zէφ?=UP.|>wb@/t 8AP H թ{* [8="w$8x5p&`C a? W ̜'ڰP (EU+"%QVlbD <*LE8qb>1^%w=f腊 #yA%eKrfj I_'%)yJ2sd&]IUr,kF\R%/+K2""&`iL%s.\񜙖fBӄJ5"kQn暼#pZSb"'zMtSob:yNxSlg=?H^y$?Tnu? "r\P45#E׸l!D[tQX Fфb^J HA֏pi瘡 PlV[XNz[ gۚʈ* Cm-ui>Trttt՘J3&WRj6^]!f0ê״:4QE=b St k[xgIPǓ̀{)F&Vh<;҉*VbXZVlf-JQNmaMWԢUZeS]R؆1,_IP^TBy[Z"nA[ U?{\˕.>\Fȶ,;ocaml-book-1.0/fr/html/book-ora047.gif0000644000000000000000000000221407073350152014272 0ustar GIF89atyyy!,tڋؼH扦ʶLĢ&<*Lf JkϩUܮt l+l ޣ(8HXXB'b8iy7Y *2* pXb*+ۺH[;;Kx Wn; buUOlj@k']BM!qT4ʑ'U~hfA6j(DC7qir`Q{xk6%'Ѫ;>-ՓR]t,׫dy)jάZV^PY1lʹv5x/߷aR…?^w+GIVrʲxIoJ.`u?)>N);1,+ݛ"e½H5gvoŒcBnHáv'9w{/a?OۘGi}P9?S 槟1t0 L@>`4b 0AaUfavRHb'Ȣ+ /H 3ֈf#7;I$CdI.L覃z@鬟룋`w{/al$<7of}o}[P;ocaml-book-1.0/fr/html/book-ora077.gif0000644000000000000000000000276707073350152014312 0ustar GIF89a!,H0I8ͻB(_ih Ktm8%p8Fr&;ϦtJv˅D7߮x gچ xKuW:F-?!O̱txv]^pJ 9 ,&30 \" U8≉{,j3CÍ 1`M •0t33I\ћOSU^Þ"{vڶMd7o'M{LS#?iA2؈2iAzzЩxc0ۢ2B($F)i:pVkmz[vknƒ[ԡf ;/Ền[\&Fc /t-m gL^q/8p{,o r\̛;7p @MI PGtdJ S'V XKMdtݵ`!؄mc6lcv]}AwM|5<303wm8BgYٍS11摇q yM nno!_1AOQϛao/C'$;:炬/g83̯o~Fs>f `@)pVs_Ot,x2,}pOM/t! ]ŏ-!g␆bL" *hش#q"<8r3gL/ׅQ+"cF8}h #/9ZG;"rui$' R򒘼%3N.b (1R<*3UR|,Y2 HHrq%/#`3,19c*ӏ:0wHyȌ& Rsum Prcdent Index Suivant

Rsum

Ce chapitre a prsent l'interface entre le langage Objective CAML et le langage C. Celle-ci fournit les outils pour manipuler les valeurs Objective CAML en C. De plus les types abstraits d'Objective CAML permettent la manipulation inverse. Son principal avantage est de permettre d'utiliser le mcanisme de GC d'Objective CAML pour des valeurs cres en C, langage dpourvu d'une telle rcupration automatique de mmoire. Cela permet de composer une application partir de composants dvelopps dans les deux langages. Enfin les exceptions Objective CAML peuvent tre manipules et dclenches en C. L'inverse n'est pas possible avec ces outils.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora022.gif0000644000000000000000000000333307073350152014266 0ustar GIF89a UUU!, ڋ޼{G扦:Lv Ǘ)#:u%giTw߱W~њHl6'mݚ_q$U ۍt:=P՜f|8̼?fu؇$A }| >xJ_|т{xؕP7?mHXjmT lWȃ3s]ؕ>u$2׊DDJП{e"_䄯hyRX%W[U%S^tfLjiBb'oYYi96)'ݓw7'yuǢ iw:&%y|ii3&hd(֚>`Ӣ)#z):+{!ɩm Zy+R[ 02my~~cnVJ/#wFi떪;*n<)*{1Ns|I߬u!/6F'Kns{򺚘}yM}xӵfx4u~=]4ȉP/砮̵sؐ:{:{nͮK7~ .:k{PL|/g+Ƀg\<_OJڃ+Ք_+7ա<|b-ЃϞLo_ŀ۟)q@fdT[f@ NNh5j0_O׼&u;±U9a19,\6ؐ4kV=&>`G(?B7Pl!{*Fà֤HtZfAeCcЃҒlkkĝ ǹM α,"i+X$௨'.Rh X%LKrH5QSÜ iC#0,.CsT/U ̎b&ST3Gjbd2 l6s4iΧ,g5ؑ ne}={Ҟ$B?3+b:JPL(%NqԦDIш^ Exercices Prcdent Index Suivant

Exercices

Les trois exercices proposs ici manipulent respectivement les descripteurs de fichier, les processus, les tubes de communication, et les signaux. Les deux premiers sont des classiques de la programmation systme sous Unix. Il peut tre intressant de comparer le code Objective CAML avec le code C que l'on trouve dans les distributions d'Unix ou de Linux.

Compter les mots : la commande wc

On veut (re)programmer la commande Unix wc qui compte le nombre de lignes, mots ou caractres que contient un fichier texte. Les mots sont spars par un espace, une tabulation ou un retour chariot. On ne compte pas les caractres sparateurs.
  1. crire une premire version (wc1) de cette commande qui ne traite qu'un seul fichier dont le nom est pass en argument sur la ligne de commande. On dfinit une liste de sparateurs ainsi que trois variables globales qui serviront aux diffrents comptages.

    # let seps = " \t" ;;
    val seps : string = " \t"
    # let nl = ref 0 ;;
    val nl : int ref = {contents=0}
    # let nw = ref 0 ;;
    val nw : int ref = {contents=0}
    # let nc = ref 0 ;;
    val nc : int ref = {contents=0}
    La fonction counts est charge du comptage d'une ligne.

    # let counts s =
    let was_sep = ref true in
    let n = String.length s in
    for i=0 to n-1 do
    let is_sep = String.contains seps s.[i] in
    if is_sep && (not !was_sep) then incr nw ;
    was_sep := is_sep
    done ;
    if not !was_sep then incr nw ;
    nc := !nc+n+1;
    incr nl ;;
    val counts : string -> unit = <fun>
    La fonction count itre la fonction de comptage d'une ligne sur l'ensemble des lignes d'un fichier.

    # let count f =
    nl := 0; nw := 0; nc := 0;
    let f_in = open_in f in
    try
    while true do
    counts (input_line f_in)
    done
    with
    End_of_file -> close_in f_in ;;
    val count : string -> unit = <fun>
    La fonction principale appelle la fonction de comptage sur le nom de fichier pass en argument et affiche les rsultats.

    # let print_count() =
    Printf.printf"\t%d" !nl ;
    Printf.printf"\t%d" !nw ;
    Printf.printf"\t%d\n" !nc ;;
    val print_count : unit -> unit = <fun>

    # let main() =
    try
    if Array.length Sys.argv < 2
    then print_string "wc1: missing file name\n"
    else ( count Sys.argv.(1) ; print_count() )
    with e -> Printf.printf "wc1: %s\n" (Printexc.to_string e) ;;
    val main : unit -> unit = <fun>
  2. crire une version plus labore (wc2) qui peut prendre en argument tout ou partie des trois options : -l, -c, -w ainsi que plusieurs noms de fichiers. Les options indiquent, respectivement, si l'on veut voir afficher le nombre de lignes, caractres ou mots. L'affichage de chaque rsultat sera prcd du nom du fichier concern. On reprend les variables globales et les fonctions counts et count de la question prcdente.

    On se donne trois variable globales donnant le statut des trois options possibles.

    # let l_opt = ref false
    and w_opt = ref false
    and c_opt = ref false ;;
    val l_opt : bool ref = {contents=false}
    val w_opt : bool ref = {contents=false}
    val c_opt : bool ref = {contents=false}
    On redfinit l'affichage en fonction du statut des options.

    # let print_count f =
    Printf.printf "%s:" f ;
    if !l_opt then Printf.printf"\t%d" !nl ;
    if !w_opt then Printf.printf"\t%d" !nw ;
    if !c_opt then Printf.printf"\t%d" !nc;
    print_newline() ;;
    val print_count : string -> unit = <fun>
    La ligne de commande est analyse pour mettre jour le statut des options ainsi que la liste des fichiers traiter.

    # let f_list = ref ([]:string list) ;;
    val f_list : string list ref = {contents=[]}

    # let read_args () =
    let usage_msg = "wc2 [-l] [-w] [-w] files..." in
    let add_f f = f_list := f::!f_list in
    let spec_list =
    [ ("-l", Arg.Set l_opt, "affichage nombre de lignes") ;
    ("-w", Arg.Set w_opt, "affichage nombre de mots") ;
    ("-c", Arg.Set c_opt, "affichage nombre de caractres") ] in
    Arg.parse spec_list add_f usage_msg ;;
    val read_args : unit -> unit = <fun>
    La fonction principale itre le comptage sur la liste des fichiers.

    # let main() =
    try
    read_args() ;
    List.iter (fun f -> count f; print_count f) !f_list
    with
    e -> Printf.printf "wc2: %s\n" (Printexc.to_string e) ;;
    val main : unit -> unit = <fun>

Pipeline pour correcteur orthographique

Cet exercice cherche enchaner une suite d'actions. Chaque action prend en entre le rsultat de l'action prcdente. La communication s'effectue avec des pipes o seule la sortie d'un processus est redirige vers l'entre du processus suivant, la manire du symbole | des interprtes de commande Unix.

  1. crire une fonction pipe_two_progs de type string * string list -> string * string list -> unit telle que pipe_two_progs (p1,[a1; ...; an]) (p2,[b1; ...; bp]) lance les programmes p1 a1 ... an et p2 b1 ... bp en redirigeant la sortie standard de p1 sur l'entre standard de p2. Les ai et bi sont les arguments de la ligne de commande de chaque programme. Voici une faon trs << unixienne >> d'implanter cette fonction.

    # let pipe_two_progs (p1, args1) (p2, args2) =
    let in2, out1 = Unix.pipe() in
    match Unix.fork() with
    0 ->
    Unix.close in2 ;
    Unix.close Unix.stdout ;
    ignore(Unix.dup out1) ;
    Unix.close out1 ;
    Unix.execvp p1 (Array.of_list args1)
    | _ ->
    Unix.close out1 ;
    Unix.close Unix.stdin ;
    ignore(Unix.dup in2) ;
    Unix.close in2 ;
    Unix.execvp p2 (Array.of_list args2) ;;
    val pipe_two_progs : string * string list -> string * string list -> unit =
    <fun>


  2. On reprend la fonction orthographe de l'exercice de la page ?? pour crire un premier programme. La modifier pour que la liste des mots incorrects soit envoye, sans traitement pralable, sous forme d'une ligne par mot sur la sortie standard .

    # let orthographe dico nom =
    let f = open_in nom in
    try
    while true do
    let s = input_line f in
    let ls = mots s in
    List.iter (Printf.printf"%s\n") (verifie dico ls)
    done ;
    failwith "cas impossible"
    with
    End_of_file -> close_in f
    | x -> close_in f ; raise x ;;
    val orthographe : arbre_lex -> string -> unit = <fun>


  3. Le deuxime programme prend une suite de chanes de caractres sur son entre standard, et la trie selon l'ordre lexicographique. On pourra utiliser la fonction Sort.list qui trie une liste selon un prdicat donn. La liste trie est ensuite affiche sur la sortie standard.

    # let trie () =
    let l = ref [] in
    try
    while true do l := Sort.list (<) ((input_line stdin)::!l) done
    with
    End_of_file -> List.iter (Printf.printf"%s\n") !l ;;
    val trie : unit -> unit = <fun>


  4. Tester la fonction pipe_two_progs avec ces deux programmes.

    # pipe_two_progs ("orthographe",["";Sys.argv.(1)]) ("tri",[]) ;;


  5. crire une fonction pipe_n_progs pour enchaner une liste de programmes. Pour allger un peu l'criture on se donne deux fonctions de redirection de l'entre et de la sortie standard.

    # let dup_stdin in_descr =
    if in_descr<>Unix.stdin then Unix.dup2 in_descr Unix.stdin ;
    Unix.close in_descr ;;
    val dup_stdin : Unix.file_descr -> unit = <fun>

    # let dup_stdout out_descr =
    if out_descr<>Unix.stdout then Unix.dup2 out_descr Unix.stdout ;
    Unix.close out_descr ;;
    val dup_stdout : Unix.file_descr -> unit = <fun>
    Pour itrer le pipeline, on dfinit une fonction rcursive dont le premier argument donne le canal d'entre du premier processus chaner.

    # let rec pipe_n_progs_loop in_descr = function
    [p,args] ->
    dup_stdin in_descr ;
    Unix.execvp p (Array.of_list args)
    | (p,args)::ps ->
    let in2, out1 = Unix.pipe() in
    ( match Unix.fork() with
    0 ->
    Unix.close in2 ;
    dup_stdin in_descr ;
    dup_stdout out1 ;
    Unix.execvp p (Array.of_list args)
    | _ ->
    Unix.close out1 ;
    pipe_n_progs_loop in2 ps )
    | _ -> () ;;
    val pipe_n_progs_loop :
    Unix.file_descr -> (string * string list) list -> unit = <fun>

    # let pipe_n_progs ps = pipe_n_progs_loop Unix.stdin ps ;;
    val pipe_n_progs : (string * string list) list -> unit = <fun>


  6. crire un programme qui supprime les occurrences multiples des lments d'une liste.

    # let rmdup () =
    let l = ref [] in
    try
    while true do
    let x = input_line stdin in
    if not (List.mem x !l) then l := x::!l
    done
    with End_of_file
    -> List.iter (Printf.printf"%s\n") !l ;;
    val rmdup : unit -> unit = <fun>


  7. Tester la fonction pipe_n_progs avec ces trois programmes.

    # pipe_n_progs [ ("orthographe",["";Sys.argv.(1)]); ("tri",[]); ("rmdup",[]) ] ;;

Trace interactive

Lors d'un calcul complexe, il peut tre utile d'interagir avec le programme pour vrifier la progression de ce calcul. On reprend pour cela l'exercice de la page ?? sur le calcul des nombres premiers contenus dans un intervalle.
  1. Modifier le programme pour qu'une variable globale result contienne tout instant le dernier des nombres premiers dj trouvs.

    # let result = ref 0 ;;
    val result : int ref = {contents=0}
    # let rec eras = function
    [] -> []
    | p::q ->
    result := p ;
    p :: (eras (List.filter (fun x -> x mod p <> 0) q)) ;;
    val eras : int list -> int list = <fun>


  2. crire une fonction sigint_handle pour le traitement du signal sigint qui affiche le contenu de result.

    # let sigint_handle (_ : int) =
    Printf.printf"Current prime number : %d\n" !result;
    flush stdout ;;
    val sigint_handle : int -> unit = <fun>


  3. Modifier le traitement par dfaut du signal sigint en lui associant la fonction de la question prcdente sigint_handle.

    # Sys.set_signal Sys.sigint (Sys.Signal_handle sigint_handle) ;;
    - : unit = ()


  4. Compiler le programme, puis lancer l'excutable avec une borne suprieure importante pour l'intervalle de recherche. Pendant cette excution, envoyer le signal sigint au processus, soit en utilisant la commande Unix kill, soit en tapant le caractre CTRL-C.
    $ ocamlc premiers.ml
    $ premiers 15000
    Current prime number : 2539
    Current prime number : 8263
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 ............
    

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora130.html0000644000000000000000000000342307421273602014466 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

La premire section prsente et commente la ralisation du module Stack de la distribution d'Objective CAML. On y montre comment obtenir une autre implantation du << mme >> module. La deuxime section introduit le langage de module d'Objective CAML pour les modules simples et en montre quelques utilisations. En particulier, on tudie comment se pose et se rsoud le partage de types entre modules. La troisime section introduit la syntaxe et l'utilisation des modules paramtrs que l'on appelle galement foncteurs. La quatrime section donne la ralisation de l'exemple classique en programmation modulaire : la gestion d'un compte bancaire avec ses diffrentes vues (le banquier et le client) et ses diffrents paramtres (devise, etc. )


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora031.gif0000644000000000000000000000212107073350152014260 0ustar GIF89ajUUU!,jڋ޼{|H扦L-Ģ ̦ \BԪ#jn ߱[~Dϣ_'8Hx#)9IYiy *:JZjzz *K;kW{;+<8 0t dJVe00Bt(`$E ӹ"6!ȁBt1 '%Ȟt8eF y4h(f@fh Jz]ZOUfxR7PCGi46R1dr k6h]2Jlf>*꯯ꚧ> i0*:ki'9;@#K .Z l:\/k"[ /l0Ǖ1gLq%qx+<\2ZN Exercices Prcdent Index Suivant

Exercices

Les philosophes dbloqus

Pour rsoudre l'ventuel blocage des philosophes orientaux, il suffit de limiter quatre l'accs simultan la table. Implantez cette solution. On rajoute un compteur indiquant le nombre de philosophes prsents ainsi que deux fonctions, entrer et sortir, charges respectivement de limiter les entres et signaler les sorties.

#

let entrer, sortir =
let n = ref 0 in
let m = Mutex.create() in
let c = Condition.create() in
let loc_entrer () =
Mutex.lock m ;
while not (!n < 4) do Condition.wait c m done ;
incr n ;
if !n > 1 then Printf.printf "%d philosophes sont a table\n" !n
else Printf.printf "%d philosophe est a table\n" !n ;
flush stdout;
Mutex.unlock m
in
let loc_sortir () =
Mutex.lock m ;
decr n ;
Mutex.unlock m ;
Condition.broadcast c
in
loc_entrer, loc_sortir ;;
val entrer : unit -> unit = <fun>
val sortir : unit -> unit = <fun>
Il suffit alors d'appeler ces fonctions en dbut et en fin de boucle d'un philosophe.

# let philosophe i =
let ii = (i+1) mod 4 in
while true do
Printf.printf "Le philosophe (%d) se pointe\n" i ;
entrer () ;
mediter 3. ;
Mutex.lock b.(i);
Printf.printf
"Le philosophe (%d) prend sa baguette gauche et medite encore un peu\n" i;
mediter 0.2; Mutex.lock b.(ii) ;
Printf.printf "Le philosophe (%d) prend sa baguette droite\n" i ;
manger 0.5 ;
Mutex.unlock b.(i) ;
Printf.printf
"Le philosophe (%d) rend sa baguette gauche et commence deja a mediter\n" i;
mediter 0.15 ;
Mutex.unlock b.(ii) ;
Printf.printf "Le philosophe (%d) rend sa baguette droite\n" i ;
sortir () ;
Printf.printf "Le philosophe (%d) se tire\n" i ;
done ;;
val philosophe : int -> unit = <fun>
Attention, cette solution supprime les inter-blocages, mais pas les famines. Pour rsoudre ce dernier problme, on peut soit se fier au hasard en introduisant un dlai d'attente en aprs la sortie d'un philosophe, soit grer explicitement une file d'attente.


La poste en fait plus

Nous proposons l'amendement suivant au bureau de poste dcrit la page ?? : certains clients impatients peuvent partir avant que leur numro ne soit appel.
  1. Ajouter la classe distrib une mthode attendre (de type int -> unit) qui met en attente l'appelant tant que le dernier numro distribu est infrieur ou gal au paramtre de la mthode (il faut modifier prendre de faon ce qu'elle mette un signal).

    # class distrib () =
    object
    val mutable n = 0
    val m = Mutex.create ()
    val c = Condition.create ()

    method attendre nc =
    Mutex.lock m ;
    while (n <= nc) do Condition.wait c m done ;
    Mutex.unlock m

    method prendre () =
    Mutex.lock m ;
    n <- n+1 ;
    let nn = n in
    Condition.broadcast c ;
    Mutex.unlock m ;
    nn
    end ;;
    class distrib :
    unit ->
    object
    val c : Condition.t
    val m : Mutex.t
    val mutable n : int
    method attendre : int -> unit
    method prendre : unit -> int
    end


  2. Modifier la mthode attendre_arrivee de la classe guichet de faon ce qu'elle retourne le boolen true si le client attendu s'est prsent et false si le client ne s'est pas prsent au bout d'un certain temps. On rajoute une mthode prive reveil charge de rveiller le guichetier en attente au bout d'un temps donn.

    #
    method private reveil t =
    let dt = delai_attente_appel /. 10.0 in
    while (Unix.gettimeofday() < t) do Thread.delay dt done;
    Condition.signal c

    method attendre_arrivee () =
    let t = Unix.gettimeofday() +. delai_attente_appel in
    let r = Thread.create self#reveil t in
    Mutex.lock m;
    while libre && (Unix.gettimeofday() < t) do
    Condition.wait c m
    done;
    (try Thread.kill r with _ -> ());
    let b = not libre in (Mutex.unlock m; b)


  3. Modifier la classe affich en lui passant en paramtre un distributeur de numros et en :
    1. ajoutant une mthode attendre_jusqu'a qui renvoie true si le numro attendu a t appel pendant un dlai d'attente donn en paramtre et false sinon ; La modification de la mthode appel a pour but de garantir la fois que ne sont appels que des numros effectivement distribus et que lorsqu'un numro d'appel est affich, il existe bien un guichet qui attend ce numro.

      # class affich (d:distrib) =
      object
      val mutable nc = 0
      val m = Mutex.create ()
      val c = Condition.create ()

      method attendre n =
      Mutex.lock m ;
      while nc < n do Condition.wait c m done ;
      Mutex.unlock m

      method attendre_jusqu'a n t =
      Mutex.lock m ;
      while (nc < n) && (Unix.gettimeofday() < t) do Condition.wait c m done ;
      let b = not (nc < n) in
      Mutex.unlock m ;
      b

      method appel (g:guichet) =
      Mutex.lock m ;
      d#attendre nc ;
      nc <- nc+1 ;
      g#set_nc nc ;
      Condition.broadcast c ;
      Mutex.unlock m

      end ;;
      class affich :
      distrib ->
      object
      val c : Condition.t
      val m : Mutex.t
      val mutable nc : int
      method appel : guichet -> unit
      method attendre : int -> unit
      method attendre_jusqu'a : int -> float -> bool
      end


    2. modifiant la mthode appel pour qu'elle prenne en paramtre un guichet et mette jour le champ nc de ce guichet (il faut rajouter une mthode de mise jour dans la classe guichet).

  4. Modifier la fonction guichetier pour tenir compte des attentes infructueuses.

    # type bureau = { d: distrib; a: affich; gs: guichet array }
    val delai_service : float = 4
    val delai_arrivee : float = 2
    val delai_guichet : float = 0.5
    val delai_attente_client : float = 0.7

    let guichetier ((a : affich), (g : guichet)) =
    while true do
    a#appel g ;
    Printf.printf"Guichet %d appelle %d\n" g#get_ng g#get_nc ;
    if g#attendre_arrivee () then g#attendre_depart ()
    else
    begin
    Printf.printf"Guichet %d n'attend plus %d\n" g#get_ng g#get_nc ;
    flush stdout
    end ;
    Thread.delay (Random.float delai_guichet)
    done ;;
    val guichetier : affich * guichet -> unit = <fun>


  5. crire une fonction client_impatient qui simule le comportement d'un client impatient.

    # val chercher_guichet : 'a -> < get_nc : 'a; .. > array -> int = <fun>

    let client_impatient b =
    let n = b.d#prendre() in
    let t = Unix.gettimeofday() +. (Random.float delai_attente_client) in
    Printf.printf "Arrivee client impatient %d\n" n; flush stdout;
    if b.a#attendre_jusqu'a n t
    then
    let ig = chercher_guichet n b.gs in
    b.gs.(ig)#arriver() ;
    Printf.printf "Le client %d occupe le guichet %d\n" n ig ;
    flush stdout ;
    Thread.delay (Random.float delai_service) ;
    b.gs.(ig)#partir() ;
    Printf.printf "Le client %d s'en va\n" n
    else
    Printf.printf "Le client %d, las d'attendre, s'en va\n" n
    flush stdout ;;
    Characters 518-531:
    This function is applied to too many arguments

Producteurs et consommateurs d'objet

Cet exercice reprend l'algorithme des producteurs-consommateurs avec la variante suivante : le magasin de stockage est de taille finie (i.e. un tableau plutt qu'une liste gre en FIFO). De plus, nous proposons d'en faire une implantation utilisant, l'instar du bureau de poste, les objets pour modliser les ressources.
  1. Dfinir une classe produit de signature :

    # class produit (s:string) =
    object
    val nom = s
    method nom = nom
    end ;;
    class produit : string -> object val nom : string method nom : string end

    class produit : string ->
    object
    val nom : string
    method nom : string
    end


  2. Dfinir une classe magasin telle que :

    # class magasin n =
    object(self)
    val mutable taille = n;
    val mutable np = 0
    val mutable buffer = ([||] : produit array)
    val mutable ip = 0 (* Indice producteur *)
    val mutable ic = 0 (* Indice consommateur *)

    val m = Mutex.create ()
    val c = Condition.create ()

    initializer buffer<-Array.create n (new produit "empty")

    method display1 () =
    let i = ip mod taille in
    Printf.printf "Ajout (%d)%s\n" i ((buffer.(i))#nom)

    method deposer p =
    Mutex.lock m ;
    while (ip-ic+1 > Array.length(buffer)) do Condition.wait c m done ;
    buffer.(ip mod taille) <- p ;
    self#display1() ;
    ip <- ip+1 ;
    Mutex.unlock m ;
    Condition.signal c

    method display2 () =
    let i = ic mod taille in
    Printf.printf "Retrait (%d)%s\n" i ((buffer.(i))#nom)

    method prendre () =
    Mutex.lock m ;
    while(ip == ic) do Condition.wait c m done ;
    self#display2() ;
    let r = buffer.(ic mod taille) in
    ic<- ic+1 ;
    Mutex.unlock m ;
    Condition.signal c ;
    r
    end ;;
    class magasin :
    int ->
    object
    val mutable buffer : produit array
    val c : Condition.t
    val mutable ic : int
    val mutable ip : int
    val m : Mutex.t
    val mutable np : int
    val mutable taille : int
    method deposer : produit -> unit
    method display1 : unit -> unit
    method display2 : unit -> unit
    method prendre : unit -> produit
    end

    class magasin : int ->
    object
    val mutable buffer : produit array
    val c : Condition.t
    val mutable ic : int
    val mutable ip : int
    val m : Mutex.t
    val mutable np : int
    val taille : int
    method deposer : produit -> unit
    method prendre : unit -> produit
    end
    Les indices ic et ip sont manipuls respectivement par les producteurs et les consommateurs. L'indice ic donne l'indice du dernier produit pris et ip celui du dernier produit stock. Le compteur np donne le nombre de produits en stock. L'exclusion mutuelle et la mise en attente des producteurs et des consommateurs seront gres par les mthodes de cette classe.

  3. Dfinir une fonction consommateur : magasin -> string -> unit.

    # let consommateur mag na =
    while true do
    let p = mag#prendre() in
    Printf.printf "Le consommateur %s prend le produit %s\n" na p#nom ;
    flush stdout ;
    Thread.delay(Random.float(3.0))
    done ;;
    val consommateur :
    < prendre : unit -> < nom : string; .. >; .. > -> string -> unit = <fun>


  4. Dfinir une fonction creer_produit de type string -> produit. Le nom donn un produit sera compos de la chane passe en argument concatne un numro de produit incrment chaque appel de la fonction.
    Utiliser cette fonction pour dfinir producteur : magasin -> string -> unit.

    # let producteur =
    let num = ref 0 in
    let creer_produit () =
    let p = new produit("lessive-"^(string_of_int !num)) in
    incr num ;
    p
    in
    function mag -> function nm ->
    while true do
    let p = creer_produit () in
    mag#deposer(p) ;
    Printf.printf"Production de %s\n" p#nom ;
    flush stdout ;
    Thread.delay (Random.float (1.0))
    done ;;
    val producteur : < deposer : produit -> '_a; _.. > -> '_b -> unit = <fun>

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora173.html0000644000000000000000000000256207421273603014501 0ustar Notes
1
Sous Unix, chaque utilisateur appartient un ou plusieurs groupes d'utilisateurs ce qui permet une hirarchisation de leurs droits.
2
Le type file_perm est un alias pour le type int.
3
Les options et le comportement de cette commande ne sont pas standardiss. Aussi l'exemple que nous donnons peut ne pas tre reproductible.
4
Nous rappelons que les signaux sont grs de faon asynchrone. Ainsi, si deux fils meurent l'un aprs l'autre, il se peut que le signal du premier n'ait pas t trait.
ocaml-book-1.0/fr/html/book-ora035.gif0000644000000000000000000000630307073350152014272 0ustar GIF89aUUU999rrr!,x0I8ͻ`(dih Bplx|?F,Ȥ65!Jt,,جv*PLJBL^| vxlu:8vyi0}kt_\Ybujnx53Q~V^ M- uiS#OriDìs}C}µԦvuˡP MbغVݘ 3Cb xŬ%ի^^3Ç@RǞV*JR@D9:`iH(`AI3L7kȩO*xd!tѣrIӧPFtUU^ɪkg`ÊKٳhӪ]˶mYpr[d.ݻw-},0†&^Ď#KL˘3k̹ϠCMӨS^ͺװM;vؤ`k ->q'qÿ?krKGk^=w-vNW9vluS'ŗ/4X CSSH 6(؁%@Ea,SӀbhՆ6A56m'b=1TH⋬SSgXDIHcS64)$H'[LMz%_QVYT4an^fda:՘h]ƹfntix afyƎ땲4$%_IPnerf^F(mBbRTæ7|2((xZ>ji@R4tӭ![Ҟ!Bhl JCkK^ ʚ >-æ צnزINFoHjLQk.%o8^cR.>v L1_-?<s p;vLL\#C˛ t}=9XSͳht`-c3F|lw-Ok_΋UN;|m5Yӝ7ra}=R`Ӆ7܉7m6%މ_^z+#PuPk.1[pw#κŜmG.[NB@;|;RYL.n%hl?~#WY̪ٻ_(L0pY+P/}10(P1Ǫ~ Dnb*` ya\z3LuBu+:*LjuS{"/ȵÁ\B^gp Fi"/V##\%1kT#͘E1#*cǶ IBL"IdzshGvA{IPw.%)QbMjz"Y- hi[1yVNlQt *sk!@K6$F'(XӁMئ7שM]T3~hH7Ӱs:V>C|D%Nk.a=u~vdd8ELpfĨKR'.%%O`J!--gQOY"L.3&]aISzOԅ@*; UR"SP5b TNSWʈ}h Ay˴~TR9,ӭI:V(|Mp:yFNqhG *Ӡ1f'66 mZ-JOM,]4EqvS:7BE.mIV[ZElZ($@Z إ>V6Rf l'ǚxK^רXaː B1HLҒtMJlrX2a uW@2\7L%,6 ׹"eAqb8܍tQ ,` ŝC`V.l!4$9YUK.MRcjn2u3:(& eZ020f VޒkmQˋS?IŠ^U %[^v*پ*_YN'ΨH~TB|{[]k:]*ra_3 U뭵(FG+HQ!ju7ebˊbx3>r Ordre d'valuation des arguments Prcdent Index Suivant

Ordre d'valuation des arguments

Dans un langage fonctionnel pur, l'ordre d'valuation des arguments n'a pas d'importance. Comme il n'y a ni modification d'tat mmoire, ni rupture de calcul, il n'y a aucun risque d'influence du calcul d'un argument sur un autre. Par contre en Objective CAML, o il existe des valeurs modifiables physiquement et des exceptions, il y a pril ne pas tenir compte de l'ordre d'valuation des arguments. L'exemple suivant est spcifique la version 2.04 d'Objective CAML pour Linux sur machine Intel :

# let new_print_string s = print_string s; String.length s ;;
val new_print_string : string -> int = <fun>
# (+) (new_print_string "Hello ") (new_print_string "World!") ;;
World!Hello - : int = 12
L'affichage des deux chanes montre que la deuxime chane s'affiche avant la premire.

Il en est de mme avec les exceptions :

# try (failwith "fonction") (failwith "argument") with Failure s -> s;;
- : string = "argument"


Si l'on dsire prciser l'ordre d'valuation des arguments, il est ncessaire d'effectuer des dclarations locales forant cet ordre avant l'appel de la fonction. L'exemple prcdent peut alors se rcrire ainsi :

# let e1 = (new_print_string "Hello ")
in let e2 = (new_print_string "World!")
in (+) e1 e2 ;;
Hello World!- : int = 12


En Objective CAML, l'ordre d'valuation des arguments n'est pas spcifi. En fait, ce jour, toutes les mises en oeuvre d'Objective CAML valuent les arguments de droite gauche. Nanmoins l'utilisation de ce trait d'implantation peut se rvler dangereux pour de futures versions du langage modifiant l'implantation.

On retrouve l'ternel dbat sur la conception des langages. Faut-il ne pas spcifier volontairement certains traits du langage et demander aux programmeurs de ne pas s'en servir sous peine de rsultats diffrents de leur programme selon les mises en oeuvre des compilateurs, ou faut-il tout spcifier et donc autoriser les programmeurs utiliser le langage dans son ensemble quitte compliquer la tche de mise en oeuvre et interdire certaines optimisations ?


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora003.html0000644000000000000000000002023107421273601014460 0ustar Description du langage Prcdent Index Suivant

Description du langage

Objective CAML est un langage fonctionnel :
il manipule les fonctions comme tant des valeurs du langage. Celles-ci peuvent tre utilises en tant que paramtres d'autres fonctions ou tre retournes comme rsultat d'un appel de fonction.

Objective CAML est typ statiquement :
la vrification de la compatibilit entre les types des paramtres formels et des paramtres d'appel est effectue au moment de la compilation du programme. Ds lors, il n'est pas ncessaire de faire ces vrifications durant l'excution du programme ce qui accrot son efficacit. En outre, la vrification de type permet d'liminer la plupart des erreurs introduites par maladresse ou tourderie et contribue la sret de l'excution.

Objective CAML est polymorphe paramtrique :
une fonction qui n'explore pas la totalit de la structure d'un de ses arguments accepte que celui-ci ait un type non entirement dtermin. Ce paramtre est alors dit polymorphe. Cette particularit permet de dvelopper un code gnrique utilisable pour des structures de donnes diffrentes tant que la reprsentation exacte de cette structure n'a pas besoin d'tre connue par le code en question. L'algorithme de typage est mme de faire cette distinction.

Objective CAML possde une infrence de types :
le programmeur n'a besoin de donner aucune information de type l'intrieur de son programme. Le langage se charge seul de dduire du code le type le plus gnral des expressions et des dclarations qui y figurent. Cette infrence est effectue conjointement la vrification, lors de la compilation du programme.

Objective CAML est muni d'un mcanisme d'exceptions :
il est possible de rompre l'excution normale d'un programme un endroit et de la reprendre un autre endroit du programme prvu cet effet. Ce mcanisme permet de grer les situations exceptionnelles, mais il peut aussi tre adopt comme style de programmation.

Objective CAML possde des traits impratifs :
les entres-sorties, les modifications physiques de valeurs et les structures de contrle itratives sont possibles sans avoir recours aux traits de la programmation fonctionnelle. Le mlange des deux styles est bien entendu accept et offre une grande souplesse de dveloppement ainsi que la possibilit de dfinir des structures de donnes nouvelles.

Objective CAML excute des processus lgers (threads) :
les principaux outils de cration, de synchronisation pour la gestion de la mmoire partage et de communication entre diffrents processus lgers sont prdfinis.

Objective CAML communique sur le rseau Internet :
les bases ncessaires l'ouverture de canaux de communication entre diffrentes machines sont prdfinies et permettent la ralisation d'applications suivant l'architecture client-serveur.

Objective CAML dispose de nombreuses bibliothques :
structures de donnes classiques, entres-sorties, interfaage avec les ressources du systme, analyses lexicale et syntaxique, calcul sur les grands nombres, valeurs persistantes, etc.

Objective CAML dispose d'un environnement de programmation :
incluant une boucle d'interaction, une trace de l'excution, un calcul des dpendances et une analyse de performance.

Objective CAML interagit avec le langage C :
via l'appel de fonctions C partir d'un programme Objective CAML et rciproquement, permettant ainsi l'accs aux nombreuses bibliothques C.

Objective CAML dispose de trois modes d'excution :
interactif par le biais d'une boucle d'interaction, compilation vers du code-octet interprt par une machine virtuelle, compilation vers du code machine. Le programmeur peut donc choisir entre souplesse de dveloppement, portabilit du code objet sur diffrentes architectures ou performance sur une architecture donne.

Structuration d'un programme

La ralisation d'applications importantes oblige le programmeur ou l'quipe de dveloppement se poser des questions d'organisation et de structuration. En Objective CAML, on dispose de deux modles dont les avantages et les particularits sont distincts.
Modle des modules paramtrs :
les donnes et les traitements sont regroups au sein d'une mme entit deux facettes : d'un ct le code proprement dit, de l'autre son interface. La communication entre modules s'effectue via leur interface. La description d'un type peut tre masque en n'apparaissant pas dans l'interface du module. Ces types de donnes abstraits facilitent les modifications d'implantation l'intrieur d'un module sans affecter les autres modules qui s'en servent. De plus, les modules peuvent tre paramtrs par d'autres modules augmentant ainsi leur rutilisabilit.
Modle objet :
les descriptions des traitements et des donnes sont regroupes dans des entits appeles classes; un objet est une instance (valeur) d'une classe. La communication entre objets est ralise par <<envoi de message>>, l'objet receveur dtermine l'excution (liaison retarde) le traitement correspondant au message. En cela, la programmation objet est <<dirige>> par les donnes. La structuration d'un programme provient des relations entre classes, en particulier l'hritage permet de dfinir une classe par extension d'une autre. Ce modle accepte les classes concrtes, abstraites et paramtriques. De plus, il introduit le polymorphisme d'inclusion en dfinissant la relation de sous-typage entre classes.

Le choix entre ces deux modles permet une grande souplesse dans l'organisation logique d'une application et facilite sa maintenance et son volution. Il y a dualit entre ces deux modles. On ne peut pas augmenter les composants d'un type dans un module (pas d'extensibilit des donnes), mais on peut ajouter de nouveaux traitements (extensibilit des traitements) sur ces donnes. En objet, on peut ajouter des sous-classes une classe (extensibilit des donnes) pour traiter des nouveaux cas, mais on ne peut pas ajouter de nouveaux traitements visibles de la classe anctre (pas d'extensibilit des traitements). Nanmoins la combinaison des deux offre de nouvelles extensibilits pour les traitements et les donnes.

Sret et efficacit de l'excution

Objective CAML possde une excellente sret d'excution de ses programmes sans sacrifier pour autant leur efficacit. Fondamentalement le typage statique est une garantie d'absence d'erreur de type l'excution et apporte une information statique utile pour le compilateur sans grever les performances par des tests dynamiques de types et cela y compris pour l'extension objet. De mme le rcuprateur automatique de mmoire est un gage de bonne sret. Celui d'Objective CAML est, de plus, particulirement efficace. Le mcanisme d'exceptions garantit que le programme ne se retrouvera pas dans un tat incohrent suite une division par zro ou un accs en dehors des bornes d'un tableau.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora195.html0000644000000000000000000007535607421273602014517 0ustar Bote outils client-serveur Prcdent Index Suivant

Bote outils client-serveur

Nous prsentons un ensemble de modules pour la construction de client-serveur entre programmes Objective CAML. Cette bote outils est ensuite utilise dans les deux applications suivantes.

Une application se distingue d'une autre par le protocole qu'elle utilise et par les traitements qu'elle y associe. Pour le reste, savoir les mcanismes d'attente de connexion, de dtachement du traitement de la connexion sur un autre processus, les lectures et critures sur une socket, les applications sont trs semblables les unes aux autres.

Profitant de la possibilit de mlanger la gnricit modulaire et l'extension des objets que nous offre Objective CAML, nous allons raliser un ensemble de foncteurs prenant comme argument un protocole de communication et engendrant des classes gnriques implantant les mcanismes des clients et des serveurs. Il ne nous restera ensuite qu' les sous-classer pour obtenir des traitements particuliers.

Protocoles

Un protocole de communication est un type de donnes qu'il est possible de traduire sous forme de chanes de caractres afin de faire transiter d'une machine une autre des donnes par une socket. Ceci peut se traduire sous la forme d'une signature.

# module type PROTOCOL =
sig
type t
val to_string : t -> string
val of_string : string -> t
end ;;


La signature impose que le type de donnes soit monomorphe, mais hormis cette restriction, du moment qu'il est possible de le traduire en chane de caractres et inversement, nous pouvons choisir comme structure de donnes des valeurs aussi complexes que l'on souhaite. En particulier, rien ne nous interdit d'avoir comme donne un objet.

# module Integer =
struct
class integer x =
object
val v = x
method x = v
method str = string_of_int v
end
type t = integer
let to_string o = o#str
let of_string s = new integer (int_of_string s)
end ;;


En faisant quelques restrictions sur les types de donnes manipulables, nous pouvons utiliser le module Marshal, dcrit page ??, pour dfinir les fonctions de traduction.

# module Make_Protocole = functor ( T : sig type t end ) ->
struct
type t = T.t
let to_string (x:t) = Marshal.to_string x [Marshal.Closures]
let of_string s = (Marshal.from_string s 0 : t)
end ;;


Communication

Puisqu'un protocole est une donne qu'il est possible de traduire sous la forme d'une chane de caractres, nous pouvons en faire un persistant et le stocker dans un fichier.

La seule difficult pour lire une valeur depuis un fichier quand on ne connat pas son type est qu'a priori nous ne connaissons pas la taille de la donne en question. Et puisque le fichier en question sera en fait une socket, nous ne pouvons pas nous fier au marqueur de fin de fichier. Pour rsoudre ce problme, nous faisons prcder la donne par la taille en nombre de caractres lire. Les douze premiers caractres contiennent sa taille et des espaces.

Le foncteur Com prend en paramtre un module de signature PROTOCOL et dfinit les fonctions d'mission et de rception des valeurs codes dans le protocole.

# module Com = functor (P : PROTOCOL) ->
struct
let send fd m =
let mes = P.to_string m in
let l = (string_of_int (String.length mes)) in
let buffer = String.make 12 ' ' in
for i=0 to (String.length l)-1 do buffer.[i] <- l.[i] done ;
ignore (ThreadUnix.write fd buffer 0 12) ;
ignore (ThreadUnix.write fd mes 0 (String.length mes))

let receive fd =
let buffer = String.make 12 ' '
in
ignore (ThreadUnix.read fd buffer 0 12) ;
let l = let i = ref 0
in while (buffer.[!i]<>' ') do incr i done ;
int_of_string (String.sub buffer 0 !i)
in
let buffer = String.create l
in ignore (ThreadUnix.read fd buffer 0 l) ;
P.of_string buffer
end ;;
module Com :
functor(P : PROTOCOL) ->
sig
val send : Unix.file_descr -> P.t -> unit
val receive : Unix.file_descr -> P.t
end
Notons que nous utilisons les fonctions read et write du module ThreadUnix et non celles du module Unix; cela nous permettra d'utiliser les fonctions du module dans un thread sans bloquer l'excution des autres processus.

Serveur

Un serveur est ralis comme une classe abstraite paramtre par le type de donnes du protocole. Son constructeur prend comme argument le numro du port et le nombre de connexions simultanes acceptables. La mthode de traitement d'une requte est abstraite; elle doit tre implante dans une sous-classe de server pour obtenir une classe concrte.

# module Server = functor (P : PROTOCOL) ->
struct
module Com = Com (P)

class virtual ['a] server p np =
object (s)
constraint 'a = P.t
val port_num = p
val nb_pending = np
val sock = ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0

method start =
let host = Unix.gethostbyname (Unix.gethostname()) in
let h_addr = host.Unix.h_addr_list.(0) in
let sock_addr = Unix.ADDR_INET(h_addr, port_num) in
Unix.bind sock sock_addr ;
Unix.listen sock nb_pending ;
while true do
let (service_sock, client_sock_addr) = ThreadUnix.accept sock
in ignore (Thread.create s#treat service_sock)
done
method send = Com.send
method receive = Com.receive
method virtual treat : Unix.file_descr -> unit
end
end ;;


Afin de fixer les ides, nous reprenons le service majuscule comme illustration mais en donnant la possibilit d'envoyer des listes de mots.

# type message = Str of string | LStr of string list ;;
# module Maj_Protocol = Make_Protocole (struct type t=message end) ;;
# module Maj_Server = Server (Maj_Protocol) ;;

# class maj_server p np =
object (self)
inherit [message] Maj_Server.server p np
method treat fd =
match self#receive fd with
Str s -> self#send fd (Str (String.uppercase s)) ;
Unix.close fd
| LStr l -> self#send fd (LStr (List.map String.uppercase l)) ;
Unix.close fd
end ;;
class maj_server :
int ->
int ->
object
val nb_pending : int
val port_num : int
val sock : Unix.file_descr
method receive : Unix.file_descr -> Maj_Protocol.t
method send : Unix.file_descr -> Maj_Protocol.t -> unit
method start : unit
method treat : Unix.file_descr -> unit
end


Le traitement se dcompose en la rception de la requte, son filtrage, son traitement et l'mission du rsultat. Le foncteur permet de se concentrer sur le service pour raliser le serveur, le reste est gnrique. Cependant, si on souhaite avoir un mcanisme diffrent, comme par exemple grer des acquittements, rien n'interdit de redfinir les mthodes de communication hrites.

Client

Pour raliser des clients utilisant un protocole donn, nous dfinissons trois fonctions gnralistes :
  • connect : tablit une connexion avec un serveur, elle prend son adresse (adresse IP et numro de port) et rend un descripteur de fichier correspondant une socket connecte au serveur.
  • emit_simple : ouvre une connexion, envoie un message et referme la connexion.
  • emit_answer : idem que la prcdente, mais attend la rponse du serveur avant de refermer la connexion.

# module Client = functor (P : PROTOCOL) ->
struct
module Com = Com (P)

let connect addr port =
let sock = ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0
and in_addr = (Unix.gethostbyname addr).Unix.h_addr_list.(0)
in ThreadUnix.connect sock (Unix.ADDR_INET(in_addr, port)) ;
sock

let emit_simple addr port mes =
let sock = connect addr port
in Com.send sock mes ; Unix.close sock

let emit_answer addr port mes =
let sock = connect addr port
in Com.send sock mes ;
let res = Com.receive sock
in Unix.close sock ; res
end ;;
module Client :
functor(P : PROTOCOL) ->
sig
module Com :
sig
val send : Unix.file_descr -> P.t -> unit
val receive : Unix.file_descr -> P.t
end
val connect : string -> int -> Unix.file_descr
val emit_simple : string -> int -> P.t -> unit
val emit_answer : string -> int -> P.t -> P.t
end
Les deux dernires fonctions sont de plus haut niveau que la premire. Le vecteur de la liaison entre le client et le serveur n'apparat pas. L'utilisateur de emit_answer n'a mme pas besoin de savoir que le calcul qu'il demande est effectu sur une machine distante. Pour lui, il invoque une fonction qui est reprsente par une adresse et un port avec un argument qui est le message envoy, et une valeur lui est retourne. Le ct distribu peut lui paratre anecdotique.

Un client du service majuscule est excessivement ais raliser. En supposant que la machine boulmich hberge ce service sur le port 12345; la fonction list_uppercase peut se dfinir par un appel au service.

# let list_uppercase l =
let module Maj_client = Client (Maj_Protocol)
in match Maj_client.emit_answer "boulmich" 12345 (LStr l)
with Str x -> [x]
| LStr x -> x ;;
val list_uppercase : string list -> string list = <fun>


Pour en faire plus

La premire amlioration apporter notre bote outils est une gestion des erreurs qui ici est totalement absente. Une rcupration des exceptions qui surviennent lors de la rupture d'une connexion et un mcanisme de <<nouvel essai>> seraient les bienvenus.

Dans la mme veine, le client et le serveur gagneraient tre munis d'un mcanisme de timeout permettant de borner le temps d'attente d'une rponse.

Le fait d'avoir ralis le serveur gnrique comme une classe, qui de surcrot est paramtre par le type de donnes qui transitent sur le rseau, permet de l'tendre facilement pour augmenter ou modifier son comportement afin de raliser les amliorations souhaites.

Une autre approche est d'enrichir les protocoles de communication. On peut par exemple ajouter des requtes d'acquittement au protocole, ou encore accompagner chaque requte d'un checksum permettant de vrifier que le rseau n'a pas corrompu les donnes.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora055.html0000644000000000000000000001373507421273601014502 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

Bien qu'en programmation graphique et vnementielle, le style de programmation soit impratif, il est non seulement possible, mais bien souvent utile, d'introduire des oprateurs plus fonctionnels de manipulation d'objets graphiques. Un bon exemple vient de l'utilisation de la bibliothque MLgraph,

Lien


http://www.pps.jussieu.fr/~cousinea/MLgraph/mlgraph.html
qui implante le modle graphique de PostScript, et propose des oprateurs fonctionnels de manipulation des images. Elle est dcrite dans [CCS96] et utilise abondamment dans [CM95] pour le placement optimis des arbres et pour la construction de dessins dans le style d'Escher.

Une caractristique intressante de la bibliothque Graphics est d'tre portable pour les interfaces graphiques des systmes Windows, MacOS et Unix. Cette notion de bitmap virtuel se retrouve dans plusieurs langages comme Le_Lisp ou plus rcemment en Java. Malheureusement en Objective CAML la bibliothque Graphics ne possde pas de composant interactif pour la construction d'interfaces. Une des applications dcrites dans la partie II de ce livre contient les premires briques de la bibliothque Upi. Celle-ci s'inspire de l'Abstract Windowing Toolkit des premires versions de Java. On s'aperoit qu'il est relativement ais d'tendre des fonctionnalits de cette bibliothque grce aux valeurs fonctionnelles du langage. Le chapitre 16 compare d'ailleurs l'adaptation de la programmation objet et de la programmation fonctionnelle et modulaire pour la construction d'interfaces graphiques. L'exemple d'Upi est fonctionnel et impratif, mais il est aussi possible de n'utiliser que le style fonctionnel. C'est typiquement le cas pour les langages fonctionnels purs. On peut citer les systmes Fran et Fudget, dvelopps en Haskell et drivs. Le systme Fran permet de construire des animations 2D, 3D interactives, c'est--dire avec vnements entre objets anims ou utilisateurs.

Lien


http://www.research.microsoft.com/~conal/fran/
La bibliothque Fudget est une bibliothque pour la construction d'interfaces graphiques.

Lien


http://www.cs.chalmers.se/ComputingScience/Research/Functional/Fudgets/


Une des difficults, quand on veut programmer une interface graphique pour son application, est de savoir laquelle choisir parmi les nombreux outils existants. Il ne suffit pas de dterminer le langage et le systme pour fixer le choix de l'outil. Pour Objective CAML, il en existe plusieurs plus ou moins finaliss :
  • l'encapsulation de la libX, pour X-Windows;
  • la librt, une bibliothque pour X-Windows;
  • ocamltk, adaptation de Tcl/Tk, portable;
  • mlgtk, adaptation de Gtk, portable.
On trouve les liens vers ces dveloppements la << bosse du chameau >> :

Lien


http://caml.inria.fr/hump.html


Enfin nous avons parl uniquement de la programmation en 2D. La tendance est l'augmentation d'une dimension. Que cela soit le modle VRML, ou l'extension Java-3D, les langages fonctionnels doivent aussi rpondre cette ncessit. En fonctionnel pur, le systme Fran offre des possibilits intressantes d'interaction entre sprites. Plus proche d'Objective CAML on peut utiliser la bibliothque VRcaML ou l'environnement de dveloppement SCOL.

la manire de MLgraph a t dveloppe la bibliothque VRcaML qui intgre une partie du modle graphique de VRML Objective CAML.

Lien


http://www.pps.jussieu.fr/~emmanuel/Public/enseignement/VRcaML
On peut donc construire une scne 3D anime. Le rsultat produit alors un fichier VRML directement visualisable.

Toujours dans la ligne de Caml, le langage SCOL, est un langage fonctionnel de communication muni d'importantes bibliothques de manipulation 2D et 3D agrment d'un environnement de dveloppement pour le non-informaticien.

Lien


http://www.cryo-networks.com
L'intrt du langage SCOL et de son environnement de dveloppement est de pouvoir raliser des applications rparties, en client-serveur, facilitant la fabrication de sites Internet. On prsente la programmation distribue en Objective CAML au chapitre 20.








Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora025.html0000644000000000000000000000346707421273601014500 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

Ce chapitre continue la prsentation des lments de base du langage Objective CAML entame dans le chapitre prcdent mais en s'intressant cette fois-ci aux constructions impratives. Il se dcoupe en cinq sections. La premire est la plus importante, elle prsente les diffrentes structures de donnes physiquement modifiables et dcrit leur reprsentation mmoire. La seconde montre assez brivement les entres-sorties de base du langage. La troisime section s'intresse aux nouvelles structures de contrle itratives. La quatrime section discute de l'impact des traits impratifs sur le droulement d'un programme, en particulier sur l'ordre d'valuation des arguments d'une fonction. La dernire section reprend l'exemple de la calculatrice du chapitre prcdent pour en faire une calculatrice mmoire.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora208.html0000644000000000000000000000336707421273603014504 0ustar Introduction Prcdent Index Suivant

Introduction

Le systme de type d'Objective CAML serait beaucoup plus simple si le langage tait purement fonctionnel. Par << malheur >> les extensions du langage introduisent des extensions du langage de type et du mcanisme d'infrence : nous en avons eu l'illustration avec les variables de type faibles (voir page ??) rendues ncessaires par les extensions impratives.

Le typage des objets introduit une notion de type cyclique associe au mot cl as (voir page ??) qui peut tre utilise hors du concept de la programmation par objet. Cette annexe dcrit cette extension du langage de type qui est accessible par une option du compilateur.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora198.html0000644000000000000000000000205407421273603014504 0ustar Notes
1
Cet en-tte est gnralement affich dans le bandeau suprieur de la fentre des navigateurs.
2
...en ayant soin de mettre jour les URL en fonction de votre machine
3
Rien en fait n'empche d'utiliser GET pour cela, mais cela ne correspond pas la norme.
ocaml-book-1.0/fr/html/book-ora059.html0000644000000000000000000035143207421273601014505 0ustar Interprte BASIC Prcdent Index Suivant

Interprte BASIC

L'application prsente dans cette section est un interprte de programmes Basic. C'est donc un programme sachant excuter d'autres programmes crits en Basic. Bien entendu, nous ne dcrirons qu'un langage restreint, il ne contient que les instructions suivantes :

  • PRINT expression

       Affiche le rsultat de l'valuation de l'expression.

  • INPUT variable

       Affiche un prompt l'cran (?), attend qu'un entier soit entr au clavier, et affecte la variable avec cette valeur.

  • LET variable = expression

       Affecte la variable avec le rsultat de l'valuation de l'expression.

  • GOTO numro de ligne

       Reprend l'excution la ligne mentionne.

  • IF condition THEN numro de ligne

       Reprend l'excution la ligne mentionne si la condition est vraie.

  • REM chane de caractres quelconque

       Commentaire sur une ligne.
Chaque ligne d'un programme Basic est tiquete par son numro de ligne et ne contient qu'une seule instruction. Par exemple, un programme calculant puis affichant la factorielle d'un entier entr au clavier s'crit :
 
 5  REM  entree de l'argument 
10  PRINT " factorielle de :"
20  INPUT A
30  LET B = 1 
35  REM debut de la boucle 
40  IF A <= 1 THEN 80 
50  LET B = B * A
60  LET A = A - 1
70  GOTO 40 
75  REM le resultat est affiche
80  PRINT B
Nous souhaitons de surcrot raliser un mini-diteur fonctionnant sur le principe d'une boucle d'interaction. Il devra rendre possible l'ajout de nouvelles lignes, l'affichage d'un programme et l'valuation. L'excution du programme prcdent est lance par la commande RUN. Voici un exemple d'valuation de ce programme :
> RUN
 factorielle de : ? 5
120
Nous dcomposons la ralisation de cet valuateur en plusieurs parties relativement distinctes :
Description de la syntaxe abstraite
: il faut dfinir des types de donnes permettant de reprsenter les programmes Basic ainsi que leurs constituants (lignes, instructions, expressions, etc.).
Affichage du programme
: cette partie consiste transformer la reprsentation interne des programmes Basic en chanes de caractres afin de les visualiser.

Analyse lexicale et analyse syntaxique
: ces deux parties ralisent l'opration inverse, savoir transformer une chane de caractres en la reprsentation interne d'un programme Basic (sa syntaxe abstraite).

valuation
: le coeur de l'valuateur gouvernant l'excution du programme. Nous verrons qu'un langage fonctionnel comme Objective CAML est particulirement bien adapt ce genre de problme.

Boucle d'interaction
: elle met en oeuvre ce qui prcde.

Syntaxe abstraite

La figure 6.2 prsente la syntaxe concrte du Basic que nous allons implanter par une grammaire sous la forme BNF. Cette faon de prsenter la syntaxe d'un langage est dcrite au chapitre 11, page ??.

Op_Unaire ::= -    |    !
Op_Binaire ::= +    |    -    |    *    |    /    |    %
  | =    |    <    |    >    |    <=    |    >=    |    <>
  | &    |    ' | '
Expression ::= entier
  | variable
  | "chane de caractres"
  | Op_Unaire   Expression
  | Expression   Op_Binaire   Expression
  | ( Expression )
Instruction ::= REM chane de caractres
  | GOTO entier
  | LET variable = Expression
  | PRINT Expression
  | INPUT variable
  | IF Expression THEN entier
 
Ligne ::= entier Instruction
 
Programme ::= Ligne
  | Ligne Programme
 
Commande ::= Ligne | RUN | LIST | END

Figure 6.2 : Grammaire BASIC


Remarquons que la dfinition des expressions ne garantit pas qu'une expression bien forme soit valuable. Par exemple, 1+"hello" est une expression et pourtant il n'est pas possible de l'valuer. Ce choix dlibr permet de simplifier la syntaxe abstraite du langage Basic et de raccourcir l'analyse syntaxique. Le prix payer est la possibilit que des programmes Basic syntaxiquement corrects puissent produire une erreur d'excution due une incohrence de type.

La dfinition des types de donnes Objective CAML pour cette syntaxe abstraite dcoule immdiatement de la dfinition de la syntaxe concrte en utilisant un type somme :

# type op_unr = OPPOSE | NON ;;
# type op_bin = PLUS | MOINS | MULT | DIV | MOD
| EGAL | INF | INFEQ | SUP | SUPEQ | DIFF
| ET | OU ;;
# type expression =
ExpInt of int
| ExpVar of string
| ExpStr of string
| ExpUnr of op_unr * expression
| ExpBin of expression * op_bin * expression ;;
# type instruction =
Rem of string
| Goto of int
| Print of expression
| Input of string
| If of expression * int
| Let of string * expression ;;
# type ligne = { num : int ; inst : instruction } ;;
# type program = ligne list ;;


On dfinit les lments de syntaxe abstraite pour les commandes du mini-diteur de programme :

# type phrase = Ligne of ligne | List | Run | End ;;


Il est d'usage d'allger l'criture des expressions arithmtiques en autorisant le programmeur ne pas mentionner toutes les parenthses. Par exemple, l'expression 1+3*4 est habituellement interprte comme 1+(3*4). On attribue, cette fin, un entier chacun des oprateurs du langage :

# let priority_ou = function NON -> 1 | OPPOSE -> 7
let priority_ob = function
MULT | DIV -> 6
| PLUS | MOINS -> 5
| MOD -> 4
| EGAL | INF | INFEQ | SUP | SUPEQ | DIFF -> 3
| ET | OU -> 2 ;;
val priority_ou : op_unr -> int = <fun>
val priority_ob : op_bin -> int = <fun>
Ces entiers indiquent ce que l'on appelle la priorit des oprateurs. Nous verrons comment elle est utilise pour l'affichage des programmes et l'analyse syntaxique.

Affichage des programmes

Pour afficher un programme contenu en mmoire, il faut savoir transformer une ligne de programme, sous forme de syntaxe abstraite, en une chane de caractres.

La conversion des oprateurs est immdiate :

# let pp_opbin = function
PLUS -> "+" | MULT -> "*" | MOD -> "%" | MOINS -> "-"
| DIV -> "/" | EGAL -> " = " | INF -> " < "
| INFEQ -> " <= " | SUP -> " > "
| SUPEQ -> " >= " | DIFF -> " <> " | ET -> " & " | OU -> " | "
let pp_opunr = function OPPOSE -> "-" | NON -> "!" ;;
val pp_opbin : op_bin -> string = <fun>
val pp_opunr : op_unr -> string = <fun>
L'affichage des expressions tiendra compte des rgles de priorit pour engendrer un parenthsage allg. Par exemple, on ne parenthse une sous-expression droite d'un oprateur que lorsque son oprateur principal a une priorit infrieure l'oprateur principal de l'expression entire. De plus, les oprateurs arithmtiques sont associatifs gauche, c'est--dire que l'expression 1-2-3 est implicitement parenthse comme (1-2)-3.

Pour ce faire, nous avons recours deux fonctions auxiliaires ppg et ppd pour traiter respectivement les sous-arbres gauches et les sous-arbres droits. Ces fonctions prennent deux paramtres, d'une part l'arbre d'expression afficher mais aussi la priorit de l'oprateur qui est en amont de cet arbre afin de dcider s'il y a lieu de parenthser l'expression. Nous distinguons les sous-arbres gauches des sous-arbres droits pour traiter l'associativit des oprateurs. En cas d'galit de priorit des oprateurs on ne mettra pas de parenthse pour l'oprande gauche alors qu'elles sont indispensables dans le cas de l'oprande droit comme dans les expressions 1-(2-3) et 1 + ( 2 + 3).

On considre l'arbre initial comme un sous-arbre gauche sous un oprateur de priorit minimale (0). Voici la fonction pp_expresssion d'affichage des expressions :

# let parenthese x = "(" ^ x ^ ")";;
val parenthese : string -> string = <fun>
# let pp_expression =
let rec ppg pr = function
ExpInt n -> (string_of_int n)
| ExpVar v -> v
| ExpStr s -> "\"" ^ s ^ "\""
| ExpUnr (op,e) ->
let res = (pp_opunr op)^(ppg (priority_ou op) e)
in if pr=0 then res else parenthese res
| ExpBin (e1,op,e2) ->
let pr2 = priority_ob op
in let res = (ppg pr2 e1)^(pp_opbin op)^(ppd pr2 e2)
(* parenthse si la priorit n'est pas suprieure *)
in if pr2 >= pr then res else parenthese res
and ppd pr exp = match exp with
(* les sous-arbres droits ne diffrent *)
(* que pour les oprateurs binaires *)
ExpBin (e1,op,e2) ->
let pr2 = priority_ob op
in let res = (ppg pr2 e1)^(pp_opbin op)^(ppd pr2 e2)
in if pr2 > pr then res else parenthese res
| _ -> ppg pr exp
in ppg 0 ;;
val pp_expression : expression -> string = <fun>


L'affichage des instructions utilise la fonction prcdente sur les expressions. L'affichage d'une ligne ajoutera un numro de ligne devant l'instruction.

# let pp_instruction = function
Rem s -> "REM " ^ s
| Goto n -> "GOTO " ^ (string_of_int n)
| Print e -> "PRINT " ^ (pp_expression e)
| Input v -> "INPUT " ^ v
| If (e,n) -> "IF "^(pp_expression e)^" THEN "^(string_of_int n)
| Let (v,e) -> "LET " ^ v ^ " = " ^ (pp_expression e) ;;
val pp_instruction : instruction -> string = <fun>
# let pp_ligne l = (string_of_int l.num) ^ " " ^ (pp_instruction l.inst) ;;
val pp_ligne : ligne -> string = <fun>


Analyse lexicale

Les analyses lexicale et syntaxique effectuent la transformation inverse de l'affichage. Elles reoivent une chane de caractres et construisent un arbre de syntaxe. L'analyse lexicale dcoupe le texte d'une ligne d'instruction en units lexicales indpendantes appeles lexmes, dont le type Objective CAML suit :

# type lexeme = Lint of int
| Lident of string
| Lsymbol of string
| Lstring of string
| Lfin ;;
Nous avons rajout un lexme particulier pour marquer la fin d'une expression : Lfin. Il ne fait pas partie de la chane analyse, mais sera engendr par la fonction d'analyse lexicale (voir fonction lexer, page ??).

La chane que l'on souhaite analyser est conserve dans un enregistrement contenant un champ modifiable indiquant l'indice de la partie de la chane restant parcourir. La taille de la chane tant une information qui sera ncessaire en de multiples occasions et qui ne varie pas, nous la stockons dans l'enregistrement :

# type chaine_lexer = {chaine:string; mutable courant:int; taille:int } ;;
Cette reprsentation permet de dfinir l'analyse lexicale d'une chane comme l'application d'une fonction une valeur de type chaine_lexer et rendant une valeur de type lexeme. La modification de l'indice de la chane restant parcourir se fait par effet de bord.

# let init_lex s = { chaine=s; courant=0 ; taille=String.length s } ;;
val init_lex : string -> chaine_lexer = <fun>
# let avance cl = cl.courant <- cl.courant+1 ;;
val avance : chaine_lexer -> unit = <fun>
# let avance_n cl n = cl.courant <- cl.courant+n ;;
val avance_n : chaine_lexer -> int -> unit = <fun>
# let extrait pred cl =
let st = cl.chaine and ct = cl.courant in
let rec ext n = if n<cl.taille && (pred st.[n]) then ext (n+1) else n in
let res = ext ct
in cl.courant <- res ; String.sub cl.chaine ct (res-ct) ;;
val extrait : (char -> bool) -> chaine_lexer -> string = <fun>


Les fonctions suivantes extraient un lexme de la chane et positionnent le caractre courant sur le prochain caractre traiter. Les deux fonctions auxiliaires :
extrait_int et extrait_ident extraient respectivement un entier et un identificateur.

# let extrait_int =
let est_entier = function '0'..'9' -> true | _ -> false
in function cl -> int_of_string (extrait est_entier cl)
let extrait_ident =
let est_alpha_num = function
'a'..'z' | 'A'..'Z' | '0' .. '9' | '_' -> true
| _ -> false
in extrait est_alpha_num ;;
val extrait_int : chaine_lexer -> int = <fun>
val extrait_ident : chaine_lexer -> string = <fun>


La fonction lexer utilise les deux fonctions prcdentes pour extraire un lexme.

# exception LexerErreur ;;
exception LexerErreur
# let rec lexer cl =
let lexer_char c = match c with
' '
| '\t' -> avance cl ; lexer cl
| 'a'..'z'
| 'A'..'Z' -> Lident (extrait_ident cl)
| '0'..'9' -> Lint (extrait_int cl)
| '"' -> avance cl ;
let res = Lstring (extrait ((<>) '"') cl)
in avance cl ; res
| '+' | '-' | '*' | '/' | '%' | '&' | '|' | '!' | '=' | '(' | ')' ->
avance cl; Lsymbol (String.make 1 c)
| '<'
| '>' -> avance cl;
if cl.courant >= cl.taille then Lsymbol (String.make 1 c)
else let cs = cl.chaine.[cl.courant]
in ( match (c,cs) with
('<','=') -> avance cl; Lsymbol "<="
| ('>','=') -> avance cl; Lsymbol ">="
| ('<','>') -> avance cl; Lsymbol "<>"
| _ -> Lsymbol (String.make 1 c) )
| _ -> raise LexerErreur
in
if cl.courant >= cl.taille then Lfin
else lexer_char cl.chaine.[cl.courant] ;;
val lexer : chaine_lexer -> lexeme = <fun>


Le principe de la fonction lexer est des plus simples, elle filtre le caractre courant d'une chane et, suivant sa valeur, rend le lexme correspondant en ayant avanc le caractre courant de la chane jusqu'au caractre suivant. Cette approche est bien adapte car mis part pour deux caractres, des lexmes diffrents peuvent se distinguer ds leur premier caractre. Dans le cas de '<' nous sommes par contre obligs d'examiner le caractre suivant pour savoir s'il s'agit de = ou de > ce qui produira un lexme diffrent. De mme pour le caractre '>'.

Analyse syntaxique

L'unique difficult de l'analyse syntaxique de notre langage provient des expressions. En effet, la connaissance du dbut de l'expression ne suffit pas pour dterminer sa structure. Supposons que nous ayons analys la portion de chane 1+2+3. Suivant ce qui suit, par exemple : +4 ou *4, nous obtiendrons des arbres d'expression dont la structure concernant 1+2+3 n'est pas la mme (voir figure 6.3).


Figure 6.3 : Basic : exemple d'arbres de syntaxe abstraite


Cependant, la structure de l'arbre correspondant 1+2 est la mme dans les deux cas. Nous pouvons donc la construire. comme le sort rserv +3 est encore indtermin, nous stockerons temporairement cette information jusqu' ce que nous possdions suffisamment d'information pour pouvoir la traiter.

Nous allons procder la construction de l'arbre de syntaxe abstraite en ayant recours un automate pile proche de celui que construit l'outil yacc (voir page ??). Les lexmes sont lus l'un aprs l'autre et sont empils tant que l'information n'est pas suffisante pour construire l'expression. Lorsque nous savons dcider de la structure des lments d'expressions empils, nous les retirons de la pile et les remplaons par l'expression construite. Cette opration s'appelle rduction.

Le type des lments empils est le suivant :

# type exp_elem =
Texp of expression (* expression *)
| Tbin of op_bin (* oprateur binaire *)
| Tunr of op_unr (* oprateur unaire *)
| Tpg (* parenthse gauche *) ;;
Remarquons que les parenthses droites ne sont pas stockes car seules les parenthses gauche sont significatives pour l'opration de rduction.

La figure 6.4 illustre le principe de l'utilisation de la pile pour l'analyse de l'expression (1+2*3)+4. Le caractre surmontant la flche est le caractre courant de la chane.


Figure 6.4 : Basic : exemple de construction d'un arbre de syntaxe abstraite


On dfinit l'exception suivante pour les erreurs de syntaxe.

# exception ParseErreur ;;
La premire tape effectuer est de retrouver les oprateurs partir des symboles :

# let symb_unr = function
"!" -> NON | "-" -> OPPOSE | _ -> raise ParseErreur
let symb_bin = function
"+" -> PLUS | "-" -> MOINS | "*" -> MULT | "/" -> DIV | "%" -> MOD
| "=" -> EGAL | "<" -> INF | "<=" -> INFEQ | ">" -> SUP
| ">=" -> SUPEQ | "<>" -> DIFF | "&" -> ET | "|" -> OU
| _ -> raise ParseErreur
let tsymb s = try Tbin (symb_bin s) with ParseErreur -> Tunr (symb_unr s) ;;
val symb_unr : string -> op_unr = <fun>
val symb_bin : string -> op_bin = <fun>
val tsymb : string -> exp_elem = <fun>


La fonction suivante (reduit) implante l'opration de rduction de la pile. Il y a deux cas considrer, la pile commence par :
  • une expression suivie d'un oprateur unaire
  • une expression suivie d'un oprateur binaire et d'une expression.
De plus, reduit prend en argument la priorit minimale qu'un oprateur doit avoir pour que la rduction de la pile ait lieu. Pour rduire sans condition, il suffit de donner une profondeur minimale nulle.

# let reduit pr = function
(Texp e)::(Tunr op)::st when (priority_ou op) >= pr
-> (Texp (ExpUnr (op,e)))::st
| (Texp e1)::(Tbin op)::(Texp e2)::st when (priority_ob op) >= pr
-> (Texp (ExpBin (e2,op,e1)))::st
| _ -> raise ParseErreur ;;
val reduit : int -> exp_elem list -> exp_elem list = <fun>


Notez que les lments d'expressions sont empils suivant l'ordre de lecture, d'o la ncessit d'inverser les deux oprandes d'une opration binaire.

La fonction principale de l'analyse syntaxique est la fonction empile_ou_reduit qui, suivant le lexme donn en argument, ajoute un nouvel lment dans la pile ou procde une rduction.

# let rec empile_ou_reduit lex stack = match lex , stack with
Lint n , _ -> (Texp (ExpInt n))::stack
| Lident v , _ -> (Texp (ExpVar v))::stack
| Lstring s , _ -> (Texp (ExpStr s))::stack
| Lsymbol "(" , _ -> Tpg::stack
| Lsymbol ")" , (Texp e)::Tpg::st -> (Texp e)::st
| Lsymbol ")" , _ -> empile_ou_reduit lex (reduit 0 stack)
| Lsymbol s , _
-> let symbole =
if s<>"-" then tsymb s
(* lever l'ambigut du symbole ``-'' *)
(* suivant la pile (i.e dernier exp_elem empil) *)
else match stack
with (Texp _)::_ -> Tbin MOINS
| _ -> Tunr OPPOSE
in ( match symbole with
Tunr op -> (Tunr op)::stack
| Tbin op ->
( try empile_ou_reduit lex (reduit (priority_ob op)
stack )
with ParseErreur -> (Tbin op)::stack )
| _ -> raise ParseErreur )
| _ , _ -> raise ParseErreur ;;
val empile_ou_reduit : lexeme -> exp_elem list -> exp_elem list = <fun>


Une fois tous les lexmes isols et empils, l'arbre de syntaxe abstraite doit tre finalement construit avec les lments restant dans la pile. C'est le but de la fonction reduit_tout. Si l'expression analyser est bien forme, il ne doit plus rester dans la pile qu'un seul lment contenant l'arbre de cette expression.

# let rec reduit_tout = function
| [] -> raise ParseErreur
| [Texp x] -> x
| st -> reduit_tout (reduit 0 st) ;;
val reduit_tout : exp_elem list -> expression = <fun>


La fonction parse_exp est la fonction principale de l'analyse des expressions. Elle parcourt une chane de caractres, en extrait les diffrents lexmes et les passe la fonction empile_ou_reduit. L'analyse s'arrte lorsque le lexme courant satisfait un prdicat pass en argument.

# let parse_exp fin cl =
let p = ref 0 in
let rec parse_un stack =
let l = ( p:=cl.courant ; lexer cl)
in if not (fin l) then parse_un (empile_ou_reduit l stack)
else ( cl.courant <- !p ; reduit_tout stack )
in parse_un [] ;;
val parse_exp : (lexeme -> bool) -> chaine_lexer -> expression = <fun>
Notons que le lexme qui a dtermin l'arrt du parcours n'a pas servi construire l'expression. Il faut donc restaurer son indice de dbut (variable p) pour qu'il puisse tre trait par la suite.

Passons maintenant l'analyse d'une ligne d'instruction :

# let parse_inst cl = match lexer cl with
Lident s -> ( match s with
"REM" -> Rem (extrait (fun _ -> true) cl)
| "GOTO" -> Goto (match lexer cl with
Lint p -> p
| _ -> raise ParseErreur)
| "INPUT" -> Input (match lexer cl with
Lident v -> v
| _ -> raise ParseErreur)
| "PRINT" -> Print (parse_exp ((=) Lfin) cl)
| "LET" ->
let l2 = lexer cl and l3 = lexer cl
in ( match l2 ,l3 with
(Lident v,Lsymbol "=") -> Let (v,parse_exp ((=) Lfin) cl)
| _ -> raise ParseErreur )
| "IF" ->
let test = parse_exp ((=) (Lident "THEN")) cl
in ( match ignore (lexer cl) ; lexer cl with
Lint n -> If (test,n)
| _ -> raise ParseErreur )
| _ -> raise ParseErreur )
| _ -> raise ParseErreur ;;
val parse_inst : chaine_lexer -> instruction = <fun>


Enfin, voici la fonction principale d'analyse syntaxique des commandes entres par l'utilisateur depuis la boucle d'interaction :

# let parse str =
let cl = init_lex str
in match lexer cl with
Lint n -> Ligne { num=n ; inst=parse_inst cl }
| Lident "LIST" -> List
| Lident "RUN" -> Run
| Lident "END" -> End
| _ -> raise ParseErreur ;;
val parse : string -> phrase = <fun>


valuation

Un programme Basic est constitu d'une suite de lignes d'instruction. Son excution dmarre la premire ligne. L'interprtation d'une ligne de programme consiste donc effectuer le travail demand par l'instruction qui s'y trouve. Il y a trois familles d'instruction : les entres-sorties (PRINT et INPUT), les dclarations ou modifications de variables (LET) et les branchements (GOTO et THEN). Les instructions d'entres-sorties grent l'interaction avec l'utilisateur et excuteront les fonctions quivalentes en Objective CAML.

Les dclarations de variables et leurs modifications ont besoin de savoir calculer la valeur d'une expression arithmtique et de connatre la localisation mmoire de la variable. L'valuation d'une expression retourne une valeur entire ou boolenne ou des chanes de caractres. Nous les regroupons sous le type valeur.

# type valeur = Vint of int | Vstr of string | Vbool of bool ;;


La dclaration d'une variable doit pouvoir rserver une place mmoire pour le rangement de la valeur qui lui est attribue. De mme la modification d'une variable ncessite la modification de la valeur associe son nom. Pour cela l'valuation d'un programme Basic utilise un environnement qui contient la liaison entre un nom de variable et sa valeur. On le reprsente par une liste d'associations, liste de couples (nom,valeur) :
 
# type environnement = (string * valeur) list ;;
On accde au contenu d'une variable par son nom. La modification de la valeur d'une variable change l'association.

Les instructions de branchements, inconditionnels ou conditionnels, prcisent quel numro de ligne l'excution du programme doit se poursuivre. Par dfaut, la nouvelle instruction excuter est la suivante. Pour cela il est ncessaire de conserver le numro de la prochaine ligne excuter.

La structure de liste d'instructions qui reprsente le programme dit sous la boucle d'interaction ne convient pas une excution raisonnablement efficace du programme. En effet, pour raliser les instructions de saut (If et Goto) nous devrions reparcourir la totalit de la liste d'instructions pour trouver la ligne indique par le saut. Il est possible d'accder directement une ligne indique par une instruction de saut en substituant la structure de liste, une structure de tableau et en utilisant les indices de localisation des instructions la place des numros de ligne. Pour cela, nous soumettrons l'ensemble des instructions du programme un prtraitement, dit d'assemblage avant chaque excution rclame par une commande RUN. Pour des raisons de commodit que nous expliquons au paragraphe suivant, nous reprsentons un programme assembl pour l'excution non pas comme un simple tableau d'instructions, mais comme un tableau de lignes :

# type code = ligne array ;;


Comme pour la calculatrice des chapitres prcdents, l'valuateur manipule un tat que l'valuation de chaque instruction modifie. Les informations connatre chaque instant sont le programme en entier, la prochaine ligne excuter et les valeurs des variables. Le programme excut n'est pas exactement celui construit sous la boucle d'interaction : plutt que d'excuter une liste d'instructions, nous manipulerons un tableau d'instructions. L'tat d'excution d'un programme est donc :

# type etat_exec = { ligne:int ; xprog:code ; xenv:environnement } ;;


Il y a deux sources d'erreur l'valuation d'une ligne de programme : le calcul d'une expression et un branchement sur une ligne qui n'existe pas. Il nous faut donc les grer pour que l'interprte s'arrte correctement en affichant un message d'erreur. On dfinit une exception et une fonction la dclenchant tout en indiquant quel numro de ligne elle s'est produite.

# exception RunErreur of int
let runerr n = raise (RunErreur n) ;;
exception RunErreur of int
val runerr : int -> 'a = <fun>


Assemblage
Le processus d'assemblage d'un programme donn sous forme de liste de lignes numrotes (type program) consiste transformer cette liste en tableau puis ajuster les instructions de saut. Pour raliser cet ajustement, il suffit de conserver l'association entre les numros de lignes et l'indice correspondant du tableau. C'est pour obtenir facilement cette association que nous construisons un tableau de lignes numrotes. Pour retrouver l'indice auquel est associ une ligne, on parcourt ce tableau. Si le numro de ligne recherch n'est pas trouv, on lui assigne, par convention, l'indice erron -1.

# exception Resultat_cherche_indice of int ;;
exception Resultat_cherche_indice of int
# let cherche_indice tprog num_ligne =
try
for i=0 to (Array.length tprog)-1 do
let mun_i = tprog.(i).num
in if mun_i=num_ligne then raise (Resultat_cherche_indice i)
else if mun_i>num_ligne then raise (Resultat_cherche_indice (-1))
done ;
(-1 )
with Resultat_cherche_indice i -> i ;;
val cherche_indice : ligne array -> int -> int = <fun>

# let assemble prog =
let tprog = Array.of_list prog in
for i=0 to (Array.length tprog)-1 do
match tprog.(i).inst with
Goto n -> let indice = cherche_indice tprog n
in tprog.(i) <- { tprog.(i) with inst = Goto indice }
| If(c,n) -> let indice = cherche_indice tprog n
in tprog.(i) <- { tprog.(i) with inst = If (c,indice) }
| _ -> ()
done ;
tprog ;;
val assemble : ligne list -> ligne array = <fun>


valuation des expressions
La fonction d'valuation des expressions parcourt en profondeur l'arbre de syntaxe abstraite et effectue les oprations indiques chaque noeud de l'arbre.

L'exception RunErreur est dclenche dans les cas suivants : incohrence de type, division par zro, variable non dclare.

# let rec eval_exp n envt expr = match expr with
ExpInt p -> Vint p
| ExpVar v -> ( try List.assoc v envt with Not_found -> runerr n )
| ExpUnr (OPPOSE,e) ->
( match eval_exp n envt e with
Vint p -> Vint (-p)
| _ -> runerr n )
| ExpUnr (NON,e) ->
( match eval_exp n envt e with
Vbool p -> Vbool (not p)
| _ -> runerr n )
| ExpStr s -> Vstr s
| ExpBin (e1,op,e2)
-> match eval_exp n envt e1 , op , eval_exp n envt e2 with
Vint v1 , PLUS , Vint v2 -> Vint (v1 + v2)
| Vint v1 , MOINS , Vint v2 -> Vint (v1 - v2)
| Vint v1 , MULT , Vint v2 -> Vint (v1 * v2)
| Vint v1 , DIV , Vint v2 when v2<>0 -> Vint (v1 / v2)
| Vint v1 , MOD , Vint v2 when v2<>0 -> Vint (v1 mod v2)

| Vint v1 , EGAL , Vint v2 -> Vbool (v1 = v2)
| Vint v1 , DIFF , Vint v2 -> Vbool (v1 <> v2)
| Vint v1 , INF , Vint v2 -> Vbool (v1 < v2)
| Vint v1 , SUP , Vint v2 -> Vbool (v1 > v2)
| Vint v1 , INFEQ , Vint v2 -> Vbool (v1 <= v2)
| Vint v1 , SUPEQ , Vint v2 -> Vbool (v1 >= v2)

| Vbool v1 , ET , Vbool v2 -> Vbool (v1 && v2)
| Vbool v1 , OU , Vbool v2 -> Vbool (v1 || v2)

| Vstr v1 , PLUS , Vstr v2 -> Vstr (v1 ^ v2)
| _ , _ , _ -> runerr n ;;
val eval_exp : int -> (string * valeur) list -> expression -> valeur = <fun>


valuation des instructions
Pour raliser la fonction d'valuation d'une ligne d'instruction, nous avons besoin de quelques fonctions auxiliaires.

On ajoute une nouvelle association dans un environnement en supprimant ventuellement la liaison d'une variable de mme nom :

# let rec ajoute v e env = match env with
[] -> [v,e]
| (w,f)::l -> if w=v then (v,e)::l else (w,f)::(ajoute v e l) ;;
val ajoute : 'a -> 'b -> ('a * 'b) list -> ('a * 'b) list = <fun>


Enfin l'affichage d'une valeur d'un entier ou d'une chane de caractres sera utile pour l'instruction PRINT.

# let print_valeur v = match v with
Vint n -> print_int n
| Vbool true -> print_string "true"
| Vbool false -> print_string "false"
| Vstr s -> print_string s ;;
val print_valeur : valeur -> unit = <fun>


L'valuation d'une instruction est une transition menant d'un tat un autre. En particulier sont modifis d'une part l'environnement si l'instruction est une affectation et d'autre part la prochaine ligne excuter. Par convention, si la prochaine ligne excuter sort du tableau contenant le code du programme, sa valeur est -1.

# let ligne_suivante etat =
let n = etat.ligne+1 in
if n < Array.length etat.xprog then n else -1 ;;
val ligne_suivante : etat_exec -> int = <fun>
# let eval_inst etat =
match etat.xprog.(etat.ligne).inst with
Rem _ -> { etat with ligne = ligne_suivante etat }
| Print e -> print_valeur (eval_exp etat.ligne etat.xenv e) ;
print_newline () ;
{ etat with ligne = ligne_suivante etat }
| Let(v,e) -> let ev = eval_exp etat.ligne etat.xenv e
in { etat with ligne = ligne_suivante etat ;
xenv = ajoute v ev etat.xenv }
| Goto n -> { etat with ligne = n }
| Input v -> let x = try read_int ()
with Failure "int_of_string" -> 0
in { etat with ligne = ligne_suivante etat;
xenv = ajoute v (Vint x) etat.xenv }
| If (t,n) -> match eval_exp etat.ligne etat.xenv t with
Vbool true -> { etat with ligne = n }
| Vbool false -> { etat with ligne = ligne_suivante etat }
| _ -> runerr etat.ligne ;;
val eval_inst : etat_exec -> etat_exec = <fun>


chaque appel, la fonction de transition eval_inst cherche la ligne excuter (variable lc) et le numro de la ligne suivante (variable ns). Si la dernire ligne du programme est atteinte, on attribue ns la valeur -1. Cela nous permettra d'arrter l'excution.

valuation d'un programme
On applique rcursivement les transitions jusqu' obtenir un tat o le numro de la ligne courante est -1.

# let rec run etat =
if etat.ligne = -1 then etat else run (eval_inst etat) ;;
val run : etat_exec -> etat_exec = <fun>


Mise en oeuvre

Il ne nous reste plus qu' raliser un mini-diteur et d'assembler toutes les briques ralises dans les sections prcdentes.

La fonction inserer ajoute, la bonne place, une nouvelle ligne dans un programme.

# let rec inserer ligne p = match p with
[] -> [ligne]
| l::prog ->
if l.num < ligne.num then l::(inserer ligne prog)
else if l.num=ligne.num then ligne::prog
else ligne::l::prog ;;
val inserer : ligne -> ligne list -> ligne list = <fun>


La fonction print_prog affiche le source d'un programme.

# let print_prog prog =
let print_ligne x = print_string (pp_ligne x) ; print_newline () in
print_newline () ;
List.iter print_ligne prog ;
print_newline () ;;
val print_prog : ligne list -> unit = <fun>


La fonction une_commande traite l'ajout d'une ligne ou le lancement d'une commande. Elle agit sur l'tat de la boucle d'interaction constitu d'un programme et d'un environnement. Cet tat, reprsent par le type etat_boucle, n'est pas celui de l'valuation d'un programme.

# type etat_boucle = { prog:program; env:environnement } ;;
# exception Fin ;;

# let une_commande etat =
print_string "> " ; flush stdout ;
try
match parse (input_line stdin) with
Ligne l -> { etat with prog = inserer l etat.prog }
| List -> (print_prog etat.prog ; etat )
| Run
-> let tprog = assemble etat.prog in
let xetat = run { ligne = 0; xprog = tprog; xenv = etat.env } in
{etat with env = xetat.xenv }
| End -> raise Fin
with
LexerErreur -> print_string "Illegal character\n"; etat
| ParseErreur -> print_string "syntax error\n"; etat
| RunErreur n ->
print_string "runtime error at line ";
print_int n ;
print_string "\n";
etat ;;
val une_commande : etat_boucle -> etat_boucle = <fun>


La fonction principale est la fonction go qui lance la boucle d'interaction de notre Basic.

# let go () =
try
print_string "Mini-BASIC version 0.1\n\n";
let rec loop etat = loop (une_commande etat) in
loop { prog = []; env = [] }
with Fin -> print_string "A bientt...\n";;
val go : unit -> unit = <fun>
La boucle est ralise par la fonction locale loop. On en sort lorsque l'exception Fin a t dclenche par la fonction une_commande.

Exemple : C+/C-

On reprend l'exemple du jeu C+/C- dcrit au chapitre 3, page ??. Voici le programme Basic quivalent au programme Objective CAML donn :

10 PRINT "Donner le nombre cache : "
20 INPUT N
30 PRINT "Donner un nombre : "
40 INPUT R
50 IF R = N THEN 110
60 IF R < N THEN 90
70 PRINT "C-"
80 GOTO 30
90 PRINT "C+"
100 GOTO 30
110 PRINT "BRAVO"
Voici, pour finir, la trace de l'excution de ce programme.

> RUN
Donner le nombre cach : 
64
Donner un nombre : 
88
C-
Donner un nombre : 
44
C+
Donner un nombre : 
64
BRAVO

Pour en faire plus

Le Basic que nous venons de programmer reste minimal. Pour ceux qui veulent aller plus loin, nous proposons en exercice quelques pistes pour enrichir l'valuateur.
  1. Les flottants : tel quel, notre langage ne connat que les entiers, les chanes et les boolens. Ajouter les flottants ainsi que leurs principales oprations dans la grammaire du langage. Outre l'analyse lexicale, il faut modifier l'valuation en tenant compte des conversions implicites entre entiers et flottants.

  2. Les tableaux : il s'agit ici d'ajouter la syntaxe l'instruction DIM var[x] qui permet de dclarer un tableau var de taille x et l'expression var[i] qui rfrence le ime lment du tableau var.

  3. Directives : nous devrions aussi ajouter les directives SAVE "nom_fichier" et LOAD "nom_fichier" qui respectivement enregistre ou charge un programme Basic sur ou depuis le disque dur.

  4. Sous-programmes : l'appel un sous-programme s'effectue en utilisant l'instruction GOSUB numro de ligne qui effectue un branchement ce numro de ligne tout en conservant le numro de la ligne d'appel. L'instruction RETURN reprend l'excution la ligne suivant la dernire instruction GOSUB excute si elle existe et sort du programme sinon. Pour ajouter cette possibilit, il faudra grer dans l'valuation, en plus de l'environnement, une pile contenant les adresses de retour des diffrents appels GOSUB. L'instruction GOSUB permet de dfinir des sous-programmes rcursifs.

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora193.html0000644000000000000000000000136407421273603014502 0ustar Notes
1
Comme la reprsentation XDR (eXternal Data Representation) qui tait faite pour faire communiquer des programmes C.
ocaml-book-1.0/fr/html/book-ora075.gif0000644000000000000000000001166107073350152014301 0ustar GIF89aycUUU999!,ycX0I8ͻ`(dihp,tmxl ,vƤr6$tJZzvL^d@4-x$Еm9#y~'|vtoX>iL!/DjX.. v<=x*fR v ,&Rf "YmW];:J*ÅVw HBhZi˪@8Q4~Ҋ!=acF,uErJLRNcrKUgٴc|O-Ⱥv٤lF wM y\TMDx̙~vҏN: ͽΔsރ ཷ y泓jw{Ś7n9ϻ3nV^=gܫwx~_~޳< O[8|'2x痾=)S7Aŀ} z)&D HBp0|)_G]a{2ip}k U. Mt ." (CMa(BX ed`ˈCpQxKcט)t& J45ȯz1~AQ=R܂)!Lqp*'%,@2ő (E%y-MrdL$%?1ZBr0Q?-2wY 4Lc>L6+^`$U &lǓ9-Ӊqb ª5k l 3hEE'B9QvRch8b%i{*w<% ,.UutJf_yY^gY/?Q䖝ezzG2;[nYy.2*3CֲZ=hxū{l ؓ"3.,u3(Ⱥ{\ 9Ġ/`JehYaC hA fCgCy  =&EKP7jTAi)#"C8a69ՋPF[B;:]\thw6lnC]8܈g"}H޶UorٱPiR`s7c՞N/7n.+86I؄8Hy 'ZT_Fʷ 'VP y7qd`EPk%8lf]8>wEn3`x(ma'E  b n)'[h 1Rn&Dnr /!o]W8ۇX9`sA x1>5 3Řka3N\r|!mn1:}ܨdњ ( rcf u!nmTy7}O}ŜɁ JI1M{TK aʘ(5H+ث(+UhӊՇYW;< |'g 7ָˬ;Kؗztp׌Ҙ#ͤ ͪQ2PkOΠmsM ٦Χݹq̑Ϻ*PhLٟ tt{ mq|;|v 땉=fE҆,'lE 0M c-yໝdōv~9Ρ "]O(-:sk0+8,6N8"$@. ;ocaml-book-1.0/fr/html/book-ora140.html0000644000000000000000000000413207421273602014465 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

Ce chapitre prsente l'extension objet du langage Objective CAML. Cette extension ne modifie en rien les lments du langage dj tudis dans les chapitres prcdents. Seuls quelques nouveaux mots cls sont rservs pour la syntaxe de la partie objet.

La premire section dcrit la syntaxe de la dclaration de classe, de l'instanciation d'objet et de l'envoi de message. La deuxime section explique les diffrentes relations entre classes. La troisime section prcise la notion de type objet et montre la richesse de l'extension grce aux classes abstraites, l'hritage multiple et la gnricit des classes paramtres. La quatrime section dtaille la relation de sous-typage et montre son intrt travers le polymorphisme d'inclusion. La cinquime section s'intresse au style fonctionnel en objet o l'on ne modifie plus l'tat interne d'un objet mais o on retourne une copie modifie de l'objet receveur. La sixime section prcise les autres lments de l'extension objet que sont les interfaces et dclarations locales dans les classes qui permettent de crer des variables de classe.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora088.html0000644000000000000000000001152507421273602014504 0ustar Gestion mmoire en Objective CAML Prcdent Index Suivant

Gestion mmoire en Objective CAML

Le GC d'Objective CAML est un GC qui combine les diffrentes techniques rencontres prcdemment. C'est un GC deux gnrations : les anciens et les nouveaux. Il utilise principalement un Stop&Copy sur la jeune gnration (GC mineur) et un Mark&Sweep incrmentiel sur la gnration ancienne (GC majeur).

Un objet jeune qui survit un GC mineur est dplac dans la gnration ancienne. Le Stop&Copy utilise l'espace de la gnration ancienne comme espace to-space. Quand il a fini, l'ensemble de l'espace from-space est compltement libre.

Lorsque nous avons prsent les GC gnration, nous avons signal la difficult que prsentent les langages fonctionnels impurs : une valeur de l'ancienne gnration peut rfrencer un objet de la nouvelle gnration. En voici un petit exemple :

# let ancien = ref [1] ;;
val ancien : int list ref = {contents=[1]}
(* ... *)
# let jeune = [2;5;8] in
ancien := jeune ;;
- : unit = ()
Le commentaire (* ... *) remplace une longue squence de code pendant laquelle ancien est pass dans l'ancienne gnration. Le GC mineur doit tenir compte de certaines valeurs de l'ancienne gnration. On doit donc tenir jour une table des rfrences de l'ancienne gnration vers la nouvelle qui fait partie de l'ensemble des racines dont part le GC mineur. Cette table de rfrence grossit trs peu et elle devient vide juste aprs un GC mineur.

Il est noter que le Mark&Sweep de la gnration ancienne est incrmentiel. C'est--dire qu'une partie du GC majeur se droule chaque GC mineur. Le GC majeur est un Mark&Sweep qui suit l'algorithme prsent page ??. L'intrt de cette approche incrmentielle est de diminuer les temps d'attente du GC majeur, en avanant la phase de marquage chaque GC mineur. Quand un GC majeur se dclenche, le marquage se termine sur les parties non traites et la phase de rcupration est enclenche. Enfin comme le Mark&Sweep peut fragmenter de manire importante la gnration ancienne, un algorithme de compaction peut tre dclench aprs le GC majeur.

On obtient au total les tapes suivantes :
  1. GC mineur : effectue le Stop&Copy sur la gnration jeune, vieillit les objets survivants en les changeant de zone et puis fait une partie du Mark&Sweep de la gnration ancienne.
    Il choue si le changement de zone choue, on passe alors l'tape 2.
  2. Fin du cycle du GC majeur.
    En cas d'chec on passe l'tape 3.
  3. GC majeur de nouveau, pour vrifier si des objets compts comme utiliss pendant les phases incrmentielles sont devenus libres.
    En cas d'chec on passe l'tape 4.
  4. Compaction de la gnration ancienne pour obtenir un espace contigu maximal. En cas d'chec de cette dernire phase, il ne reste plus de possibilit et le programme choue.
Le module GC permet de dclencher une des phases du GC.

Une dernire particularit de la gestion mmoire d'Objective CAML est que l'espace du tas n'est pas allou une fois pour toute en dbut de programme, mais il volue au cours du temps (augmentation ou diminution d'une taille donne).


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora207.html0000644000000000000000000001130707421273603014474 0ustar Texte Prcdent Index Suivant

Texte

Bien que l'informatique soit devenue une activit industrielle, bien des gards le succs d'un langage de programmation est affaire de subjectivit. Si << le coeur a ses raisons que la raison ignore >> alors Objective CAML est un choix raisonnable pour un amant de coeur.

Il repose sur des fondements thoriques solides tout en disposant d'un large spectre de paradigmes de programmation. Si on ajoute la simplicit de la prise de contact avec le langage que permet la boucle d'interaction, cela en fait un langage parfaitement adapt l'enseignement.
  • Les types structurs et les types abstraits permettent d'aborder les problmes d'algorithmique et leurs structures de donnes complexes tout en s'abstrayant des problmes de reprsentation mmoire et d'allocation.
  • Le modle thorique fonctionnel sous-jacent au langage fournit une introduction prcise aux notions d'valuation et de typage dont << l'honnte programmeur >> se doit d'tre instruit.
  • Les diffrents modles de programmation peuvent tre abords indpendamment les uns des autres : de la structuration modulaire ou par objets des logiciels la programmation systme de bas niveau, il est peu de domaines o Objective CAML ne soit pas pertinent.
  • Son adquation avec la programmation symbolique en fait un excellent support pour des enseignements thoriques comme la compilation ou l'intelligence artificielle.
Pour ces qualits, Objective CAML est souvent utilis comme support d'enseignement l'initiation l'informatique aussi bien que pour des cours de programmation avance qui explicitent le lien entre le haut niveau d'abstraction du langage et son excution. Nombre d'enseignants ont t et restent sduits par l'intrt pdagogique d'Objective CAML et, par voie de consquence, nombre d'informaticiens y ont t forms.

Un des premiers sujets de satisfaction du dveloppement en Objective CAML est son confort d'utilisation. Le compilateur se charge rapidement et son infrence statique de types ne laisse rien chapper. D'autres analyses statiques du code donnent de prcieux indices d'anomalies sinon d'erreurs pour le programmeur : les filtrages incomplets sont signals, l'application partielle d'une fonction dans une squence est dtecte, etc. ce premier sujet de satisfaction s'en ajoute un second : le compilateur engendre trs rapidement un code efficace.

Performance du compilateur, concision d'expression de la programmation fonctionnelle, qualit et diversit des bibliothques font d'Objective CAML un langage parfaitement adapt aux besoins du << logiciel jetable >>. Mais ce serait le rduire que de le cantonner ce seul domaine d'application. Pour ces mmes raisons, Objective CAML est un outil prcieux d'exprimentation et de prototypage d'applications. De plus, lorsqu'aux traits dj cits viennent s'ajouter les mcanismes de structuration des modules et des objets, le langage ouvre la voie la conception et au dveloppement d'applications abouties.

Enfin, Objective CAML et sa communaut de dveloppeurs forment un milieu extrmement ractif l'innovation dans le domaine de la programmation. La libre disponibilit et la diffusion des sources du langage offrent un terrain d'exprimentation ouvert aux concepts mergeants.

L'apprentissage d'Objective CAML rclame un certain effort au programmeur familier d'autres langages. Et ce, alors mme que l'objet de son tude est en volution constante. Nous esprons que sans masquer la complexit de certains concepts, cet ouvrage facilitera cette phase d'apprentissage et pourra ainsi acclrer le retour sur investissement du dveloppeur d'applications en Objective CAML.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora051.html0000644000000000000000000005737007421273601014501 0ustar vnements Prcdent Index Suivant

vnements

La gestion des vnements produits dans la fentre graphique permet de construire une interaction conviviale entre l'utilisateur et le programme. Les vnements traits par Graphics sont l'appui d'une touche au clavier, le clic de la souris et le mouvement de celle-ci.

Le style de programmation change ainsi que l'organisation du programme. Celui-ci devient une boucle sans fin d'attente d'vnements. chaque nouveau dclenchement d'un vnement, celui-ci est trait, puis le programme revient dans la boucle d'attente sauf lorsqu'un vnement est prvu pour provoquer la sortie du programme.

Types et fonctions sur les vnements

La fonction principale d'attente d'vnements est wait_next_event de type event list -> status.

Les diffrents vnements sont donns par le type somme event.

type event = Button_down | Button_up | Key_pressed | Mouse_motion | Poll ;;
Les quatre premires valeurs correspondent, respectivement, l'appui ou au relchement d'un bouton de souris, au dplacement de la souris et l'appui d'une touche. L'attente est bloquante, sauf si le constructeur Poll est ajout la liste des vnements. Cette fonction retourne une valeur de type status :

type status =
{ mouse_x : int;
mouse_y : int;
button : bool;
keypressed : bool;
key : char};;


C'est un enregistrement contenant la position de la souris, un boolen indiquant si un bouton est press, un autre boolen pour le clavier et un caractre correspondant au caractre de la touche utilise. Les fonctions suivantes exploitent les donnes de l'enregistrement des vnements :
  • mouse_pos : unit -> int * int : retourne la position de la souris par rapport la fentre, si la souris est ailleurs, les coordonnes sont en dehors des bornes de la fentre.
  • button_down : unit -> bool : indique la pression sur un bouton de la souris.
  • read_key : unit -> char : renvoie un caractre tap au clavier; l'attente est bloquante.
  • key_pressed : unit -> bool : indique si une touche du clavier est presse; l'attente est non bloquante.
La gestion d'vnements propose par Graphics est vraiment minimale pour la construction d'interfaces interactives. Nanmoins, le code est portable sur diffrents systmes graphiques comme Windows, MacOS ou X-Windows. C'est d'ailleurs pour cela que cette bibliothque ne tient pas compte des diffrents boutons de la souris. En effet les Mac n'en possdent qu'un seul. Les autres vnements : exposition et changement de taille de la fentre ne sont pas accessibles et sont laisss la charge de la bibliothque.

Squelette de programme

Tous les programmes d'interface utilisateur comprennent une boucle potentiellement infinie d'attente d'action de l'utilisateur. Une fois celle-ci transmise, le programme effectue le travail li cette action. La fonction suivante possde cinq paramtres fonctionnels. Les deux premiers servent dmarrer et fermer l'application. Les deux suivants sont les fonctions de traitement des vnements clavier et souris. Le dernier permet la gestion des exceptions lors des diffrents calculs de l'application. On suppose que les vnements associs la sortie de l'application dclenchent l'exception Fin.

# exception Fin;;
exception Fin
# let squel f_init f_end f_key f_mouse f_except =
f_init () ;
try
while true do
try
let s = Graphics.wait_next_event
[Graphics.Button_down; Graphics.Key_pressed]
in if s.Graphics.keypressed then f_key s.Graphics.key
else if s.Graphics.button
then f_mouse s.Graphics.mouse_x s.Graphics.mouse_y
with
Fin -> raise Fin
| e -> f_except e
done
with
Fin -> f_end () ;;
val squel :
(unit -> 'a) ->
(unit -> unit) ->
(char -> unit) -> (int -> int -> unit) -> (exn -> unit) -> unit = <fun>


Le squelette est utilis pour programmer une mini-machine crire. L'appui d'une touche affiche le caractre tap. Le clic d'une souris change le point courant. Le caractre '' fait sortir du programme. La seule difficult de ce programme est le saut de ligne. On suppose, pour simplifier, que la hauteur des caractres n'excde pas douze pixels.

# let next_line () =
let (x,y) = Graphics.current_point()
in if y>12 then Graphics.moveto 0 (y-12)
else Graphics.moveto 0 y ;;
val next_line : unit -> unit = <fun>
# let trait_char c = match c with
'' -> raise Fin
| '\n' -> next_line ()
| '\r' -> next_line ()
| _ -> Graphics.draw_char c ;;
val trait_char : char -> unit = <fun>
# let go () = squel
(fun () -> Graphics.clear_graph () ;
Graphics.moveto 0 (Graphics.size_y() -12) )
(fun () -> Graphics.clear_graph())
trait_char
(fun x y -> Graphics.moveto x y)
(fun e -> ()) ;;
val go : unit -> unit = <fun>
Ce programme ne gre pas l'effacement d'un caractre par l'appui de la touche DEL.

Exemple : le tlcran

Le tlcran est ce petit jeu de dessin pour la coordination des mouvements. Un point apparat sur une ardoise. Ce point peut se dplacer en X et en Y en utilisant deux boutons de contrle de ces axes, sans jamais relever le crayon. On cherche simuler ce comportement pour illustrer l'interaction entre un programme et un utilisateur. Pour cela on reprendra le squelette dcrit prcdemment. On utilisera certaines touches du clavier pour indiquer un mouvement sur un des axes.

On dfinit tout d'abord le type etat qui est un enregistrement dcrivant la taille de l'ardoise en nombre de positions en X et en Y, la position courante du point, le facteur d'chelle pour la visualisation, la couleur du trait, la couleur du fond et la couleur pour reprer le point courant.

# type etat = {maxx:int; maxy:int; mutable x : int; mutable y :int;
scale:int;
bc : Graphics.color;
fc: Graphics.color; pc : Graphics.color} ;;


La fonction draw_point affiche un point en lui donnant comme paramtres les coordonnes, le facteur d'chelle et la couleur.

# let draw_point x y s c=
Graphics.set_color c;
Graphics.fill_rect (s*x) (s*y) s s ;;
val draw_point : int -> int -> int -> Graphics.color -> unit = <fun>


Toutes les fonctions d'initialisation, de gestion de l'interaction et de sortie du programme recevront un paramtre correspondant l'tat. Les quatre premires fonctions sont dfinies ci-aprs :

# let t_init e () =
Graphics.open_graph (":0 " ^ (string_of_int (e.scale*e.maxx)) ^
"x" ^ (string_of_int (e.scale*e.maxy))) ;
Graphics.set_color e.bc ;
Graphics.fill_rect 0 0 (e.scale*e.maxx+1) (e.scale*e.maxy+1) ;
draw_point e.x e.y e.scale e.pc ;;
val t_init : etat -> unit -> unit = <fun>
# let t_end e () =
Graphics.close_graph() ;
print_string "A bientt..."; print_newline() ;;
val t_end : 'a -> unit -> unit = <fun>
# let t_mouse e x y = () ;;
val t_mouse : 'a -> 'b -> 'c -> unit = <fun>
# let t_except e ex = () ;;
val t_except : 'a -> 'b -> unit = <fun>


La fonction t_init ouvre la fentre graphique et affiche le point courant, t_end ferme cette fentre et affiche un message, t_mouse et t_except ne font rien. Le programme ne grera pas les vnements souris, ni les exceptions qui risquent de se dclencher l'excution. La fonction importante est celle de la gestion du clavier t_key :

# let t_key e c =
draw_point e.x e.y e.scale e.fc;
(match c with
'8' -> if e.y < e.maxy then e.y <- e.y + 1;
| '2' -> if e.y > 0 then e.y <- e.y - 1
| '4' -> if e.x > 0 then e.x <- e.x - 1
| '6' -> if e.x < e.maxx then e.x <- e.x + 1
| 'c' -> Graphics.set_color e.bc;
Graphics.fill_rect 0 0 (e.scale*e.maxx+1) (e.scale*e.maxy+1);
Graphics.clear_graph()
| 'f' -> raise Fin
| _ -> ());
draw_point e.x e.y e.scale e.pc;;
val t_key : etat -> char -> unit = <fun>


Elle affiche le point courant de la couleur du trait, et selon le caractre pass modifie, si possible, les coordonnes du point courant (caractres : '2', '4', '6', '8'), efface l'cran (caractre : 'c') ou dclenche l'exception Fin (caractre : 'f'), puis affiche le nouveau point courant. Les caractres autres que ceux mentionns sont ignors. Le choix des caractres de dplacement du curseur provient de la disposition du pav numrique : les touches choisies correspondent aux chiffres indiqus et aux flches de direction. Il est utile alors d'activer le pav numrique pour l'ergonomie du programme.

On dfinit enfin un tat et on applique la fonction squelette de la manire suivante :

# let etel = {maxx=120;maxy=120; x= 60; y= 60;
scale=4; bc=Graphics.rgb 130 130 130;
fc=Graphics.black; pc = Graphics.red};;
val etel : etat =
{maxx=120; maxy=120; x=60; y=60; scale=4; bc=8553090; fc=0; pc=16711680}
# let ardoise () =
squel (t_init etel) (t_end etel) (t_key etel)
(t_mouse etel) (t_except etel);;
val ardoise : unit -> unit = <fun>


L'appel ardoise affiche la fentre graphique puis attend une interaction clavier. La figure 5.8 montre un dessin ralis par ce programme.


Figure 5.8 : Le tlcran



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora217.html0000644000000000000000000005721207421273603014502 0ustar Index des concepts Index

Index des concepts

  • abstraction, 2
  • affectation, 3
  • agrgation, 15
  • allocation
    • dynamique, 9
    • statique, 9
  • analyse lexicale, 11
    • flot, 11
    • ocamllex, 11
  • analyse syntaxique, 11
    • ascendante, 11
    • conflit, 11, 11
      • shift-reduce, 11
    • descendante, 11
    • flot, 11, 11
    • ocamlyacc, 11
  • appel de fonction, voir application
  • application, 2
    • d'une fonction, 2
    • partielle, 2
  • arbre, 2
  • arit, 2

  • BNF, voir grammaire
  • bibliothque
    • standard, 8
  • bibliothque, 8
    • prcharge, 8
  • bit d'tiquette, 9
  • boolen, 2
  • boucle d'interaction, 7
    • construction, 7
    • directives, 7
    • options, 7
  • byte-code, voir code-octet

  • C (le langage), 12
  • callback, 12
  • canal de communication, 3, 18
  • caractre, 2
  • chane de caractres, 2, 3
    • reprsentation, 12
  • chargement dynamique, 8
  • classe, 15
    • abstraite, 15
    • dclaration locale, 15
    • hritage, 15
    • hritage multiple, 15
    • instance, 15, 15
    • interface, 15
    • paramtre, 15
    • type, 15
    • type ouvert, 15
    • variable de, 15
    • virtuelle, 15
  • classique (mode), B
  • client-serveur, 20
  • code-octet, 7
    • chargement dynamique, 8
    • compilateur de, voir compilateur
  • compilateur, 7
    • debug, 10
    • code-octet, 7, 7
    • commande, 7
    • dition de liens, 7
    • natif, 7
    • profiling, 10
  • compilation, 7
    • portabilit, 7
    • unit, 7
  • concurrence, 19, 19
  • conditionnelle, 2
  • conflit, voir analyse syntaxique
  • constructeur, 2, 2
    • constant, 2
    • fonctionnel, 2
    • polymorphe, B
  • contrainte, 14
  • couple, 2

  • dclaration
    • d'une exception, 2
    • externe, 12
    • par filtrage, 2
    • de fonction, 2
    • globale, 2
    • globale simultane, 2
    • locale, 2
    • locale simultane, 2
    • d'un oprateur, 2
    • rcursive, 2
    • de type, 2
      • porte, 2
    • de valeur, 2
  • debug, voir mise au point
  • destructeur, 9
  • digest, voir empreinte

  • dition de liens, 7
    • avec C, 12
  • empreinte, 8
  • enregistrement, 2
    • champ modifiable, 3
  • entier, 2
  • entres-sorties, 8, 8
  • entres-sorties
    • avec C, 12
  • environnement, 2, 12
  • envoi de message, 15
  • valuation
    • ordre d'valuation, 3
    • retarde, 4
  • vnement, 5
  • excutable autonome, 7
  • exception, 2
    • affichage, 8
    • avec C, 12
    • dclaration, 2
    • dclenchement, 2
    • rcupration, 2
  • exclusion mutuelle, 19
  • expression
    • fonctionnelle, 2
    • rgulires, 11
    • rationnelles, 11


  • famine, 19
  • fermeture, 2, 2
    • avec C, 12
    • reprsentation, 12
  • fichier
    • extension
      • .ml, 7
      • .mli, 7
      • .mll, 11
      • .mly, 11
    • interface, 7
  • filtrage, 2
    • exhaustif, 2
    • destructif, 4
  • finalisation, 12
  • flot, 4
    • analyse lexicale, 11
    • analyse syntaxique, 11, 11
  • flottant, 2
    • reprsentation, 12
    • tableau de, 12
  • foncteur, 14
  • fonction, 2
    • d'ordre suprieur, 2
    • dclaration avec C, 12
    • de finalisation, 12
    • mutuellement rcursives, 2
    • partielle, 2
    • polymorphe, 2
      • trace, 10
    • rcursive, 2
      • trace, 10
    • rcursive terminale, 4
  • fonctionnelle, 2

  • GC, 9, 9, Mark& Sweep9
    • gnration, 9
    • racines ambigus, 9
    • avec C, 12
    • incrmentiel, 9
    • majeur, 9
    • mineur, 9
    • Stop&Copy, 9
  • garbage collector, voir GC
  • glaneur de cellules, voir GC
  • grammaire, 11
    • contextuelle, 11
    • dfinition, 11
    • rgle de, 11
  • grands nombres, 8

  • Has-a, 15
  • hachage, 8
  • heap, 9
  • hritage, 15

  • Is-a, 15
  • identificateur, 11
  • inlining, 7
  • installation d'Objective CAML, 1
    • MacOS, 1
    • aide en ligne HTML, 1
    • Linux, 1
    • Unix, 1
    • Windows, 1
  • instance (de classe), 15
  • interface, 14, 15
    • avec C, 12, 12
    • avec le systme, 8
    • graphique, 13
  • interoprabilit, 12

  • label, B
  • lecteur-crivain, 19
  • liaison
    • retarde, 15
    • statique, 2
  • ligne de commande, 8
    • analyse de, 8
  • linarisation, 8
  • liste, 2
    • d'association, 8
    • filtrage, 2


  • Mark&Sweep, 9
  • mmoire
    • allocation, 9
    • cache, 9
    • dynamique, 9
    • rcupration, 9, 9
    • rcupration explicite de, 9
    • rcupration
      • explicite, 9
      • implicite, 9
    • statique, 9
  • mmoire
    • gestion, 9, 9
    • rcupration, 9
      • automatique, 9
  • mthode, 15
    • abstraite, 15
    • virtuelle, 15
  • machine abstraite, 7
  • mise au point, 10, 10
  • moderne (mode), B, B
  • modifiable, 3
  • module, 14
    • contrainte, 14
    • dclaration locale, 14
    • dpendance, 10
    • ouverture, 8
    • paramtr, 14
    • sous-module, 14
  • motif
    • combinaison, 2
    • filtrage, 2
    • garde, 2
    • intervalle de caractres, 2
    • nommage, 2
    • universel, 2, 2


  • n-uplet, 2
  • notation pointe, 8

  • OCamlBrowser, B
  • objet, 15
    • copie, 15
    • cration, 15
  • oprateur
    • associativit, 11
    • dclaration, 2
    • priorit, 11
  • optionnel (argument), B

  • paresseuses (donnes), 4
  • partage, 8
  • pattern matching, voir filtrage
  • persistance, 8
  • pile d'excution, 9
  • pile d'excution, 10
  • pipe, voir tube de communication
  • pointeur, 3
    • faible, 9
  • polymorphisme, 2
    • d'inclusion, 15
  • porte d'une variable, 2
  • portabilit, 7
  • prise de communication, 20
  • processus, 18
  • producteur-consommateur, 19
  • produit cartsien, 2
  • profiling, 10
  • protocole, 20
    • http, 20


  • racine, 9
    • ambigu, 9
  • rfrence, 3
  • rgle de production, voir grammaire

  • Stop&Copy, 9
  • smaphore, 19
  • squence, 3
  • section critique, 19
  • self, 15
  • session, 7
  • signal, 18
  • signature, 14
  • socket, voir prise de communication
  • sous-typage, 15
  • stream, voir flot
  • strict (langage), 4
  • string, voir chane de caractres
  • structure, 14
  • super, 15
  • synchronisation, 19

  • tableau, 3
  • tas, 9
  • this, 15
  • thread, voir processus lger
  • toplevel, voir boucle d'interaction
  • trace, 10
  • tube de communication, 18
    • nomm, 18
  • type
    • abstrait, 14
    • constructeur, 2
    • contrainte, 2, 14, 15, 15
    • cyclique, A
    • dclaration, 2
    • enregistrement, 2
    • numr, 2
    • fonctionnel, 2, 2
    • mutuellement rcursif, 2
    • objet, 15
    • ouvert, 15, 15
    • paramtr, 2, 2, 2
    • produit, 2
    • rcursif, 2
    • somme, 2, 2
      • reprsentation, 12
    • union, 2


  • UML, 15
  • unit de compilation, 7
  • unit lexicale, 11
    • ocamlyacc, 11


  • valeur
    • construction de, 9
    • dclaration, 2
    • dclaration globale, 2
    • dclaration locale, 2
    • exploration, 10, 12
    • fonctionnelle, 12
    • immdiate, 3, 12
    • partage, 3
    • persistante, 8
      • type, 8
    • reprsentation, 12
      • en C, 12
    • structure, 3, 12
  • variable
    • d'instance, 15
    • de type, 2
    • lie, 2
    • libre, 2, 2
    • de type faible, 3
  • vecteur, voir tableau

  • Zinc, 7
    • interprte, 7
  • zombie, 18

Index ocaml-book-1.0/fr/html/book-ora001.gif0000644000000000000000000003253207073350152014266 0ustar GIF89aû{{{th!p==!l=Jw===Fwt1W1Sh_N{(c(_tp(h$htǁ{p1_wp1[ccc{{___5Sw!h5W5SBJw$c(19{wp===9NЌptl_{,c111,_{ǿph[wlhW(h!!!5[5W{tlwthwph=N{=J{9Jwwǁ==,cc[J_WF_SF=N$cwt,[p,W{=={$lp$h{t1_{!l!ht5W1Swpc==BJ{5Ntpctlc{,cpl_,_{w(_lh[!tBB!p(h$h1[$,51W9S{{9J{{w,,,lcWhcSh_Sc[N(c_WJ_SJ{wl9N9JÑ{w,_t,[$l,h!,# H*\ȰÇ#JHŋ3jȑb" CIɓ(S\ɲ˗0cʜI͛8sl @@ JѣH*]ʴӧPJJիX6g`~CϔhN[0jzժݻxػ߿VbÇeY̸cN"GfBJNhys%n=ӨSNb˞=$s'Ļ߽?ަq)_С(zuسkߎ 3o9_^=-YeO}?" X$ .ÃF(U=( jT!׉qV^,׋{a"㌘؍88< )D^bHqƒLY E)bW&m\6)bRb jf u)tYgtM7u|٧v'bF&6(eD*)-VJf(jjꩢ4*P8!*Q(FPY¦Er!l/6׳68m^Dfmg߆+.ۺnV=ڛosFu=0l1 l1Q "q:1Tz5: @A'ib@^E(jBnՕ4W0H66x#icD>$_$K:-L[%X%_i|g yzR`g(كz>*Nj)QLƟ1)D Ժ@! la*E'481.r%܉$90esdBը03P:Iv]pG/RC_4>}ת p?u(*W@X WMi8Ke:04rAvnCd4tS]$иŘZB#ev'}Fij̅.E6SՆ!gk׬qglH4zɧm{>MU^ (u(#|F"(cYLwGŒI-R*ȋ1uK9STp^ TdXdN LY9@M- P&%Af.T7e.+\J9_uKea wKa*vT5+X oٸ `q!JT9Tw`G׳G>:VgU۱ aScVuxke+5Uu]CZz`{{O_>=,LZ1,^rlLAB]HtFcv5 74dYGFr]~#\*I.ݖ|V+}Ka[˜3UԻk8g3d'N[|Z7s!=ٸgo MgB 3YS;)<]k]Eanuů=gQ z=ο{ܲ_}Ix(}7^?>K^pG;nzޣ'J_s>?rC_|^ճէϿb~Wz~z H{t'~1G~hs_Wwǀ؁ t$X&xL瀓~s؂}0q/x8:<؃>@B8ԆFxHcyJ؄NGH{))1Y5A(S(}28cĂ`5qgZhh6vGZpPxxvgvm?ϴ@ц?!r8zh|-5GuQ'ukSXwB'xhxVC QxTxu#wYe?|_8b8iW'GF=H@qFHz} 7Uȋhq{ z}uk8n ?q^7DPSaT؈7zWzhY'֘?g+ͨ7v!Xftv@8a+ ?,ӐcXhHuɌ@a?Ww)|8|$9X!)rxZZPXc87yuvijQQS9QهQVɆZ xٕ:ؓZ7x~iɖgʈ!蓗^?TȓRw0HiǘH,Ӎ1)z7{1uc~w8FwLtWɈZID>Xٝ¸}W2BIy?W+-|1hVhfh8ZXFɟ' E~xj?ڡ 9$$z(j*wZ9 ^yF6z8T٠<ڣ>B:D:@zHڣELjIPM:TQz@ʤh(MZ^z b:dZfzhh lڦnZhjr:fvzIzk~||[ i]ڨ*J:Zizکj:zЩiʩz*ꨴZڥʦz_Z*Je2ںڭC:1ߊ嚮:<bຮ:p'QZw:v.jrz B'dpZٚzT*5p(,jtTB#v$[r/ %/+ @`%@pVYг@oRfVBk0pW0д]#R;SEZ b!^=A`f GpE`>}P |k&0#q,P,KT b b 10xw`vP 0 [p_[0[;k!+qʰ 4P*"$bi3C#V`&k$Vv d.܋PZB s&&5bWDvV>x)CgV7"p"*R yCԵ@0 c 0Z0 ekj˶p+PyO8;;K { K`뺰+ .`+KS  )`;"[dL:`Ka a˽N5Cf2kR5f׳="XkK|6 96H  gKk{>uP"L‚K‰˸-0l{á;{ۺ{)ď% ѱ_cT{:(Ke*n-[03{7jWҳYBE'+brO LT;V+?+zT^9S]90 < 9 bk̶nktk}k†+.{2ʝ<>LA ~ l;ʌђK<Û8-G7Շ/]"ӿ˥ u MzT,AIF;2BB;+-!;K&VCvfCaRRg<` ПGE}Q|K}b>ґmғ*һxp 00_S#TtdHAݼ&^ŵԷM}.iqoL]qdvx֠l~*ci}݇jmHY9u]c x=Y{؃] lئL`cN}Ҕ߂kxm=ޯ- d=}ipK&#ϋ#[IBK۵R+EDmf{em)̭}9<БF\E@')O,o1oÏM%(]],W:G2% -~Ԍ6j&OUV|HX0plE}Ju0o^7NrTABXQ`O ]mV4.6s=/%ulWV\iv6!\$[MqEwq>ұtsY[]]EwOٿ'Yއ'5K6m9]p҉w&򾇗[on }ӵnzwEY~zU߿za=u @9u%%t*+u+AN͎DA?Nx1{`"B.ƆI֚ء,س6zyo_E0npe+Ŧ๯SP_AJ[\z@t25vKl #۸Fy"$!ΰȱȭo}21Ǐ룟29d Erxd&-سiphd(E IK%2Mz;BβcM+% fˆɲ"3X8PҘǼr1T"i$8H:ӚL2'yJlZ l^ Xb49{*mRySj Dꐜbe6qp5ibeӁbV~+WZSjic &u=UmlywUb-'5"f,bT+& _ +JZmMi[*]I-u ѷo9սzp,"#<Ƒ%hp;\uX]8sލA%^ Ls%/^W|YC*wpumUܲ vś0_@Kdzs5fu9Ț]_+#v.}1rh\s}ɼ1Oq۫"WďsPv27rGNouy}yCg+t[ r\zM~N|Clh:%MCѫO'_hU~t}XMmi"q39w-v{ntq}N [ ]Nv}NcTߜcc朗9;3 3|h3f94]nz=O[gL}_|K֟/<zG`;s<σA:+<{< >7AAB۾  ?",3$+8Bҫ>;K+C-Cc{3:DB|%@T{W3SD?+TD,?@<> C[%8l67#[—q@/L),@',KؓSPTQT??a+b4R@KdZ|ERD-E;|ICD*El]|FDFScFEFh]tL;BBDqloFG?~4st/c4HtĤ=C@H@|A)sAO@P\BHRȏ|FhGŋܴl̿FȺHpnE{ECɆ<Į4ʣ4I|ֺ"dIGDHYܮKA#C!6$Tlˋó H$ \KJCE|˹K$;w k &ò,-uELJIǿǖE<;ʫwlxIɄG{4̾BL"d LJAjzlM8 N $ɎdJLF?TItFд-dUVjsBJZSV=U]Źyrk= Sm$%-X Ġ9T7T]WvuפiREᗅ5x5֔V$5X.%XeqeX^T_Xcw0WE֙MY[{MSSW])2*zՆUޜQoUQ˭TYR:=*`۱%۱۳E۴Ťe۶[۸]%ۋ ڌ]J٦ٻY=Z;-Z VM_-JDžȅߡʵ.]%E\eݏ4U*٥ڵ]j}+%5EUeu^-' W1K%5E_پZ|75Q " ^_^6>]_EK5N^ ^Ճ&ޘ $RhҮE`6N`ؿYYpZ_a$fbUm(_n.['.b.`~ުTT-4V5&0nōY[&b).c)Ve]Cc6&B6 vZH`=HI TF.cN:f;c;O]֨2K@^ wg߉)gj[kF6GuYYGffބ^nng}wi^f*F~߇IFꥆWhj^il`?jDj&y`kjoV\pXߜ낾~k gX^NlnkU{뒎k鿦m16WS (MN/FFmhhjӞNbNV8!dlߞdvZ^?6af^mܶdž@>m^lv+.Ⱦf.d&n6YVc^AVnnnĶSg&.Vo2^efno~m2konmkR|.l qWq&o6ͮQnppbp^l)lq76fc͞㍦Wm]N~yrxpo#'sNrvq!?q2ws%&'(qe~y@8j9~j@DiS:o./muhprsFfpE7%tGmSwuMu~tvvo;t^oVp'p<]ƅvNvOvvL?d7aWg}:mJwfK|nw(OP+c7`GwdҶt}o~R,_ex'ffysu@dWximaxiu=*,k{vwgng_nb/oS/yt%Z;oq'u?zEO]zqծ7ly|yv'0~xxu =oOtU8roŇppLz|7'|5(VOɧY_Hx\ygﳇG||'evWwħ}~w1OyWV>~Ux٧}2|C'oj/}^nr~pk?}s/soCyxrrsG,H „ 2lC'RhQ Č72G)iҤNqӥ%̘2gҬi&Μ:wl'P)MH)R]0%ȨRRj*֬Z$ydʕ-MLj,ڴjײe-\7f|)I6}/.,+]"aY!-Ȓƭ\6ivm@^4<4MT:Ut|i6ܺe=T^Lem8ʱJҧݽ{nO%u9nV%c8go]7F h/" 7Aez [PU H`rءU桔Kxm ~5^8#XsV|tx$Iv#bAXQ#OA~5$(1wsQy&(ZDq9adMRY]a޹zhBLݞ{gbi߅`kcfV棡&eyh\nʩj** *½("蝌6*e3YYWZNY,ʢny)ꬸV٪⚸; ;l|n;0;ֽ&MZ!~mQ\1~JzӶm.bl'gL~2N!|3`? ۰fmE#=Pq 35^Wm"_ʻkmL;8J5=)7XŇ 'ۼ}- 7ϯKZci6޻?(nd量ᚷ 0z.d=i{^ݸ Ju1Iv?؁,NQJ>Qt L06ljKl<]w{ xhs ن6CF<"%2N|"()RV"HEVχj AmO-0Ҩ5n|#(9ұvr=~'MdⳠ/䃾X&2~&S?[^"dǤ7md-k)$3'f7v֗e3xt Q4djvG6q\ %O ]|uюnt=jˬ&ޗ#>)/w~5EEi馇"~˓S?Y1k.{N1 XS~}ms6-qG97ӭu~7-yӻ7}7;ocaml-book-1.0/fr/html/book-ora163.html0000644000000000000000000002534007421273602014476 0ustar Prsentation de la partie IV Prcdent Index Suivant

Prsentation de la partie IV

Cette quatrime partie est une introduction aux concepts de la programmation parallle et prsente les modles mmoire partage et mmoire rpartie. Il n'est pas ncessaire de possder un super-calculateur parallle pour pouvoir exprimer des algorithmes concurrents ou raliser des applications rparties. Nous dfinissons dans ce prambule les diffrents termes utiliss dans les chapitres suivants.

Dans le cadre de la squentialit, une instruction d'un programme s'excute aprs une autre. On parle alors de dpendance causale. Ces programmes possdent la proprit de dterminisme. Pour un mme jeu de donnes en entre, un mme programme termine ou bien ne termine pas, et dans le cas o il termine, produit le mme rsultat. Le dterminisme indique qu'une cause a un et un seul effet. Les seules exceptions, dj rencontres en Objective CAML, proviennent des fonctions prenant en entre une information extrieure au programme, comme la fonction Sys.time.

Dans le cadre de la programmation parallle, un programme est dcoup en plusieurs processus actifs. Chacun de ces processus est squentiel, mais plusieurs instructions, appartenant aux diffrents processus, sont excutes en parallle, c'est--dire << en mme temps >>. La squentialit se transforme en concurrence. On parle alors d'indpendance causale. Une cause peut avoir plusieurs effets, mutuellement exclusifs (un seul effet se produit). Une consquence immdiate est de perdre la proprit de dterminisme : un mme programme, pour une mme donne, termine ou ne termine pas en pouvant produire lorsqu'il termine des rsultats diffrents.

Pour contrler l'excution d'un programme parallle, il est ncessaire d'utiliser deux nouvelles notions :
  • la synchronisation qui introduit une attente d'une condition sur plusieurs processus;
  • la communication pour l'envoi d'informations d'un ou plusieurs processus un ou plusieurs processus.
Du point de vue de la causalit, la synchronisation assure que plusieurs causes indpendantes doivent s'tre produites avant que l'effet puisse avoir lieu. La communication possde une contrainte temporelle : un message ne peut pas tre reu avant d'avoir t mis. La communication peut s'effectuer selon plusieurs modes : communication d'un processus un autre (point--point) ou en diffusion (un--tous ou tous--tous).

Les deux modles de programmation parallle, dcrits la figure 17.13, diffrent sur le contrle de l'excution par les formes de synchronisation et de communication.


Figure 17.13 : Modles de paralllisme


Chaque processus Pi correspond un processus squentiel. L'ensemble de ces processus, interagissant sur une mmoire commune (M), ou communiquant via un mdia, constitue une application parallle.

Modle mmoire partage
La communication dans le modle mmoire partage est implicite. L'information est transmise lors de l'criture dans une zone de la mmoire partage, et rcupre quand un autre processus vient lire cette zone. Par contre la synchronisation doit tre explicite, en utilisant des instructions lmentaires de gestion de l'exclusion mutuelle et d'attente sur condition.

Ce modle est utilis ds que l'on se sert de manire concurrente une ressource commune. On peut citer en particulier la construction des systmes d'exploitation.

Modle mmoire rpartie
Dans ce modle chaque processus squentiel Pi possde sa propre mmoire prive Mi. Il est le seul y avoir accs. Les processus doivent alors communiquer pour transfrer de l'information travers un mdia assurant ces transferts. La difficult de ce modle provient de l'implantation du mdia. Les programmes s'en chargeant s'appellent des protocoles.

Ceux-ci sont organiss en couches. Les protocoles de haut niveau, implantant des services labors, utilisent les couches de plus bas niveaux.

Il existe plusieurs types de communication selon la capacit du mdia stocker de l'information et selon le caractre bloquant ou non de l'mission et de la rception. On parle de communications synchrones quand le transfert d'information n'est possible qu'aprs une synchronisation globale des processus metteur et rcepteur. Dans ce cas l'mission et la rception peuvent tre bloquantes.

Si le mdia possde une capacit de stockage, il peut conserver des messages mis en vue de leur acheminement ultrieur. La communication peut tre alors asynchrone et l'mission non bloquante. Il est toutefois ncessaire de spcifier la capacit de stockage du mdia, l'ordre d'acheminement, les dlais et la fiabilit des transmissions.

Enfin si l'mission est non bloquante avec un mdia ne pouvant conserver les messages, on obtient une communication vanescente : seuls les processus rcepteurs prts reoivent le message mis qui est alors perdu pour les autres.

Dans le modle mmoire rpartie la communication est explicite alors que la synchronisation est implicite (elle est en fait produite par la communication). C'est le dual du modle mmoire partage.

Paralllisme physique et logique
Le modle mmoire rpartie est valable dans le cas de paralllisme physique (rseau d'ordinateurs) ou logique (processus Unix communiquant par tubes de communication ou processus lgers communiquant par canaux). Il n'y a pas de valeurs globales connues par tous les processus (comme une horloge globale).

Le modle mmoire partage est plus proche du paralllisme physique, une mme mmoire est effectivement partage. Nanmoins, rien n'empche de simuler une mmoire partage sur un rseau d'ordinateurs.

Cette quatrime partie va donc montrer comment construire des applications parallles en Objective CAML en utilisant les deux modles prsents. Elle s'appuie principalement sur la bibliothque Unix, qui interface les appels systme Unix Objective CAML, et la bibliothque Thread qui implante les processus lgers. La bibliothque Unix est en grande partie porte sous Windows, en particulier les fonctions sur les descripteurs de fichier. Ceux-ci sont utiliss pour lire et crire sur des fichiers, mais aussi pour les tubes de communication (pipe) et pour les prises de communication rseau (socket).

Le chapitre 18 dcrit les traits essentiels de la bibliothque Unix en s'intressant tout particulirement la communication d'un processus vers l'extrieur ou vers d'autres processus. La notion de processus de ce chapitre est celui de << processus lourd >> la Unix. Leur cration s'effectue par l'appel systme fork qui duplique le contexte d'excution et la zone mmoire des donnes en crant une filiation entre processus. L'interaction entre processus est ralise soit par des signaux, soit travers des tubes de communication.

Le chapitre 19 reprend la notion de processus en prsentant les processus lgers de la bibliothque Thread. la diffrence des processus lourds prcdents, ceux-ci ne dupliquent que le contexte d'excution d'un processus existant. La zone de donnes est donc commune au thread crateur et au thread cr. Selon le style de programmation, les processus lgers d'Objective CAML permettent de rendre compte du modle de paralllisme mmoire partage (style impratif) ou du modle mmoire distincte (style purement fonctionnel). La bibliothque Thread comprend plusieurs modules permettant le lancement et l'arrt de threads, la gestion de verrou pour l'exclusion mutuelle, l'attente sur condition et la communication entre threads via des canaux propres aux threads. Dans ce modle, il n'y a pas gain de temps d'excution d'un programme, y compris sur les machines multi-processeurs. Nanmoins l'expression d'algorithmes parallles y est grandement facilite.

Le chapitre 20 se consacre la fabrication d'applications rparties sur le rseau Internet. Il prsente ce rseau du point de vue des protocoles de bas niveau. Grce aux prises de communication, plusieurs processus s'excutant sur diffrentes machines peuvent communiquer entre eux. La communication travers des sockets est une communication point--point asynchrone. Le rle des diffrents processus entrant en jeu dans la communication d'une application rpartie est en gnrale asymtrique. C'est le cas dans une architecture client-serveur. Le serveur est un processus acceptant des requtes et tchant d'y rpondre. Le client, autre processus, envoie une requte au serveur en esprant une rponse. De nombreux services accessibles sur le rseau Internet suivent cette architecture.

Le chapitre 21 prsente une bibliothque et deux applications compltes. La bibliothque permet de dfinir la communication entre clients et serveurs partir d'un protocole donn. La premire application reprend les robots du chapitre 17 pour en donner une version rpartie. La deuxime application construit un serveur HTTP pour la gestion d'un formulaire de requtes reprenant les lments de la gestion d'associations prsente au chapitre 6.




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora026.html0000644000000000000000000007066407421273601014504 0ustar Structures de donnes physiquement modifiables Prcdent Index Suivant

Structures de donnes physiquement modifiables

Les valeurs des types suivants : vecteurs, chanes de caractres, enregistrements champs mutables et rfrences sont des valeurs structures dont les composants peuvent tre physiquement modifis.

Nous avons vu qu'une variable Objective CAML lie une valeur garde cette valeur jusqu' la fin de son existence. On ne peut modifier cette liaison que par une redfinition et, dans ce cas, il ne s'agit pas proprement parler de la << mme >> variable puisque une nouvelle variable de mme nom vient masquer l'ancienne qui n'est plus accessible directement mais qui reste inchange. Avec les valeurs modifiables, on peut changer la valeur associe une variable sans avoir redclarer cette dernire. On a accs la valeur d'une variable aussi bien en lecture qu'en criture.

Vecteurs

Les vecteurs, ou tableaux une dimension, regroupent un nombre connu d'lments de mme type. On peut crire directement un vecteur en numrant ses valeurs encadres par les symboles [| et |] et spares par un point virgule la manire des listes.

# let v = [| 3.14; 6.28; 9.42 |] ;;
val v : float array = [|3.14; 6.28; 9.42|]
La fonction de cration Array.create prend le nombre d'lments du vecteur, une valeur initiale et retourne un nouveau vecteur.

# let v = Array.create 3 3.14;;
val v : float array = [|3.14; 3.14; 3.14|]


L'accs et la modification d'un lment s'effectuent en prcisant l'indice de l'lment dsign :

Syntaxe


expr1 . ( expr2 )

Syntaxe


expr1 . ( expr2 ) <- expr3


expr1 doit tre un vecteur (type array) dont les valeurs sont du type de expr3. L'expression expr2 doit, bien entendu, tre de type int. La modification est une expression de type unit. Le premier lment d'un vecteur a l'indice 0 et le dernier la longueur du vecteur moins 1. Les parenthses entourant l'expression de l'indice sont obligatoires.

# v.(1) ;;
- : float = 3.14
# v.(0) <- 100.0 ;;
- : unit = ()
# v ;;
- : float array = [|100; 3.14; 3.14|]


Si l'indice utilis pour accder un lment d'un tableau est en dehors de l'intervalle des indices de celui-ci, alors une exception sera dclenche au moment de l'accs.

# v.(-1) +. 4.0;;
Uncaught exception: Invalid_argument("Array.get")
Cette vrification est effectue au moment de l'excution du programme, ce qui peut ralentir l'excution. Nanmoins elle est indispensable pour viter de remplir des zones mmoire en dehors de l'espace allou un vecteur, ce qui provoquerait des erreurs d'excution graves.

Les fonctions ddies la manipulation des tableaux font partie du module Array de la bibliothque standard. On en donne la description au chapitre 8 (page ??). Nous utiliserons, dans les exemples ci-dessous, les trois fonctions suivantes du module Array :
  • create qui cre un vecteur d'une taille donne avec une valeur initiale donne.
  • length qui donne la longueur d'un vecteur.
  • append qui concatne deux vecteurs.

Partage de valeurs dans un vecteur

Tous les lments du vecteur contiennent la valeur passe leur cration. Cela implique un partage de cette valeur si celle-ci est une valeur structure. Crons, par exemple une matrice comme vecteur de vecteurs l'aide de la fonction create du module Array.

# let v = Array.create 3 0;;
val v : int array = [|0; 0; 0|]
# let m = Array.create 3 v;;
val m : int array array = [|[|0; 0; 0|]; [|0; 0; 0|]; [|0; 0; 0|]|]





Figure 3.1 : Reprsentation mmoire d'un vecteur partageant ses lments


Si l'on modifie l'un des champs du vecteur v ayant servi la cration de m, on modifie du coup toutes les <<lignes>> de la matrice (voir les figures 3.1 et 3.2).

# v.(0) <- 1;;
- : unit = ()
# m;;
- : int array array = [|[|1; 0; 0|]; [|1; 0; 0|]; [|1; 0; 0|]|]





Figure 3.2 : Modification d'lments d'un vecteur partag


Il y a duplication si la valeur d'initialisation du vecteur (second argument pass Array.create) est une valeur immdiate et partage si cette valeur est une valeur structure.

On appelle valeurs immdiates, les valeurs dont la taille ne dpasse pas la taille uniforme des valeurs d'Objective CAML, c'est dire un mot mmoire. Ce sont les entiers, les caractres , les boolens et les constructeurs constants. Les autres valeurs, dites valeurs structures, sont reprsentes par un pointeur sur une zone mmoire. Cette distinction est dtaille au chapitre 9 (page ??).
Les vecteurs de nombres flottants sont un cas particulier. Bien que les nombres flottants soient des valeurs structures, la cration d'un vecteur de flottants effectue une copie de la valeur initiale. Cela pour des questions d'optimisation. Le chapitre 12 sur l'interface avec le langage C (page ??) dcrit ce cas particulier.

Matrices non rectangulaires

Une matrice, vecteur de vecteur, peut ne pas tre rectangulaire. En effet rien n'empche de modifier un des vecteurs lments par un vecteur d'une autre longueur. Cela est utile pour limiter la taille d'une telle matrice. La valeur t suivante construit une matrice triangulaire pour les coefficients du triangle de Pascal.

# let t = [|
[|1|];
[|1; 1|];
[|1; 2; 1|];
[|1; 3; 3; 1|];
[|1; 4; 6; 4; 1|];
[|1; 5; 10; 10; 5; 1|]
|] ;;
val t : int array array =
[|[|1|]; [|1; 1|]; [|1; 2; 1|]; [|1; 3; 3; 1|]; [|1; 4; 6; 4; ...|]; ...|]
# t.(3) ;;
- : int array = [|1; 3; 3; 1|]
Dans cet exemple, l'lment du vecteur t l'indice i est un vecteur d'entiers de taille i+1. Pour manipuler de telles matrices, il est ncessaire de calculer la taille de chaque lment vecteur.

Copie de vecteurs

Quand on copie un vecteur ou que l'on concatne deux vecteurs, le rsultat obtenu est un nouveau vecteur. Une modification des vecteurs originaux n'entrane pas de modification des copies, sauf, comme toujours modifier les valeurs partages.

# let v2 = Array.copy v ;;
val v2 : int array = [|1; 0; 0|]
# let m2 = Array.copy m ;;
val m2 : int array array = [|[|1; 0; 0|]; [|1; 0; 0|]; [|1; 0; 0|]|]
# v.(1)<- 352;;
- : unit = ()
# v2;;
- : int array = [|1; 0; 0|]
# m2 ;;
- : int array array = [|[|1; 352; 0|]; [|1; 352; 0|]; [|1; 352; 0|]|]


On s'aperoit dans cet exemple que la copie de m ne copie que les pointeurs vers v. Si un des lments de v est modifi, alors m2 le sera aussi. La concatnation cre un nouveau vecteur dont la taille est gale la somme des tailles des deux autres.

# let mm = Array.append m m ;;
val mm : int array array =
[|[|1; 352; 0|]; [|1; 352; 0|]; [|1; 352; 0|]; [|1; 352; 0|];
[|1; 352; ...|]; ...|]
# Array.length mm ;;
- : int = 6
# m.(0) <- Array.create 3 0 ;;
- : unit = ()
# m ;;
- : int array array = [|[|0; 0; 0|]; [|1; 352; 0|]; [|1; 352; 0|]|]
# mm ;;
- : int array array =
[|[|1; 352; 0|]; [|1; 352; 0|]; [|1; 352; 0|]; [|1; 352; 0|];
[|1; 352; ...|]; ...|]


Par contre la modification de v, valeur partage dans m et mm, aura un effet sur ces deux matrices.

# v.(1) <- 18 ;;
- : unit = ()
# mm;;
- : int array array =
[|[|1; 18; 0|]; [|1; 18; 0|]; [|1; 18; 0|]; [|1; 18; 0|]; [|1; 18; ...|];
...|]


Chanes de caractres

Les chanes de caractres peuvent tre considres comme un cas spcial de vecteurs de caractres. Nanmoins pour des questions d'occupation mmoire1 leur type est spcialis. De plus l'accs aux lments possde une syntaxe particulire :

Syntaxe


expr1 . [expr2]
Les lments d'une chane de caractres peuvent tre modifis physiquement :

Syntaxe


expr1 . [expr2] <- expr3


# let s = "salut";;
val s : string = "salut"
# s.[2];;
- : char = 'l'
# s.[2]<-'Z';;
- : unit = ()
# s;;
- : string = "saZut"


Champs modifiables des enregistrements

Les champs d'un enregistrement peuvent tre dclars modifiables. Il suffit de l'indiquer dans la dclaration du type de l'enregistrement par le mot cl mutable.

Syntaxe


type nom = { ...; mutable nomi : t ; ...}


Voici un petit exemple dfinissant un type d'enregistrement pour les points du plan :

# type point = { mutable xc : float; mutable yc : float } ;;
type point = { mutable xc: float; mutable yc: float }
# let p = { xc = 1.0; yc = 0.0 } ;;
val p : point = {xc=1; yc=0}


La valeur d'un champ dclar mutable est donc modifiable suivant la syntaxe :

Syntaxe


expr1 . nom <- expr2


L'expression expr1 doit tre d'un type enregistrement possdant la champ nom. L'opration de modification retourne une valeur de type unit.

# p.xc <- 3.0 ;;
- : unit = ()
# p ;;
- : point = {xc=3; yc=0}


On peut crire une fonction de dplacement d'un point, modifiant les coordonnes de celui-ci. On utilise la dclaration locale par filtrage pour squentialiser les effets de bord.

# let moveto p dx dy =
let () = p.xc <- p.xc +. dx
in p.yc <- p.yc +. dy ;;
val moveto : point -> float -> float -> unit = <fun>
# moveto p 1.1 2.2 ;;
- : unit = ()
# p ;;
- : point = {xc=4.1; yc=2.2}


Il est possible de mlanger des champs modifiables et des champs non modifiables dans la dfinition d'un enregistrement. Seuls ceux spcifis mutable sont alors modifiables :

# type t = { c1 : int; mutable c2 : int } ;;
type t = { c1: int; mutable c2: int }
# let r = { c1 = 0; c2 = 0 } ;;
val r : t = {c1=0; c2=0}
# r.c1 <- 1 ;;
Characters 0-9:
The label c1 is not mutable
# r.c2 <- 1 ;;
- : unit = ()
# r ;;
- : t = {c1=0; c2=1}


Nous donnons page ?? un exemple d'utilisation des enregistrements champs modifiables et des tableaux pour implanter une structure de pile.

Rfrences

Objective CAML fournit un type polymorphe ref qui peut tre vu comme le type des pointeurs sur une valeur quelconque ; en Objective CAML, on parlera plutt de rfrence sur une valeur. Une valeur rfrence peut tre modifie. Le type ref est dfinit comme un enregistrement un champ modifiable :
type 'a ref = {mutable contents:'a}
Ce type est muni de raccourcis syntaxiques prdfinis. On construit une rfrence sur une valeur par la fonction ref. La valeur rfrence peut tre atteinte par la fonction prfixe (!). La fonction modifiant le contenu d'une rfrence est la fonction infixe (:=).

# let x = ref 3 ;;
val x : int ref = {contents=3}
# x ;;
- : int ref = {contents=3}
# !x ;;
- : int = 3
# x := 4 ;;
- : unit = ()
# !x ;;
- : int = 4
# x := !x+1 ;;
- : unit = ()
# !x ;;
- : int = 5


Polymorphisme et valeurs modifiables

Le type ref est paramtr. C'est ce qui permet de l'utiliser pour crer des rfrences sur des valeurs de n'importe quel type. Cependant, il faut apporter quelques restrictions au type des valeurs rfrences : on ne peut pas autoriser la cration d'une rfrence sur une valeur de type polymorphe sans prendre quelques prcautions.

Supposons qu'il n'y ait aucune restriction, on pourrait alors dclarer :
let x = ref [] ;;
La variable x serait alors de type 'a list ref et l'on pourrait en modifier la valeur de faon incohrente avec le typage statique fort d'Objective CAML :

x := 1 :: !x ;;
x := true :: !x ;;
On aurait ainsi une mme variable de type int list un moment et de type bool list un autre.

Pour viter une telle situation, le mcanisme d'infrence de type d'Objective CAML utilise une nouvelle catgorie de variables de type : les variables de type faible. Syntaxiquement, elles sont distingues par le caractre soulign (_) qui les prfixe.

# let x = ref [] ;;
val x : '_a list ref = {contents=[]}
La variable de type '_a n'est pas un paramtre de type, mais une inconnue en attente d'instanciation : c'est la premire utilisation de x aprs sa dclaration qui fixera la valeur que prendra '_a et ce, dans tous les types qui en dpendent et de faon dfinitive :

# x := 0::!x ;;
- : unit = ()
# x ;;
- : int list ref = {contents=[0]}
La variable x est dsormais de type int list ref.

Un type contenant une inconnue est en fait monomorphe bien que son type n'ait pas t prcis. Il n'est pas possible d'instancier cette inconnue par un type polymorphe.

# let x = ref [] ;;
val x : '_a list ref = {contents=[]}
# x := (function y -> ())::!x ;;
- : unit = ()
# x ;;
- : ('_a -> unit) list ref = {contents=[<fun>]}
Dans cet exemple, bien que nous ayons instanci l'inconnue de type avec un type a priori polymorphe ('a -> unit), le type est rest monomorphe avec une nouvelle inconnue de type.

Cette restriction du polymorphisme n'est pas rserve aux rfrences mais s'applique toute valeur contenant une partie modifiable : les vecteurs et les enregistrements ayant au moins un champ dclar mutable, etc. Tous les paramtres de type, mme ceux sans rapport avec une composante modifiable sont alors des variables de type faible.

# type ('a,'b) t = { ch1 :'a list ; mutable ch2 : 'b list } ;;
type ('a, 'b) t = { ch1: 'a list; mutable ch2: 'b list }
# let x = { ch1 = [] ; ch2 = [] } ;;
val x : ('_a, '_b) t = {ch1=[]; ch2=[]}


Warning


Cette modification du typage de l'application a des consquences sur les programmes fonctionnels purs.


Lorsque l'on applique une valeur polymorphe une fonction polymorphe, on obtient galement une variable de type faible, car il ne faut pas exclure que la fonction construise des valeurs physiquement modifiables. En d'autres termes, le rsultat d'une application est toujours monomorphe.

# (function x -> x) [] ;;
- : '_a list = []


On obtient le mme rsultat avec l'application partielle :

# let f a b = a ;;
val f : 'a -> 'b -> 'a = <fun>
# let g = f 1 ;;
val g : '_a -> int = <fun>


Pour retrouver un type polymorphe, il faut abstraire le second argument de f puis l'appliquer :

# let h x = f 1 x ;;
val h : 'a -> int = <fun>


En effet, l'expression qui dfinit h est l'expression fonctionnelle function x -> f 1 x. Son valuation produira une fermeture qui ne risque pas de produire d'effet de bord puisque le corps de la fonction n'est pas valu.

De faon gnral, on distingue les expressions dites << non expansives >>, dont on est sr que le calcul ne risque pas d'effectuer un effet de bord, des autres, dites << expansives >>. Le systme de types d'Objective CAML classifie les expressions du langage selon leur forme syntaxique :
  • les expressions << non expansives >> incluent principalement les variables, les constructeurs de valeurs non modifiables et l'abstraction;
  • les expressions << expansives >> incluent principalement l'application, les constructeurs de valeurs modifiables. Il faut y rajouter des structures de contrles telles la conditionnelle ou le filtrage.

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora172.html0000644000000000000000000000547607421273602014506 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

Le module Unix reprend, jusque dans leurs noms, les fonctions principales des bibliothques du systme Unix. La plupart des paradigmes de programmation utiliss ne sont pas spcifiques Objective CAML. Le lecteur pourra se rfrer aux ouvrages classiques sur la programmation systme. Citons [Rif90], ou [CDM96] plus spcifique Linux.

D'autre part, un excellent polycopi de cours de Xavier Leroy [Ler92] ayant pour thme la programmation systme en Caml-Light est disponible l'adresse suivante :

Lien


http://pauillac.inria.fr/~xleroy/publi/unix-in-caml.ps.gz


L'implantation du module Unix est l'exemple type de la coopration entre C et Objective CAML. Un grand nombre des fonctions de cette bibliothque sont de simples appels aux fonctions systmes C accompagns du petit travail de transcription des types de donnes. Les programmes sources de cette implantation sont de bons exemples pour l'interfaage d'un programme Objective CAML avec une bibliothque C. Ces programmes sont dans les rpertoires otherlibs/unix et otherlibs/win32unix de la distribution d'Objective CAML.

Ce chapitre a fait l'impasse sur plusieurs fonctionnalits du module Unix. Certains points seront abords dans le chapitre 20 : prises de communication et adressage Internet. Les autres points, tels la notion de terminal, de systme de fichiers, etc, ne sont pas traits dans cet ouvrage. Ils demeurent dcouvrir dans l'un des ouvrages cits ci-dessus.








Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora094.html0000644000000000000000000000211707421273603014477 0ustar Notes
1
Dans la version en ligne de l'ouvrage, le gris est lgrement bleut.
2
La plupart des valeurs ne survivent pas un GC
3
La seule exception, en Objective CAML, concerne les tableaux de flottants (voir chapitre 12, page ??.
ocaml-book-1.0/fr/html/book-ora005.html0000644000000000000000000000127407421273603014472 0ustar Notes
1
Institut National de Recherche en Informatique et Automatique.
ocaml-book-1.0/fr/html/book-ora016.html0000644000000000000000000024445207421273601014501 0ustar Dclaration de types et filtrage de motif Prcdent Index Suivant

Dclaration de types et filtrage de motif

Bien que les types prdfinis d'Objective CAML permettent de construire des donnes structures, grce aux n-uplets et aux listes, il est ncessaire de pouvoir dfinir de nouveaux types pour dcrire des donnes structures particulires. En Objective CAML, les dclarations de type sont rcursives et peuvent tre paramtres par des variables de type, la manire du type 'a list dj rencontr. L'infrence de types tient compte de ces nouvelles dclarations pour produire le type d'une expression. La construction de valeurs de ces nouveaux types utilise les constructeurs dcrits dans leur dfinition. Une spcificit des langages de la famille ML est le filtrage de motif. Il permet un accs simple aux composants de structures de donnes complexes. La dfinition d'une fonction correspond le plus souvent un filtrage par motif sur un de ses paramtres permettant une dfinition par cas de la fonction.

Nous prsentons tout d'abord le filtrage de motif sur les types prdfinis, pour ensuite dcrire les diffrentes dclarations de types structurs, la construction de telles valeurs ainsi que l'accs leurs composants par filtrage de motif.

Filtrage de motif (pattern matching)

Un motif, pattern en anglais, n'est pas proprement parler une expression d'Objective CAML. Il s'agit plutt d'un assemblage correct (syntaxiquement, et du point de vue des types) d'lments tels que les constantes des types de base (int, bool, char, ..), les variables, les constructeurs et le symbole _ dit motif universel. D'autres symboles servent l'criture de motifs. Nous les introduirons au fur et mesure des besoins.

Le filtrage par motif s'applique aux valeurs. Il sert reconnatre la forme de cette valeur et permet d'orienter le calcul en consquence en associant chaque motif une expression calculer.

Syntaxe


match expr with
| p1 -> expr1
:
| pn -> exprn

L'expression expr est filtre squentiellement par les diffrents motifs p1, ..., pn. Si un des motifs (par exemple pi) est cohrent par rapport la valeur de expr alors la branche de calcul correspondante (expri) est value. Les diffrents motifs pi sont du mme type. Il en est de mme pour les diffrentes expressions expri. La barre verticale prcdant le premier motif est optionnelle.

Exemples

Voici deux faons de dfinir, par filtrage de motif, une fonction imply de type (bool * bool) -> bool implantant l'implication logique. Un motif pour les couples a la forme ( , ).

La premire version de imply numre tous les cas possibles comme le ferait une table de vrit :

# let imply v = match v with
(true,true) -> true
| (true,false) -> false
| (false,true) -> true
| (false,false) -> true;;
val imply : bool * bool -> bool = <fun>


En utilisant des variables regroupant plusieurs cas, on obtient une dfinition plus compacte.

# let imply v = match v with
(true,x) -> x
| (false,x) -> true;;
val imply : bool * bool -> bool = <fun>
Ces deux versions de imply calculent la mme fonction. C'est--dire qu'elles retournent les mmes valeurs pour les mmes entres.

Motif linaire

Un motif doit tre obligatoirement linaire, c'est--dire qu'une variable donne ne peut y figurer au plus qu'une fois. Ainsi, on aurait pu esprer pouvoir crire :

# let equal c = match c with
(x,x) -> true
| (x,y) -> false;;
Characters 35-36:
This variable is bound several times in this matching
Mais cela aurait exig du compilateur de savoir raliser les tests d'galit. Or, la ralisation de tels tests soulve immdiatement de nombreux problmes. Si l'on se contente de l'galit physique entre valeurs, on obtient un systme trop faible, incapable de reconnatre l'galit entre deux occurrences de la liste [1; 2], par exemple. Si l'on dcide d'utiliser une galit structurelle, on court le risque d'avoir explorer, infiniment, des structures circulaires. Les fonctions rcursives, par exemple sont des structures circulaires, mais on peut construire galement des valeurs rcursives, donc circulaires, non fonctionnelles (voir page ??).

Motif universel

Le symbole _ filtre toutes les valeurs possibles. Il est appel motif universel. Il peut tre utilis pour filtrer des types complexes. On l'utilise, par exemple, pour simplifier encore la dfinition de la fonction imply :

# let imply v = match v with
(true,false) -> false
| _ -> true;;
val imply : bool * bool -> bool = <fun>


Une dfinition par filtrage doit considrer l'ensemble des cas possibles des valeurs filtres. Si tel n'est pas le cas, le compilateur affiche un message d'avertissement :

# let is_zero n = match n with 0 -> true ;;
Characters 17-40:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
1
val is_zero : int -> bool = <fun>


En effet si le l'argument d'appel est diffrent de 0 la fonction ne sait pas quelle valeur retourner. On peut alors complter le filtrage en utilisant le motif universel.

# let is_zero n = match n with
0 -> true
| _ -> false ;;
val is_zero : int -> bool = <fun>


Si, l'excution, aucun motif n'est slectionn, alors une exception est dclenche. Ainsi, on peut crire :

# let f x = match x with 1 -> 3 ;;
Characters 11-30:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
0
val f : int -> int = <fun>
# f 1 ;;
- : int = 3
# f 4 ;;
Uncaught exception: Match_failure("", 11, 30)
L'exception Match_Failure est dclenche l'appel de f 4, et si elle n'est pas rcupre entrane l'arrt du calcul en cours (voir ??)

Combinaison de motifs

La combinaison de plusieurs motifs permet d'obtenir un nouveau motif qui pourra dstructurer une valeur selon l'un ou l'autre de ses motifs originaux. La forme syntaxique est la suivante :

Syntaxe


p1 | ...| pn
Elle construit un nouveau motif par combinaison des motifs p1, ...et pn. La seule contrainte forte est de refuser tout nommage l'intrieur de ces motifs. Donc chacun d'eux ne devra contenir que des valeurs constantes ou le motif universel. L'exemple suivant montre comment vrifier qu'un caractre est une voyelle.
 
# let est_une_voyelle c = match c with
'a' | 'e' | 'i' | 'o' | 'u' | 'y' -> true
| _ -> false ;;
val est_une_voyelle : char -> bool = <fun>
# est_une_voyelle 'i' ;;
- : bool = true
# est_une_voyelle 'j' ;;
- : bool = false


Filtrage d'un paramtre

Le filtrage de motif est essentiellement utilis pour la dfinition de fonctions par cas. Pour allger l'criture de ces dfinitions, la construction syntaxique function autorise le filtrage d'un paramtre :

Syntaxe


function | p1 -> expr1
  | p2 -> expr2
    :
  | pn -> exprn

La barre verticale prcdant le premier motif est l aussi optionnelle. En fait, tel Mr Jourdain, chaque fois que nous dfinissons une fonction, nous utilisons un filtrage de motif. En effet, la construction function x -> expression , est une dfinition par filtrage utilisant un unique motif rduit une variable. On peut utiliser cette particularit avec des motifs simples comme dans :

# let f = function (x,y) -> 2*x + 3*y + 4 ;;
val f : int * int -> int = <fun>


De fait la forme
function p1 -> expr1 | ...| pn -> exprn
est quivalente
function expr -> match expr with p1 -> expr1 | ...| pn -> exprn

En utilisant l'quivalence des dclarations mentionne page ??, on crira :

# let f (x,y) = 2*x + 3*y + 4 ;;
val f : int * int -> int = <fun>
Mais cette criture naturelle n'est possible que si la valeur filtre appartient un type n'ayant qu'un seul constructeur. Si tel n'est pas le cas, le filtrage n'est pas exhaustif :

# let is_zero 0 = true ;;
Characters 13-21:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
1
val is_zero : int -> bool = <fun>


Nommage d'une valeur filtre

Lors d'un filtrage de motif, il est parfois pratique de nommer tout ou partie du motif. La forme syntaxique suivante introduit le mot cl as qui associe un nom un motif.

Syntaxe


( p as nom )
Ceci est utile lorsqu'on a besoin de dstructurer une valeur tout en la conservant dans son intgralit. Dans l'exemple suivant, la fonction min_rat rend le plus petit rationnel d'un couple de rationnels. Ces derniers sont reprsents par un couple numrateur et dnominateur.

# let min_rat cr = match cr with
((_,0),c2) -> c2
| (c1,(_,0)) -> c1
| (((n1,d1) as r1), ((n2,d2) as r2)) ->
if (n1 * d2 ) < (n2 * d1) then r1 else r2;;
val min_rat : (int * int) * (int * int) -> int * int = <fun>
Pour comparer deux rationnels, il est ncessaire de les dstructurer pour pouvoir nommer leur numrateur et leur dnominateur (n1, n2, d1 et d2), mais il faut rendre le couple initial (r1 ou r2). La construction as nous permet ce nommage de parties d'une mme valeur. Cela vite de devoir reconstruire le rationnel retourn en rsultat.

Filtrage avec gardes

Le filtrage avec gardes correspond l'valuation d'une expression conditionnelle immdiatement aprs le filtrage d'un motif. Si cette expression renvoie true, alors l'expression associe au motif est value, sinon le filtrage continue avec le motif suivant.

Syntaxe


match expr with
:
| pi when condi -> expri
:



L'exemple suivant utilise deux gardes pour tester l'galit entre deux rationnels.

# let eq_rat cr = match cr with
((_,0),(_,0)) -> true
| ((_,0),_) -> false
| (_,(_,0)) -> false
| ((n1,1), (n2,1)) when n1 = n2 -> true
| ((n1,d1), (n2,d2)) when ((n1 * d2) = (n2 * d1)) -> true
| _ -> false;;
val eq_rat : (int * int) * (int * int) -> bool = <fun>
Dans le filtrage du quatrime motif, si la garde choue, le filtrage se poursuit sur le cinquime motif.

Remarque


La vrification de l'exhaustivit du filtrage effectue par Objective CAML suppose que l'expression conditionnelle de la garde puisse tre fausse. En consquence, elle ne tient pas compte de ce motif puisqu'il n'est pas possible de savoir, avant l'excution, si la garde sera ralise ou non.
L'exhaustivit du filtrage dans l'exemple suivant ne pourra pas tre dtecte.

# let f = function x when x = x -> true;;
Characters 10-40:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
_
val f : 'a -> bool = <fun>


Filtrage d'intervalles de caractres

Dans le cadre du filtrage sur des caractres, il est fastidieux de construire la combinaison de tous les motifs correspondant un intervalle de caractres. En effet, si l'on dsire tester si un caractre est bien une lettre, il faudra au minimum crire 26 motifs simples et les combiner. Objective CAML autorise pour les caractres, l'criture de motifs de la forme :

Syntaxe


'c1' .. 'cn'
Elle est quivalente la combinaison : 'c1' | 'c2' | ...| 'cn'.

Par exemple le motif '0' .. '9' correspond au motif '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'. La premire forme est plus agrable lire et plus rapide crire.

Warning


Ce trait fait partie des extensions du langage et pourra voluer dans les prochaines versions.
En utilisant les motifs combins et les intervalles, on dfinit une fonction discriminant les caractres selon plusieurs critres.

# let char_discriminate c = match c with
'a' | 'e' | 'i' | 'o' | 'u' | 'y'
| 'A' | 'E' | 'I' | 'O' | 'U' | 'Y' -> "Voyelle"
| 'a'..'z' | 'A'..'Z' -> "Consonne"
| '0'..'9' -> "Chiffre"
| _ -> "Autre" ;;
val char_discriminate : char -> string = <fun>
On peut noter que l'ordre des ensembles de motifs a son importance. En effet le second ensemble de motifs contient le premier, mais il n'est examin qu'aprs l'chec du premier.

Filtrage des listes

Comme nous l'avons vu, une liste peut tre :
  • soit vide (la liste est de la forme []),
  • soit compose d'un premier lment (sa tte) et d'une sous-liste (sa queue). La liste est alors de la forme t::q.
Ces deux critures possibles d'une liste sont utilisables comme des motifs et permettent de filtrer une liste.

# let rec size x = match x with
[] -> 0
| _::queue_x -> 1 + (size queue_x) ;;
val size : 'a list -> int = <fun>
# size [];;
- : int = 0
# size [7;9;2;6];;
- : int = 4


On peut donc reprendre les exemples dcrits prcdemment (voir page ??) en utilisant le filtrage de motifs, comme par exemple pour l'itration sur les listes.

# let rec fold_left f a = function
[] -> a
| tete::queue -> fold_left f (f a tete) queue ;;
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a = <fun>
# fold_left (+) 0 [8;4;10];;
- : int = 22


Dclaration de valeurs par filtrage d'un motif

La dclaration de valeurs utilise en fait le filtrage de motifs. La dclaration let x = 18 filtre la valeur 18 avec le motif x. Tout motif est accept comme filtre d'une dclaration, les variables du motif sont associes aux valeurs qu'elles filtrent.

# let (a,b,c) = (1, true, 'A');;
val a : int = 1
val b : bool = true
val c : char = 'A'
# let (d,c) = 8, 3 in d + c;;
- : int = 11
La porte des variables de filtrage est celle usuelle de la dclaration de valeurs y compris pour les dclarations locales. Ici, c reste associe la valeur 'A'.

# a + (int_of_char c);;
- : int = 66


Comme tout filtrage, celui-ci peut ne pas tre exhaustif.

# let [x;y;z] = [1;2;3];;
Characters 5-12:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]
val x : int = 1
val y : int = 2
val z : int = 3
# let [x;y;z] = [1;2;3;4];;
Characters 4-11:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]
Uncaught exception: Match_failure("", 4, 11)


Tout motif est accept, y compris les constructeurs , le motif universel et les motifs combins.

# let tete :: 2 :: _ = [1; 2; 3] ;;
Characters 5-19:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]
val tete : int = 1
# let _ = 3. +. 0.14 in "PI" ;;
- : string = "PI"


Ce dernier exemple a peu d'intrt dans le monde fonctionnel dans la mesure o la valeur 3.14 calcule n'est pas nomme et donc perdue.

Dclaration de types

Les dclarations de type sont un autre constituant possible d'une phrase Objective CAML. Elles permettent de dfinir de nouveaux types correspondant aux structures de donnes originales utilises dans un programme. Il y a deux grandes familles de types : les types produit pour les n-uplets ou les enregistrements ; les types somme pour les unions.

Les dclarations de type utilisent le mot cl type.

Syntaxe


type nom = typedef ;;
la diffrence des dclarations de variables, les dclarations de types sont par dfaut rcursives. C'est--dire que les dclarations de types, quand elles sont combines, permettent de dclarer des types mutuellement rcursifs.

Syntaxe


type nom1 = typedef1
and nom2 = typedef2
  :    
and nomn = typedefn ;;

Les dclarations de types peuvent tre paramtres par des variables de type. Un nom de variable de type commence toujours par une apostrophe (caractre ') :

Syntaxe


type 'a nom = typedef ;;
Lorsqu'ils sont plusieurs, les paramtres de types sont dclars comme un n-uplet devant le nom du type :

Syntaxe


type ('a1 ...'an) nom = typedef ;;
Seuls les paramtres de type dfinis dans la partie gauche de la dclaration de type peuvent apparatre dans sa partie droite.

Remarque


L'afficheur de types d'Objective CAML renomme les paramtres de type rencontrs, le premier se nomme 'a, le deuxime 'b et ainsi de suite.


On peut toujours dfinir un nouveau type partir d'un type ou de plusieurs types existants.

Syntaxe


type nom = expression de type
Ceci est utile pour contraindre un type que l'on trouve trop gnral.

# type 'param couple_entier = int * 'param ;;
type 'a couple_entier = int * 'a
# type couple_precis = float couple_entier ;;
type couple_precis = float couple_entier


Nanmoins sans contrainte sur les types, l'infrence produira le type le plus gnral.

# let x = (3, 3.14) ;;
val x : int * float = 3, 3.14


Mais on peut utiliser la contrainte de type pour voir apparatre le nom dsir :

# let (x:couple_precis) = (3, 3.14) ;;
val x : couple_precis = 3, 3.14


Enregistrements

Les enregistrements sont des n-uplets dont chaque champ est nomm la manire des record de Pascal ou des struct de C. Un enregistrement correspond toujours la dclaration d'un nouveau type. Un type enregistrement est dfini par la dclaration de son nom, du nom de chacun de ses champs et de leur type.

Syntaxe


type nom = { nom1 : t1; ...; nomn : tn } ;;
On peut dfinir un type reprsentant les nombres complexes par :

# type complex = { re:float; im:float } ;;
type complex = { re: float; im: float }


La cration d'une valeur de type enregistrement s'effectue en donnant une valeur chacun des champs (dans un ordre quelconque).

Syntaxe


{ nomi1 = expri1; ...; nomin = exprin } ;;
Par exemple, on cre un nombre complexe de partie relle 2. et de partie imaginaire 3. :

# let c = {re=2.;im=3.} ;;
val c : complex = {re=2; im=3}
# c = {im=3.;re=2.} ;;
- : bool = true


Dans le cas o des champs sont manquants, l'erreur suivante se produit :

# let d = { im=4. } ;;
Characters 9-18:
Some labels are undefined


L'accs un champ est possible de deux faons : par la notation pointe ou par le filtrage de certains champs.

La syntaxe de la notation pointe est usuelle :

Syntaxe


expr.nom
L'expression expr doit tre d'un type enregistrement possdant le champ nom.

Le filtrage d'un enregistrement permet de connatre la valeur associe un certain nombre de champs. Un motif de filtrage pour un enregistrement a la syntaxe suivante :

Syntaxe


{ nomi = pi ; ...; nomj = pj }
Les motifs sont droite du symbole = (pi, ..., pj). Il n'est pas ncessaire de faire figurer tous les champs d'un enregistrement dans un tel motif.

La fonction add_complex accde aux champs par la notation pointe alors que la fonction mult_complex y accde par filtrage.

# let add_complex c1 c2 = {re=c1.re+.c2.re; im=c1.im+.c2.im};;
val add_complex : complex -> complex -> complex = <fun>
# add_complex c c ;;
- : complex = {re=4; im=6}
# let mult_complex c1 c2 = match (c1,c2) with
({re=x1;im=y1},{re=x2;im=y2}) -> {re=x1*.x2-.y1*.y2;im=x1*.y2+.x2*.y1} ;;
val mult_complex : complex -> complex -> complex = <fun>
# mult_complex c c ;;
- : complex = {re=-5; im=12}


L'intrt des enregistrements, par rapport aux n-uplets, est au moins double :
  • une information descriptive et discriminante grce aux noms des champs : cela permet en particulier de simplifier les motifs de filtrage;
  • un accs identique, par son nom, de n'importe quel champ de l'enregistrement : l'ordre des champs n'a plus d'importance, seul leur nom compte.
L'exemple suivant montre la facilit d'accs aux champs des enregistrements par rapport au n-uplets :

# let a = (1,2,3) ;;
val a : int * int * int = 1, 2, 3
# let f tr = match tr with x,_,_ -> x ;;
val f : 'a * 'b * 'c -> 'a = <fun>
# f a ;;
- : int = 1
# type triplet = {x1:int; x2:int; x3:int} ;;
type triplet = { x1: int; x2: int; x3: int }
# let b = {x1=1; x2=2; x3=3} ;;
val b : triplet = {x1=1; x2=2; x3=3}
# let g tr = tr.x1 ;;
val g : triplet -> int = <fun>
# g b ;;
- : int = 1


Pour le filtrage de motif, il n'est pas ncessaire d'indiquer tous les champs de l'enregistrement filtrer. Le type infr est alors celui du dernier type enregistrement dclar contenant ces champs.

# let h tr = match tr with {x1=x} -> x;;
val h : triplet -> int = <fun>
# h b;;
- : int = 1


Il existe une construction permettant de crer un enregistrement identique un autre sauf pour quelques champs. C'est souvent utile pour les enregistrements contenant de nombreux champs.

Syntaxe


{ nom with nomi= expri ; ...; nomj=exprj}


# let c = {b with x1=0} ;;
val c : triplet = {x1=0; x2=2; x3=3}
Une nouvelle copie de la valeur de b est cre o seul le champ x1 a une nouvelle valeur.

Warning


Ce trait fait partie des extensions du langage et pourra voluer dans les prochaines versions.


Types somme

la diffrence des n-uplets ou des enregistrements, qui correspondent un produit cartsien, la dclaration d'un type somme correspond une union d'ensembles. On regroupe dans un mme type des types diffrents (par exemple des entiers et des chanes de caractres). Les diffrents membres de la somme sont discrimins par des constructeurs qui permettent d'une part, comme leur nom l'indique, de construire les valeurs de ce type et d'autre part d'accder aux composantes de ces valeurs grce au filtrage de motif. Appliquer un constructeur un argument, c'est indiquer que la valeur retourne appartient ce nouveau type.

On dclare un type somme en donnant le nom de ses constructeurs et le type de leur ventuel argument.

Syntaxe


type nom = ...
  | Nomi ...
  | Nomj of tj ...
  | Nomk of tk * ...* tl ...;;



Un nom de constructeur est un identificateur particulier : Un nom de constructeur est un identificateur particulier :

Warning


Les noms des constructeurs commencent toujours par une majuscule.


Constructeurs constants

On appelle constructeur constant un constructeur qui n'attend pas d'argument. Les constructeurs constants peuvent tre par la suite utiliss directement comme valeur du langage, comme une constante.

# type piece = Pile | Face;;
type piece = | Pile | Face
# Pile;;
- : piece = Pile
Le type bool peut se dfinir de cette manire.

Constructeurs avec arguments

Les constructeurs peuvent avoir des arguments. Le mot cl of indique le type des arguments du constructeur. Cela permet de regrouper sous un mme type des objets de types diffrents, chacun tant introduit avec un constructeur particulier.

Voici un exemple classique de dfinition d'un type de donnes pour reprsenter les cartes dans un jeu, ici le Tarot. On dfinit les types couleur et carte de la manire suivante :

# type couleur = Pique | Coeur | Carreau | Trefle ;;
# type carte =
Roi of couleur
| Dame of couleur
| Cavalier of couleur
| Valet of couleur
| Petite_carte of couleur * int
| Atout of int
| Excuse ;;


La cration d'une valeur du type carte s'effectue par application d'un constructeur une valeur de son type.

# Roi Pique ;;
- : carte = Roi Pique
# Petite_carte(Coeur, 10) ;;
- : carte = Petite_carte (Coeur, 10)
# Atout 21 ;;
- : carte = Atout 21


Et voici, par exemple, la fonction toutes_les_cartes qui construit la liste de toutes les cartes d'une couleur passe en argument.

# let rec interval a b = if a = b then [b] else a::(interval (a+1) b) ;;
val interval : int -> int -> int list = <fun>
# let toutes_les_cartes s =
let les_figures = [ Valet s; Cavalier s; Dame s; Roi s ]
and les_autres = List.map (function n -> Petite_carte(s,n)) (interval 1 10)
in les_figures @ les_autres ;;
val toutes_les_cartes : couleur -> carte list = <fun>
# toutes_les_cartes Coeur ;;
- : carte list =
[Valet Coeur; Cavalier Coeur; Dame Coeur; Roi Coeur; Petite_carte (Coeur, 1);
Petite_carte (Coeur, 2); Petite_carte (Coeur, 3); Petite_carte (Coeur, ...);
...]


Pour manipuler les valeurs des types somme, on utilise le filtrage de motif. L'exemple suivant construit les fonctions de conversion de valeurs de type couleur et de type carte en chanes de caractres (type string) :

# let string_of_couleur = function
Pique -> "pique"
| Carreau -> "carreau"
| Coeur -> "coeur"
| Trefle -> "trfle" ;;
val string_of_couleur : couleur -> string = <fun>
# let string_of_carte = function
Roi c -> "roi de " ^ (string_of_couleur c)
| Dame c -> "dame de " ^ (string_of_couleur c)
| Valet c -> "valet de " ^ (string_of_couleur c)
| Cavalier c -> "cavalier de " ^ (string_of_couleur c)
| Petite_carte (c, n) -> (string_of_int n) ^ " de "^(string_of_couleur c)
| Atout n -> (string_of_int n) ^ " d'atout"
| Excuse -> "excuse" ;;
val string_of_carte : carte -> string = <fun>
L'alignement des motifs permet une lecture aise de ces fonctions.

Le constructeur Petite_carte est considr comme un constructeur deux arguments. Le filtrage d'une telle valeur ncessite de nommer ses deux composants.

# let est_petite_carte c = match c with
Petite_carte v -> true
| _ -> false;;
Characters 45-59:
The constructor Petite_carte expects 2 argument(s),
but is here applied to 1 argument(s)


Pour viter de devoir nommer chaque composant d'un constructeur, on le dclare en ayant un seul argument en parenthsant le type n-uplet associ. Le filtrage des deux constructeurs suivants diffre.

# type t =
C of int * bool
| D of (int * bool) ;;
# let acces v = match v with
C (i, b) -> i,b
| D x -> x;;
val acces : t -> int * bool = <fun>


Types rcursifs

La dfinition de types rcursifs est indispensable tout langage algorithmique pour dcrire les structures de donnes usuelles (listes, piles, arbres, graphes, etc. ). Pour cela, la dfinition de types (type) est en Objective CAML par dfaut rcursive, la diffrence de la dclaration de valeurs (let).

Le type des listes prdfini en Objective CAML n'admet qu'un seul paramtre. On peut vouloir, dans une structure de liste, stocker des valeurs appartenant deux types diffrents, par exemple, des entiers (int) ou des caractres (char). Dans ce cas, on dfinit :

# type int_or_char_list =
Nil
| Int_cons of int * int_or_char_list
| Char_cons of char * int_or_char_list ;;

# let l1 = Char_cons ( '=', Int_cons(5, Nil) ) in
Int_cons ( 2, Char_cons ( '+', Int_cons(3, l1) ) ) ;;
- : int_or_char_list =
Int_cons (2, Char_cons ('+', Int_cons (3, Char_cons ('=', Int_cons (...)))))


Types paramtrs

Un utilisateur peut galement dclarer des types avec paramtres. Ceci permet de gnraliser l'exemple des listes contenant des valeurs de deux types diffrents.

# type ('a, 'b) list2 =
Nil
| Acons of 'a * ('a, 'b) list2
| Bcons of 'b * ('a, 'b) list2 ;;

# Acons(2, Bcons('+', Acons(3, Bcons('=', Acons(5, Nil))))) ;;
- : (int, char) list2 =
Acons (2, Bcons ('+', Acons (3, Bcons ('=', Acons (...)))))


On peut, videmment, instancier les paramtres 'a et 'b avec un mme type.

# Acons(1, Bcons(2, Acons(3, Bcons(4, Nil)))) ;;
- : (int, int) list2 = Acons (1, Bcons (2, Acons (3, Bcons (4, Nil))))


Cette utilisation du type list2 peut, comme dans l'exemple prcdent, servir marquer les entiers pairs et les entiers impairs. On extrait alors la sous-liste des entiers pairs pour construire une liste ordinaire.

# let rec extract_odd = function
Nil -> []
| Acons(_, x) -> extract_odd x
| Bcons(n, x) -> n::(extract_odd x) ;;
val extract_odd : ('a, 'b) list2 -> 'b list = <fun>
La dfinition de cette fonction ne donne aucune indication sur la nature des valeurs stockes dans la structure. C'est pourquoi son type est paramtr.

Porte des dclarations

Les noms de constructeurs obissent la mme discipline de porte que les dclarations globales : une redfinition masque l'ancienne. Nanmoins les valeurs d'un type masqu existent toujours. La boucle d'interaction ne distinguera pas ces deux types l'affichage. D'o certains messages d'erreur peu clairs.

Dans ce premier exemple, le constructeur constant Nil du type int_or_char a t masqu par la dclaration des constructeurs du type ('a, 'b) list2.

# Int_cons(0, Nil) ;;
Characters 13-16:
This expression has type ('a, 'b) list2 but is here used with type
int_or_char_list


Ce deuxime exemple provoque un message d'erreur assez droutant, du moins la premire fois qu'il apparat. Soit le petit programme suivant :

# type t1 = Vide | Plein;;
type t1 = | Vide | Plein
# let vide_t1 x = match x with Vide -> true | Plein -> false ;;
val vide_t1 : t1 -> bool = <fun>
# vide_t1 Vide;;
- : bool = true


Ensuite, nous redclarons le type t1 :

# type t1 = {u : int; v : int} ;;
type t1 = { u: int; v: int }
# let y = { u=2; v=3 } ;;
val y : t1 = {u=2; v=3}


Alors si l'on applique la fonction vide_t1 sur une valeur du nouveau type t1, on obtient le message d'erreur suivant :

# vide_t1 y;;
Characters 9-10:
This expression has type t1 but is here used with type t1
La premire occurrence de t1 reprsente le premier type dfini alors que la deuxime correspond au deuxime type.

Types fonctionnels

Le type de l'argument d'un constructeur peut tre quelconque. En particulier, il peut tout aussi bien contenir le type d'une fonction. Le type suivant construit des listes dont tous les lments sauf le dernier sont des valeurs fonctionnelles.

# type 'a listf =
Val of 'a
| Fun of ('a -> 'a) * 'a listf ;;
type 'a listf = | Val of 'a | Fun of ('a -> 'a) * 'a listf


Puisque les valeurs fonctionnelles sont des valeurs manipulables dans le langage, on peut construire des valeurs de type listf :

# let huit_div = (/) 8 ;;
val huit_div : int -> int = <fun>
# let gl = Fun (succ, (Fun (huit_div, Val 4))) ;;
val gl : int listf = Fun (<fun>, Fun (<fun>, Val 4))
et des fonctions filtrant de telles valeurs :
 
# let rec compute = function
Val v -> v
| Fun(f, x) -> f (compute x) ;;
val compute : 'a listf -> 'a = <fun>
# compute gl;;
- : int = 3


Exemple : reprsentation des arbres

Les arbres sont des structures rcurrentes en programmation. Les types rcursifs permettent de dfinir et manipuler facilement de telles structures. Nous donnons dans ce paragraphe deux exemples de structures arborescentes.

Arbres binaires
On dfinit une structure d'arbre binaire dont les noeuds sont tiquets par des valeurs d'un mme type en dclarant :

# type 'a arbre_bin =
Empty
| Node of 'a arbre_bin * 'a * 'a arbre_bin ;;


On utilise cette structure pour dfinir un petit programme de tri utilisant des arbres binaires de recherche. Un arbre binaire de recherche a cette proprit que toutes les valeurs du fils gauche sont infrieures celle de la racine, et toutes celles du fils droit lui sont suprieures. La figure 2.5 donne un exemple d'une telle structure pour des entiers. Les noeuds vides (constructeur Empty) y sont reprsents par des petits carrs ; les autres (constructeur Node), par un cercle o est inscrit la valeur stocke.




Figure 2.5 : Arbre binaire de recherche


On en extrait d'un arbre binaire de recherche une liste trie par un parcours infixe effectue par la fonction suivante :

# let rec list_of_arbre = function
Empty -> []
| Node(fg, r, fd) -> (list_of_arbre fg) @ (r :: (list_of_arbre fd)) ;;
val list_of_arbre : 'a arbre_bin -> 'a list = <fun>


Pour obtenir partir d'une liste un arbre binaire de recherche, on dfinit une fonction d'ajout.

# let rec ajout x = function
Empty -> Node(Empty, x, Empty)
| Node(fg, r, fd) -> if x < r then Node(ajout x fg, r, fd)
else Node(fg, r, ajout x fd) ;;
val ajout : 'a -> 'a arbre_bin -> 'a arbre_bin = <fun>


La fonction de transformation de liste en arbre s'obtient par itration sur la liste de la fonction ajout.

# let rec arbre_of_list = function
[] -> Empty
| t::q -> ajout t (arbre_of_list q) ;;
val arbre_of_list : 'a list -> 'a arbre_bin = <fun>


La fonction de tri est alors simplement la composition des fonctions arbre_of_list et list_of_arbre.

# let tri x = list_of_arbre (arbre_of_list x) ;;
val tri : 'a list -> 'a list = <fun>
# tri [5; 8; 2; 7; 1; 0; 3; 6; 9; 4] ;;
- : int list = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9]


Arbres planaires gnraux
Dans cette partie, nous utilisons les fonctions prdfinies du module List (voir page ??) suivantes :
  • List.map : qui applique une fonction tous les lments d'une liste et retourne la liste des rsultats;
  • List.fold_left : qui est une version quivalente de la fonction fold_left dfinie page ??;
  • List.exists qui applique une fonction valeur boolenne tous les lments d'une liste, si une de ces applications vaut true alors le rsultat est true, sinon la fonction retourne false.
Un arbre planaire gnral est un arbre dont on ne fixe pas a priori le nombre de fils : chaque noeud on associe une liste de fils dont la longueur peut varier.

# type 'a arbre = Empty
| Node of 'a * 'a arbre list ;;
L'arbre vide est reprsent par la valeur Empty, une feuille est un noeud sans fils soit de la forme Node(x,[]), soit de la forme dgnre Node(x, [Empty;Empty; ..]). Il est alors relativement ais d'crire des fonctions de manipulation de tels arbres comme l'appartenance d'un lment un arbre ou le calcul de la hauteur de l'arbre.

Pour tester l'appartenance d'un lment e, on utilise l'algorithme suivant : si l'arbre est vide alors e n'appartient pas cet arbre, sinon e appartient l'arbre si et seulement s'il est gal l'tiquette de la racine, ou s'il appartient un des fils.

# let rec appartient e = function
Empty -> false
| Node(v, fs) -> (e=v) or (List.exists (appartient e) fs) ;;
val appartient : 'a -> 'a arbre -> bool = <fun>


Pour calculer la hauteur d'un arbre, on utilise la dfinition suivante : un arbre vide est de hauteur 0 et sinon la hauteur de l'arbre est gale la hauteur du plus haut sous arbre augmente de 1.

# let rec hauteur =
let max_list l = List.fold_left max 0 l in
function
Empty -> 0
| Node (_, fs) -> 1 + (max_list (List.map hauteur fs)) ;;
val hauteur : 'a arbre -> int = <fun>


Valeurs rcursives non fonctionnelles

La dclaration rcursive de valeurs non fonctionnelles permet de construire des structures de donnes circulaires.

La dclaration suivante construit une liste circulaire un lment.

# let rec l = 1::l ;;
val l : int list =
[1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; ...]


L'application d'une fonction rcursive sur une telle liste risque de boucler jusqu' saturation de la mmoire.

# size l ;;
Stack overflow during evaluation (looping recursion?).


L'galit structurelle reste utilisable avec de telles listes uniquement lorsque l'galit physique est d'abord vrifie :

# l=l ;;
- : bool = true


En revanche, si l'on dfinit une nouvelle liste, pourtant gale, il ne faut pas utiliser le test d'galit structurelle sous peine de voir votre programme boucler indfiniment. Nous ne recommandons donc pas de tenter d'valuer l'exemple suivant :
let rec l2 = 1::l2 in l=l2 ;;
Par contre, l'galit physique reste toujours possible.

# let rec l2 = 1::l2 in l==l2 ;;
- : bool = false


Le prdicat == teste l'galit d'une valeur immdiate ou le partage d'un objet structur (galit sur l'adresse de la valeur). Nous allons nous en servir pour vrifier qu'en explorant une liste nous ne repassons pas par une sous-liste dj examine. En premier lieu, nous dfinissons la fonction memq qui vrifie la prsence d'un lment dans une liste en s'appuyant sur l'galit physique. C'est le pendant de la fonction mem qui teste elle l'galit structurelle; ces deux fonctions appartiennent au module List.

# let rec memq a l = match l with
[] -> false
| b::l -> (a==b) or (memq a l) ;;
val memq : 'a -> 'a list -> bool = <fun>


La fonction de calcul de la taille est redfinie en conservant la liste des listes dj examines et en s'arrtant si une liste est rencontre une seconde fois.

# let special_size l =
let rec size_aux previous l = match l with
[] -> 0
| _::l1 -> if memq l previous then 0
else 1 + (size_aux (l::previous) l1)
in size_aux [] l ;;
val special_size : 'a list -> int = <fun>
# special_size [1;2;3;4] ;;
- : int = 4
# special_size l ;;
- : int = 1
# let rec l1 = 1::2::l2 and l2 = 1::2::l1 in special_size l1 ;;
- : int = 4



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora028.html0000644000000000000000000010047307421273601014476 0ustar Structures de contrle Prcdent Index Suivant

Structures de contrle

Les entres-sorties et les valeurs modifiables produisent des effets de bord. Leur utilisation est facilite par un style de programmation impratif muni de nouvelles structures de contrles. Nous prsentons dans ce paragraphe la structure squentielle et les structures itratives.

Nous avons dj rencontr la structure de contrle conditionnelle la page ?? dont la forme abrge if then prend son sens dans le monde impratif. On crira, par exemple :

# let n = ref 1 ;;
val n : int ref = {contents=1}
# if !n > 0 then n := n-1 ;;
Characters 20-21:
This expression has type int ref but is here used with type int


Squence

La premire de des structures typiquement imprative est la squence. Elle permet l'valuation ordonne de gauche droite d'une suite d'expressions spares par un point-virgule.

Syntaxe


expr1 ; ...; exprn
Une squence est une expression dont la valeur est celle de sa dernire expression (ici, exprn). Nanmoins toutes les expressions sont values, et en particulier leurs effets de bord sont pris en compte.

# print_string "2 = "; 1+1 ;;
2 = - : int = 2


Avec effet de bord, on retrouve la construction usuelle des langages impratifs.

# let x = ref 1 ;;
val x : int ref = {contents=1}
# x:=!x+1 ; x:=!x*4 ; !x ;;
- : int = 8


Comme les valeurs prcdant un point-virgule sont oublies, Objective CAML met un avertissement quand elles ne sont pas de type unit.

# print_int 1; 2 ; 3 ;;
Characters 14-15:
Warning: this expression should have type unit.
1- : int = 3


Pour viter ce message, on peut utiliser la fonction ignore :

# print_int 1; ignore 2; 3 ;;
1- : int = 3


Un message diffrent est obtenu si la valeur possde un type fonctionnel car Objective CAML souponne que vous avez oubli un paramtre d'une fonction.

# let g x y = x := y ;;
val g : 'a ref -> 'a -> unit = <fun>
# let a = ref 10;;
val a : int ref = {contents=10}
# let u = 1 in g a ; g a u ;;
Characters 13-16:
Warning: this function application is partial,
maybe some arguments are missing.
- : unit = ()
# let u = !a in ignore (g a) ; g a u ;;
- : unit = ()


En rgle gnrale on parenthse les squences pour en clarifier la porte. Syntaxiquement, le parenthsage peut prendre deux formes :

Syntaxe


( expr )

Syntaxe


begin expr end
On peut maintenant crire de faon plus naturelle le programme C+/C- de la page ?? :

# let rec cpcm n =
print_string "taper un nombre : ";
let i = read_int () in
if i = n then print_string "BRAVO\n\n"
else
begin
if i < n then print_string "C+\n" else print_string "C-\n" ;
cpcm n
end ;;
val cpcm : int -> unit = <fun>


Boucles

Les structures de contrle itratives sont elles aussi en dehors du monde fonctionnel. La condition de rptition, ou de sortie, d'une boucle n'a de sens que si une modification physique de la mmoire permet d'en changer la valeur. Il existe deux structures de contrle itratives : la boucle for pour une itration borne et la boucle while pour une itration non borne. Les structures de boucle elles-mmes sont des expressions du langage. Elles retournent donc une valeur : la constante () du type unit.

La boucle for peut tre croissante (to) ou dcroissante (downto) d'un pas de un.

Syntaxe


for nom = expr1 to expr2 do expr3 done
for nom = expr1 downto expr2 do expr3 done

Les expressions expr1 et expr2 sont de type int. Si expr3 n'est pas de type unit, le compilateur engendre un message d'avertissement.

# for i=1 to 10 do print_int i; print_string " " done; print_newline() ;;
1 2 3 4 5 6 7 8 9 10
- : unit = ()
# for i=10 downto 1 do print_int i; print_string " " done; print_newline() ;;
10 9 8 7 6 5 4 3 2 1
- : unit = ()


La boucle non borne est la boucle << tant que >> dont la syntaxe est :

Syntaxe


while expr1 do expr2 done
L'expression expr1 doit tre de type bool. Et, comme pour la boucle for, si expr2 n'est pas de type unit, le compilateur engendre un message d'avertissement.

# let r = ref 1
in while !r < 11 do
print_int !r ;
print_string " " ;
r := !r+1
done ;;
1 2 3 4 5 6 7 8 9 10 - : unit = ()


Il est important de comprendre que les boucles sont des expressions comme les autres qui calculent la valeur () du type unit.

# let f () = print_string "-- fin\n" ;;
val f : unit -> unit = <fun>
# f (for i=1 to 10 do print_int i; print_string " " done) ;;
1 2 3 4 5 6 7 8 9 10 -- fin
- : unit = ()
Notez que la chane "-- fin\n" est affiche aprs qu'aient t affichs les entiers de 1 10 : on a l la manifestation que les arguments (ici la boucle) sont valus avant d'tre passs la fonction.

En programmation imprative, le corps d'une boucle (l'expression expr) ne calcule pas de valeur, mais procde par effets de bords. En Objective CAML, lorsque le corps d'une boucle n'est pas de type unit, le compilateur affiche un avertissement, comme pour la squence :

# let s = [5; 4; 3; 2; 1; 0] ;;
val s : int list = [5; 4; 3; 2; 1; 0]
# for i=0 to 5 do List.tl s done ;;
Characters 17-26:
Warning: this expression should have type unit.
- : unit = ()


Exemple : implantation de piles

La structure de donnes 'a pile sera implante sous forme d'un enregistrement contenant un tableau d'lments et la premire position libre dans ce tableau. Voici le type correspondant :

# type 'a pile = { mutable ind:int; taille:int; mutable elts : 'a array } ;;
Le champ taille contient la taille maximale de la pile.

Les oprations sur ces piles seront init_pile pour l'initialisation d'une pile, empile pour empiler un lment dans une pile et depile pour retourner le sommet de pile et le dpiler.

# let init_pile n = {ind=0; taille=n; elts =[||]} ;;
val init_pile : int -> 'a pile = <fun>
Cette fonction ne peut pas crer un tableau non vide car il faudrait lui fournir la valeur avec lequel le construire. C'est pour cela que le champ elts reoit un tableau vide.

Deux exceptions sont dclares pour grer les tentatives de dpilement d'une pile vide et d'ajout d'un lment sur une pile pleine. Elles sont utilises dans les fonctions depile et empile.

# exception Pile_vide ;;
# exception Pile_pleine ;;

# let depile p =
if p.ind = 0 then raise Pile_vide
else (p.ind <- p.ind - 1; p.elts.(p.ind)) ;;
val depile : 'a pile -> 'a = <fun>
# let empile e p =
if p.elts = [||] then
(p.elts <- Array.create p.taille e;
p.ind <- 1)
else if p.ind >= p.taille then raise Pile_pleine
else (p.elts.(p.ind) <- e; p.ind <- p.ind + 1) ;;
val empile : 'a -> 'a pile -> unit = <fun>


Voici un petit exemple d'utilisation de cette structure :

# let p = init_pile 4 ;;
val p : '_a pile = {ind=0; taille=4; elts=[||]}
# empile 1 p ;;
- : unit = ()
# for i = 2 to 5 do empile i p done ;;
Uncaught exception: Pile_pleine
# p ;;
- : int pile = {ind=4; taille=4; elts=[|1; 2; 3; 4|]}
# depile p ;;
- : int = 4
# depile p ;;
- : int = 3


Si l'on dsire viter que l'ajout d'un lment dans une pile provoque le dclenchement de l'exception Pile_pleine, on peut agrandir le tableau. Pour cela le champ taille doit tre galement modifiable :

# type 'a pile =
{mutable ind:int ; mutable taille:int ; mutable elts : 'a array} ;;
# let init_pile n = {ind=0; taille=max n 1; elts = [||]} ;;
# let n_empile e p =
if p.elts = [||]
then
begin
p.elts <- Array.create p.taille e;
p.ind <- 1
end
else if p.ind >= p.taille then
begin
let nt = 2 * p.taille in
let nv = Array.create nt e in
for j=0 to p.taille-1 do nv.(j) <- p.elts.(j) done ;
p.elts <- nv;
p.taille <- nt;
p.ind <- p.ind + 1
end
else
begin
p.elts.(p.ind) <- e ;
p.ind <- p.ind + 1
end ;;
val n_empile : 'a -> 'a pile -> unit = <fun>


Il faut nanmoins faire attention aux structures de donnes pouvant s'agrandir outrance. Voici un petit exemple o la pile initiale grandit selon les besoins.

# let p = init_pile 4 ;;
val p : '_a pile = {ind=0; taille=4; elts=[||]}
# for i = 1 to 5 do n_empile i p done ;;
- : unit = ()
# p ;;
- : int pile = {ind=5; taille=8; elts=[|1; 2; 3; 4; 5; 5; 5; 5|]}
# p.taille ;;
- : int = 8


Il est d'usage d'ajouter la fonction depile la possibilit de diminuer la taille de la pile pour viter d'utiliser trop d'espace mmoire.

Exemple : calcul sur les matrices

Dans cet exemple on cherche dfinir un type pour les matrices, tableaux deux dimensions contenant des nombres flottants, et crire quelques oprations sur les matrices. Le type mat monomorphe est un enregistrement contenant les dimensions et les lments de la matrice. Les fonctions creer_mat, acces_mat et mod_mat sont respectivement les fonctions de cration, d'accs un lment et de modification d'un lment.

# type mat = { n:int; m:int; t: float array array };;
type mat = { n: int; m: int; t: float array array }
# let creer_mat n m = { n=n; m=m; t = Array.create_matrix n m 0.0 } ;;
val creer_mat : int -> int -> mat = <fun>
# let acces_mat m i j = m.t.(i).(j) ;;
val acces_mat : mat -> int -> int -> float = <fun>
# let mod_mat m i j e = m.t.(i).(j) <- e ;;
val mod_mat : mat -> int -> int -> float -> unit = <fun>
# let a = creer_mat 3 3 ;;
val a : mat = {n=3; m=3; t=[|[|0; 0; 0|]; [|0; 0; 0|]; [|0; 0; 0|]|]}
# mod_mat a 1 1 2.0; mod_mat a 1 2 1.0; mod_mat a 2 1 1.0 ;;
- : unit = ()
# a ;;
- : mat = {n=3; m=3; t=[|[|0; 0; 0|]; [|0; 2; 1|]; [|0; 1; 0|]|]}


La somme de deux matrices a et b est une matrice c  telle que   cij = aij + bij.

# let add_mat p q =
if p.n = q.n && p.m = q.m then
let r = creer_mat p.n p.m in
for i = 0 to p.n-1 do
for j = 0 to p.m-1 do
mod_mat r i j (p.t.(i).(j) +. q.t.(i).(j))
done
done ;
r
else failwith "add_mat : dimensions incompatibles";;
val add_mat : mat -> mat -> mat = <fun>
# add_mat a a ;;
- : mat = {n=3; m=3; t=[|[|0; 0; 0|]; [|0; 4; 2|]; [|0; 2; 0|]|]}


Le produit de deux matrices a et b est une matrice c  telle que cij = k=1k=ma aik. bkj

# let mul_mat p q =
if p.m = q.n then
let r = creer_mat p.n q.m in
for i = 0 to p.n-1 do
for j = 0 to q.m-1 do
let c = ref 0.0 in
for k = 0 to p.m-1 do
c := !c +. (p.t.(i).(k) *. q.t.(k).(j))
done;
mod_mat r i j !c
done
done;
r
else failwith "mul_mat : dimensions incompatibles" ;;
val mul_mat : mat -> mat -> mat = <fun>
# mul_mat a a;;
- : mat = {n=3; m=3; t=[|[|0; 0; 0|]; [|0; 5; 2|]; [|0; 2; 1|]|]}



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora038.html0000644000000000000000000007151307421273601014501 0ustar Quel style choisir ? Prcdent Index Suivant

Quel style choisir ?

Il n'est pas ici question de religion ni d'esthtique, il n'y a pas a priori un style plus joli ou meilleur que l'autre. En revanche, selon le problme rsoudre un style peut tre plus adquat que l'autre.

La premire rgle appliquer est celle de la simplicit. L'algorithme raliser (qu'il soit crit dans un livre ou qu'il soit l'tat de germe dans l'esprit du programmeur) est lui-mme dcrit dans un certain style; il est naturel d'utiliser le mme style pour en faire l'implantation.

Le second critre de choix est l'efficacit du programme. On peut dire qu'un programme impratif (bien crit) est plus efficace que son homologue fonctionnel, mais dans de trs nombreux cas la diffrence n'est pas suffisamment notable pour justifier de compliquer le code en adoptant un style impratif l o le fonctionnel serait naturel. La fonction map de la section prcdente fournit un bel exemple de problme naturellement exprim dans le style fonctionnel et qui ne gagne rien tre crit en impratif.

Squence ou composition de fonctions

Nous avons vu que ds qu'un programme effectue des effets de bord, il est ncessaire d'expliciter prcisment l'ordre d'valuation des lments de ce programme. Ceci peut se faire dans les deux styles :
fonctionnel :
en utilisant le fait que Objective CAML est un langage strict, c'est--dire que l'argument est calcul avant l'application de la fonction. L'expression (f (g x)) est calcule en valuant d'abord (g x), puis en passant le rsultat comme argument de f. Avec des expressions plus complexes, on peut nommer le rsultat intermdiaire avec la construction let in mais le principe reste le mme : let aux=(g x) in (f aux).
impratif :
en utilisant la squence ou les autres structures de contrle (boucles). Dans ce cas, le rsultat n'est pas une valeur rendue par une fonction, mais ses effets de bord sur la mmoire : aux:=(g x) ; (f !aux).
Examinons ce problme de choix de style sur un exemple. L'algorithme de tri rapide sur un vecteur se dcrit rcursivement comme ceci :
  1. choisir un pivot : choisir l'indice d'un lment du tableau;
  2. permuter autour du pivot : permuter les lments du tableau de sorte que les lments infrieurs au pivot soient situs avant le pivot et vice et versa;
  3. trier (suivant le mme algorithme) les deux tableaux obtenus : les lments prcdant le pivot et ceux le suivant.
Le choix de l'algorithme, savoir de modifier un tableau pour que ses lments soient tris, nous incite utiliser un style impratif au moins pour les manipulations des donnes.

On commence par dfinir une fonction permutant deux lments d'un tableau.

# let permute_element tab n p =
let aux = tab.(n) in tab.(n) <- tab.(p) ; tab.(p) <- aux ;;
val permute_element : 'a array -> int -> int -> unit = <fun>


Le choix d'un bon pivot est dterminant pour l'efficacit de l'algorithme, mais nous retenons ici le choix le plus simple : rendre l'indice du premier lment du (sous) tableau.

# let choisir_pivot tab debut fin = debut ;;
val choisir_pivot : 'a -> 'b -> 'c -> 'b = <fun>


crivons l'algorithme que nous souhaitons utiliser pour permuter les lments du tableau, autour du pivot.
  1. on positionne le pivot au dbut du tableau en le permutant;
  2. i est l'indice de second lment du tableau;
  3. j est l'indice du dernier lment du tableau;
  4. si l'lment d'indice j est plus grand que le pivot on le permute avec l'lment d'indice i et on incrmente i; sinon on dcrmente j;
  5. tant que i est infrieur j on recommence l'opration prcdente;
  6. rendu ce point chaque lment d'indice infrieur i (ou j) est infrieur au pivot, les autres lui sont suprieurs; si l'lment d'indice i est infrieur au pivot on le permute avec le pivot, sinon on permute son prdcesseur.
L'implantation de cet algorithme va naturellement adopter des structures de contrle impratives.

# let permute_pivot tab debut fin ind_pivot =
permute_element tab debut ind_pivot ;
let i = ref (debut+1) and j = ref fin and pivot = tab.(debut) in
while !i < !j do
if tab.(!j) >= pivot then decr j
else
begin
permute_element tab !i !j ;
incr i
end
done ;
if tab.(!i) > pivot then decr i ;
permute_element tab debut !i ;
!i
;;
val permute_pivot : 'a array -> int -> int -> int -> int = <fun>
Outre son effet sur le tableau, cette fonction retourne en rsultat l'indice du pivot.

Il ne nous reste plus qu' composer les diffrentes tapes et mettre en place la rcursion sur les sous-tableaux.

# let rec quick tab debut fin =
if debut < fin
then
let pivot = choisir_pivot tab debut fin in
let place_pivot = permute_pivot tab debut fin pivot in
quick (quick tab debut (place_pivot-1)) (place_pivot+1) fin
else tab ;;
val quick : 'a array -> int -> int -> 'a array = <fun>


Nous avons ici utilis les deux styles. Le pivot choisi sert d'argument la permutation autour de ce pivot, et le rang du pivot obtenu aprs permutation est un argument de l'appel rcursif. Par contre, le tableau obtenu aprs permutation n'est pas retourn par la fonction permute_pivot, ce rsultat est rendu par effet de bord. En revanche, la fonction quick retourne un tableau et le tri des sous-tableaux est obtenu par composition des appels rcursifs.

La fonction principale est :

# let quicksort tab = quick tab 0 ((Array.length tab)-1) ;;
val quicksort : 'a array -> 'a array = <fun>
C'est une fonction polymorphe car la relation d'ordre < sur les lments du tableau est elle mme polymorphe.

# let t1 = [|4;8;1;12;7;3;1;9|] ;;
val t1 : int array = [|4; 8; 1; 12; 7; 3; 1; 9|]
# quicksort t1 ;;
- : int array = [|1; 1; 3; 4; 7; 8; 9; 12|]
# t1 ;;
- : int array = [|1; 1; 3; 4; 7; 8; 9; 12|]
# let t2 = [|"le"; "petit"; "chat"; "est"; "mort"|] ;;
val t2 : string array = [|"le"; "petit"; "chat"; "est"; "mort"|]
# quicksort t2 ;;
- : string array = [|"chat"; "est"; "le"; "mort"; "petit"|]
# t2 ;;
- : string array = [|"chat"; "est"; "le"; "mort"; "petit"|]


Partage ou copie de valeurs

Tant que les valeurs que nous manipulons ne sont pas modifiables, il n'est pas utile de savoir si elles sont partages ou non.

# let id x = x ;;
val id : 'a -> 'a = <fun>
# let a = [ 1; 2; 3 ] ;;
val a : int list = [1; 2; 3]
# let b = id a ;;
val b : int list = [1; 2; 3]
Que b soit une copie de la liste a ou que se soit la mme liste ne change rien puisque ce sont de toutes faons des valeurs intangibles. Mais si la place d'entiers nous mettons des valeurs modifiables, nous avons besoin de savoir si la modification d'une valeur entrane la modification de l'autre.

L'implantation du polymorphisme d'Objective CAML effectue une copie des valeurs immdiates et le partage des valeurs structures. Bien que le passage des arguments soit toujours par valeur, seul le pointeur d'une valeur structure est copi. C'est bien le cas de la fonction id :

# let a = [| 1 ; 2 ; 3 |] ;;
val a : int array = [|1; 2; 3|]
# let b = id a ;;
val b : int array = [|1; 2; 3|]
# a.(1) <- 4 ;;
- : unit = ()
# a ;;
- : int array = [|1; 4; 3|]
# b ;;
- : int array = [|1; 4; 3|]


Nous avons ici un vrai choix de programmation pour dcider quelle est la manire la plus efficace de reprsenter une donne. D'un ct, l'utilisation de valeurs modifiables permet de faire des manipulations en place (c'est--dire sans faire d'allocation) mais oblige, dans certains cas, faire des copies l o l'utilisation d'une valeur non modifiable aurait permis de faire un partage. Nous illustrons ceci avec deux faons d'implanter les listes.

# type 'a liste_fixe = LFnil | LFcons of 'a * 'a liste_fixe ;;
# type 'a liste_modifiable = LMnil | LMcons of 'a * 'a liste_modifiable ref ;;


Les listes fixes sont strictement quivalentes aux listes d'Objective CAML alors que les listes modifiables sont plus dans un style la C o un maillon est une valeur plus une rfrence sur le maillon suivant.

Avec les listes fixes, il n'y a qu'une seule faon d'crire la concatnation et elle impose de dupliquer la structure de la premire liste; en revanche, la seconde liste peut tre partage avec le rsultat.

# let rec concat l1 l2 = match l1 with
LFnil -> l2
| LFcons (a,l11) -> LFcons(a, (concat l11 l2)) ;;
val concat : 'a liste_fixe -> 'a liste_fixe -> 'a liste_fixe = <fun>

# let lf1 = LFcons(1, LFcons(2, LFnil))
and lf2 = LFcons(3, LFcons(4, LFnil)) ;;
val lf1 : int liste_fixe = LFcons (1, LFcons (2, LFnil))
val lf2 : int liste_fixe = LFcons (3, LFcons (4, LFnil))
# let lf3 = concat lf1 lf2 ;;
val lf3 : int liste_fixe =
LFcons (1, LFcons (2, LFcons (3, LFcons (4, LFnil))))
# lf1==lf3 ;;
- : bool = false
# let tlLF l = match l with
LFnil -> failwith "Liste vide"
| LFcons(_,x) -> x ;;
val tlLF : 'a liste_fixe -> 'a liste_fixe = <fun>
# tlLF(tlLF(lf3)) == lf2 ;;
- : bool = true
Grce ces exemples, nous voyons que les premiers maillons de lf1 et de lf3 sont distincts alors que la seconde partie de lf3 est exactement lf2.

Avec les listes modifiables, nous avons le choix entre modifier les arguments (fonction concat_share) ou crer une nouvelle valeur (fonction concat_copy)

# let rec concat_copy l1 l2 = match l1 with
LMnil -> l2
| LMcons (x,l11) -> LMcons(x, ref (concat_copy !l11 l2)) ;;
val concat_copy :
'a liste_modifiable -> 'a liste_modifiable -> 'a liste_modifiable = <fun>
Cette premire solution, concat_copy, donne un rsultat similaire la fonction prcdente concat. Voici une seconde solution qui partage entirement arguments et rsultat.

# let concat_share l1 l2 =
match l1 with
LMnil -> l2
| _ -> let rec set_last = function
LMnil -> failwith "concat_share : cas impossible !!"
| LMcons(_,l) -> if !l=LMnil then l:=l2 else set_last !l
in
set_last l1 ;
l1 ;;
val concat_share :
'a liste_modifiable -> 'a liste_modifiable -> 'a liste_modifiable = <fun>
La concatnation avec partage ne demande aucune allocation (on n'utilise pas le constructeur LMcons), on se contente de faire pointer le dernier maillon de la premire liste vers la seconde. Mais alors, la concatnation est susceptible de modifier les arguments qui sont passs la fonction.

# let lm1 = LMcons(1, ref (LMcons(2, ref LMnil)))
and lm2 = LMcons(3, ref (LMcons(4, ref LMnil))) ;;
val lm1 : int liste_modifiable =
LMcons (1, {contents=LMcons (2, {contents=LMnil})})
val lm2 : int liste_modifiable =
LMcons (3, {contents=LMcons (4, {contents=LMnil})})
# let lm3 = concat_share lm1 lm2 ;;
val lm3 : int liste_modifiable =
LMcons (1, {contents=LMcons (2, {contents=LMcons (...)})})
On obtient bien le rsultat escompt pour lm3. Par contre la valeur lie lm1 t modifie.

# lm1 ;;
- : int liste_modifiable =
LMcons (1, {contents=LMcons (2, {contents=LMcons (...)})})
Cela peut donc avoir des consquences dans le reste du programme.

lments de choix

Dans un programme purement fonctionnel, il est interdit d'effectuer des effets de bord, cela prohibe la manipulation de structures de donnes mutables, les exceptions et les entres-sorties. Nous donnons une dfinition moins restrictive du style fonctionnel en disant qu'une fonction ne modifiant pas son environnement global peut tre utilise dans un style fonctionnel. Une telle fonction peut manipuler localement des valeurs mutables (et donc tre crite dans un style impratif) mais ne doit pas modifier de variables globales ni ses arguments. Nous les autorisons de surcrot lever des exceptions. Vu de l'extrieur, ces fonctions peuvent tre considres comme des << boites noires >> dont le comportement est assimilable celui d'une fonction purement fonctionnelle si ce n'est qu'elles sont susceptibles de rompre le contrle par le dclenchement d'une exception. Dans le mme esprit, une valeur mutable qui n'est plus modifie aprs son initialisation peut tre utilise dans un style fonctionnel.

D'un autre cot, un programme crit dans un style impratif bnficie tout de mme des avantages apports par Objective CAML : la sret du typage statique, la gestion automatique de la mmoire, le mcanisme d'exception, le polymorphisme paramtrique et l'infrence de types.

Le choix entre style impratif et style fonctionnel dpend de l'application dvelopper. On peut nanmoins indiquer certaines orientations selon les caractristiques de celle-ci et les critres jugs important dans son dveloppement.
  • choix des structures de donnes : de l'utilisation de structures de donnes mutables ou non va dcouler le choix du style de programmation adopter. En effet, le style fonctionnel est par nature incompatible avec la modification des valeurs mutables. Par contre, la construction et le parcours d'une valeur sont les mmes quelque soit son statut. Nous rejoignons la discussion sur << modification en place vs copie >> tenue la page ??. Nous y reviendrons en voquant les critres d'efficacit.
  • structures de donnes imposes : si le programme doit modifier des donnes mutables, le style impratif est le seul possible. Si par contre, il est juste ncessaire de parcourir les valeurs, alors l'adoption du style fonctionnel garantit l'intgrit des donnes.
    L'utilisation de structures de donnes rcursives entranent l'usage de fonctions elles-mmes rcursives. Celles-ci peuvent tre dfinies en utilisant les traits de l'un ou de l'autre des styles, mais il est souvent plus facile de rsonner par cration d'une valeur suivant une dfinition par rcurrence ce qui correspond une approche fonctionnelle, que d'itrer des traitements par rcursion sur cette valeur. Le style fonctionnel permet de dfinir des itrateurs gnriques sur la structure de donnes manipule, ce qui factorise le dveloppement et le rend plus rapide.

  • critres d'efficacit : la modification en place est incomparablement plus efficace que la cration d'une valeur. Quand l'efficacit du code est un critre prpondrant, il fera le plus souvent pench la balance vers le style impratif. Notons cependant que la ncessit de dpartager les valeurs peut s'avrer une tche trs ardue et en dfinitive plus coteuse que de copier les valeurs ds le dpart.
    La pleine fonctionnalit a un cot : l'application partielle et l'utilisation de fonctions passes en argument d'autres fonctions ont un cot l'excution plus important que celui de l'application totale d'une fonction provenant d'une dclaration. L'usage de ce trait minemment fonctionnel doit donc tre proscrit des parties d'un programme o l'efficacit est critique.

  • critres de dveloppement : le plus haut niveau d'abstraction de la programmation fonctionnelle permet d'crire plus rapidement, un code plus compact et contenant moins d'erreurs que son quivalent impratif qui par nature est plus verbeux. Le style fonctionnel apparat comme avantag pour les contraintes que posent le dveloppement d'applications consquentes. L'indpendance d'une fonction par rapport son contexte d'valuation rend le code fonctionnel plus facilement dcoupable en petites units examinables sparment et en fin de compte plus lisible.
    La plus grande modularit du style fonctionnel avec la possibilit de passer des fonctions (donc des traitements) en argument d'autres fonctions fait que les programmes crits dans ce style sont plus aisment rutilisables.
Ces quelques remarques montrent qu'il est souvent judicieux de mlanger les deux styles de programmation dans une application. Le style fonctionnel est plus rapide dvelopper et confre une organisation plus simple de l'application; mais les sections critiques en temps d'excution auront intrt tre dvelopp dans un style impratif plus efficace.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora151.html0000644000000000000000000000225707421273603014476 0ustar Notes
1
Il existe plusieurs notations pour dcrire les relations, en particulier UML (Unified Modeling Language).
2
La notation pointe qui est d'usage en programmation par objet pour l'envoi de message tant dj utilise pour les enregistrements et les modules, il devenait difficile de l'utiliser de nouveau.
3
Le private Objective CAML correspond au protected d'Objective C et de Java
ocaml-book-1.0/fr/html/book-ora150.html0000644000000000000000000000722007421273602014467 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

La littrature sur la programmation par objets et les langages par objets est considrable, chaque langage objet implantant un modle diffrent.

Un ouvrage gnral et toujours d'actualit pour sa premire partie, est << Langages Objets >> ([MNC+89]) qui explique la dmarche objet. Un livre plus pointu, << Langages et modles objets >> [DEMN98], fait un tat de l'art du domaine.

Sur la modlisation, l'ouvrage <<Design patterns>> ([GHJV98]) propose un catalogue de modles de conception donnant une bonne description des possibilits de rutilisabilit.

Pour la notation UML, le site de rfrence est celui de Rational :

Lien


http://www.rational.com/uml/resources


Pour les langages fonctionnels avec une extension objet, on peut citer les <<Lisp>> objets, provenant du monde SMALLTALK, et CLOS (pour Common Lisp Object System) ainsi que de nombreux Scheme implantant les fonctions gnriques la CLOS.

D'autres propositions de langages par objets ont t faites pour les langages fonctionnels typs dynamiquement, comme par exemple Haskell, langage fonctionnel pur, qui autorise le polymorphisme paramtrique et ad hoc pour la surcharge.

L'article [RV98] prsente l'extension objet d'Objective CAML d'un point de vue thorique.

Plusieurs cours en ligne peuvent tre consults pour approfondir le typage statique objet d'Objective CAML.

Le cours de Mara-Virginia Aponte.

Lien


http://tulipe.cnam.fr/personne/aponte/ocaml.html


Une courte prsentation des objets par Didier Rmy.

Lien


http://pauillac.inria.fr/~remy/objectdemo.html


Le cours de Didier Rmy au Magistre MMFAI

Lien


http://pauillac.inria.fr/~remy/classes/magistere/


Le cours de Roberto Di Cosmo au Magistre MMFAI.

Lien


http://www.dmi.ens.fr/users/dicosmo/CourseNotes/OO/









Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora081.gif0000644000000000000000000000476407073350152014304 0ustar GIF89a'UUU999!,'H0I8ͻ H&PJ-óZ(~6mH_8cO[ʘ4IlZWzxLvlzn|N~Ar~Zd| 1Z$ -Ӧ(-I ϡAص- (*% ![' >{~ànaڛ.32kmXѠNe(!1..!m&m" N^#{|ْѣ \"]R ӧP)JԪXE]U֮`M+ٳ L۷pʝKnlSZg~r;i\ mRrmTŘ/ Fi|b͕׊EQ'Dڔ蒌N[xm NFQ:;,$I6Wx6-(笗W}ۛNЇ{/ci=jWw~w)T2N1 NNL00SNT x70F2~w*h;;Ԯ5;7mǞCӗ~43|s/=gs,l+l>觯R_ɻ(nqF,X2Qe#(ꑍ Ⱦ'1,Dю_l$!G$$%LBp~L"刺P|)7U^##xX\-IW\q E Q r|-߈IeoM8 K 4MC.&8IL4r8W;23"4EΓ=]P-rȖN)r8PY`$Up Ґӣ蔢Jn)QҧĴt#J9jQβ ĩ)F BtTGP}L=_SڲcEжtEU[6ON=asMeY)BjF*T&nkiHTW+_b--RֺTcͫ3̪[:͞Me*/ v6D mr--*L_YӘ5b(?c,o3{ڮo>ݭi{{5&k[f_HC׹-eܝw°ZjQm%3HGfƁ.}'XF78ȎPʼnJH.:[`^m=K D9ys6\^80;a dP".E{^73I74% E>gKcJG5$VKM+[~.amu (IoF/+a>V]>mr桪h?+C3\o6j4Zb'3])VMx .B瘉~9 ?IҧY^F\ )lyYim&3JcQ_ Qzꡚ1*7ӭyĔ;*EbM88cZRfv$P֨K}h? [;@j46;<[xp%psᶛۺs90.lC05 lw9qQ.\CEL@tg' $RG7᥏/Ƕl#ţ8:Oev38Aھ'up4FxՉR/B{%2O`gyv޽,zЗ~'qWoz'gPLhCz@{';ocaml-book-1.0/fr/html/book-ora037.gif0000644000000000000000000000607207073350152014277 0ustar GIF89aUUU!,ڋ޼H9 G@>A hj J%" ` Ԯ #VU /k,4k`7HXhSuuv) ax2 *:JZjz +;K[k{ ,ոv .߾Pѻ*ᢆ Nt1n۰ re6<|om>:W0=:?|ޡ>7!gW;ZRlδO'Ҽk}^*{k=75:2P&>wZ-G^>Z(z -֝z}ųTf7Vٷ_T9PtDRA]v _ Jp_]( ^6" GXYaI\0[v)@ԌWMg[w*B JSĥtjf@O˚IҼZ7-%w #+LoKsSmCn7 +:h-=lhh*j+Cpͯ@xnW lLjTX4\ٜ eB,,`]\1Ȍs=<Jx ׳xRȗx\|X\ d[~7OxK7kR-?LuڃW͎kۼ9k_hMmJ <Ȱ- IdaZGȽ zF4< b) {2T imV::%L [/=Oiּ-!՘F, ~L܋1Y-`0.хbT1q[D2Ġ*[#ՈvDJ`ᬥ# }9,l7qPԋu( 쒣LE&{Ȁ( kFYJyt TI\]eFKTnrTGaQq#EZc3iNg#\"V@Ќ eG?뉳/R`@FM,hSC6ĐCItE+*0("ёt )LjhM+[';~d;ʴmFv9[hG2O,"nN04UL#R kUH2n !1tʼD՜ssD `dv] 8=4}fϟeAf~V684J eK9;m.+tIQS!}nY_-:ԲukmkjOm$ږ*.⭎4")^\Wn9n΂9ɏj4*5ӽG|2k>a<?6 }D/:r m.ǍК9~-u:"{iӰG}RWi$kC[FȠ1@G؆" g7՞gV`푙x܉|#>\ '#gA|^k~2R>pt?zMw[x;:Y|G2ޏ|:,1#1 nBs'o?W=/dNiݗ? ~=swoe?{Ksg~ &e xWavx\gy7Xg_){⇁/ht79|s=?A(v`C1zF_͑JWH1u7ՄQ8>UVh`]%%TWW_d((Uso( m(׵%uh^jyx{8_} gH^1KUrXb耓h{hnuhȉǐR!(nXXNXjHkps@S<h}XxJi ǔPaHO)?W;ocaml-book-1.0/fr/html/book-ora203.html0000644000000000000000000003301207421273603014465 0ustar Langages fonctionnels proches Prcdent Index Suivant

Langages fonctionnels proches

Il existe plusieurs langages proches d'Objective CAML, soit par le ct fonctionnel, soit par le typage. Objective CAML est issu de la famille ML, et possde donc des cousins dont les plus proches sont outre-atlantique et outre-manche dans la ligne de SML (Standard ML). La famille Lisp, et en particulier le langage Scheme, diffre de ML principalement par son typage dynamique. Deux langages paresseux, Miranda et Haskell, reprennent ou tendent le typage de ML dans le cadre de l'valuation retarde. Deux langages fonctionnels Erlang et SCOL dvelopps respectivement par les socits Ericsson et Cryo-Networks sont tourns vers la communication.

Famille ML

La famille ML comprend deux branches principales : Caml (Categorical Abstract Machine Language) et ses drivs Caml-Light et Objective CAML, SML (Standard ML) et ses descendants SML/NJ et mossml. Caml, l'anctre, a t dvelopp entre 1986 et 1990 par le projet FORMEL de l'INRIA en collaboration avec l'Universit Paris 7 et l'cole Normale Suprieure. Son implantation reposait sur la bibliothque d'excution de Le_Lisp. Il intgrait dans le langage la dfinition de grammaires et d'afficheurs ce qui permet la communication de valeurs entre le langage dcrit et Caml. Son systme de types tait plus contraignant pour les valeurs physiquement modifiables en ce qu'il n'autorisait pas que de telles valeurs fussent polymorphes. Son premier descendant, Caml-Light, n'utilisait plus la machine CAM, mais la Zinc pour l'implantation. Le nom a nanmoins t conserv pour montrer sa filiation. Il a apport une implantation plus lgre en optimisant l'allocation des fermetures et utilisait un GC performant prcurseur du GC actuel. Cet allgement permettait de l'utiliser sur les micros-ordinateurs de l'poque. Les diffrentes versions de Caml-Light ont volu vers le typage actuel des traits impratifs et se sont enrichies de nombreuses bibliothques. Le rejeton suivant, Caml Special Light ou CSL, a introduit les modules paramtrs et le compilateur natif. Enfin le benjamin est actuellement Objective CAML qui ajoute principalement l'extension objet CSL. Comme il n'y a jamais eu de spcification complte du langage Caml, ces diffrentes volutions ont pu s'effectuer en toute libert.

L'approche SML a t inverse. La spcification formelle [MTH90] a t donne avant la premire implantation. Elle est difficile lire, et un deuxime livre en donne un commentaire ([MT91]). Cette dmarche, spcification puis implantation, a permis la ralisation de plusieurs implantations, dont la plus connue est SML/NJ (Standard ML of New Jersey) de Lucent (ex-ATT). Ds l'origine, SML a intgr des modules paramtrs. Son systme de typage initial tait diffrent de celui de Caml pour les traits impratifs en introduisant un niveau de faiblesse sur les variables de type. Les diffrences entre les deux langages sont dtailles dans [CKL96]. Ces diffrences s'estompent avec le temps. Les deux familles ont le mme systme de type pour le noyau fonctionnel et impratif, Objective CAML possde maintenant des modules paramtrs. SML a aussi subi des volutions, le rapprochant d'Objective CAML comme pour les types enregistrements. Si les deux langages ne fusionnent pas, cela provient principalement de leur dveloppement spar. Il est noter qu'il existe un environnement de dveloppement commercial pour SML, MLWorks, de chez Harlequin :

Lien


http://www.harlequin.com/products/
Une implantation de SML, mossml, reposant sur la bibliothque d'excution de Caml-Light a aussi t implante.

Scheme

Le langage Scheme (1975) est un dialecte du langage Lisp (1960). Il est normalis (IEEE Std 1178-1990). C'est un langage fonctionnel valuation stricte, muni de traits impratifs, typ dynamiquement. Sa syntaxe est rgulire et particulire par l'usage des parenthses. La structure de donne principale est la paire pointe (quivalent du couple ML) avec laquelle on construit des listes possiblement htrognes. La boucle principale d'une boucle d'interaction Scheme s'crit (print (eval (read))). La fonction read lit l'entre standard et construit une expression Scheme. La fonction eval value l'expression construite et la fonction print affiche le rsultat. Scheme possde un systme de macro-expansion trs pratique qui, associ la fonction eval, permet de construire facilement des extensions du langage. Il permet non seulement d'effectuer des ruptures de calcul (exceptions) mais aussi des reprises de calcul grce aux continuations. Une continuation correspond un point de calcul. La forme spciale call_cc lance un calcul avec la possibilit de reprendre celui-ci au niveau de la continuation courante, c'est dire du retour de ce calcul. Il existe de trs nombreuses implantation de Scheme. Il est mme utilis comme langage de macros pour le logiciel de retouche d'images GIMP. Scheme est un excellent laboratoire exprimental pour l'implantation de nouveaux concepts de programmation squentielle ou parallle (grce aux continuations).

Langages valuation retarde

la diffrence de ML ou Lisp, les langages valuation retarde ne calculent pas les paramtres d'appel des fonctions leur passage, mais quand l'valuation du corps de la fonction le ncessite. Il existe une version << paresseuse >> de ML appele Lazy ML mais les reprsentants principaux de cette famille de langages sont Miranda et Haskell.

Miranda

Miranda([Tur85]) est un langage fonctionnel pur. C'est dire sans effet de bord. Un programme Miranda est une suite d'quations dfinissant les fonctions et les structures de donnes.

Lien


http://www.engin.umd.umich.edu/CIS/course.des/cis400/miranda/miranda.html


Par exemple la fonction fib se dfinit ainsi :
 
 fib a = 1, a=0 
       = 1, a=1 
       = fib(a-1) + fib(a-2), a>1
La slection des quations se fait soit par des gardes (expressions conditionnelles) comme ci-dessus, soit par un filtrage de motif comme dans l'exemple ci-dessous :
fib 0 = 1
fib 1 = 1
fib a = fib(a-1)+ fib(a-2)
Ces deux mthodes peuvent se mlanger.

Les fonctions sont d'ordre suprieur et peuvent tre values partiellement. L'valuation est paresseuse, aucune sous-expression n'est calcule jusqu'au moment o sa valeur devient ncessaire. Ainsi, les listes Miranda sont naturellement des flots.

Miranda a une syntaxe concise pour les structures infinies (listes, ensembles) : [1..] reprsente la liste de tous les entiers. La liste des valeurs de la fonction de Fibonacci s'crit brivement : fibs = [a | (a,b) <- (1,1),(b,a+b)..]. Comme les valeurs ne sont calcules que pour leur utilisation, la dclaration de fibs ne cote rien.

Miranda est fortement typ en utilisant un systme de type la Hindley-Milner. Sa discipline de type est essentiellement la mme que ML. Il accepte la dfinition de donnes par l'utilisateur.

Miranda est l'archtype des langages fonctionnels purs paresseux.

Haskell

Le site principal du langage Haskell contient les rapports de dfinition du langage et de ses bibliothques, ainsi que les principales implantations de celui-ci.

Lien


http://www.haskell.org


Plusieurs ouvrages sont consacrs la programmation fonctionnelle en Haskell, un des plus rcents est [Tho99].

C'est un langage qui reprend presque tous les nouveaux concepts des langages fonctionnels. Il est pur (sans effet de bord), paresseux (non-strict), muni d'un polymorphisme ad hoc (pour la surcharge) en plus du polymorphisme paramtrique la ML.

Polymorphisme ad hoc
Ce systme est diffrent du polymorphisme vu jusqu' prsent. En ML une fonction polymorphe ne regarde pas ses arguments polymorphes. Le traitement est identique pour tous les types. En Haskell c'est le contraire. Une fonction polymorphe peut avoir un comportement diffrent selon le type de ses arguments polymorphes. Cela autorise la surcharge de fonctions.

L'ide de base est de dfinir des types de classes qui regroupent des ensembles de fonctions surcharges. Une dclaration de classe dfinit une nouvelle classe et les oprateurs que celle-ci autorise. Une dclaration d'instance (d'une classe) indique qu'un certain type est une instance d'une classe. Cela inclut la dfinition des oprateurs surchargs de sa classe pour ce type.

Par exemple la classe Num a la dclaration suivante :
class Num a where
  (+)    :: a -> a -> a
  negate :: a -> a
On peut maintenant dclarer une instance Int de la classe Num de cette manire :
instance Num Int where
  x + y    =  addInt x y
  negate x = negateInt x
Et l'instance Float :
instance Num Float where
  x + y    =  addFloat x y
  negate x = negateFloat x
L'application de negate Num aura un comportement diffrent si l'argument est de l'instance Int ou Float.

L'autre intrt des classes provient de l'hritage entre classes. La classe descendante rcupre les fonctions dclares par son anctre. Ses instances peuvent en modifier le comportement.

Autres caractristiques
Les autres caractristiques du langage Haskell sont principalement les suivantes :
  • un systme d'entres-sorties purement fonctionnel utilisant des monades;
  • les tableaux sont construits paresseusement;
  • les vues qui permettent diffrentes reprsentations d'un mme type de donnes.
En fait il contient peu prs tous les traits pointus issus de la recherche dans le domaine des langages fonctionnels. C'est son avantage et son inconvnient.

Langages de communication

ERLANG

ERLANG est un langage fonctionnel typ dynamiquement pour la programmation concurrente. Il a t dvelopp par la socit Ericsson dans le cadre d'applications pour les tlcommunications. Il est maintenant en open source. Le site principal d'accs au langage est le suivant :

Lien


http://www.erlang.org
Il est conu pour que la cration de processus et leur communication soient aises. Les communications se font par envoi de messages et elles peuvent tre soumises des dlais. Il est facile de dfinir des protocoles via des ports. Chaque processus possde son propre dictionnaire de dfinition. La gestion des erreurs utilise un mcanisme d'exceptions et de signaux qui peuvent se propager entre les processus. De nombreuses applications de tlphonie ont t ralises avec Erlang, faisant gagner un temps de dveloppement non ngligeable.

SCOL

Le langage SCOL est un langage de communication pour la construction de mondes 3D. Il est dvelopp par la socit Cryo Networks :

Lien


http://www.cryo-networks.com
Son noyau est proche de celui de Caml : il est fonctionnel, statiquement typ, polymorphe paramtrique avec infrence de types. Il est << multimedia >> grce ses API pour le son, la 2D et la 3D. Le moteur 3D est trs efficace. L'originalit de SCOL provient de la communication entre machines virtuelles au travers de canaux. Un canal est un couple (environnement, liaison rseau). La liaison est une socket (TCP ou UDP).

L'originalit de SCOL est d'avoir rsolu simplement le problme de la scurisation de code tlcharg : seul le texte des programmes circule sur le rseau. La machine rceptrice type le programme pass puis l'excute garantissant que le code produit provient bien du compilateur officiel. Pour mettre en oeuvre une telle solution, sans sacrifier la vitesse de transfert et de rception, le choix d'un langage fonctionnel statiquement typ s'est impos pour la concision des sources qu'il autorise.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora029.gif0000644000000000000000000000624007073350152014275 0ustar GIF89aʽ}}}{}{{y׮!A,AHBB6%CD EE!F"З؂ۦ߮Ჱ庺҂@4H YȐᎇ!HD1qく ?Ir$(OXɒe0_Isf8oɓg@JHZXP^JuXbʕ낯`K,hXvm=|P` 6oEw2dɒ)S\3f͚9shI37esժYvliӶm.޼ :+{,,*l,:',J7;,ZG{--jW :i6[n;|Cpr1sI7]e]m }xfy詷^{]ᖛ}=q\r2לsE u5QNT!z=D)#6yEiB)0#dcc cCvBRX `U$=S& @Wb[v".YifoœtD':h (tWXFi CJ+'H f0^:cij䩩.$@r-+z+A*B lFIi☣Z'*9՚nzהI9Qp{,6rz/ þ)h ;->Æp]VfkhInرr(#23lJ.:'snЄKt 3rwrhNmmVs,%Gd8 }ؼFm/J,N 5)kH5.>[+n~%0jvyLtbtzgjajO]{󸹞e;;GOK_]ݞm.}~|Ͷ~{r>lAɚb2a%o֞["I]cj{\=%w Oj XB@AQDQwAe1dL!Aʄwӄ/O}akՇ ph?mm{/s#b0ԬyW_x~=`1Z -È1*w;ӧ$L$"gF2$@JZҒ Ȥ&PNzғ(EyR8H*UVL,cZڲȥ.s^җ0b&H2Ɂf:ә4ЁjZӚȦ6Ƀnz8rH:9vӝ<)zӞȧ>~p@JЂMBІ:ʆD'JъZͨF7юz HGJҒ(MJWRM4J,Lҗ8$tJQ(M PB4i@*U>TG զժW-*MZUZ5TѪZeY:jd+HU6b+X*׾nE +\׺o_kQ.UEX;TLefO:zvhGKҚMjWkڶUkg9ԭb?!pԷ-'kn/AG43,tY5S;أVnu;]S{W*65q*_e"ޚX敫kovഺenhkkTտ啬wʷ ~_ 6~!߯ޥ0"%1xU_ 0:Fvnek|&du Y+w[:(nyL gכ-Wʽm<`w*sx2r-Y[VBˆNF;ѐ'MJ[Ҙδ7N{ӠGMRԨNWj"zV)|M2u[ C$l;KG{ְqBFZs k9оi-}k;;2F6sn^9>7mu3>lvێoX}&x^n yxvn]pJ' GNFuD+8nn|[mpGY/^k88*tBWOy- ٬$٭gm #?-΃.dYљYF : ײ惯{=\g]vp~w~y:%ϽwkFǽ/j ÿi6/keO\f?ٟk嵭uh?g_`]#}'vc7x (~ɶsUg瀲eo7y4pkI|welyo'fxy_0 mxWnv2ȂF<w)yE džrCK؄NPR8R|Y\Gm@hs~$zA^}ZU\h_Of4|iH&Tgy-w>|8x8tkXe&]gh E]n` u8HVKP8(hR(؉-(QP8tHg{WdYhV]=ȋ6拇(_Fg{p 8XeXR;ocaml-book-1.0/fr/html/book-ora060.gif0000644000000000000000000000725107073350152014273 0ustar GIF89aUUUrrr!,H0I8ͻ`(dIhlp,tmߵ:pTxȤl:%ǧt:F2*.n-%<<{"w]kFO _ |ÂbuZΌanޮx% '!i%e8싥O?? m BtaB0pW2Ȁ Ɋsx%'S67*'FcpB|G dD2G 8f-&%φ~N:jQ\+8Nژ$r1%I2]͠UJw̃m`ŐyTi_k8 %bX3-/in ` zꏏ.efN+> -zYWӿ<#\fπԁ@KUp*8W@M?DWӖ78lPB@0 ;4 m$ %h/ d q h?%jҡd3q/ .⩀t+)")ed"rUd4̷ #6 D dƮ" P&>)$Z(4ihDZow"=/9·~&q͌z36Z.֬I' VkeִF>fycmqXqIrk._`6qabFTDb^άGY&k`wxS7x;dVWf+<mh|0rOA2^zU s&*+%ٴUJ:MRcS\Ԩ9bJC}DK=c w!eLGScHw0NDV&ǵȯNSA-7 VoR8 Q5`+DfcTV0Dv R5H֏ uTPG$@[dVk*B@2"ȩ5@#[m)Yɞv:¥O}s$.ƹ56X0Lzⷴhi Zme=WT"Xr= xYklYjov#]ћ౎cC+fB7a koj_ȧ9󦢣1kˈMY¸2@)$b (dA6͋rI+m'Jd=Gro! ki6w#m7\f;cx'!\zi;K튅kVJNZN S489/yq.ļ/O(M5UyU1^tLwhFT9t-fd0Uc|ŃD6r[q?3F|Kx^ny'GS/g-BMu&⢗ɨop?O,u1`^fk37qN~uxZq#Ky{B={GЭשh:z:*kJ>G[tCo'Khh$5.hkSs=*5~ ;YXܴj0j6b%# ( /ܶ XD7 ;1po9?V6?ZEA3?DGxlIXCKhM>C&?XPaVwŅ!T@5zpNQCSfo~Zmnl\Xo-cQG]GT] H3^%o\HggJ);qzբiy#[mYcy$R9]NzS WJ3wS742(}Q3wn6v?=aq7B}iSb"vfhbиBhtcψv(W; O'{ikZepcF9cfeɢ(~UdiԐW/3Uhz&5!׀wpTxパ6 x`orTs/ &,9{m148ف۶X> nHӂXbcZv} ZH+BD]PV"rq(X=]L6pСlj6`izI^t(QCGa?gs`ؗzc`ňQ@\GV7y+0r9zILFI[aٚyO郶3ؑ)Y~\zfOȍry9~y5 S#yuUG*9YyZC< 1"= .@#E M RȡSE [8\fHV+k8Y@hP9c_Mz{('{ pCLppuhg]iq6c-!VÀH85hKg;WB?7Qlg"X9tp}pMhrwgHv]:&mꦚyiH}4hg8{^S90NrWzx| '-Ez0UcADMavC@ɩ'?Uf 6A='30>1Q:b*Sq)JbeJz^7ZmC؊ 6:ʭЋZ? 蚮y;ocaml-book-1.0/fr/html/book-ora175.html0000644000000000000000000000337307421273602014503 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

La premire section prcise les interactions possibles entre processus lgers, pour ensuite dtailler le module Thread et montrer comment excuter plusieurs processus dans une mme application.

La deuxime partie s'intresse la synchronisation entre threads par exclusion mutuelle (module Mutex) et avec attente sur condition (module Condition). Deux exemples complets montrent les difficults inhrentes ce modle.

La troisime section explique le mode de communication par vnements fourni par le module Event et les nouvelles possibilits qu'il apporte.

La quatrime section conclut ce chapitre par la ralisation d'une file d'attente commune aux diffrents guichets d'un bureau de poste.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora132.html0000644000000000000000000011001007421273602014457 0ustar Langage des modules simples Prcdent Index Suivant

Langage des modules simples

Le langage Objective CAML possde un sous-langage de modules qui vient s'ajouter au noyau du langage. Dans ce cadre, l'interface d'un module est appele sa signature et son implantation est appele structure. Lorsqu'il n'y a pas d'ambigut, nous utiliserons plutt le terme << module >> pour dsigner la structure.



La syntaxe de dclaration des signatures et des structures est la suivante :

Syntaxe


module type NOM =
  sig
    dclarations de l'interface
  end

Syntaxe


module Nom =
  struct
    dfinition de l'implantation
  end



Warning


Le nom d'un module doit imprativement commencer par une majuscule. Celui d'une signature est libre, mais par convention on utilise des noms en majuscules.


On peut galement utiliser les signatures ou des structures anonymes. On crit alors simplement :

Syntaxe


sig dclarations end

Syntaxe


struct dfinitions end
Nous utiliserons les expressions signature et structure pour dsigner soit des noms de signature et de structure, soit leur expression anonyme.

Toute structure a par dfaut une signature calcule par l'infrence de types qui reprend l'intgralit des dfinitions contenues dans la structure. On peut lors de la dfinition d'une structure, prciser quelle est la signature attendue en rajoutant une contrainte selon l'une des deux syntaxes suivantes :

Syntaxe


module Nom : signature = structure

Syntaxe


module Nom = (structure : signature)
Lorsqu'une signature attendue est prcise, le systme vrifie que tout ce qui est dclar dans la signature est dfini dans la structure Nom et que les types sont cohrents. En d'autres termes, la signature attendue est incluse dans la signature par dfaut. Si tel est le cas, Nom devient un module de signature signature et, de faon analogue ce qui se passait avec les fichiers d'interface, seules les dclarations apparaissant dans la signature sont accessibles l'utilisateur du module.

L'accs aux entits dclares d'un module se fait en utilisant la notation pointe :

Syntaxe


Nom1.nom2


On dit alors que le nom nom2 est qualifi.

On peut rendre implicite le nom du module en utilisant la directive d'ouverture des modules :

Syntaxe


open Nom
Ds lors, on peut utiliser les noms des entits sans les qualifier. L'ouverture d'un module provoque, en cas d'identit de nom, le masquage des entits pralablement dfinies, la faon des redfinitions d'identificateurs.

Deux modules pour les piles

Reprenons les piles en utilisant le langage des modules. Nous commenons par dfinir la signature d'une pile en reprenant les dclarations du fichier stack.mli :

# module type STACK =
sig
type 'a t
exception Empty
val create: unit -> 'a t
val push: 'a -> 'a t -> unit
val pop: 'a t -> 'a
val clear : 'a t -> unit
val length: 'a t -> int
val iter: ('a -> unit) -> 'a t -> unit
end ;;
module type STACK =
sig
type 'a t
exception Empty
val create : unit -> 'a t
val push : 'a -> 'a t -> unit
val pop : 'a t -> 'a
val clear : 'a t -> unit
val length : 'a t -> int
val iter : ('a -> unit) -> 'a t -> unit
end

On obtient une premire implantation des piles en utilisant le module de la bibliothque standard :

# module Stack_distrib = Stack ;;
module Stack_distrib :
sig
type 'a t = 'a Stack.t
exception Empty
val create : unit -> 'a t
val push : 'a -> 'a t -> unit
val pop : 'a t -> 'a
val clear : 'a t -> unit
val length : 'a t -> int
val iter : ('a -> unit) -> 'a t -> unit
end


On en dfinit une seconde utilisant des tableaux :

# module Stack_perso =
struct
type 'a t = { mutable sp : int; mutable c : 'a array }
exception Empty
let create () = { sp=0 ; c = [||] }
let clear s = s.sp <- 0; s.c <- [||]
let size = 5
let increase s = s.c <- Array.append s.c (Array.create size s.c.(0))
let push x s =
if s.c = [||] then ( s.c <- Array.create size x; s.sp <- succ s.sp )
else ( (if s.sp = Array.length s.c then increase s) ;
s.c.(s.sp) <- x ;
s.sp <- succ s.sp )
let pop s = if s.sp =0 then raise Empty
else let x = s.c.(s.sp) in s.sp <- pred s.sp ; x
let length s = s.sp
let iter f s = for i=0 to pred s.sp do f s.c.(i) done
end ;;
module Stack_perso :
sig
type 'a t = { mutable sp: int; mutable c: 'a array }
exception Empty
val create : unit -> 'a t
val clear : 'a t -> unit
val size : int
val increase : 'a t -> unit
val push : 'a -> 'a t -> unit
val pop : 'a t -> 'a
val length : 'a t -> int
val iter : ('a -> 'b) -> 'a t -> unit
end


Les deux modules utilisent un type concret diffrent pour implanter le type t.

# Stack_distrib.create () ;;
- : '_a Stack_distrib.t = <abstr>
# Stack_perso.create () ;;
- : '_a Stack_perso.t = {Stack_perso.sp=0; Stack_perso.c=[||]}


On retrouve l'abstraction de type en forant la signature du second module.

# module Stack_perso = (Stack_perso : STACK) ;;
module Stack_perso : STACK
# Stack_perso.create() ;;
- : '_a Stack_perso.t = <abstr>


Les deux modules Stack_perso et Stack_distrib n'ont en commun que le nom des fonctions qu'ils implantent. Par contre, leurs types sont diffrents; il n'est donc pas possible d'utiliser les fonctions de l'un pour manipuler les valeurs de l'autre :

# let s = Stack_distrib.create() ;;
val s : '_a Stack_distrib.t = <abstr>
# Stack_perso.push 0 s ;;
Characters 19-20:
This expression has type 'a Stack_distrib.t = 'a Stack.t
but is here used with type int Stack_perso.t


Mme si les deux modules avaient possd un type t de mme implantation, le fait d'abstraire ce type en contraignant le module avec la signature STACK interdit la possibilit de partager les valeurs entre les deux modules.

# module S1 = ( Stack_perso : STACK ) ;;
module S1 : STACK
# module S2 = ( Stack_perso : STACK ) ;;
module S2 : STACK
# let s = S1.create () ;;
val s : '_a S1.t = <abstr>
# S2.push 0 s ;;
Characters 10-11:
This expression has type 'a S1.t but is here used with type int S2.t


Objective CAML ne dispose pour vrifier la compatibilit des types que de leur nom (leur implantation tant abstraite) et ici ils sont diffrents : S1.t et S2.t. C'est prcisment cette restriction qui permet l'abstraction de type en interdisant l'accs la dfinition des types dont on veut masquer l'implantation.

Modules et porte lexicale

Nous donnons dans ce paragraphe deux exemples d'utilisation de signatures pour masquer certaines dclarations.

Masquage de types

Abstraire un type permet de restreindre ses valeurs celles qu'il est possible de construire avec les fonctions que dclare la signature du module o ce type est dfinit. Dans l'exemple suivant, nous obtenons des entiers dont la construction nous assure qu'ils sont obligatoirement diffrents de 0.
 
# module Int_Star =
( struct
type t = int
exception Isnul
let of_int = function 0 -> raise Isnul | n -> n
let mult = (+)
end
:
sig
type t
exception Isnul
val of_int : int -> t
val mult : t -> t -> t
end
) ;;
module Int_Star :
sig type t exception Isnul val of_int : int -> t val mult : t -> t -> t end


Masquage de valeurs

Le masquage d'une valeur permet de raliser un gnrateur de symboles analogue celui vu page ??.

On dfinit la signature GENSYM contenant seulement deux dclarations de fonctions pour la gnration de symboles.

# module type GENSYM =
sig
val reset : unit -> unit
val next : string -> string
end ;;


On implante ensuite une structure cohrente pour une telle signature :

# module Gensym : GENSYM =
struct
let c = ref 0
let reset () = c:=0
let next s = incr c ; s ^ (string_of_int !c)
end;;
module Gensym : GENSYM


La rfrence c de la structure Gensym n'est pas accessible en dehors des deux fonctions exportes.

# Gensym.reset();;
- : unit = ()
# Gensym.next "T";;
- : string = "T1"
# Gensym.next "X";;
- : string = "X2"
# Gensym.reset();;
- : unit = ()
# Gensym.next "U";;
- : string = "U1"
# Gensym.c;;
Characters 0-8:
Unbound value Gensym.c


La dclaration de c peut tre considre comme locale la structure module Gensym puisqu'elle est masque par la signature associe au module. La contrainte de signature nous a permis de reproduire plus simplement la dfinition des fonctions reset_s et new_s qui utilisaient une dclaration locale (voir page ??).

Diffrentes vues d'un mme module

Le langage de module avec contraintes de signature permet d'offrir plusieurs vues d'une mme structure. On pourra, par exemple avoir un << super-utilisateur >> du module Gensym qui est capable de remettre jour le compteur et un utilisateur ordinaire qui ne peut que crer un nouveau symbole sans matriser le compteur. Pour obtenir ce dernier, il suffit de poser la signature :

# module type USER_GENSYM =
sig
val next : string -> string
end;;


On cre ensuite le module correspondant par la dclaration :

# module UserGensym = (Gensym : USER_GENSYM) ;;
module UserGensym : USER_GENSYM
# UserGensym.next "U" ;;
- : string = "U2"
# UserGensym.reset() ;;
Characters 0-16:
Unbound value UserGensym.reset


Pour raliser ce nouveau module on a rutilis le module Gensym. De plus, les deux modules partagent le mme compteur :

# Gensym.next "U" ;;
- : string = "U3"
# Gensym.reset() ;;
- : unit = ()
# UserGensym.next "V" ;;
- : string = "V1"


Partage de types entre modules

L'incompatibilit entre types abstraits signale un peu avant (page ??) pose problme lorsque l'on dsire partager un type abstrait entre plusieurs modules. Nous examinons deux faons de procder au partage. L'une est une construction explicite du langage de modules, l'autre utilise la structure de bloc lexical des modules.

Partage par contrainte

Illustrons le problme l'aide du petit exemple suivant. On dfinit un module M qui fournit un type abstrait M.t. Nous le restreignons ensuite selon deux signatures diffrentes n'autorisant pas les mmes oprations.

# module M =
(
struct
type t = int ref
let create() = ref 0
let add x = incr x
let get x = if !x>0 then (decr x; 1) else failwith "Empty"
end
:
sig
type t
val create : unit -> t
val add : t -> unit
val get : t -> int
end
) ;;

# module type S1 =
sig
type t
val create : unit -> t
val add : t -> unit
end ;;

# module type S2 =
sig
type t
val get : t -> int
end ;;
# module M1 = (M:S1) ;;
module M1 : S1
# module M2 = (M:S2) ;;
module M2 : S2


Pour obtenir l'identification dsire des types M1.t et M2.t, Objective CAML dispose d'une syntaxe pour contraindre un type normalement abstrait dans une signature.

Syntaxe


NOM with type t1 = t2 and ...
Il s'agit d'une contrainte de type forant le type t1 dclar par la signature NOM tre gal au type t2.

On peut poser des contraintes globales sur tous les types d'un module en utilisant la contrainte :

Syntaxe


with module Nom1 = Nom2


En utilisant de telles contraintes de partage, on peut dclarer les deux modules M1 et M2 comme manipulant la mme structure de donnes.

# module M1 = (M:S1 with type t = M.t) ;;
module M1 : sig type t = M.t val create : unit -> t val add : t -> unit end
# module M2 = (M:S2 with type t = M.t) ;;
module M2 : sig type t = M.t val get : t -> int end
# let x = M1.create() in M1.add x ; M2.get x ;;
- : int = 1


Partage et sous-modules

Une autre solution pour assurer le partage de type est d'utiliser le mcanisme des sous-modules. En dfinissant deux sous-modules (M1 et M2) partageant un type de donnes abstrait d'un module englobant M, nous pouvons parvenir au rsultat souhait.

# module M =
( struct
type t = int ref
module M_hide =
struct
let create() = ref 0
let add x = incr x
let get x = if !x>0 then (decr x; 1) else failwith"Empty"
end
module M1 = M_hide
module M2 = M_hide
end
:
sig
type t
module M1 : sig val create : unit -> t val add : t -> unit end
module M2 : sig val get : t -> int end
end ) ;;
module M :
sig
type t
module M1 : sig val create : unit -> t val add : t -> unit end
module M2 : sig val get : t -> int end
end


On obtient bien le rsultat voulu et une valeur cre par M1 peut tre manipule par M2 :

# let x = M.M1.create() ;;
val x : M.t = <abstr>
# M.M1.add x ; M.M2.get x ;;
- : int = 1
On rajoute cependant un peu de lourdeur par rapport la solution prcdente : l'accs aux fonctions de M1 et M2 se fait via le module englobant M.

Modules simples et extension

Un module est une entit dfinie une fois pour toutes. En particulier, lorsque nous dfinissons un type abstrait l'aide du mcanisme de modules nous ne pouvons plus en tendre les traitements. En particulier, s'il n'a pas t dfini de fonction de cration, on ne pourra jamais obtenir de valeur de ce type !

Une faon brutale d'augmenter les traitements fournis par un module est d'diter les sources et de rajouter ce que l'on dsire dans la signature et la structure. Mais alors, on n'a plus du tout affaire au mme module et toutes les applications qui utilisaient la version originale du module sont recompiler. Notons cependant que si la redfinition des composants du module n'a pas modifi les lments de l'interface originale, il suffit uniquement de recompiler l'ensemble de l'application sans avoir modifier ce qui avait t crit.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora112.html0000644000000000000000000000206207421273603014465 0ustar Notes
1
Le type lexeme est dfini page ??
2
Il est de tradition de noter le mot vide par la lettre grecque epsilon : e
3
Nous soulignons le morceau trait chaque tape et nous indiquons la rgle utilise.
ocaml-book-1.0/fr/html/book-ora016.gif0000644000000000000000000007524607073350152014305 0ustar GIF89aǶy0A(A aH 00( 3a0,(ǖaaL7(( 77(YYauae0yau(QYU(qua0 Q7צ7hyhADY7(ahh Yyyhy0,HY;QaQνQH03qaqyHy77HYYH7A7AUQ(yaU(ayHLA7QyA 7H7 h]q}YDzˎHeyǮ7H0HQye 0AauYHUQHD0YQyHhyYuYYh߮׽qq]AHUh07aYha(YYh7quqa0qlQqhQνӶy ߦ߆H]qםǢA7(hYAyh Y]qyyY禪׮yyyHD(ΝaqQaL0l 00AYeQq}߮qyAaHqߦ7,qhqY,(ylQ0 }yh׽ǎ7HHYHYǺ  a Ǯ׽QqQAHAǝ(Aq yHQAyqa˝7Dyay΢hqaHhQ(Ƕ7Q0]H7Lhy (7Haǽ(YlA߮YhhqU7heY7ν!, H*\ȰÇ#J\*2Ep""*1=(@DEs/Ay *"s6s~IRFN&aUL5J]ޤ&MFW"5jHcGbE[{Vu:ZQjS ŶE)SIs9LĞȓXǐ *s,$aF5tnEN5hRѠ =TlR5>6ܕr#U+8Iۓ}UQPĊQʴYu%798#"#U\rc HTb(Ulu$S$:3@R]ct>hJEfeU6J%ѢO->Ѩ REVr~'!Tbum&نqQ{ &-PaYHi=&y1mU]ZցPx(0K(Ԅ&-4g4b'!vޢZf}4YfVTPJYX X=YN= FҗUGI'XK)Jp8'g1Ic'RuIZFar[j#[Q(թ=}5:ma8[ 8U2ʊ+!5Rvb`'TkOœ OuŽD[-QdUQ7:G5R[)(Po՞d:𡮋ngy-k]Qe'ܘ1*2l+ed{vg&4 H9 `mRK in%Rq -j<`FbysG^'!UmJJ~hEnUmh1<[4J1׸hv0|S1^d7sQ%I!K߿̬[kȂFZp-+f P זII'۾&}gwnk0ns :ԏQB`i)$R;/6 Dx  m. mGb5Q13g%}'48~B'-Hypxiv9 5:]\0c"كKDV»og|i,!4YV`*-5҈(čEPaq,DAn),[^ G$ fhN>aԵ"\Qc,h c7 JfͰ2gH|F#IFLi0P+~D.%C g*Am"Oy26,4z,LCL$7"1 },^~GcQeo!Qĵ=Hl=j0 ˨U! o,0o$F(HGťŗѢь1)uܓNw“Ad1l\f2E6(#'Cjҏ4v,oBW$ uO R8$7ŊŬտ7EE-{ʞ,0F1l.5L]uTxq*`Eu|Q<6DA[a"KVi+ZDːET˶#)ʴPL[z Bڕ.WhM) H$3Ñj2-P0ȨEpIMkЦI 9ᭅ ?BNw[JB19.w%X$YÂz/ %) u( 3ե0@Z i |@-ġ YB nqɲRq DfRzGJ")#"Q1If5ϺVˠ귌%Y`aՠFj5B'=R{XJOzCRPs(BK~z pr])( ` 2V$4Et@P$0v x"KR"3woKF 5W": # B֕`=s]C5i}MLg20,P 4 ]:f%2SɖZљ%u3[ s9d-rX]Ёe*3M*p\1 :8C pPj hSUA&m'wIe}l5w's&t1U{.KC;h[p ݦ ':TCpAdseQa`6&5!ҐȼD J -K"ɨZElɏdד88 T`5P$y\V$SI3g ffCAl{7\덗뷳db_X={kj`I;V Qg0aN xKb3i2DzK&yp%^ 3lzd[FF :_b# ]P .p 5D1d~6WulArEU!DS=EbDuHkXsiCohg"!_$3 @|0tWPlIwRHAr#TDu[CNBny"#0ӅYZ].Od2bjAP`7 @ut Ԡ 0 J`=c0>+b~f0|"dVoUP=DC1BM4zD6ӣ(O'%]&RhLdwuJ8" d@ ? p 0@W0 PHΠl%qn,R"YV9m%EaD5G$H- A4U]&s5V2CPwb M  ?'iW@ w - 0E,Hy0E5vci4!a0GL2hI6sRdQ%7"p{ 7 MP 0??xfU~8 IzwK+˳{unSZA2K"Jv DE"%O؎- 3wq60cX+Pp ppDX huJI 'RpH/Ypsfb-9?!_Q*ĕ$"[AH(g0 Jkdm#zWtE@x?lP ̆!in0 @ .0Pi3݁+6"d2705Tc$wc}xօ*YaqqSq_ " dH ?i{5@ nuJЃvGZw']8&fBrw6/JeBs6&ulkS9lf(PPx: @ .{PpZP(aOQ#QF'_t #q\4W[gzp"})g`HDH@ W{Y ׉uD:  @ ՠxߡyՈ8"gʅv:7C\d?!w*sX]2:X$$3J,6(&a ?Pu@  @DWh?0 . ׀zspq_e%6UwF"q862CK#[mgYТCTa0 r z0 ?0!@P9ΰYZ #PL;f?7 52[O6Ưd n]s{ =3=fOAJ_F"D j09(0 y͖չ  ~@1Cn9d/b7J#w,yq KKS^JL_b&@T˅t˳Wd `n ~0(lpn@! @P ! ǹ!k?@zWnE^[hA3Q5;TeJA9 #T*Ņ,0 50 5xWǨ*shl0 p .ypǕa/w[h4wuiJp%@4$Ut+i5pP O0?!pH f P n.@u`0!P twjys3r ma"Ww>$20[޷a)D"ɦNpxW8H l?` 9w`+UEҵT롦s^Q /pEǾ @t0g -Dќt[Mհ W # M ZacqT~%-^AOpQɣ7nV(*xVW!4"/;wC8@?M0 5 u?@  Zkk୅!P@  jp dbE8ϛV~DuR`W lQ$!)-bYVjmYijz7sP`H@0 W@ :8jPP΀9~ *4SjS"pu9 $0 F"P͂7]ań : SPRt(@0i唬΀ el .ξ0 .P 5PΠԠŠpY27ݳsL0e豌H.X"Ǝ5u]hgp@0@PZm`n Ζ0ܺpު^0ϐ pՙ~ x` kU1h:D[3M b"Mo掗᱂Aju+ -3E`Φvl-Q,QXVi,D` \h0' M} EΝ4i\@e J=%kV4g!6|e 6^R_,eˉ(~e(L*SlJ_KSw g,P›Yl9j{ɥ@1*!z n谢5jꗦ** In+$Z܈ mhz\> vjEhbeAfF1:0̙+&1 $+)%Y$qlgX0\)Mb~C oF@;\b6lssPH"I'}iIj*,JQ,zjB 5QK Q&/ZyGTVag A/ *p x)dOY!,h:hOVSBV \fAj~5[8mq>%'Z'nUQPVed5YIŊ2;\ vQ;EF4JfmpVIi!:mB0"qx,HK-.,HLqi ?fNeaٸkIsn)'-cBp8+*ֈ'U26],lB0jFn@Al OFAX(Gb!z*\(%,Pa-xF2**ƱźP! &@8 Q&\\rlc;]mXHoq9*xc|9( Ɣ^+p?iH3D "Єk%3 Li!  L~1Кa갆0 fy(ש^wbⰱd$1&𦓜'6A$$I)rM^$9 (\ XC#~P? =P ڡ uP |.K5!/jh#Y!q2zf#]ڰ7I(E1M #bN1ATBʑ`~IAH@#\Ƹ!?5 q@C*iС`$|%L2 7`MB/AkT  a2WEs(>)NGUHviHYMTc)Ȑ|t`vx#X~B_C-`D@ D$1k@7O\A\lu6Qf?d6ȉ'}OBV)@#SU[(Cݷ*hBcQoUQ!R `GPSvgD5fajpCBXV=U ֠ul^nS)Ϲ!Eq%Qp2>hu$:>TIp"+[+ lQ QH8 @rOPgƾgSj(V\=lJ*Q^(@ :A8 .ȨC3P&_d D60J`r*XtX] U^h|p7(3+lMnKVP(9ʐ݀y*Fh1Z=X1P_XkAŁμ)nT`j:!˖% Cՠ,:|hsg22X+1V)4@-. HT@z5p$ ypzطzS?h 7hA1U 8\ %pIiY (\"#Qo0.?Iˤ&(Y3.^ɕ&<<"Ђz@mcHk pj )AYsP&"89UHL`p Ђՠy(2yN# Ax&<}-"i+'4),)Ɠ)RCH:l$zk@XKmQ;q ibp&y9kzF@ ҀD h> 8s3{`*Ɋ!G#"cis\a(s"\X6{"8!؀D2 %؁y )~&@pP kHB y FbвApI@ 2ch h<٪iJ?7pcC(FJ.LjR+J+_2І,paH'j jIX\՘8 hhCp UGXb qb/!tɉQ(R,?iGy9#'ʚh Ri4b1{PЂCCHH 5F+ uP9,&h"1HAάjp x%pD:`ʬʘ;6FH-س ?5]a<ۜ☶㊎8ը ,ʂ8^P$I؂cC Tg@ h BbYRX0 H sx1Xv(3H͕c40]R7B(HH%H%BLj=^xg/ b(:H#yh ёCZ&&HɅ I 6z_Y, )8A'I86P7V -- 5.%P4XTX6\gP4 ̓u7EY.ڦu :YІFX=¨YsRPDIuH,p肭M,[<4" 5B Z1X7y@ x@ Is%@:+@Ljɨ$H. / @5@%)qBGK\b/3]?bŪ= NmPC)09_8zc%F3$)8+p`Wa\yɆY2:˪.-4j+xKJ*0>H|`p0]lMk 20}`{-@ܩc@5f7z8tKc jX x, q8bp~k-ȽȬb=Gl *Dz1@h X?cʇ3 b*ʶ+Jp'+@Pn`7ph$i&뜀'XŌ=jhRݙ䃘Z ^7]K&d)X,!9JpUC߬J+f9 }5%2J j0uӆ6Å]gHP@8 !@0@n@t$}\p b2u] jJmLNN6 PidkOhZ6W^`Sa Ne ~7aEg|=r<"`[u} T; L!H>I@#UIU?(vPNI0 S-+Z¶jB{ h#ee(E8 jXjh'0EIJhٷ͈j$RCb(eGܝ ,UGЀe3ȖI8^YnN(#ɘgbZU--4- lb{(5@C@50_(*9 ttZ2 Xq*f 0GX lصbPZBl9I5HP]p2gUvM-SL.A%ʰ8`64oȂN8Op\@bBQ_\ P>FP <H>I+iHj hkPNmp _Ҕbp1q) *pXf'OFY~BsuAt7`ƨ5C pOT=87Ѹ,Dp,l:B:81 UHxApl_\ Ad=Qu10O$p~>^$#R e9gy$P@lؚH&p[XcOK?8>Y$s* w*@AǪe ̈́l*d AI@9l޳[n%Ma_M؆mD>(7hgP&, : kHh'g2 @G)L#,ibO+ga8@ [u>bMYҟz?ˆ@.9 0H^x8'$uv AEX[ =im`hX eJ b[jn !E|Ӥ_1`&W!TFuYxA5 ԨU5m.: 'j]bzq,M)1TB Y0 Q LDI9h#kpUUljT2DjG]Hƅ(s뭯s޲YFgڳgcm[mړLZjeoڴsj^,QZ5MpMgK'bU§.[Sl?V0KITÅ5-k*V"Lb$%8:Κ5Ī}*bD]&A-U0Ȇ\mz;vl`nڰQ{(p^ϭY "Á3 u\ik0H}ZXEH Hn8o8!G?XBL\Kìa.$PԀX@D6uVyb~gx\Ǟ=wExW~7VhL @/$C<[L@X &JqԀ tԀ.cW( U8>Q֘@c1 c5:UGGU͑,>B!ՖMyg(mߖ_UZ~`6] A 4?B 4/׀DOPU9yE%KNM,9IN0#Hd JruT͒K R SJ0AHD.TZA֮7|rXV9 f`/fM#6h;A/\ s /j1O}Má>TĴJ8HXcM,&_WMIL񐷭1K2UМz$B%vK4g (gH10;7' VMٖY߯hV6đ7 pC6!b0A431YHq3&9F,ohtAݹm?D±u`%WPՑijhiDr rxoT/;*lT=[KZ Z+ | 0@.q C &Fpj?xQu4u@SȄ-pbAP5La)$49.iB|* XIPRT \`6 `4bzYFqр„֩E;+dv̨@#PckPB'g0 0<8`Qx&yG28Np 4ƀ)#`֠@,RPCDG@ odԠbLSC4ZhKV"5)k@5RS (9-kY&=,J-. ь4@C\@ p$qy,职fΤ/1k؃*rG~D&6X#8HpT`' pT :`41yƱFc@,N0r&{Bj60uBà, $H ^w&xES9IXxº"( :c82$ЂiP%*Lqdc=~ Ԡb=fDRMkk\7-AS0P4bE# aF"LG5\R@z3Ej:JI|-a)\dg$|C!c 1P#VP5P"_IHrQsTM,{̀Xrc$ԠD 0Y?0DQx(B ٘54eUX@H S$šXqPX0Ib͂0\N0U*r7N }XY7d'LB2T $@ d/<Ή" A22d'4|BlDr5i$TLrZ9R @!zR⛵|ta$ZA% @ =t6XB5# @:d&LJR \/A8\ѝ9( *JTX (̽TN%آ9B24B#tA0AvX #̀2* B:, tsԛAtm#LB||T0=ƫq|@+#$ |B54tȍ¤Z5#"ͶTWDǙ#h,@@ B;@ X A B B#@N2<_#@Wl[-Er-B!pB P@;)A0H$W)4<, r5Kd Wi4/&<+SLBLHw!P/AŐRAn ¿L1Ձ|"jl6zmJi=x'`7Ò48.@KOCЃҾ iP-.PHԋt_f$b /!yH @3(Ku aNH_!!H1t"lcl[5/x{hFJU%P4,xB#,4.<4C L]P5thR!5CUi22H1-FZ8^O5@@C"+ @rDxi.(l(m+̵nRJyTP4,@2@zyP ejDٕ'Z.늭)Vg .L.U֨N1e!tpQeTVdjmO#=V3=u>թ5(+̂TYP6Ζ=bm@eQEaC.(PL={0`sn1 *Rag _5H65-胎o Lx+㥂F•jJ]UG7 -=)-LRTSdBIKARGV҂jp@:Ρ5 -^o7. " 9K NʡgOb |[PC"BCe eQ*Pjl# qa:k 7bK"n?TH*_F##$$'F`х* 0p @,Ԥ,,{YdžuK;1%LQd _\Ǘ:fPCAev+BPf,q&*`)% (XgUcEFd`%_$QEhYb=t+ZfCCg$k>؂u+7Nq$,$ABn pǐ'0\2I3' *PP'P#_Kj&ВU=fI SF&# ANJm7iU*SeZNbD{:?9Şs֏/za&!F`! tPegHE&0Třjb>Z 78# Q/#Q^9iUI֩,pcVEVeL %.l^[ykk@@{~1DE&&h't`3N`jj'vy.Rö+ 5F7\q.P!37ԃ#BTi"6l QI(,~`d@` #xp ]\(7.KD.@t`xb4b1FHP, yT >A0nPH I$$@f6bs3h+hȅ*p *f=]5 cYFxp'Qq!iZW`vH ?C .(`<edB00DFFT>fHpdө͌8=4OJ(jL#6X *%-зCj`_,,L}-֥?ݓ_ ]#WG`A @ 0ġ F2bd@²a$T5*G>gyds#scpHL D`U910]8+KfWr9ցN= c #WUMva'258@(Fh#4R0,#;؀ z@4H@ ԽyB!j7K`1xT-3u , 羙7u@=&݀j\łxWb#P=-o8n IN<ͫ`q( h/J,@'&%$8J*vЃp k,`δ$azƙnXzӍvtCj$`ހs0qp$[5.p105X`Ђ]"N…sy\V% "@HG Z Z'" gP .'dq^ NA. A  b!A@z+`j~ L@ cxczFpihE ,:0# , r*"!g&mU~!l8,J. ?2)ʾ2@rAz ;t̡B` *!a|Qh*l#%d0H$@GBX/4[E+>aŪa-, $,PhMPcT|yk ZVVR` 6 c, g`"pnlmga'Ą A['^ i\(x~'Nb+9yC4N 5t׸>!~Р $ZR@EPKFFl3$,cT`1gz p'nV@fc, Ƥ .j0j1Gkl ZءA~@ 5 PaJ |u >|!6ldAhNL0b-!SJߢΧ24u txM )IB5 qƖbW3jUT s~?va# gp|e=ycª$8m+X8s8T@ (` \ v< 2Z `*&prNR&f|~C",x j @d$=ϊBb 0Bts%./OB!j!A 2L!a.` =6^@J@p v䄤t+)g:Aa` T@HFB da7fx:@d,Wrw"O}VB rca 0a!# A @ sf hAZIIT@p&qy$O` avT"- F>aC+zd$WѨA=&{XZMzg4O ApaV`RL`R"֡ L8ahIA;B a&-5j \r $Ky| $Fezac_.# + AN(N7hf碢T  x!8|k;a !:5/:;"9LL&0y BGYUiL*֓$~&)U\U Xc -[_(GN鼑",Ë|x R ^gx@ 7O2&RBXyT vID D YbrwQH9* j˫H|@TvN,@v@a\ sf\m xRs%` l = \x S0("/~~#|WXb!6ޡW$h U2ʨ `>p G>&%B@sraJ3?@$.= ;qM%!e"A &@ A `8`6&iU$):9ڡa$^a*+b RB ʝ$z.=vd@JDyLxxdrE[bg0{z!Ȁ&֩F* $aZeaFr&{IApZX:̤xBܛnz%7 ֓X`w`U?idaGa:0xRa]]` f AЀޠ43\+ KXp@ l:#q^*xBMo=27bC j44C6:|0D@5Daц2e6y >p5Y N? m:fFU0Eڳbk)V 3fXj-*IT@tMn0=;5LV-ASlbG:z͚UZ հ-̰.P֨_pYEͬ kԨ$mHtCP=vh6meu5pÙ!"j@6T0Dz=Z pJ X b9]gcݡR*kp\'~ .f[Zy%[ U1O$_ @,WaSMQ5Q]4kh@5bD MԐj8F19R#F=*E4NV4B !H50 EL9HJ,ZUR~T1`B.X 'P |F_uX/a$P@PSň[lYYPG(6P~, ͲC.2"x2C(D0C}ġw  @$4|!X3BA+l T |TErB:B5:DVGcS5VF:W<&(!d͂0gWMl@(Pl?`bZG(HDp!RL% F ^ oy zÐ#.P'B F1p 5`Z) J@ʣ%" @ j@׃h уF-0668Ȝu>}r4 $`**D# DcA*(.9 1 ` ;xP cPgāX DF\" t *F@*abq +)zH@N5k ϔF*r A "B+a}r4 ,KFkkB/N_] k$ |B$ L?DA jSTQ;p4H FL"Lb .  @X(jh,़ J ٠д% ?PеIcADhN|Ur cH, @.P5 ;U %p\`)m9L:P+.W0 7$юRG7B= ʃ(~;I`.þ@Cy6Hcd4(UF5E#z1<) <"z̷A:~ j ҬN&|B j#"Kj1EGE HGb,S^kA;h`gb($TbLUĔ"O|s Œ$T'c,c#ɨ(x.װXV9 n8< H"&A2\՘RyA\!RAT+,I4HA;hAxYSmش*GH[AdԲY"C<nmQ1`hPpwXǞq|B 8 AB3J/ BɘE}., G(݅,Q0>q 1 ȚV6h ܠ ($%ZP!~\ր(8q2`~Y`:@_Dh8 FS#^ B^p t@H{=bj!9>$ Wx$ j|CkD `T!;+GP5Sq ZY_fD b xg63b/ ]E5r1SxTa0jP EA #0O R6e?qs7@v. F5Sg oxr Q8@)pYY1p. zPpmsAl@$|&c?)s 0 h C-M &2\v`sK 0  n*0W0.pOn`= 2ϰ' Πy1[#sQz dpu*u#35x^DjT5W87RUMC'~Z/ϐ6MHNwN\ @j?j0 .0 ޠ!P  7 ; Α_ D@ up FFH!](#@ % Z Pl:Qѣ%R@Qp! guuxϐu0/Q QQ$@9@G! ްq o d0Pp = 0BHA ÐtzhlA^0Ȗ@Btڠ7mR PWX@Pf8 MA(E&O#4D4d 9wNW `1 eG`> G3 l!԰Ӏkp p>H# use AW T \T  gc ugA})RS,S P 0MؑbU( \t9*P 6#2jPPn V% @ cE)eu :R 7|'#k5)Qup$:Q7 Yր -p'!DU6! .h2 rx2)y \9W?z P 0p0 =0B)WV`"..u8P'C5mo~>NT v0Yn0НQ YM` "]@ ‰U6U p0 W0 n  :Y p?VD sXe?J"j`Q԰ x"Z . < _8 TO'u6a.<\e d@ PPo/lq 9cJN)@jà;+n!PW4Bk53a!@   z*A!0pJ 5ku0|q ` ;pl n|2  y O 7V@Nyʀ*ϐJ.@M0 6[T e8W dW!ഀNlI.` TTQd ߁ p=WRW! UM@?PqqssZ ~6Zzp^0'\/*pӀ;An Wp ٶΠ!H#t@]H0BpǰZ 簵M'0P ҃zRZ;ڏ77pC (!#O(R"Wa93 K>$@lm   JWW0v GJY.# `PۜQF5 @&QYj~zfgo0B|K!PO` T  @AERo|%EL& W@sp Pp< 4A*@dpנ0 }[ )HJ(NQ͂0#u /mZԑz(H&plD@G v@]Fqwsq@ʠ0 .p$Ћ0 *P V 0p 1HQנM3RuMkwG+5p _}XxlWgȁvQ 'zPe[DA'a3xEui1 P fky@`P2`@HmB"g ~:ΠD0߀ -c1Qа{C.{Z80ulAl|m5pTUUNQd ᥳp]A.`@lzp tфO@Q.`#tT^,#"=OT` ntI$(ft O >6 Tw/*)p$@: CE#0^@ ݄%h4^cv:ST޽FV. PLBGtHm~N.$Q W PMh44 # @,zq*h 7 ɠp rUkCE`Mp\ pmn5`.F 'YZ`8@rcAX S6 XL +"8Ǎɽ!RDf:՞]Qº .4BOԊB~Xb¤k.Yr~tI1O.hT+:*ՠ2-]^nӧPe 4UL lLP"Tb I$kl-QT'A 6-lF6@!ܹƁ)SₖwN)UB5dk k(v+Ubٖ"Dž"#2Xu5PĐ@ 370D'SD ƗdHQԨd>:@aj dq ,cIT>yW$^[]yj΅_)(=iua+<Dm.0jh>XB@ {&V+T.Yk%lg_$ '8 Bu&1dԠ< ~ቚd #S(G.HTbөE/YC C +mBT;EiK: * @ hbІh 8UGQ81x1`4%43( "j ac_xFD+ OI$rr* **&6d[^h ÑYg@cS q(HBl0c4S&41(Be1aZ[X7B I@x:٫ňP3 ~c':]  'Û5s ";” {h3KFF<8ppvH%Xv@k-Pp:3030{4v @ ׉ kz:i=PΉFv` C@ܑ5JF؍ZI#27aۙ -200 ;z|1J0SXz55`(kpQ7I`YF8|Sˠ!9<0jɱ[hWSF%9%J<ӹjkh(%r‰҂lz`}Ğɨáih8Y#5+J#y+|EHcQ7̅RK@ _Sm45 ؘ2Ñ1>:4ÅXTQGP<ȌL "8:^"x9즘Ë $L5l>"@T;]AjଷtF(P;㓭ZKJ(?_CH1q*<V`Q8YL8H-z8y+/P:yXcxKb52r\ ҉Dd kpiT 'xA&P}50> B00*EIha`|IQЂA!"e p֫X@hCgh \` Oqx''0S_#,:ԃy5nP50HI!7ѕH sADU3p@@xl e]VfmVg}VhViVjVkVlVi ;ocaml-book-1.0/fr/html/book-ora096.html0000644000000000000000000000311207421273602014474 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

Ce court chapitre prsente les outils d'analyse de programme de la distribution du langage Objective CAML. La premire section dcrit la commande ocamldep qui construit les dpendances d'un ensemble de fichiers Objective CAML participant une mme application. La deuxime section s'intresse aux outils de mise au point comme la trace des applications de fonctions ou l'outil de mise au point ocamldebug fonctionnant sous Unix. La troisime section regarde l'outil dit de profiling qui permet d'analyser l'excution d'une application en vue de son optimisation.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora168.html0000644000000000000000000010041207421273602014475 0ustar Processus Prcdent Index Suivant

Processus

Unix est un systme qui associe chaque excution d'un programme un processus. Dans [CDM96] Card, Dumas et Mvel rsument ainsi la diffrence entre programme et processus : << un programme en lui-mme n'est pas un processus : un programme est une entit passive (un fichier excutable rsidant sur un disque), alors qu'un processus est une entit active avec un compteur ordinal spcifiant l'instruction suivante excuter et un ensemble de ressources associes. >>

Unix est un systme dit multi-tches : plusieurs processus peuvent tre excuts simultanment. Il est premptif, l'excution des processus est confie un processus particulier charg de leur ordonnancement. Un processus n'est donc pas entirement matre de ses ressources. Au premier chef, un processus n'est pas matre du moment de son excution et ce n'est pas parce qu'il est cr qu'un processus est excut sur le champ.

Chaque processus dispose de son propre espace mmoire. Les processus peuvent communiquer travers des fichiers ou des tubes de communication. Nous sommes en prsence du modle de paralllisme mmoire distribue simul sur une seule machine.

Le systme attribue aux processus un identificateur unique : un entier appel PID (Process IDentifier). De plus, sous Unix, l'exception notable du processus initial, tout processus est engendr par un autre processus que l'on appelle son pre.

On peut connatre l'ensemble des processus actifs par la commande Unix ps3 :
$ ps -f
PID    PPID    CMD
1767   1763   csh
2797   1767   ps -f
L'emploi de l'option -f fait apparatre, pour chaque processus actif, son identificateur (PID), celui de son pre (PPID) et le programme invoqu (CMD). Ici, nous avons deux processus, l'interprte de commandes csh et la commande ps elle-mme. On note que ps tant invoque depuis l'interprte de commandes csh, le pre de son processus est le processus associ l'excution de csh.

Excution d'un programme

Environnement d'excution

Trois valeurs sont associes un programme excut depuis un interprte de commandes du systme d'exploitation :
  1. la ligne de commande ayant servi son excution qui est contenue dans la valeur Sys.argv,
  2. les variables d'environnement de l'interprte de commandes que l'on peut rcuprer grce la fonction Sys.getenv,
  3. un statut d'excution lorsque le programme termine.
Ligne de commande
La ligne de commande permet de rcuprer les arguments ou options d'appel d'un programme. Celui-ci peut alors dterminer son comportement en fonction de ces valeurs. En voici un petit exemple. On crit le petit programme suivant dans le fichier argv_ex.ml :

if Array.length Sys.argv = 1 then
Printf.printf "Hello world\n"
else if Array.length Sys.argv = 2 then
Printf.printf "Hello %s\n" Sys.argv.(1)
else Printf.printf "%s : trop d'arguments\n" Sys.argv.(0)


On le compile :
$ ocamlc -o argv_ex argv_ex.ml
Et on excute successivement :
$ argv_ex
Hello world
$ argv_ex lecteur
Hello lecteur
$ argv_ex cher lecteur
./argv_ex : trop d'arguments
Variables d'environnement
Les variables d'environnement contiennent diffrentes valeurs ncessaires la bonne marche du systme ou de certaines applications. Le nombre et le nom de ces variables dpendent la fois du systme d'exploitation et de configurations propres aux utilisateurs. Le contenu de ces variables est accessible par la fonction getenv qui prend en argument le nom d'une variable d'environnement sous forme d'une chane de caractres :

# Sys.getenv "HOSTNAME";;
- : string = "zinc.pps.jussieu.fr"


Statut d'excution

La valeur de retour d'un programme est un entier fix, en gnral, automatiquement par le systme suivant que le programme se termine en erreur ou non. Le dveloppeur peut toujours mettre fin explicitement son programme en prcisant la valeur du statut d'excution par appel la fonction :

# Pervasives.exit ;;
- : int -> 'a = <fun>


Lancement de processus

Un programme est lanc partir d'un processus que l'on appelle le processus courant. L'excution du programme devient un nouveau processus. On obtient trois cas de figure :
  • les deux processus sont indpendants et peuvent s'excuter concurremment;
  • le processus pre est en attente sur la fin d'excution du processus fils;
  • le processus lanc remplace le processus pre qui disparat.
On peut aussi dupliquer le processus courant et obtenir ainsi deux instances du mme processus qui ne diffrent que par leur PID. C'est le fameux fork que nous dcrivons dans la suite.

Processus indpendants

Le module Unix offre une fonction portable de lancement d'un processus correspondant l'excution d'un programme.

# Unix.create_process ;;
- : string ->
string array ->
Unix.file_descr -> Unix.file_descr -> Unix.file_descr -> int
= <fun>
Le premier argument est le nom du programme (qui peut tre un chemin), le deuxime, le tableau d'arguments du programme, les trois derniers sont les descripteurs devant servir l'entre standard, la sortie standard et la sortie en erreur du processus. La valeur de retour est le numro du processus cr.

Il existe une variante de cette fonction permettant de prciser la valeur de variables d'environnement :

# Unix.create_process_env ;;
- : string ->
string array ->
string array ->
Unix.file_descr -> Unix.file_descr -> Unix.file_descr -> int
= <fun>
Ces deux fonctions sont utilisables sous Unix ou Windows.

Empilement de processus

Il n'est pas toujours utile que le processus lanc le soit de manire concurrente. En effet le processus pre peut avoir besoin d'attendre la fin du processus qu'il vient de lancer pour poursuivre sa tche. Les deux fonctions suivantes prennent comme argument le nom d'une commande et l'excutent.

# Sys.command;;
- : string -> int = <fun>
# Unix.system;;
- : string -> Unix.process_status = <fun>
Elles diffrent par le type du code retour. Le type process_status est dtaill la page ??. Pendant l'excution de la commande le processus pre est bloqu.

Remplacement de processus courant

Le remplacement du processus courant par la commande qu'il vient de lancer permet de limiter le nombre de processus en cours d'excution. Les quatre fonctions suivantes effectuent ce travail :

# Unix.execv ;;
- : string -> string array -> unit = <fun>
# Unix.execve ;;
- : string -> string array -> string array -> unit = <fun>
# Unix.execvp ;;
- : string -> string array -> unit = <fun>
# Unix.execvpe ;;
- : string -> string array -> string array -> unit = <fun>
Leur premier argument est le nom du programme. En utilisant execvp ou execvpe, ce nom peut indiquer un chemin dans l'arborescence des fichiers. Le second argument contient les arguments du programme qu'il est possible de passer sur la ligne de commande. Le dernier argument des fonctions execve et execvpe permet en plus d'indiquer la valeur des variables systme utiles au programme.

Cration d'un processus par duplication

L'appel systme originel de cration de processus sous Unix est :

# Unix.fork ;;
- : unit -> int = <fun>


La fonction fork engendre un nouveau processus et non un nouveau programme. Son effet exact est de dupliquer le processus appelant. Le code du nouveau processus est le mme que celui de son pre. Sous Unix un mme code peut servir plusieurs processus, chacun possdant son propre contexte d'excution. On parle alors de code rentrant.

Voyons cela sur le petit programme suivant (on utilise la fonction getpid qui retourne le PID du processus associ l'excution du programme) :
Printf.printf "avant fork : %d\n" (Unix.getpid ())  ;;
flush stdout ;;
Unix.fork () ;;
Printf.printf "aprs fork : %d\n" (Unix.getpid ()) ;;
flush stdout ;;


On obtient l'affichage suivant :
avant fork : 1447
aprs fork : 1447
aprs fork : 1448


Aprs l'excution du fork, deux processus excutent la suite du code. C'est ce qui provoque deux fois l'affichage du PID << aprs >>. On remarque qu'un des processus a gard le PID de dpart (le pre) alors que l'autre en a un nouveau (le fils) qui correspond la valeur de retour de l'appel fork. Pour le processus pre la valeur de retour de fork est le PID du fils alors que pour le fils, elle vaut 0.

C'est cette diffrence de valeur de retour de fork qui permet, dans un mme programme source, de diffrencier le code excut par le fils du code excut par le pre :
Printf.printf "avant fork : %d\n" (Unix.getpid ())  ;;
flush stdout ;;
let pid = Unix.fork () ;;
if pid=0 then (* -- Code du fils *)
Printf.printf "je suis le fils : %d\n" (Unix.getpid ())
else (* -- Code du pere *)
Printf.printf "je suis le pre : %d du fils : %d\n" (Unix.getpid ()) pid ;;
flush stdout ;;


Voici la trace de l'excution de ce programme :
avant fork : 1456
je suis le pre : 1456 du fils : 1457
je suis le fils : 1457


On peut aussi utiliser la valeur de retour dans un filtrage :
match Unix.fork () with
0 -> Printf.printf "je suis le fils : %d\n" (Unix.getpid ())
| pid -> Printf.printf "je suis le pre : %d du fils : %d\n"
(Unix.getpid ()) pid ;;


La fcondit d'un processus peut tre trs grande. Elle est cependant limite un nombre fini de descendants par la configuration du systme d'exploitation. L'exemple suivant cre deux gnrations de processus avec grand pre, pres, fils, oncles et cousins.
let pid0 = Unix.getpid ();;
let print_generation1 pid ppid =
Printf.printf "Je suis %d, fils de %d\n" pid ppid;
flush stdout ;;

let print_generation2 pid ppid pppid =
Printf.printf "Je suis %d, fils de %d, petit fils de %d\n"
pid ppid pppid;
flush stdout ;;

match Unix.fork() with
0 -> let pid01 = Unix.getpid ()
in ( match Unix.fork() with
0 -> print_generation2 (Unix.getpid ()) pid01 pid0
| _ -> print_generation1 pid01 pid0)
| _ -> match Unix.fork () with
0 -> ( let pid02 = Unix.getpid ()
in match Unix.fork() with
0 -> print_generation2 (Unix.getpid ()) pid02 pid0
| _ -> print_generation1 pid02 pid0 )
| _ -> Printf.printf "Je suis %d, pre et grand pre\n" pid0 ;;




On obtient :
Je suis 1548, pre et grand pre
Je suis 1549, fils de 1548
Je suis 1550, fils de 1548
Je suis 1552, fils de 1549, petit fils de 1548
Je suis 1553, fils de 1550, petit fils de 1548


Ordre et moment d'excution

En enchanant sans prcaution la cration de processus, on peut obtenir des effets potiques la M. Jourdain :
match Unix.fork () with
0 -> Printf.printf "Marquise " ; flush stdout
| _ -> match Unix.fork () with
0 -> Printf.printf "vos beaux yeux me font " ; flush stdout
| _ -> Printf.printf"mourir d'amour\n" ; flush stdout ;;





Ce qui peut donner le rsultat suivant :
mourir d'amour
Marquise vos beaux yeux me font


Pour obtenir un M. Jourdain prosateur, il faut que notre programme soit capable de s'assurer lui-mme de l'ordre d'excution des processus le composant. De faon plus gnrale, lorsqu'une application met en oeuvre plusieurs processus elle doit, quand besoin est, s'assurer de leur synchronisation. Selon le modle de paralllisme utilis, cette synchronisation est ralise par la communication entre processus ou par des attentes sur condition. Cette problmatique est plus amplement prsente dans les deux prochains chapitres. En attendant, on peut rendre M. Jourdain prosateur de deux faons :
  • laisser du temps au fils pour crire son bout de phrase avant d'crire le sien propre.
  • attendre la mort du fils qui aura crit son bout de phrase avant d'crire son propre bout de phrase.
Dlai d'attente
Un processus peut suspendre son activit en appelant la fonction :

# Unix.sleep ;;
- : int -> unit = <fun>
L'argument fournit le nombre de secondes de suspension du processus appelant avant la reprise d'activit.

En utilisant cette fonction, on crira :
match Unix.fork () with
0 -> Printf.printf "Marquise " ; flush stdout
| _ -> Unix.sleep 1 ;
match Unix.fork () with
0 -> Printf.printf"vos beaux yeux me font "; flush stdout
| _ -> Unix.sleep 1 ; Printf.printf"mourir d'amour\n" ; flush stdout ;;


Et on pourra obtenir :
Marquise vos beaux yeux me font mourir d'amour


Nanmoins, cette mthode n'est pas sre. A priori, rien n'empche le systme d'allouer suffisamment de temps l'un des processus pour qu'il puisse la fois raliser son temps de sommeil et son affichage. Nous prfrerons donc dans notre cas la mthode ci-dessous qui squentialise les processus.

Attente de terminaison du fils
Un processus pre a la possibilit d'attendre la mort de son fils par appel la fonction :

# Unix.wait ;;
- : unit -> int * Unix.process_status = <fun>


L'excution du pre est suspendue jusqu' terminaison de l'un de ses fils. Si un wait est excut par un processus n'ayant plus de fils, alors l'exception Unix_error est dclenche. Nous reviendrons ultrieurement sur la valeur de retour de wait. Ignorons la pour l'instant et faisons dire de la prose M. Jourdain :
match Unix.fork () with
0 -> Printf.printf "Marquise " ; flush stdout
| _ -> ignore (Unix.wait ()) ;
match Unix.fork () with
0 -> Printf.printf "vos beaux yeux me font " ; flush stdout
| _ -> ignore (Unix.wait ()) ;
Printf.printf "mourir d'amour\n" ;
flush stdout




Et, de fait, il dit :
Marquise vos beaux yeux me font mourir d'amour


Warning


fork est propre au systme Unix


Filiation, mort et funrailles d'un processus

La fonction wait n'a pas pour seule utilit l'attente de terminaison du fils utilise ci-dessus. Elle est galement charge de consacrer la mort du processus fils.

Lorsqu'un processus est cr, le systme ajoute une entre dans la table qui lui sert grer l'ensemble des processus. Lorsqu'il meurt, un processus ne disparat pas automatiquement de cette table. C'est au pre, par appel wait, d'assurer la suppression de ses fils de la table des processus. S'il ne le fait pas, le processus fils subsiste dans la table des processus. On parle alors de processus zombie.

Au dmarrage du systme, est lanc un premier processus appel init. Aprs initialisation d'un certain nombre de paramtres du systme, un rle essentiel de ce << grand anctre >> est de prendre en charge les processus orphelins et d'excuter le wait qui les rayera de la table des processus leur terminaison.

Attente de fin d'un processus donn

Il existe une variante de la fonction wait, nomme waitpid et porte sous Windows :

# Unix.waitpid ;;
- : Unix.wait_flag list -> int -> int * Unix.process_status = <fun>
Le premier argument spcifie les modalits d'attente et le second, quel processus ou quel groupe de processus il faut traiter.

la mort d'un processus, deux informations sont accessibles au pre comme rsultat de l'appel wait ou waitpid : le numro du processus termin et son statut de terminaison. Ce dernier est reprsent par une valeur de type Unix.process_status. Ce type a trois constructeurs dont chacun a un argument entier.
  • WEXITED n : le processus s'est termin normalement avec le code de retour n.
  • WSIGNALED n : le processus a t tu par le signal n.
  • WSTOPPED n : le processus a t stopp par le signal n.
La dernire valeur n'a de sens que pour la fonction waitpid qui peut, par son premier argument, se mettre l'coute de tels signaux. Nous dtaillons les signaux et leur traitement page ??.

Gestion de l'attente par un anctre

Pour viter de grer soi-mme la terminaison d'un processus fils, il est possible de faire traiter cette attente par un processus anctre. C'est l'astuce du << double fork >> qui permet un processus de n'avoir pas se soucier des funrailles de ses descendants en les confiant au processus init. En voici le principe : un processus P0 cre un processus P1 qui son tour cre un troisime processus P2 puis termine. Ainsi, P2 se retrouve orphelin et est adopt par init qui se chargera d'attendre sa terminaison. Le processus initial P0 peut alors excuter un wait sur P1 qui sera de courte dure. L'ide est de confier au petit fils le travail que l'on aurait confi au fils.

Le schma de mise en oeuvre est le suivant :

# match Unix.fork() with (* P0 cre P1 *)
0 -> if Unix.fork() = 0 then exit 0 ; (* P1 cre P2 et termine *)
Printf.printf "P2 fait son travail\n" ;
exit 0
| pid -> ignore (Unix.waitpid [] pid) ; (* P0 attend la mort de P1 *)
Printf.printf "P0 peut faire autre chose sans attendre\n" ;;
P2 fait son travail
P0 peut faire autre chose sans attendre
- : unit = ()
Nous verrons une utilisation de ce principe pour traiter les requtes envoyes un serveur au chapitre 20.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora202.html0000644000000000000000000000776607421273602014504 0ustar Applications ralises en Objective CAML Prcdent Index Suivant

Applications ralises en Objective CAML

Un certain nombre d'applications ont t ralises en Objective CAML. Nous ne parlerons que des applications << publiques >>, c'est--dire que l'on peut utiliser soit gratuitement soit en les achetant.

Comme les autres langages fonctionnels, Objective CAML est un bon langage d'implantation de compilateurs. Le bootstrap1 du compilateur ocaml en est un exemple probant. De mme de nombreuses extensions au langage ont t apportes, comme vu prcdemment pour la programmation parallle, mais aussi sur le typage comme O'Labl (dont une partie est en cours d'intgration en Objective CAML, voir annexe B) ou sur les units physiques. Les liens vers ces applications se retrouvent sur la << bosse du chameau >>.

La deuxime spcialit d'Objective CAML concerne les systmes d'aide la preuve. Le dveloppement majeur en ce domaine est le logiciel Coq qui accompagne quasiment depuis son origine l'volution de Caml. Historiquement, ML a t conu comme langage de manipulation du systme LCF (Logic for Computable Functions), avant de devenir indpendant de cette application. Il est donc naturel de le retrouver comme langage d'implantation d'un important systme d'aide la preuve.

Un troisime domaine d'application concerne le paralllisme (voir page ??) et la communication dont un bon exemple est le systme Ensemble.

Lien


http://www.cs.cornell.edu/Info/Projects/Ensemble/


Une liste, non exhaustive, d'applications significatives dveloppes en Objective CAML est maintenue sur le site Caml de l'Inria :

Lien


http://caml.inria.fr/users_programs-eng.html


Citons en particulier hevea qui est un traducteur LATEX vers HTML que nous avons utilis pour raliser la version HTML de ce livre qui se trouve sur le cdrom l'accompagnant.

Lien


http://pauillac.inria.fr/~maranget/hevea/


Quoique d'importance, les applications que nous venons de citer ne reprsentent pas ce, qu'en dbut de ce chapitre, nous avons baptis une << application phare >>. De mme, elles n'explorent pas un nouveau domaine de spcialit montrant la pertinence d'utilisation d'Objective CAML. Il n'est pas vident que cet exemple puisse tre issu du monde acadmique. Il est plus probable qu'il vienne du monde industriel, soit en liaison avec une normalisation du langage (voire sa spcification formelle), soit pour les besoins d'applications devant intgrer divers styles de programmation et de structuration du logiciel.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora039.html0000644000000000000000000006653107421273601014506 0ustar Mlange des styles Prcdent Index Suivant

Mlange des styles

Comme nous l'avons dj mentionn, un langage offrant la fois des traits fonctionnels et impratifs permet de pouvoir choisir le style le plus appropri pour chaque partie de l'implantation d'un algorithme. On peut tout aussi bien utiliser conjointement les deux aspects du langage dans une mme fonction. C'est ce que nous illustrons dans ce paragraphe.

Fermetures et effets de bord

Il est d'usage, quand une fonction effectue un effet de bord, de la traiter comme une procdure et de retourner la valeur (), de type unit. Nanmoins, dans certains cas, il peut tre utile d'effectuer l'effet de bord l'intrieur d'une telle fonction et de retourner une valeur pertinente. Nous avons dj utilis ce mlange des styles dans la fonction permute_pivot du tri rapide.

L'exemple suivant est un gnrateur de symboles qui permet d'engendrer un nouveau symbole chaque appel de la fonction. On utilise simplement un compteur qui est incrment chaque appel.

# let c = ref 0;;
val c : int ref = {contents=0}
# let reset_symb = function () -> c:=0 ;;
val reset_symb : unit -> unit = <fun>
# let new_symb = function s -> c:=!c+1 ; s^(string_of_int !c) ;;
val new_symb : string -> string = <fun>
# new_symb "VAR" ;;
- : string = "VAR1"
# new_symb "VAR" ;;
- : string = "VAR2"
# reset_symb () ;;
- : unit = ()
# new_symb "WAR" ;;
- : string = "WAR1"
# new_symb "WAR" ;;
- : string = "WAR2"


On peut cacher au reste du programme la rfrence c en crivant :

# let (reset_s , new_s) =
let c = ref 0
in let f1 () = c := 0
and f2 s = c := !c+1 ; s^(string_of_int !c)
in (f1,f2) ;;
val reset_s : unit -> unit = <fun>
val new_s : string -> string = <fun>


Cette dclaration cre un couple de fonctions qui partagent toutes deux la variable locale ( la dclaration) c. L'utilisation de ces deux fonctions produit la mme excution que les dfinitions prcdentes.

# new_s "VAR";;
- : string = "VAR1"
# new_s "VAR";;
- : string = "VAR2"
# reset_s();;
- : unit = ()
# new_s "WAR";;
- : string = "WAR1"
# new_s "WAR";;
- : string = "WAR2"


Cet exemple permet d'illustrer la reprsentation des fermetures. Une fermeture peut tre considre comme un couple contenant d'une part le code (c'est dire la partie function) et d'autre part un environnement local, contenant les valeurs des variables libres de la fermeture. La figure 4.1 montre la reprsentation mmoire des fermetures reset_s et new_s.




Figure 4.1 : Reprsentation mmoire de fermetures


Ces deux fermetures partagent le mme environnement (la valeur de c). Quand l'une modifie la rfrence c, elle modifie le contenu d'une zone mmoire partage par l'autre fermeture.

Modifications physiques et exceptions

Les exceptions permettent de rcuprer des situations o le calcul ne peut pas se poursuivre. Dans ces cas, un rcuprateur d'exceptions permet de continuer le calcul sachant qu'une des branches a chou. Le problme avec les effets de bord provient alors de l'tat des donnes modifiables quand l'exception se dclenche. On ne peut pas garantir cet tat s'il y a eu des modifications physiques dans la branche de calcul qui choue.

Dfinissons la fonction d'incrment (++) fonctionnant l'instar de l'oprateur C :

# let (++) x = x:=!x+1; x;;
val ++ : int ref -> int ref = <fun>


L'exemple suivant montre un petit calcul o une division par zro intervient en concomitance avec un effet de bord :

# let x = ref 2;;
val x : int ref = {contents=2}
(* 1 *)
# !((++) x) * (1/0) ;;
Uncaught exception: Division_by_zero
# x;;
- : int ref = {contents=2}
(* 2 *)
# (1/0) * !((++) x) ;;
Uncaught exception: Division_by_zero
# x;;
- : int ref = {contents=3}
La variable x n'est pas modifie lors du calcul de l'expression en (*1*), par contre elle l'est pour l'expression en (*2*). Sauf savoir sauver les valeurs initiales, les constructions try .. with .. ne doivent pas (dans la partie with ..) tenir compte des variables modifiables impliques dans l'expression ayant dclench une exception.

Structures de donnes fonctionnelles modifiables

Une manire de voir qu'en programmation fonctionnelle un programme, au sens d'une expression fonctionnelle, est aussi une donne manipulable, est d'crire les listes d'association sous forme d'expressions fonctionnelles. On peut en effet voir, et implanter, une liste d'association ('a * 'b) list comme une fonction partielle de 'a (l'ensemble des cls) dans 'b (l'ensemble des valeurs associes). Autrement dit, une fonction de type 'a -> 'b.

La liste vide est la fonction non dfinie que l'on simule par le dclenchement d'une exception :

# let nil_assoc = function x -> raise Not_found ;;
val nil_assoc : 'a -> 'b = <fun>


On peut alors crire la fonction add_assoc qui ajoute un lment une liste, c'est--dire qui tend la fonction pour une nouvelle entre :

# let add_assoc (k,v) l = function x -> if x = k then v else l x ;;
val add_assoc : 'a * 'b -> ('a -> 'b) -> 'a -> 'b = <fun>
# let l = add_assoc ('1', 1) (add_assoc ('2', 2) nil_assoc) ;;
val l : char -> int = <fun>
# l '2' ;;
- : int = 2
# l 'x' ;;
Uncaught exception: Not_found


On peut rcrire la fonction mem_assoc :

# let mem_assoc k l = try (l k) ; true with Not_found -> false ;;
val mem_assoc : 'a -> ('a -> 'b) -> bool = <fun>
# mem_assoc '2' l ;;
- : bool = true
# mem_assoc 'x' l ;;
- : bool = false


En revanche, il n'est pas immdiat d'crire une fonction retirant un lment de la liste car on n'a plus accs aux valeurs captures par les fermetures. Pour cela on masquera l'ancienne valeur par le dclenchement de l'exception Not_found.

# let rem_assoc k l = function x -> if x=k then raise Not_found else l x ;;
val rem_assoc : 'a -> ('a -> 'b) -> 'a -> 'b = <fun>
# let l = rem_assoc '2' l ;;
val l : char -> int = <fun>
# l '2' ;;
Uncaught exception: Not_found


On peut, bien videment, crer des rfrences et travailler par effet de bord sur de telles valeurs. Il faut cependant prendre quelques prcautions.

# let add_assoc_bis (k,v) l = l := (function x -> if x=k then v else !l x) ;;
val add_assoc_bis : 'a * 'b -> ('a -> 'b) ref -> unit = <fun>


On obtient pour l une fonction qui pointera sur elle-mme et donc bouclera. Ce fcheux effet de bord est d au fait que le drfrencement !l est dans la porte de la fermeture function x ->. La valeur de !l n'est pas value la compilation, mais l'excution. ce moment l, l pointe sur la valeur modifie par add_assoc. Il faut donc amender notre dfinition en utilisant la fermeture cre par notre dfinition primitive de add_assoc :

# let add_assoc_bis (k, v) l = l := add_assoc (k, v) !l ;;
val add_assoc_bis : 'a * 'b -> ('a -> 'b) ref -> unit = <fun>
# let l = ref nil_assoc ;;
val l : ('_a -> '_b) ref = {contents=<fun>}
# add_assoc_bis ('1',1) l ;;
- : unit = ()
# add_assoc_bis ('2',2) l ;;
- : unit = ()
# !l '1' ;;
- : int = 1
# !l 'x' ;;
Uncaught exception: Not_found


Structures paresseuses modifiables

L'utilisation de traits impratifs combins avec un langage fonctionnel procure de bons outils d'implantation de langages informatiques. Nous proposons dans ce paragraphe d'illustrer ce trait par l'implantation de structures de donnes valuation retarde. Une telle structure de donnes n'est pas compltement value. Son valuation avance selon l'usage qui en est fait.

Pour simuler l'valuation retarde, souvent utilise dans les langages fonctionnels purs, on utilise des valeurs fonctionnelles, possiblement modifiables. L'intrt de manipuler des donnes non compltement values est au moins double : premirement, ne calculer que ce qui est effectivement utile au calcul ; deuximement, travailler sur des donnes potentiellement infinies.

On dfinit le type vm qui permet d'obtenir soit une valeur dj calcule (constructeur Imm), soit une valeur calculer (constructeur Ret) :

# type 'a v =
Imm of 'a
| Ret of (unit -> 'a);;
# type 'a vm = {mutable c : 'a v };;


Le retardement du calcul est obtenu par encapsulation dans une fermeture. La fonction d'valuation d'une telle valeur doit soit retourner une valeur dj calcule, soit valuer puis stocker une valeur non encore calcule.

# let eval e = match e.c with
Imm a -> a
| Ret f -> let u = f () in e.c <- Imm u ; u ;;
val eval : 'a vm -> 'a = <fun>


Les oprations de retardement de l'valuation et d'activation de cette valuation s'appellent aussi geler et dgeler une valeur.

On peut ainsi crire la structure conditionnelle sous forme de fonction :

# let si_ret c e1 e2 =
if eval c then eval e1 else eval e2;;
val si_ret : bool vm -> 'a vm -> 'a vm -> 'a = <fun>


Voici comment on l'utilise pour une fonction rcursive comme la factorielle :

# let rec facr n =
si_ret {c=Ret(fun () -> n = 0)}
{c=Ret(fun () -> 1)}
{c=Ret(fun () -> n*(facr(n-1)))};;
val facr : int -> int = <fun>
# facr 5;;
- : int = 120


Notons que la forme classique du if ne peut pas tre crite sous forme de fonction. En effet si l'on dfinit une fonction si de la manire suivante :

# let si c e1 e2 = if c then e1 else e2;;
val si : bool -> 'a -> 'a -> 'a = <fun>


Alors les trois arguments de si sont valus lors de leur passage. Ce qui a pour consquence que la fonction fact boucle car l'appel rcursif fact(n-1) est valu dans tous les cas, y compris dans le cas o n vaut 0.

# let rec fact n = si (n=0) 1 (n*fact(n-1)) ;;
val fact : int -> int = <fun>
# fact 5 ;;
Stack overflow during evaluation (looping recursion?).


Module Lazy

La difficult d'implantation des valeurs geles provient de la construction d'expressions non values dans le contexte de l'valuation immdiate d'Objective CAML. Notre tentative de redfinir la conditionnelle l'a montr. Plus gnralement, il n'est pas possible d'crire une fonction qui gle une valeur en construisant un objet de type vm :

# let gele e = { c = Ret (fun () -> e) };;
val gele : 'a -> 'a vm = <fun>
Cette fonction suit la stratgie d'valuation d'Objective CAML et donc value l'expression e passe en argument avant de construire la fermeture fun () -> e. On le voit sur l'exemple suivant :

# gele (print_string "trace"; print_newline(); 4*5);;
trace
- : int vm = {c=Ret <fun>}


Pour cette raison, la forme syntaxique suivante a t introduite.

Syntaxe


lazy expr


Warning


Ce trait fait partie des extensions du langage et pourra voluer dans les prochaines versions.


Le mot cl lazy appliqu une expression construit une valeur d'un type particulier dclar dans le module Lazy :

# let x = lazy (print_string "Hello"; 3*4) ;;
val x : int Lazy.status ref = {contents=Lazy.Delayed <fun>}


L'expression (print_string "Hello") n'a pas t value puisqu'aucun affichage n'a eu lieu. On peut forcer cette valuation par appel la fonction force du module Lazy :

# Lazy.force x ;;
Hello- : int = 12
On remarque qu'alors la valeur de x a chang :

# x ;;
- : int Lazy.t = {contents=Lazy.Value 12}
C'est devenu la valeur de l'expression gele, ici : 12.

Un nouvel appel la fonction force se contente alors de retourner la valeur calcule :

# Lazy.force x ;;
- : int = 12
La chane "Hello" n'est plus affiche.

Structures de donnes << infinies >>

Le deuxime intrt de l'valuation retarde est de construire des structures de donnes potentiellement infinies tel l'ensemble des entiers naturels. Comme il risquerait d'tre long de tous les calculer, l'ide ici est de ne calculer qu'une tte et de savoir passer l'lment suivant.

On dfinit une structure gnrique 'a enum qui permettra d'numrer les lments d'un ensemble.

# type 'a enum = { mutable i : 'a; f :'a -> 'a } ;;
type 'a enum = { mutable i: 'a; f: 'a -> 'a }
# let next e = let x = e.i in e.i <- (e.f e.i) ; x ;;
val next : 'a enum -> 'a = <fun>


On peut ainsi obtenir l'ensemble des entiers naturels par instanciation des champs de cette structure :

# let nat = { i=0; f=fun x -> x + 1 };;
val nat : int enum = {i=0; f=<fun>}
# next nat;;
- : int = 0
# next nat;;
- : int = 1
# next nat;;
- : int = 2
Un autre exemple est l'ensemble des lments de la suite de Fibonnacci dont la dfinition est :


u0 = 1
u1 = 1
un+2 = un + un+1
La fonction de calcul du successeur doit tenir compte de la valeur courante (un-1), mais aussi de la prcdente (un-2). C'est cela que sert l'tat c de la fermeture.

# let fib = let fx = let c = ref 0 in fun v -> let r = !c + v in c:=v ; r
in { i=1 ; f=fx } ;;
val fib : int enum = {i=1; f=<fun>}
# for i=0 to 10 do print_int (next fib); print_string " " done ;;
1 1 2 3 5 8 13 21 34 55 89 - : unit = ()



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora147.html0000644000000000000000000004621307421273602014502 0ustar Autres lments de l'extension objet Prcdent Index Suivant

Autres lments de l'extension objet

On prsente dans cette section la dclaration de types <<objet>> et surtout les dclarations locales dans les classes. Ces dernires peuvent construire des constructeurs qui possdent un environnement local, considr comme un ensemble de variables de classe.

Interface

Les interfaces de classes sont en gnral infres par le systme d'infrence de types, mais elles peuvent aussi tre dfinies par une dclaration de type. Seules les mthodes publiques apparaissent dans ce type.

Syntaxe


class type nom =
  object
    :
    val nomi : typei
    :
    method nomj : typej
    :
  end

On peut ainsi dfinir l'interface de la classe point :

# class type interf_point =
object
method get_x : int
method get_y : int
method moveto : (int * int ) -> unit
method rmoveto : (int * int ) -> unit
method to_string : unit -> string
method distance : unit -> float
end ;;


L'intrt de cette dclaration est de pouvoir utiliser le type dfini pour une contrainte de type.

# let seg_length (p1:interf_point) (p2:interf_point) =
let x = float_of_int (p2#get_x - p1#get_x)
and y = float_of_int (p2#get_y - p1#get_y) in
sqrt ((x*.x) +. (y*.y)) ;;
val seg_length : interf_point -> interf_point -> float = <fun>


Les interfaces ne peuvent masquer que les champs de variable d'instance et les mthodes prives. Elles ne peuvent en aucun cas masquer des mthodes abstraites ou des mthodes publiques.

C'est une limitation de leur usage comme le montre l'exemple suivant :

# let p = ( new point_m1 (2,3) : interf_point);;
Characters 11-29:
This expression has type
point_m1 =
< distance : unit -> float; get_x : int; get_y : int;
moveto : int * int -> unit; rmoveto : int * int -> unit;
to_string : unit -> string; undo : unit -> unit >
but is here used with type
interf_point =
< distance : unit -> float; get_x : int; get_y : int;
moveto : int * int -> unit; rmoveto : int * int -> unit;
to_string : unit -> string >
Only the first object type has a method undo


Nanmoins, les interfaces autorisent la relation d'hritage entre interfaces. Un intrt de la dfinition d'interfaces est leur utilisation conjointe avec le mcanisme de modules. Ainsi il est possible de construire la signature d'un module, utilisant des types objets, en donnant uniquement la description des interfaces des classes.

Dclarations locales dans les classes

Une dclaration de classe produit un type et un constructeur. Nous avons jusqu' maintenant, pour la clart de l'expos, prsent ces constructeurs comme des fonctions sans environnement. En fait il est possible d'une part de dfinir des constructeurs qui n'ont pas besoin de valeurs initiales pour crer une instance. C'est--dire qui ne sont plus fonctionnels. D'autre part, il est possible d'effectuer des dclarations locales dans la classe. Ces valeurs peuvent tre captures dans le constructeur, permettant ces valeurs d'tre partages par toutes les instances de la classe. Ces valeurs peuvent tre considres comme des variables de classe.

Constructeurs constants

Une dclaration de classe n'utilise pas forcment des valeurs initiales qui seront passes au constructeur. Par exemple la classe suivante :

# class exemple1 =
object
method print () = ()
end ;;
class exemple1 : object method print : unit -> unit end
# let p = new exemple1 ;;
val p : exemple1 = <obj>
Le constructeur d'instances est constant. L'allocation n'a pas besoin de valeurs initiales pour les variables d'instance. En rgle gnrale, il est prfrable d'utiliser une valeur initiale comme () pour conserver le caractre fonctionnel du constructeur.

Dclarations locales pour les constructeurs

Une dclaration de classe peut s'crire en utilisant directement l'abstraction.

# class example2 =
fun a ->
object
val mutable r = a
method get_r = r
method plus x = r <- r + x
end;;
class example2 :
int ->
object val mutable r : int method get_r : int method plus : int -> unit end


On s'aperoit mieux du caractre fonctionnel du constructeur. Le constructeur est donc une fermeture. Celle-ci peut possder un environnement qui lie des variables libres un environnement de dclarations. La syntaxe des dclarations de classes autorise les dclarations locales cette expression fonctionnelle.

Variables de classes

On appelle variables de classe, les dclarations connues au niveau de la classe, donc partages par toutes les instances de cette classe. Le plus souvent ces variables de classe peuvent tre utilises en dehors de toute cration d'instances. En Objective CAML, on peut faire partager des valeurs, en particulier modifiables, par toutes les instances d'une classe grce au caractre fonctionnel du constructeur qui possde alors un environnement non vide.

Nous illustrons cette possibilit par l'exemple suivant qui permet de tenir jour le nombre d'instances d'une classe. Pour cela on dfinit une classe abstraite paramtre 'a om.

# class virtual ['a] om =
object
method finalize () = ()
method virtual destroy : unit -> unit
method virtual to_string : unit -> string
method virtual all : 'a list
end;;


Puis on dclare la classe 'a lo dont la fonction de construction contient les dclarations locales de n pour associer un numro unique chaque instance et de l qui contient la liste des couples (numro, instance) de chaque instance encore active.

# class ['a] lo =
let l = ref []
and n = ref 0 in
fun s ->
object(self:'b )
inherit ['a] om
val mutable num = 0
val nom = s
method to_string () = s
method print () = print_string s
method print_all () =
List.iter (function (a,b) ->
Printf.printf "(%d,%s) " a (b#to_string())) !l
method destroy () = self#finalize();
l:= List.filter (function (a,b) -> a <> num) !l; ()
method all = List.map snd !l
initializer incr n; num <- !n; l:= (num, (self :> 'a om) ) :: !l ; ()
end;;
class ['a] lo :
string ->
object
constraint 'a = 'a om
val nom : string
val mutable num : int
method all : 'a list
method destroy : unit -> unit
method finalize : unit -> unit
method print : unit -> unit
method print_all : unit -> unit
method to_string : unit -> string
end


chaque cration d'une instance de la classe lo, l'initialisateur incrmente la rfrence n et ajoute le couple (numro, self) la liste l. Les mthodes print et print_all affichent respectivement l'instance rceptrice et toutes les instances de la classe contenues dans l.

# let m1 = new lo "dbut";;
val m1 : ('a om as 'a) lo = <obj>
# let m2 = new lo "entre";;
val m2 : ('a om as 'a) lo = <obj>
# let m3 = new lo "fin";;
val m3 : ('a om as 'a) lo = <obj>
# m2#print_all();;
(3,fin) (2,entre) (1,dbut) - : unit = ()
# m2#all;;
- : ('a om as 'a) list = [<obj>; <obj>; <obj>]


La mthode destroy enlve une instance de la liste des instances cres et appelle la mthode finalize pour effectuer une dernire action sur cette instance avant sa disparition de la liste. La mthode all retourne toutes les instances d'une classe cres en utilisant new.

# m2#destroy();;
- : unit = ()
# m1#print_all();;
(3,fin) (1,dbut) - : unit = ()
# m3#all;;
- : ('a om as 'a) list = [<obj>; <obj>]


Il est noter que les instances des sous-classes sont aussi conserves dans cette liste. Rien n'empche d'utiliser la mme technique en particularisant certaines de ces sous-classes. Par contre les instances obtenues par copie (Oo.copy ou {< >}) ne sont pas concernes.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora190.html0000644000000000000000000016443207421273602014504 0ustar Exercices Prcdent Index Suivant

Exercices

Les exercices proposs permettent de mettre en oeuvre diffrents types d'applications rparties. Le premier offre un nouveau service rseau permettant une mise l'heure des machines clientes. Le deuxime exercice montre comment on peut utiliser les ressources de diffrentes machines pour distribuer un calcul.

Service : horloge

Cet exercice consiste implanter un service << horloge >> qui donne l'heure tout client qui le dsire. L'ide est d'avoir ainsi une machine de rfrence pour mettre l'heure les diffrentes machines d'un rseau.

  1. Dfinir un protocole pour la transmission d'une date contenant la description du jour, mois, heure, minute, seconde.

    # type horloge = { jour:int; mois:int; heure:int; minute:int; seconde:int } ;;
    type horloge =
    { jour: int;
    mois: int;
    heure: int;
    minute: int;
    seconde: int }
    # let encode date =
    let str = String.create 5 in
    str.[0] <- char_of_int date.jour ;
    str.[1] <- char_of_int date.mois ;
    str.[2] <- char_of_int date.heure ;
    str.[3] <- char_of_int date.minute ;
    str.[4] <- char_of_int date.seconde ;
    str ;;
    val encode : horloge -> string = <fun>
    # let decode str =
    { jour = int_of_char str.[0] ;
    mois = int_of_char str.[1] ;
    heure = int_of_char str.[2] ;
    minute = int_of_char str.[3] ;
    seconde = int_of_char str.[4] } ;;
    val decode : string -> horloge = <fun>


  2. crire la fonction ou la classe de service en rutilisant un des serveurs gnriques prsents. Ce service envoie les informations de la date chaque connexion accepte, puis referme la socket. Nous utilisons la fonction main_serveur(20) :

    # main_serveur ;;
    - : (in_channel -> out_channel -> 'a) -> unit = <fun>

    # let horloge_service ic oc =
    try
    let date = Unix.localtime (Unix.time ()) in
    let date_horloge =
    { jour = date.Unix.tm_mday ;
    mois = date.Unix.tm_mon + 1 ;
    heure = date.Unix.tm_hour ;
    minute = date.Unix.tm_min ;
    seconde = date.Unix.tm_sec } in
    output_string oc (encode date_horloge) ;
    flush oc
    with exn -> print_endline "Fin du traitement"; flush stdout

    let main_horloge () = main_serveur horloge_service ;;
    val horloge_service : 'a -> out_channel -> unit = <fun>
    val main_horloge : unit -> unit = <fun>


  3. crire un client qui effectue une mise jour de sa date toutes les heures. Nous utilisons la fonction main_client20 :

    # main_client ;;
    - : (in_channel -> out_channel -> 'a) -> unit = <fun>

    # let client_horloge ic oc =
    let date = ref { jour=0; mois=0; heure=0; minute=0; seconde=0 } in
    try
    while true do
    let buffer = "xxxxx" in
    ignore (input ic buffer 0 5) ;
    date := decode buffer ;
    print_endline "BIP";
    flush stdout ;
    Unix.sleep 3600
    done
    with
    exn -> shutdown_connection ic ; raise exn ;;
    val client_horloge : in_channel -> 'a -> unit = <fun>

    # let main_horloge () = main_client client_horloge ;;
    val main_horloge : unit -> unit = <fun>


  4. Comment tenir compte du dcalage de temps d la requte? On peut mesurer le temps coul entre la demande de connexion et la rception de la rponse. On suppose que ce dlai est le double de celui mis par la rponse et on corrige le rsultat en consquence.

Une machine caf en rseau

On veut raliser un petit service simulant un distributeur de boissons. La description sommaire du protocole d'change entre un client et le service est la suivante :
  • sa connexion, le client reoit la liste des boissons disponibles ;
  • il envoie alors au serveur le nom de la boisson de son choix ;
  • le serveur lui retourne le prix payer ;
  • le client envoie ce prix, ou toute autre somme ;
  • le serveur rpond par le nom de la boisson choisie et le montant de la monnaie rendue.
Le serveur peut aussi rpondre par un message d'erreur s'il n'a pas compris une requte, n'a plus assez de monnaie, etc. Une requte d'un client ne contient toujours qu'un seul composant.

Les changes se font sous forme de chanes de caractres. Les diffrents composants des messages sont spars par deux points et toute chane se termine par :$\n.

La fonction de service communique avec la machine caf en utilisant une file d'attente pour passer les commandes et une table de hachage pour rcuprer les boissons prpares et la monnaie.

Cet exercice mettra en oeuvre la communication via des sockets, des processus lgers avec un peu de concurrence et des objets.

  1. Rcrire la fonction establish_server en utilisant les primitives de ThreadUnix. On reprend les fonctions hostaddr et my_inet_addr de ce chapitre.

    # val hostaddr : string -> Unix.inet_addr = <fun>
    val my_inet_addr : unit -> Unix.inet_addr = <fun>

    let establish_server f saddr =
    let sock = ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
    Unix.bind sock saddr;
    Unix.listen sock 5;
    while true do
    let (s,_) = ThreadUnix.accept sock in
    let ic = Unix.in_channel_of_descr s
    and oc = Unix.out_channel_of_descr s in
    ignore (Thread.create (f ic) oc)
    done;;
    val establish_server :
    (in_channel -> out_channel -> 'a) -> Unix.sockaddr -> unit = <fun>


  2. crire deux fonctions, get_request et send_answer. La premire lit et dchiffre une requte et la seconde formate et envoie une rponse partir d'une liste de chanes de caractres.

    # let read fd =
    let buf = String.create 1024 in
    let n = ThreadUnix.read fd buf 0 1024 in
    let s = String.sub buf 0 n in
    s ;;
    val read : Unix.file_descr -> string = <fun>

    # let get_request fd =
    let s = read fd in
    match Str.split (Str.regexp "[:]") (String.sub s 0 (String.index s '$')) with
    [s1] -> s1
    | _ -> failwith "BadRequestFormat" ;;
    val get_request : Unix.file_descr -> string = <fun>
    On redfinit des fonctions d'entres-sorties utilisant celles de Threadunix puis on les utilise pour get_request et send_answer. Comme elle revient souvent, on dfinit galement une fonction send_cancel qui envoie un message d'erreur.

    # let write fd s =
    let leng = (String.length s) in
    let n = ThreadUnix.write fd s 0 leng in
    if n<leng then failwith "I/O error" ;;
    val write : Unix.file_descr -> string -> unit = <fun>

    # let send_answer fd ss =
    let rec mk_answer = function
    [] -> ":$\n"
    | [s] -> s ^ ":$\n"
    | s::ss -> s ^ ":" ^ (mk_answer ss)
    in
    write fd (mk_answer ss) ;;
    val send_answer : Unix.file_descr -> string list -> unit = <fun>

    # let send_cancel = let s = "cancel:$\n" in function fd -> write fd s ;;
    val send_cancel : Unix.file_descr -> unit = <fun>


  3. crire une classe cmd_fifo pour grer les commandes en attente. On attribuera un numro unique chaque nouvelle commande. Implanter pour cela une classe num_cmd_gen.

    # class cmd_fifo =
    object(self)
    val n = new num_cmd_gen
    val f = (Queue.create (): (int*int*int) Queue.t)
    val m = Mutex.create ()
    val c = Condition.create ()

    method add num_drink paid =
    let num_cmd = n#get() in
    Mutex.lock m ;
    Queue.add (num_cmd, num_drink, paid) f ;
    Mutex.unlock m ;
    Condition.signal c ;
    num_cmd

    method wait () =
    Mutex.lock m ;
    Condition.wait c m ;
    let cmd = Queue.take f in
    Mutex.unlock m ;
    cmd
    end ;;
    class cmd_fifo :
    object
    val c : Condition.t
    val f : (int * int * int) Queue.t
    val m : Mutex.t
    val n : num_cmd_gen
    method add : int -> int -> int
    method wait : unit -> int * int * int
    end

    # class num_cmd_gen =
    object
    val mutable x = 0
    val m = Mutex.create ()

    method get() =
    Mutex.lock m ;
    x <- x+1 ;
    let r = x in
    Mutex.unlock m ;
    r
    end ;;
    class num_cmd_gen :
    object val m : Mutex.t val mutable x : int method get : unit -> int end


  4. crire une classe ready_table pour stocker les boissons prpares par la machine.

    # class ready_table size =
    object
    val t = (Hashtbl.create size : (int, (string * int)) Hashtbl.t)
    val m = Mutex.create ()
    val c = Condition.create ()

    method add num_cmd num_drink change =
    Mutex.lock m ;
    Hashtbl.add t num_cmd (num_drink, change) ;
    Mutex.unlock m ;
    Condition.broadcast c

    method wait num_cmd =
    Mutex.lock m;
    while not(Hashtbl.mem t num_cmd) do Condition.wait c m done ;
    let cmd = Hashtbl.find t num_cmd in
    Hashtbl.remove t num_cmd ;
    Mutex.unlock m ;
    cmd
    end ;;
    class ready_table :
    int ->
    object
    val c : Condition.t
    val m : Mutex.t
    val t : (int, string * int) Hashtbl.t
    method add : int -> string -> int -> unit
    method wait : int -> string * int
    end


  5. crire la classe machine qui modlise la machine caf. Elle possdera une mthode run bouclant sur la squence : attendre une commande puis la prparer, tant qu'il reste des boissons disponibles. On dfinira un type drink_descr indiquant, pour chaque boisson : son nom, la quantit en stock, la quantit restant aprs satisfaction des commandes et le prix. On utilisera galement une fonction auxiliaire array_index qui donne l'indice du premier lment d'un tableau satisfaisant un critre pass en paramtre.

    # class machine (f_cmd0:cmd_fifo) (t_ready0:ready_table) =
    object(self)
    val f_cmd = f_cmd0
    val t_ready = t_ready0
    val mutable nb_available_drinks = 0
    val drinks_table =
    [| { name="cafe"; real_stock=10; virtual_stock=10; price=300 };
    { name="the"; real_stock=5; virtual_stock=5; price=250 };
    { name="chocolat"; real_stock=10; virtual_stock=10; price=250 } |]
    val mutable cash = 0
    val m = Mutex.create()

    initializer nb_available_drinks <- Array.length drinks_table

    method get_drink_price i = drinks_table.(i).price
    method get_drink_index s = array_index drinks_table (fun d -> d.name=s)

    method get_menu () =
    let f d ns = if d.real_stock > 0 then d.name::ns else ns in
    Array.fold_right f drinks_table []

    method cancel_cmd num_drink =
    let drink = drinks_table.(num_drink) in
    drink.virtual_stock <- drink.virtual_stock+1

    method set_cmd num_drink paid = f_cmd#add num_drink paid

    method wait_cmd num_cmd = t_ready#wait num_cmd

    method deliver_drink num_drink =
    let drink = drinks_table.(num_drink) in
    drink.real_stock <- drink.real_stock-1 ;
    if drink.real_stock = 0 then nb_available_drinks <- nb_available_drinks-1

    method run() =
    while nb_available_drinks>0 do
    let (num_cmd, num_drink, amount) = f_cmd#wait () in
    let drink = drinks_table.(num_drink) in
    let change = amount - drink.price in
    Mutex.lock m ;
    if (drink.virtual_stock > 0) & (cash >= change)
    then
    begin
    drink.virtual_stock <- drink.virtual_stock-1 ;
    cash <- cash + drink.price ;
    t_ready#add num_cmd drink.name change
    end
    else t_ready#add num_cmd "cancel" 0 ;
    Mutex.unlock m
    done
    end ;;
    class machine :
    cmd_fifo ->
    ready_table ->
    object
    val mutable cash : int
    val drinks_table : drink_descr array
    val f_cmd : cmd_fifo
    val m : Mutex.t
    val mutable nb_available_drinks : int
    val t_ready : ready_table
    method cancel_cmd : int -> unit
    method deliver_drink : int -> unit
    method get_drink_index : string -> int
    method get_drink_price : int -> int
    method get_menu : unit -> string list
    method run : unit -> unit
    method set_cmd : int -> int -> int
    method wait_cmd : int -> string * int
    end

    # type drink_descr =
    { name : string;
    mutable real_stock : int;
    mutable virtual_stock : int;
    price : int } ;;

    # let array_index t f =
    let i = ref 0 in
    let n = Array.length t in
    while (!i < n) & (not (f t.(!i))) do incr i done ;
    if !i=n then raise Not_found else !i ;;
    val array_index : 'a array -> ('a -> bool) -> int = <fun>


  6. crire une fonction de service waiter.

    # let waiter mach ic oc =
    let f_in = Unix.descr_of_in_channel ic in
    let f_out = Unix.descr_of_out_channel oc in
    (try
    send_answer f_out (mach#get_menu()) ;
    let drink_name = get_request f_in in
    let num_drink = mach#get_drink_index drink_name in
    let drink_price = mach#get_drink_price num_drink in
    send_answer f_out [string_of_int drink_price] ;
    let paid = int_of_string (get_request f_in) in
    if paid < drink_price then failwith"NotEnough" ;
    let num_cmd = mach#set_cmd num_drink paid in
    let drink_name, change = mach#wait_cmd num_cmd in
    mach#deliver_drink num_drink;
    send_answer f_out [drink_name; (string_of_int change)]
    with
    Not_found -> send_cancel f_out
    | Failure("int_of_string") -> send_cancel f_out
    | Failure("I/O error") -> send_cancel f_out
    | Failure("NotEnough") -> send_cancel f_out
    | Failure("BadRequestFormat") -> send_cancel f_out
    );
    close_in ic ;
    flush oc ;
    close_out oc ;
    Thread.exit () ;;
    val waiter :
    < deliver_drink : 'a -> 'b; get_drink_index : string -> 'a;
    get_drink_price : 'a -> int; get_menu : unit -> string list;
    set_cmd : 'a -> int -> 'c; wait_cmd : 'c -> string * int; .. > ->
    in_channel -> out_channel -> unit = <fun>


  7. crire une fonction principale main qui rcupre le numro de port du service sur la ligne de commande et procde aux diverses initialisations. En particulier, la machine caf est excute par un processus.

    # let main () =
    if Array.length Sys.argv < 2
    then
    begin
    Printf.eprintf "usage : %s port\n" Sys.argv.(0) ;
    exit 1
    end
    else
    begin
    let port = int_of_string Sys.argv.(1) in
    let f_cmd = new cmd_fifo in
    let t_ready = new ready_table in
    let mach = new machine f_cmd (t_ready 13) in
    ignore (Thread.create mach#run ()) ;
    establish_server (waiter mach) (Unix.ADDR_INET (my_inet_addr (), port))
    end ;;
    val main : unit -> unit = <fun>

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora061.html0000644000000000000000000000133107421273603014466 0ustar Notes
1
acronyme de << Beginner's All purpose Symbolic Instruction Code >>.
ocaml-book-1.0/fr/html/book-ora045.html0000644000000000000000000000467707421273601014506 0ustar Introduction Prcdent Index Suivant

Introduction

Ce chapitre prsente la bibliothque Graphics fournie avec la distribution du langage Objective CAML. Cette bibliothque fonctionne de manire identique sur les principales interfaces graphiques des systmes d'exploitation les plus courants : Windows, MacOS, Unix avec X-Windows. Graphics permet la ralisation de tracs graphiques, pouvant contenir du texte et des images, et gre des vnements de base comme le clic de la souris ou l'appui d'une touche du clavier.

Le modle de programmation pour les tracs graphiques est le << modle du peintre >> : la dernire couche de peinture efface la prcdente. C'est un modle impratif dans le sens o la fentre graphique est un tableau de points que chaque primitive graphique modifie physiquement. Les interactions avec la souris et le clavier seront le cadre de la programmation par vnements : la fonction principale du programme est une boucle sans fin en attente d'une action de l'utilisateur. La venue d'un vnement entrane l'excution d'un traitement particulier, puis le retour la boucle principale en attente d'un prochain vnement.

Bien que la bibliothque Graphics soit trs simple, elle est suffisante d'une part pour introduire les concepts de base des interfaces graphiques, et d'autre part, elle contient les lments de base pour l'laboration d'interfaces graphiques plus riches et plus faciles manipuler par le programmeur.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora027.gif0000644000000000000000000000366207073350152014300 0ustar GIF89aAUUU999rrr!,Ah0I8ͻdielp,Wx|oH,*Fl:33Zجzjp[! %sD0O~fQq!xPRt?|, ϙ , P@ +2f6.JCzEFKD[$?^0У_g꿮Ȳ{@\AS5]Cpi K1$@j.72WAe!܀X$Iy+t9*vA6Hc3P\jrH4cbM W칍5QJXLTQAcO#Mrh(5xҢhU:x"g@ +K+t.?pfS 4Я6CV>Y륂^eYzTՖ>i)+d.BdE ujjQeQϧ]D׵K+TFBY2oK/(=EhWYRgYpU 5ե VcJ4VXW5T?9 k56s6;_ݏh%XDn};bXܬmrwq &ddq} 7{/y8/xގxMN9}}%5Knuv-t[~|h=ut^嶏~x;xo'7G/Wog_xC/ v>鋴~-$C!?C0$gt:A:8PIN>RB X9 _ @@%LLR?HpY5x!p BeiPT^ >>(FPB1rR jx5-m%sd1֠T >M z0k)xj7~uRMcpjdJXUT>5D[sTȊ+|TL#Ҝ_TQZ*ڲ5<2eb諒z `ڵ }iK}b+ՋU_N$ aHM5z1 ՜ֈqlzf͞9MmV^Z ,PXY6J~=$Ox-wY(D+s9n47ֽnmplxK*UMy;W2}oS2wDx&Vch iL^ƔōT$1# SX 24$Wq;ocaml-book-1.0/fr/html/book-ora122.html0000644000000000000000000000705607421273602014475 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

Pour bien comprendre le passage d'arguments en C et la reprsentation des donnes, l'ouvrage Langage C : manuel de rfrence ([HS90]) rpond aux diffrentes interrogations.

Pour la partie extension du langage C, c'est--dire sur les exceptions et le GC, plusieurs travaux ajoutent ces fonctionnalits dont C est dpourvu. Le lecteur intress par les exceptions en C pourra lire le rapport technique ([Rob89]) qui dcrit comment implanter un tel mcanisme en utilisant des macros non fermes et les ruptures de calcul setjmp/longjmp de C. La page de Hans Boehm contient une distribution d'une bibliothque pour un GC conservatif racines ambigus pour C :

Lien


http://reality.sgi.com/boehm_mti/


Pour la partie interoprabilit entre Objective CAML et C, les outils dcrits dans ce chapitre restent de bas niveau et assez fastidieux d'emploi. Nanmoins ils permettent de prciser la copie ou le partage entre donnes des deux mondes. Il existe un outil de plus haut niveau, appel CamlIDL qui permet de crer automatiquement les << souches >> (en anglais stub), ou fonctions d'encapsulation, pour l'appel de fonctions C et la conversion entre types de donnes. Les types et fonctions C sont dcrits dans un langage IDL (pour Interface Definition Language), proche de C++ donc incluant C. Cette description est ensuite passe CamlIDL pour produire les fichiers .mli, .ml et .c correspondant cette encapsulation. La distribution de cet outil s'obtient la page suivante :

Lien


http://caml.inria.fr/camlidl/


Il existe d'autres interfaces entre Objective CAML et d'autres langages. On trouvera ces contributions la page de la << bosse du chameau >> :

Lien


http://caml.inria.fr/hump.html
On peut citer plusieurs essais avec le langage Fortran ainsi qu'avec un interprte de byte-code Objective CAML crit en Java.

Enfin l'interoprabilit entre Objective CAML et d'autres langages peut aussi tre ralise par communication de valeurs sur un rseau. Ce thme sera abord au chapitre sur la programmation rpartie (20).






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora013.html0000644000000000000000000001066507421273601014473 0ustar Introduction Prcdent Index Suivant

Introduction

Le premier langage fonctionnel, Lisp, est apparu la fin des annes 1950. C'est--dire au mme moment que Fortran, premier reprsentant des langages impratifs. Ces deux langages existent toujours, bien qu'ayant fortement volus. Ils couvrent respectivement le domaine de la programmation d'applications numriques pour Fortran et d'applications symboliques pour Lisp. L'intrt de la programmation fonctionnelle provient d'une grande facilit d'criture des programmes et des valeurs qu'ils manipulent. Un programme est une fonction applique ses arguments. Il calcule un rsultat qui est retourn (quand le calcul termine) comme sortie du programme. Il devient alors facile de composer des programmes : la sortie d'un programme devient l'argument d'entre d'un autre, au sens de la composition de fonctions.

La programmation fonctionnelle repose sur un modle de calcul simple possdant trois constructions : les variables, la dfinition d'une fonction et son application un argument. Ce modle s'appelle le l-calcul et il a t introduit par Alonzo Church en 1932, donc avant le premier ordinateur. Il a t conu pour offrir un modle thorique gnral de la notion de calculabilit. En l-calcul, toutes les fonctions sont des valeurs manipulables. Elles peuvent tre utilises comme paramtres d'autres fonctions ou tre retournes comme rsultat de l'appel d'une autre fonction. La thorie du l-calcul affirme que tout ce qui est calculable (entendez programmable) peut tre crit dans ce formalisme. Cependant sa syntaxe est trop limite pour tre utilisable comme langage de programmation. En consquence, il lui a t ajout des valeurs de base (tels que les entiers ou les chanes de caractres), des oprateurs sur ces valeurs de base, des structures de contrle et les dclarations qui permettent de nommer des valeurs ou des fonctions et, en particulier, des fonctions rcursives.

Il existe plusieurs classifications des langages fonctionnels. Pour notre part, nous les distinguons suivant deux caractristiques qui nous paraissent prpondrantes  :
  • Sans effets de bord (pur) ou avec effets de bord (impur) : un langage fonctionnel pur est un langage o l'affectation n'existe pas. Tout n'y est que calcul et l'on ne s'intresse pas son droulement. Les langages fonctionnels impurs, comme Lisp ou ML, intgrent des traits impratifs comme l'affectation. Ils permettent d'crire des algorithmes dans un style plus proche de langages comme Fortran o l'ordre d'valuation des expressions est dterminant.

  • Typ dynamiquement ou typ statiquement : le typage permet de vrifier si un argument pass une fonction est bien du type du paramtre formel de la fonction. On peut faire cette vrification l'excution du programme. On appellera alors cette vrification le typage dynamique. En cas d'erreur sur les types le programme s'arrtera dans un tat cohrent. C'est le cas du langage Lisp. Cette vrification peut aussi se faire avant l'excution du programme, c'est--dire au moment de la compilation. On appelle cette vrification a priori le typage statique. tant effectue une fois pour toute, elle ne ralentira pas l'excution du programme. C'est le cas du langage ML et de ses dialectes comme Objective CAML. Seuls les programmes correctement typs, c'est--dire ceux accepts par le vrificateur de types, pourront tre compils puis excuts.

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora011.html0000644000000000000000000000161507421273603014466 0ustar Notes
1
La dnomination logiciels libres, traduction de free softwares (ou freewares), a un double sens : logiciels gratuits et logiciels dont les sources sont en libre disposition. Dans le cas prsent, tous les logiciels utiliss sont gratuits et leurs sources sont disponibles.
ocaml-book-1.0/fr/html/book-ora046.html0000644000000000000000000000346207421273601014476 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

La premire section explique comment se servir de cette bibliothque sur les diffrents systmes. La deuxime section introduit les notions de base de la programmation graphique : repre, trac, remplissage, couleurs, bitmaps. La troisime section illustre ces concepts en dcrivant et implantant des fonctions de cration et de dessin de << botes >>. La quatrime section montre l'animation d'objets graphiques et leur interaction avec le fond d'cran ou les autres objets anims. La cinquime section prsente le style de la programmation par vnements ainsi que le squelette de toute interface graphique. Enfin la dernire section utilise la bibliothque Graphics pour construire une interface graphique pour la calculatrice donne page ??.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora154.html0000644000000000000000000011401507421273602014474 0ustar Comparaison modules et objets Prcdent Index Suivant

Comparaison modules et objets

La principale diffrence entre programmation modulaire et programmation objet en Objective CAML provient du systme de types. En effet la programmation par modules reste dans le systme de types la ML, i.e. du polymorphisme paramtrique (mme code excut pour diffrents types de paramtres), alors que la programmation par objets entrane un polymorphisme ad hoc (o l'envoi d'un message un objet dclenche l'application de codes diffrents). Cela est particulirement clair avec le sous-typage. Cette extension du systme de types la ML ne peut tre simule en ML pur. Il sera toujours impossible de construire des listes htrognes sans casser le systme de types.

La programmation modulaire et la programmation par objets sont deux rponses sres (grce au typage) l'organisation logique d'un programme, permettent la rutilisabilit et la modifiabilit de composants logiciels. La programmation par objets en Objective CAML autorise le polymorphisme paramtrique (classes paramtres) et d'inclusion (envoi de messages) grce la liaison tardive et au sous-typage, avec des restrictions dues l'galit, ce qui facilite une programmation incrmentielle. La programmation modulaire permet de restreindre le polymorphisme paramtrique et utilise la liaison immdiate ce qui peut tre utile pour conserver l'efficacit de l'excution.

Le modle de la programmation modulaire permet de facilement tendre les fonctionnalits sur des types de donnes rcursifs non extensibles. Si l'on dsire ajouter un cas dans un type somme, il sera ncessaire de modifier une bonne partie des sources.
Le modle de la programmation objet dfinit un ensemble rcursif de donnes partir de classes. On prend une classe par cas de donnes.

Efficacit d'excution

La liaison retarde correspond une indirection dans le tableau des mthodes (voir page ??). Comme l'accs, de l'extrieur de la classe, une variable d'instance passe aussi par un envoi de message, cette accumulation d'indirections peut se rvler coteuse.

Pour montrer cette perte d'efficacit, nous construisons la hirarchie de classe suivante :

# class virtual test () =
object
method virtual somme : unit -> int
method virtual somme2 : unit -> int
end;;
# class a x =
object(self)
inherit test ()
val a = x
method a = a
method somme () = a
method somme2 () = self#a
end;;
# class b x y =
object(self)
inherit a x as super
val b = y
method b = b
method somme () = b + a
method somme2 () = self#b + super#somme2()
end;;


On compare ensuite les temps d'excution entre d'une part l'envoi des messages somme somme2 sur une instance de la classe b et d'autre part l'appel la fonction f suivante.

# let f a b = a + b ;;
# let iter g a n = for i = 1 to n do ignore(g a) done ; g a ;;
# let go i j = match i with
1 -> iter (fun x -> x#somme()) (new b 1 2) j
| 2 -> iter (fun x -> x#somme2()) (new b 1 2) j
| 3 -> iter (fun x -> f 1 x) 2 j ;;

# go (int_of_string (Sys.argv.(1))) (int_of_string (Sys.argv.(2))) ;;


Pour 10 millions d'itrations, on obtient les rsultats suivants :
  code-octet natif
cas 1 07,5 s 0,6 s
cas 2 15,0 s 2,3 s
cas 3 06,0 s 0,3 s
Cet exemple a t construit dans le but de montrer que la liaison retarde a un cot par rapport la liaison statique habituelle. Ce cot dpend de la quantit de calcul par rapport au nombre d'envois de messages dans une mthode. L'utilisation du compilateur natif diminue la partie calcul sans changer la partie indirection du test. On s'aperoit dans le cas 2 que les multiples indirections l'envoi du message somme2 ont un cot incompressible.

Exemple : interface graphique

La bibliothque graphique UPI (voir page ??) a t conue en utilisant le noyau fonctionnel/impratif du langage. Elle s'adapte trs facilement sous forme de modules. Chaque composant devient un module indpendant, permettant ainsi une harmonisation des noms des fonctions. Pour ajouter un composant, il est ncessaire de connatre le type concret des composants. Au nouveau module de modifier les champs ncessaires pour dcrire son affichage et ses comportements.

Elle peut aussi se rcrire en objet. Pour cela on construit la hirarchie de classes de la figure 16.1.



Figure 16.1 : hirarchie de classes pour Upi


Il est plus ais d'ajouter de nouveaux composants grce l'hritage qu'en utilisant les modules, Nanmoins, l'absence de surcharge ncessite toujours un codage des options en tant que paramtres des mthodes. L'utilisation de la relation de sous-typage permet de facilement construire la liste des composants d'un conteneur. La liaison retarde slectionne les mthodes propres au composant. L'intrt de l'objet provient aussi de la possibilit d'tendre ou de modifier le contexte graphique, et les autres types utiliss, toujours grce l'hritage. C'est pour cela que les principales bibliothques graphiques sont organises selon le modle objet.

Traduction de modules en classes

Un module simple ne dclarant qu'un seul type et ne possdant pas de fonction polymorphe indpendante du type dclar peut se traduire en une classe. Selon la nature du type utilis (type enregistrement ou type somme) on traduit le module en classe de manire diffrente.

Dclarations de types

Type enregistrement
Un type enregistrement s'crit directement sous forme d'une classe o chaque champ de l'enregistrement devient une variable d'instance.

Type somme
Un type somme se traduit en plusieurs classes en utilisant le modle de conception <<composite>>. Une classe abstraite dcrit les oprations (les fonctions) sur ce type. Chaque branche du type somme devient alors une sous-classe de la classe abstraite et implante les mthodes abstraites pour sa branche. Il n'y a plus alors de <<filtrage de motif>> mais choix de la mthode spcifique la branche.

Types paramtrs
Les types paramtrs s'implantent par des classes paramtres.

Types abstraits
On peut considrer une classe comme un type abstrait. aucun moment l'tat interne de la classe n'est visible l'extrieur de sa hirarchie. Nanmoins rien n'empche de dfinir une sous-classe pour accder aux variables d'instances d'une classe.

Types mutuellement rcursifs
Les dclarations de types mutuellement rcursifs se traduisent par des dclarations de classes mutuellement rcursives.

Dclarations de fonctions

Les fonctions dont un paramtre dpend du type t du module se traduisent en mthodes. Les fonctions o n'apparat pas le type t peuvent tre dclares private dans la mesure o leur appartenance au module n'est pas directement lie au type t. Cela a de plus l'avantage de ne pas poser problme si des variables de types apparaissent dans le type des paramtres. Se pose alors le problme des fonctions dont un paramtre est du type t et un autre est du type 'a. Ces fonctions sont trs rares dans les modules de la bibliothque standard. On distingue les modules <<particuliers>> comme Marshal ou Printf qui ont un typage non standard, des modules sur les structures linaires comme List. Pour ce dernier la fonction fold_left, de type ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a, est difficilement traduisible telle quelle dans une mthode de la classe ['b] liste car la variable de type 'a est libre et ne peut apparatre dans le type de la mthode. Plutt que d'ajouter un paramtre de type la classe liste, il est prfrable de sortir ces fonctions dans de nouvelles classes, paramtres par 2 variables de types et possdant un champ liste.

Mthodes binaires
Les mthodes binaires en dehors du sous-typage ne posent pas de difficult.

Autres dclarations
Dclarations de valeurs non fonctionnelles. On peut accepter de dclarer les valeurs non fonctionnelles en dehors des classes. Il en est de mme pour les exceptions.

Exemple : listes avec itrateur
On cherche traduire en objet un module ayant la signature LISTE suivante :

# module type LISTE = sig
type 'a liste = C0 | C1 of 'a * 'a liste
val add : 'a liste -> 'a -> 'a liste
val length : 'a liste -> int
val hd : 'a liste -> 'a
val tl : 'a liste -> 'a liste
val append : 'a liste -> 'a liste -> 'a liste
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b liste -> 'a
end ;;


On dclare tout d'abord la classe abstraite 'a liste correspondant la dfinition du type.

# class virtual ['a] liste () =
object (self : 'b)
method virtual add : 'a -> 'a liste
method virtual empty : unit -> bool
method virtual hd : 'a
method virtual tl : 'a liste
method virtual length : unit -> int
method virtual append : 'a liste -> 'a liste
end ;;


On dfinit ensuite les deux sous-classes c1_liste et c0_liste pour chaque composant du type somme. Chacune de ces classes doit dfinir les mthodes de la classe abstraite anctre.

# class ['a] c1_liste (t, q) =
object (self )
inherit ['a] liste () as super
val t = t
val q = q
method add x = new c1_liste (x, (self : 'a #liste :> 'a liste))
method empty () = false
method length () = 1 + q#length()
method hd = t
method tl = q
method append l = new c1_liste (t,q#append l)
end ;;
# class ['a] c0_liste () =
object (self)
inherit ['a] liste () as super
method add x = new c1_liste (x, (self : 'a #liste :> 'a liste))
method empty () = true
method length () = 0
method hd = failwith "c0_liste : hd"
method tl = failwith "c0_liste : tl"
method append l = l
end ;;
# let l = new c1_liste (4, new c1_liste (7, new c0_liste ())) ;;
val l : int liste = <obj>


La fonction LISTE.fold_left n'a pas t incorpore dans la classe liste pour viter d'introduire un nouveau paramtre de type. On prfre dfinir la classe fold_left pour implanter ce traitement. Pour cela, on utilise une variable d'instance fonctionnelle (f).

# class virtual ['a,'b] fold_left () =
object(self)
method virtual f : 'a -> 'b -> 'a
method iter r (l : 'b liste) =
if l#empty() then r else self#iter (self#f r (l#hd)) (l#tl)
end ;;
# class ['a,'b] gen_fl f =
object
inherit ['a,'b] fold_left ()
method f = f
end ;;


On construit alors une instance de la classe gen_fl pour l'addition :

# let afl = new gen_fl (+) ;;
val afl : (int, int) gen_fl = <obj>
# afl#iter 0 l ;;
- : int = 11


Simulation d'hritage avec des modules

La relation d'hritage entre classes permet de rcuprer dans une sous-classe l'ensemble des dclarations de variables et de mthodes de la classe anctre. On peut simuler cette relation en utilisant des modules. La sous-classe qui hrite se transforme en un module paramtr dont le paramtre est la classe anctre. L'hritage multiple augmente le nombre de paramtres du module. On reprend l'exemple classique sur les points et les points colors dcrit au chapitre 15 pour le traduire en modules.

La classe point devient le module Point de signature POINT suivante.

# module type POINT =
sig
type point
val new_point : (int * int) -> point
val get_x : point -> int
val get_y : point -> int
val moveto : point -> (int * int) -> unit
val rmoveto : point -> (int * int) -> unit
val display : point -> unit
val distance : point -> float
end ;;


La classe point_colore se transforme en un module paramtr PointColore dont le paramtre est de signature POINT.

# module PointColore = functor (P : POINT) ->
struct
type point_colore = {p:P.point;c:string}
let new_point_colore p c = {p=P.new_point p;c=c}
let get_c self = self.c
(* dbut *)
let get_x self = let super = self.p in P.get_x super
let get_y self = let super = self.p in P.get_y super
let moveto self = let super = self.p in P.moveto super
let rmoveto self = let super = self.p in P.rmoveto super
let display self = let super = self.p in P.display super
let distance self = let super = self.p in P.distance super
(* fin *)
let display self =
let super = self.p in P.display super; print_string ("de couleur "^ self.c)
end ;;


La lourdeur des dclarations <<hrites>> peut tre allge par une procdure automatique de traduction ou une extension du langage. Les dclarations rcursives de mthodes pourraient s'crire par un seul let rec ... and. L'hritage multiple entrane des foncteurs plusieurs paramtres. Le cot de la redfinition n'est pas plus grand que la liaison tardive.

La liaison tardive n'est pas implante dans cette simulation. Pour le faire, il faut dfinir un enregistrement o chaque champ correspond au type des fonctions/mthodes.

Limitations de chaque modle

Le modle fonctionnel/modulaire offre un cadre rassurant mais rigide pour la modifiabilit du code. Le modle objet d'Objective CAML souffre de la double vision des classes : structuration et type, impliquant l'absence de surcharge et l'impossibilit d'effectuer des contraintes de type d'un type anctre vers un type descendant.

Modules

Les principales limitations du modle fonctionnel/modulaire proviennent de la difficult d'extensibilit des types. Bien que les types abstraits permettent de s'affranchir de la reprsentation concrte d'un type, leur utilisation dans les modules paramtrs ncessite d'indiquer la main les galits de types entre modules, compliquant l'criture des signatures.

Dpendances rcursives
Le graphe de dpendances des modules d'une application ne contient pas de cycle (DAG). Cela implique d'une part de ne pas avoir de types mutuellement rcursifs entre deux modules et d'autre part d'empcher les dclarations de valeurs mutuellement rcursives.

Difficults d'criture des signatures
Un des intrts de l'infrence de types est de ne pas ncessiter l'criture des types des paramtres des fonctions. La description des signatures fait perdre cette pratique. Il devient ncessaire de spcifier les types des dclarations de la signature << la main>>. On peut utiliser l'option -i du compilateur ocamlc pour afficher le type de toutes les dclarations globales d'un fichier .ml et utiliser cette information pour construire la signature d'un module. Dans ce cas, on perd la dmarche <<gnie logiciel>> qui consiste spcifier le module avant de l'implanter. De plus si la signature et le module subissent des changements importants, on doit revenir l'dition de la signature. Les modules paramtrs ont besoin des signatures de leurs paramtres et celles-ci doivent aussi tre dcrites manuellement. Enfin si on associe une signature fonctionnelle un module paramtr, il n'existe pas de possibilit pour rcuprer la signature rsultat de l'application du foncteur. Cela oblige crire principalement des signatures non fonctionnelles, quitte les assembler par la suite pour construire une signature fonctionnelle.

Exportation et importation de modules
L'importation des dclarations d'un module simple s'effectue soit par la notation pointe (Module.nom), soit directement par le nom d'une dclaration (nom) si le module a t ouvert (open Module). Les dclarations de l'interface du module import ne sont pas directement exportables au niveau du module en cours de dfinition. Celui-ci a accs ces dclarations, mais elles ne sont pas considres comme des dclarations du module. Pour ce faire il est ncessaire de dclarer, la manire de la simulation d'hritage, les valeurs importes. Il en est de mme pour les modules paramtrs. Les dclarations du module paramtre ne sont pas considres comme des dclarations du module courant.

Objets

Les principales limitations du modle objet d'Objective CAML proviennent du typage.
  • pas de mthode contenant un paramtre de type libre;
  • difficult d'chappement du type de la classe dans une de ses mthodes;
  • absence de contrainte de type d'anctre vers descendant;
  • pas de surcharge.
Le point le plus droutant quand on aborde l'extension objet d'Objective CAML provient de l'impossibilit de construire des mthodes contenant un type paramtr dont le paramtre de type est libre. La dclaration d'une classe peut tre vue comme la dfinition d'un nouveau type et suit en cela la rgle gnrale qui interdit la prsence de variables de type libres dans une dclaration de type. C'est pour cela que les classes paramtres sont indispensables dans le modle objet d'Objective CAML car elles permettent de lier ces variables de type.

Absence de surcharge
Le modle objet d'Objective CAML ne permet pas la surcharge de mthode. Comme le type d'un objet correspond aux types de ces mthodes, le fait de possder plusieurs mthodes de mme nom mais de types diffrents entrane de nombreuses ambiguts, dues au polymorphisme paramtrique, que le systme ne pourrait rsoudre que dynamiquement, ce qui est contradictoire avec la vision totalement statique du typage. Prenons une classe exemple qui possde deux mthodes message possdant pour la premire un paramtre entier et pour la deuxime un paramtre flottant. Soit e une instance de cette classe et soit la fonction f suivante :

# let f x a = x#message a ;;
Les appels f e 1 et f e 1.1 ne peuvent pas statiquement tre rsolus car il n'y a pas dans le code de la fonction f d'informations spcifiques la classe exemple.

Une consquence immdiate de cette absence est l'unicit des constructeurs d'instances. La dclaration d'une classe indique les paramtres fournir la fonction de cration. Celle-ci est unique.

Initialisation
L'initialisation des variables d'instance dclares dans une classe peut tre problmatique quand elle doit se calculer partir des valeurs passes la construction.

galit entre instances
La seule galit qui fonctionne sur les objets est l'galit physique. L'galit structurelle retourne toujours false quand elle s'applique deux objets physiquement diffrents. Cela peut surprendre dans la mesure o deux instances d'une mme classe partagent la mme table des mthodes. On pourrait imaginer un test physique sur la table des mthodes et un test structurel sur les valeurs (val) des objets. Ce sont des choix d'implantation la manire du filtrage linaire.

Hirarchie de classes
On ne trouve aucune hirarchie de classes dans la distribution du langage. En effet l'ensemble des bibliothques sont fournies sous forme de modules simples ou paramtrs. Cela montre que l'extension objet du langage est encore en cours de stabilisation et milite peu en faveur de son emploi intensif.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora078.gif0000644000000000000000000000511107073350152014275 0ustar GIF89aUUU!,ڋ޼扦ʶ L L*'ť J/*ڮ nT䲙xNן1 7rm#}WHXUt5x٥( X9et)9Y*1:k!{k{T L 8LL,̜a Mp\=x}ˍM]M^m^(d̾~=:ȏgشM$tVbQyX(f`|X5HrT l2R5 mN@V$ש6H1lёIu-ՔJV\Fz[T^a(,j\rC(h櫪P,J%_J|XRS}ٸ,b!pND؉f(!aF(y^,|> 1?;ޏjp0ZN7N7);8je7P#`6qơ63$?,isCF(CH"ގi!hR F3 +BG~ELG`'Jfiyi˝xc6K u~ t؆ve fFi鉊'6.!]*J ,'dNGgIgQ`jgiF`=Azf G6uE4{;bۯL1.X[J:vd "EV](;*ݼwinzit+X p=~6Z >eƬv &PΎCoQ<: | JU20lp:l.3"xAWEk,\ء?Lps+ TQGrX+mC,eu?%h&kG(m6/uvG㬀.[n: N4WIk\7ɳK~).;N9dc&7O_?cW (}'v=Ώ.<(۟#_3a}{6U>/9}ow4č 9=s`<2P  w4 )Lhҹmv1 =RC%""G -/=+VPID%&T" 8#p|%-x+ƒe?rٌ8Ҕ4t")6zS&/{2I4gk&(aayg"$6#8)Ks^NvސfxrCg>Osb(\ j"Df:F{B}D+jv򢌨pʅΙx[êgCǝ!KcwVPZ~X+v&A%%M#*I2ϨŅr;]V׺nw ܳ:{ sr}%:˺Zex[.x<7}VlK]C-'_3ii~<Ǩ4щSU[`S .*j,8R.u#ԷBu/+u4fePա ǘ0t݀7r&hC;cU\3?KЩ/р:ϋ6h;(zя ]i^Бtzinե4Xzִok0ҭz>6v5і1]m0掄j)׫6׬0M # F;xs6QP06ܾHwͅ,kSnT![rwH99fZMtvrhOMV<4rh \syi{#~&ĭ #g_r7jC_2V7=c)nwUESNWv4IZ~ pܹ~CqLx'^ #x>?c K8'ofO&>6:\J8,xX {{Zn \崟(Fs{s>rNp#K@7Lb ;m=óv}6}}~y9+/~$q7d|F?_X(H'aP;ocaml-book-1.0/fr/html/book-ora115.html0000644000000000000000000004017207421273602014473 0ustar Communication entre C et Objective CAML Prcdent Index Suivant

Communication entre C et Objective CAML

La communication entre parties, d'un mme programme, crites en C et en Objective CAML s'effectue en crant un excutable (ou une nouvelle boucle d'interaction) contenant ces deux parties. Celles-ci ont pu tre compiles sparment. Ce sera donc l'dition de liens1 d'tablir le lien entre les noms Objective CAML et les noms C, puis de crer l'excutable. Pour cela le programme Objective CAML contiendra des dclarations externes assurant cette correspondance.

L'exemple de la figure 12.1 montre un programme compos d'une partie C et d'une partie Objective CAML.


Figure 12.1 : communication entre Objective CAML et C


On peut voir chaque partie comme contenant du code, des instructions correspondant aux dfinitions de fonctions et de calcul d'expressions pour Objective CAML, ainsi que leur zone d'allocation dynamique de mmoire. L'appel de la fonction f sur trois entiers d'Objective CAML entrane le calcul de la fonction f_c de C. Le corps de la fonction convertit les trois entiers Objective CAML en entiers C, calcule leur somme et retourne le rsultat converti en entier Objective CAML.

Nous prsentons ci-dessous les premiers lments d'interfaage entre Objective CAML et C : les dclarations externes, les contraintes sur les dfinitions de fonctions C appelables partir d'Objective CAML ainsi que les options d'dition de liens. Puis nous donnons un exemple d'utilisation des entres-sorties.

Dclarations externes

La dclaration externe de fonctions en Objective CAML a pour but d'effectuer la correspondance entre une dclaration de fonction C et un nom Objective CAML, tout en indiquant le type de celle-ci.

La syntaxe est la suivante :

Syntaxe


external nom_caml : type = "nom_C"
Cette dclaration indique que l'appel en Objective CAML de la fonction nom_caml dclenchera l'appel la fonction C nom_C avec ses arguments. L'exemple de la figure 12.1 dclare ainsi la fonction f dont l'appel correspondra l'excution de la fonction f_c.

Il est possible de dclarer une fonction externe dans une interface (i.e. dans un fichier .mli), soit en la dclarant explicitement externe, soit comme une valeur usuelle :

Syntaxe


external nom_caml : type = "nom_C"

val nom_caml : type
Dans le second cas, l'appel la fonction C passe au pralable par le mcanisme gnral des fonctions Objective CAML. C'est un peu moins efficace mais cache la nature de l'implantation de la fonction.

Dclaration des fonctions C

Les fonctions C appelables partir d'Objective CAML doivent possder le nombre d'arguments dcrit dans la dclaration externe. Ces arguments sont de type value qui est, en C, le type des valeurs d'Objective CAML. Comme celles-ci ont une reprsentation uniforme (9) un seul type C permet de coder toutes les valeurs Objective CAML. Nous exposerons page ?? l'utilisation des outils de codage-dcodage des valeurs et l'illustrerons par une fonction d'exploration des valeurs d'Objective CAML.

L'exemple de la figure 12.1 est conforme ces contraintes. La fonction f_c, lie la fonction Objective CAML de type int -> int -> int -> int est bien une fonction trois paramtres de type value qui retourne un rsultat de type value.

Le mcanisme d'valuation de l'interprte de code-octet d'Objective CAML diffre selon le nombre d'arguments2. Si le nombre d'arguments est infrieur ou gal cinq, les arguments sont empils dans la pile d'excution de la machine. Si le nombre d'arguments est suprieur cinq, c'est un tableau contenant tous les arguments qui est empil ainsi que la taille de ce tableau. Comme les fonctions C peuvent tre appeles partir de la machine abstraite, il est ncessaire de distinguer ces deux cas. Par contre l'appel de fonction partir du compilateur natif empile bien tous ses arguments. Ceux-ci pourront donc tre passs directement la fonction C.

Cas des fonctions plus de cinq arguments

Il convient dans ce cas d'crire deux fonctions C : l'une pour le code-octet et l'autre pour le code natif. La syntaxe des dclarations externes est donc tendue pour permettre une seule dclaration de fonction Objective CAML pour deux fonctions C :

Syntaxe


external nom_caml : type = "nom_C_byte_code" "nom_C_natif"
La fonction nom_C_byte_code prend deux arguments : un tableau de valeurs de type value (i.e. un pointeur de type *value) et un entier indiquant la taille de ce tableau.

Exemple

Le programme C suivant dfinit deux fonctions diffrentes pour l'addition de six entiers : plus_native et plus_bytecode qui seront utilises respectivement par le compilateur natif et le compilateur de code-octet. Il est ncessaire d'inclure le fichier mlvalues.h contenant les dfinitions des types C des valeurs Objective CAML et les macros de conversion.

#include <stdio.h>
#include <caml/mlvalues.h>

value plus_native (value x1,value x2,value x3,value x4,value x5,value x6)
{
printf("<< PLUS NATIF >>\n") ; fflush(stdout) ;
return Val_long ( Long_val(x1) + Long_val(x2) + Long_val(x3)
+ Long_val(x4) + Long_val(x5) + Long_val(x6)) ;
}

value plus_bytecode (value * tab_val, int nb_val)
{
int i;
long res;
printf("<< PLUS BYTECODE >> : ") ; fflush(stdout) ;
for (i=0,res=0;i<nb_val;i++) res += Long_val(tab_val[i]) ;
return Val_long(res) ;
}

Le programme Objective CAML exOCAML.ml suivant utilise ces fonctions C.

external plus : int -> int -> int -> int -> int -> int -> int
= "plus_bytecode" "plus_native" ;;
print_int (plus 1 2 3 4 5 6) ;;
print_newline () ;;


On compile ces programmes avec les deux compilateurs Objective CAML, et un compilateur C que nous appelons cc. Il est ncessaire de lui indiquer le chemin d'accs au fichier mlvalues.h.
$ cc -c -I /usr/local/lib/ocaml  exC.c 

$ ocamlc -custom exC.o exOCAML.ml -o ex_byte_code.exe 
$ ex_byte_code.exe
<< PLUS BYTECODE >> : 21 

$ ocamlopt exC.o exOCAML.ml -o ex_native.exe 
$ ex_native.exe 
<< PLUS NATIF >> : 21 

Remarque


La faon la plus simple pour ne pas avoir crire deux fois une mme fonction est d'utiliser la primitive native dans le code de la primitive pour le code-octet suivant le schma :
value prim_nat (value x1, ..., value xn) { ... }
value prim_bc (value *tab, int n)
{ return prim_nat(tab[0],tab[1],...,tab[n-1]) ; }


dition de liens avec C

L'dition de liens va crer un excutable partir de fichiers C et Objective CAML compils respectivement par leur propre compilateur. Le rsultat du compilateur natif est illustr la figure 12.2.



Figure 12.2 : excutable mixte


Les instructions machine provenant de la compilation des programmes C et Objective CAML sont ranges dans la zone d'allocation statique du programme. La zone d'allocation dynamique contient la pile d'excution (o sont rangs les appels en cours) et les tas de C et d'Objective CAML.

Bibliothque d'excution

Les primitives C susceptibles d'tre appeles par un programme n'utilisant que les bibliothques standard sont contenues dans la bibliothque d'excution de la machine abstraite (voir la figure 7.3 page ??). Il n'est donc pas ncessaire de prciser quoi que ce soit. En revanche, lorsqu'on utilise les bibliothques Graphics, Num ou Str il est ncessaire d'expliciter les bibliothques correspondant ces modules pour l'dition de lien. C'est la fonction de l'option -custom du compilateur. Il en va de mme pour des fonctions C que nous souhaiterions utiliser, il faut ajouter le fichier objet contenant ces fonctions au moment de l'dition de lien. Nous allons dtailler cela dans un exemple.

Diffrents cadres d'dition de liens

Il faut distinguer les cadres de compilation pour le compilateur natif, le compilateur de code-octet et la construction de boucles d'interaction. Les diffrentes options des diffrents modes de compilation sont dcrites au chapitre 7.

Pour illustrer ces diffrents modes, on reprend l'exemple de la figure 12.1 en nommant le fichier Objective CAML progocaml.ml. Celui-ci utilise la fonction externe f_c dfinie dans le fichier C progC.c. Ce dernier utilise une bibliothque C libC. Une fois ces fichiers compils de manire indpendante, leur dition de liens s'effectue par les commandes suivantes :
  • code-octet :
    ocamlc -custom -o vbc.exe progC.o libC.a progocaml.cmo
  • natif :
    ocamlopt progC.o -o vn.exe libC.a progocaml.cmx
On obtient deux excutables : vbc.exe pour la version en code-octet et vn.exe pour la version en code natif.

Construire une nouvelle machine abstraite

On peut aussi augmenter la bibliothque d'excution de la machine abstraite en intgrant de nouvelles fonctions C. Pour cela on utilisera les commandes suivantes :
ocamlc -make-runtime -o nouveau_ocamlrun progC.o libC.a
On pourra alors construire un excutable non autonome, vbcnam.exe, utilisant la nouvelle machine abstraite :
ocamlc -o vcam.exe -use-runtime nouveau_ocamlrun progC.o \
       libC.a  progocaml.cmo
Il sera excut par la commande : nouveau_ocaml vbcam.exe ,
ou directement par : vbcam.exe

Remarque


L'dition de liens en mode -custom force le compilateur parcourir les fichiers objets (.cmo) pour y rpertorier les fonctions externes mentionnes. Le code-octet ncessaire leur utilisation est engendr et ajout au code-octet provenant des programmes Objective CAML.


Construire une boucle d'interaction

Pour pouvoir utiliser une fonction externe dans une boucle d'interaction, il faut en construire une nouvelle comportant d'une part le code C de la fonction et d'autre part un programme Objective CAML contenant sa dclaration.

On suppose compil le fichier progC.c contenant la fonction f_c. On construit alors la boucle d'interaction ftop de la manire suivante :
ocamlmktop -custom -o ftop progC.o libC.a ex.ml
Le fichier ex.ml contient la dclaration externe de la fonction f. Le toplevel ftop connat alors cette fonction et dispose du code C correspondant qui est donn dans progC.o.

Entres-sorties des deux mondes

Les fonctions C et Objective CAML ne partagent pas leurs tampons d'change avec les fichiers. Soit le programme C suivant :

#include <stdio.h>
#include <caml/mlvalues.h>
value hello_world (value v)
{ printf("Hello World !!"); fflush(stdout); return v; }

On note que les critures sur la sortie standard doivent tre forces (fflush) si l'on souhaite qu'elles apparaissent dans le bon ordre.

# external caml_hello_world : unit -> unit = "hello_world"  ;;
external caml_hello_world : unit -> unit = "hello_world"
# print_string "<< " ;
caml_hello_world () ;
print_string " >>\n" ;
flush stdout ;;
Hello World !!<< >>
- : unit = ()


Cet affichage n'est pas satisfaisant. Il faut rcrire le programme Objective CAML de la manire suivante :

# print_string "<< " ; flush stdout ;
caml_hello_world () ;
print_string " >>\n" ; flush stdout ;;
<< Hello World !! >>
- : unit = ()
Le vidage des tampons qui suit systmatiquement un ordre d'criture permet de conserver l'ordre d'affichage entre les deux mondes.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora085.gif0000644000000000000000000002506707073350152014307 0ustar GIF89av  df;B3TR;TN;dYD,2$,.$,*$;=,;9,{}{,* TRD[YD[UD[][f3;5$dbL32,  323.;=33.$5;DJ;DF;TUDlrllnlf{ytdfdD$*$&$"d]LLN;LJ;,2,TDF3,.,DB3tvl$*${$"$39,35,",.32d   !C,vCREEE55 g#F xI!BArWbzUėjLUF3(S21KbDG\;vݔϟ A]갩ӆHJJ$2f*CP"@EcXKVۖmE6”w&5 ݼx|:_"-za姘Tx2g\ +7"pX"xCtcUcZ/f caΨژxϑ!*=)U}TJe,/ ;ld6&©stB;Er'm~L4箻O`>\@\ T-6^ uZ xM"`ףdx:v!*,iei2H RKos>q%BA$"ϸD|$9H:򒖴d%7INj̤'9P4*QyVd+?G:$e-mUr0q `6,cK8.ӌl9G$:uf1nz6͘LfBplfihӚ41(yS^X6Nfg?Ĥ2m409ЄRD?dꓟ4I~f@ωq;XOtb0Lge3j N~hM PkQ+(%ҞMFIS*UZUn\XTn"uD=# Z.Hԛ ԪfKO~,N3Ͳիa%+bzX6?]b;Y&Vulf!Xc$γ0.M]fWAوHu 2 wdh{=DHkp{47>@S ]x$=zjegzK0*,2 Q8V!]i-zrWb tә=#/wh/c" {HGv񮬪\<5@B9DYFyHJLٔNPL eGRyXZ\jYviYr.@fYejlhimr9tYnypiz|ٗ~!FaYeF#`f)m Uf7Yyٙ=gF:0u30gTYF<@&|~XUyn$ٛ9o<0U+Pl h[kUFTu )V'IIHJtL. wIHi쉞y !Г^hp pZ7XTٙQgfFi}׹TjTzRVU/TRSUFIY%I+:':/2Ze\]vh9|O&W*WeeAЇ֙$QBN8STV|֘=С'EXiUě/ʞ(ٙ-i-Z'ʢ(z1jj:e:vj\QV4Iyi_{OO$R!Rx]IjH}ؘEFZZHsuUyYJmRR*gT&ZkJn*ٞlzjIw:YeFH~juWVoVIU8X薅IVOζlHPFEZDgt+zaZu:lpIh{ZjFgVv+ [Zj:WFQ=vAGUѨFP P"";ypY01iY\yU N(XFJ@B &utVFWGyh{dxD&!Z\۵^`b;d[f{hjje渍)Xb|۷~;[;`9d\ 83cA``Cbd>{;[{~N& a[bd&`PE גjKn {śM(vSCj2|x 5}݆r GxIpELɍbپ5h%;t1I{h_v'T;G59'9g\ܻIؑ X G irMLky)ܴTX.lR{L9; :;\@<(hCi\}ƕPRd\fGօ{g$ gic|Er~[6Hz,ĻDxLjPA!<{|Ɏ۽+]N͋i,*mĕ͑&؟Mm,3ny[,INy?܂^F,~yՕ޼{巍͹͒L)MҎ>n軴 ݜN5WKwH.( ߪAfFF`ξb ʞ՞d,6bc%d4aϮcb+$VM֎[辺kIVaVaLۨ~]ÎWKJ=?]f^&Y kO򦮼g\^ĒNu^H᭎ .ꔞCn}ԛz=~0:?H<٬tfL;@^Mk1mk\Y]/‰SGo>}.^k\lU+B/FܒlΜt݉*XClh]w[֚v_=+ٷn׍~̆]V>ͭ?aOw\=L{LU抯ό` u٭>]_]鼬/yEܻ,މYv,HpRxv/CJrۘÒ4P"X$XE88XȨ)99HyIYi :hș: کzZ*zkX;4Tک;,98hi ڊ,I] -n[~M-Y\\O?h%G,agKt(@Mg-5n߀#;LSKhJj^j1~7#,MB! 2p"g2QJ@$=uk˱V[$szչs RMB,HbûH#67l\k8sd!.0H(/;8+<3ljTLl\dMG]9,f%8%ٰXlOƆkW-cEFF2ֽ|>̻d5ȯ^]-q~}։MVoCf>{~BS_i m7gޠu=p$`0lӈ&ړ ?%ePBI9WE2:zHQ}8Zf88}F䄿ys`MLMM#@ܥCrᘐ!E4_Le"-ȡ 果EXI}_*>*ڏ.*ICv[ Cuh!nmwܐg~r'Nz']rxelxe7 idzYBIw zx:NDW{]'Wf<(J~ nKnߪ~T,4]grz΁xafwoqz50w :ݧY>,1iL&%pk鹈Zjт-T)1Fr`x!:n.ʺ RH{HG(}Dp 6@~-dY{&]Z';,0x.opTSrVԔf{Se[YN,z\AL\wGY~uX` F ֬aJxՒ| -f#1X}LiH~,dՕR\Kw߲Ί(T7mxy"_XI)K oy1ڿw9Vi!QlĀ ꆍo@nxG:l 6BpA>x&шD.`".u1b$öh0@`]PS/jWc3|Ygd)^ WoS7866.IKEҭ~*e%S(9omEKz/({c78!a QwYݭviRە's;VPL#4ycGYZup>Kk^7Aob:Hv}e7åhڑR>w)icEI_"|{wW*D{/m KmEU'R#یV?t4=ذ{/!,峐6*ݙ>79i4_Mb8Տl͆ =׹ɭܽnյLY[W2rWNIw;)[]´$(:z4!TrԻ؇p֠ xO^w|*xiN h{;Ůyxڠ$c˲r/*w]ݸqK8}NdyhX(BKճs_}wgXw%E.GH7k}8{(GegD8XsUhxȈ8x>x5?i}-ÊfI2yJu> uJΧ4#6X4yՌ|W ^{6nu~HӨP2ȑ{QXZ'h7tDgg44i Y^җG} U$%tE)yy@t8:9#iw~ wx7'8h- Ŕvk=ɕbuYNÇQzDB)ۙr'H"g6C M4GٞHߘmS3OYaxJ>9z<)mٜ((I؊I#>:$i=ʤMOrCRlU͆f:mƥUZ\ʥ[cڥ`l^[bl&qJVsjqڥƧ}bJgzfVfZJ&mxJ{p l*z7 uʪڪJZJʪKzPꫪǫ&J R"T2j YjJ::՚ڭJr  YP=%0X0`j ) YJ URAʰ^;^ڪ+Z7FCŸ J KcŮ=Xٰ=00V+p:)%PY YpV b5)JjY^:WkT;˭F'nYyBY X 6{`?;0jb%*!.;+RcJKK*]_m'%s麮mT tY`%+වUPN; B[ǫH+ %g{ [:tȑ%DFZbѳƺ 4PpQ; + ȫj䠻pJ;z \qgf{†h[tH ~j rK@ڬ9 Pe``ʽ\˰{۰Z{831tCUH'ySy L| N":ڭV˰S˫C^̾c Extensions du langage Prcdent Index Suivant

Extensions du langage

O'Labl apporte trois extensions Objective CAML. Deux d'entre elles ne modifient pas le reste du langage dans le sens o un programme crit dans la version 2.04 conserve le mme comportement dans la version 2.99. La troisime extension et les modifications qui ont t apportes aux bibliothques standards sont susceptibles de changer le sens des programmes crits sans tenir compte des nouveaux traits du langage. Pour assurer une compatibilit avec le pass, il existe deux modes distincts pour utiliser Objective CAML 2.99 :
  1. Le mode classique : c'est l'utilisation par dfaut du compilateur et de la boucle d'interaction. L'extension constitue par les labels est restreinte pour ne pas modifier la smantique des programmes qui ne les utilisent pas.
  2. Le mode moderne : il est choisi explicitement. L'extension des labels permet plus de choses, mais l'utilisation des bibliothques demande d'utiliser les spcificits de ce nouveau trait.

Moderne ou Classique

Le compilateur et la boucle d'interaction possdent une nouvelle option (-modern) pour explicitement utiliser les nouveaux traits des labels. Par dfaut, le style classique restreint les possibilits des labels et conserve la compatibilit avec les compilateurs prcdents.
$ ocamlc -modern .....
$ ocaml -modern .....
La boucle d'interaction autorise de commuter les deux modes pendant son utilisation en utilisant la directive #modern.

# #modern true ;; (* passe en mode moderne *)
# #modern false ;; (* passe en mode classique *)


Warning


Un programme correct en 2.04, mme s'il n'utilise aucune des extensions de la 2.99, peut ne plus tre correct dans la nouvelle version.


Labels

Un est une annotation apporte aux arguments d'une fonction dans sa dclaration et dans son application. Il se prsente comme un identificateur spar du paramtre (formel ou effectif) d'une fonction par le symbole ':'.

On trouve les labels dans les dclarations de fonctions :

Syntaxe


let fonction label:p = exp
dans les dclarations anonymes avec le mot cl fun :

Syntaxe


fun label:p -> exp
et dans le paramtre effectif d'une fonction :

Syntaxe


( fonction label:exp )


labels dans les types
Les labels donns aux arguments d'une expression fonctionnelle apparaissent dans son type et annotent les types des arguments auxquels ils rfrent.

# let add op1:x op2:y = x + y ;;
val add : op1:int -> op2:int -> int = <fun>

# let mk_triplet arg1:x arg2:y arg3:z = (x,y,z) ;;
val mk_triplet : arg1:'a -> arg2:'b -> arg3:'c -> 'a * 'b * 'c = <fun>


Si on souhaite donner le mme identificateur au label et la variable, il n'est pas utile de le rpter car c'est celui pris par dfaut.

Syntaxe


fun :p -> exp

# let mk_triplet :arg1 :arg2 :arg3 = (arg1,arg2,arg3) ;;
val mk_triplet : arg1:'a -> arg2:'b -> arg3:'c -> 'a * 'b * 'c = <fun>


L'utilisation du caractre : dans les dclarations de labels impose d'utiliser des espaces pour les autres utilisations de ce symbole des endroits o il pourrait y avoir ambigut.

# let id x:int = int ;; (* x est le label du paramtre int *)
val id : x:'a -> 'a = <fun>
# let id x : int = x ;; (* x est un paramtre de type int *)
val id : int -> int = <fun>


Warning


La contrainte d'un type dans une expression doit se faire en respectant la syntaxe des dclarations de labels.


Il n'est pas autoris de dfinir des labels dans une dclaration de fonction par filtrage; en consquence le mot cl function ne peut pas tre utilis pour une fonction avec label.

# let f = function arg:x -> x ;;
Characters 18-22:
Syntax error
# let f = fun arg:x -> x ;;
val f : arg:'a -> 'a = <fun>


labels en mode classique

Dans un premier temps nous prsentons les labels dans le mode classique.

Application
Les labels peuvent tre utiliss pour appliquer les arguments une fonction, mais ils ne sont pas obligatoires.

# mk_triplet '1' 2 3.0 ;;
- : char * int * float = '1', 2, 3
# mk_triplet arg1:'1' arg2:2 arg3:3.0 ;;
- : char * int * float = '1', 2, 3
# mk_triplet '1' arg2:2 3.0 ;;
- : char * int * float = '1', 2, 3


Les labels ne modifient pas les possibilits d'application partielle.

# let couple = mk_triplet arg1:() ;;
val couple : arg2:'_a -> arg3:'_b -> unit * '_a * '_b = <fun>


Warning


Dans le mode classique, les arguments d'une fonction doivent tre appliqus dans l'ordre de leur dclaration.


# let couple = mk_triplet arg2:() ;;
Characters 30-32:
Expecting function has type arg1:'a -> arg2:'b -> arg3:'c -> 'a * 'b * 'c
This argument cannot be applied with label arg2:


Lisibilit du code
L'intrt principal des labels est d'associer au type d'un argument une annotation qui rend plus explicite l'interface d'une fonction.

# String.sub ;;
- : string -> pos:int -> len:int -> string = <fun>
Plus aucun doute, la fonction String.sub prend en argument une chane, la position du premier caractre, et la longueur de la chane extraire.

Les fonctions des bibliothques de la distribution d'Objective CAML 2.99 ont t << labelises >>. Le tableau B.1 donne les conventions qui ont t utilises.


label signification
pos: une position (dans une liste, une chane, un tableau, etc.)
len: une taille (length)
buf: une chane utilise comme tampon (buffer)
src: la source d'une opration
dst: la destination d'une opration
fun: une fonction appliquer
pred: un prdicat
acc: un accumulateur
to: un canal de sortie (out_channel)
key: une cl utilise dans un index (liste d'associations, etc.)
data: une valeur associe utilise dans un index
mode: un mode ou une liste d'options
perm: des permissions de fichiers

Figure B.1 : conventions sur les labels


Labels en mode moderne

La principale diffrence avec le mode classique est que dans le mode moderne, il est obligatoire de donner explicitement les labels lors de l'application d'une expression fonctionnelle.

# let add :x :y = x + y ;;
val add : x:int -> y:int -> int = <fun>
# #modern false ;;
# add 1 2 ;;
- : int = 3
# #modern true ;;
# add 1 2 ;;
Characters 4-5:
Expecting function has type x:int -> y:int -> int
This argument cannot be applied without label


Warning


Dans le mode moderne, il est obligatoire de donner le nom des labels aux arguments qui en possdent un.


La consquence de cette obligation est que l'ordre des arguments possdant un label devient pour le coup indiffrent puisqu'on peut les identifier par leur label. Objective CAML 2.99 possde un systme permettant de commuter les arguments d'une fonction et donc de les appliquer dans n'importe quel ordre.

# add x:1 y:2 ;;
- : int = 3
# add y:2 x:1 ;;
- : int = 3


C'est particulirement utile quand on souhaite faire l'application partielle d'un argument qui n'est pas le premier de la dclaration.

# let add_x_2 = add y:2 ;;
val add_x_2 : x:int -> int = <fun>
# add_x_2 x:1 ;;
- : int = 3


Les arguments possdant le mme label ou n'ayant pas de label ne commutent pas entre eux. Une application dans un tel cas considre le premier argument qui a ce label (respectivement qui n'a pas de label).

# let test arg1:_ arg2:_ _ arg2:_ _ = () ;;
val test : arg1:'a -> arg2:'b -> 'c -> arg2:'d -> 'e -> unit = <fun>

# test arg2:() ;; (* le premier label arg2: de la dclaration *)
- : arg1:'a -> 'b -> arg2:'c -> 'd -> unit = <fun>

# test () ;; (* le premier argument sans label de la dclaration *)
- : arg1:'a -> arg2:'b -> arg2:'c -> 'd -> unit = <fun>


Paramtres optionnels

Objective CAML 2.99 autorise la dfinition de fonctions avec des arguments labeliss optionnels. De tels arguments sont dfinis avec une valeur par dfaut qui est la valeur donne au paramtre si l'application n'en donne pas une autre explicitement.

Syntaxe


fun ?(nom:p = exp1) -> exp2


Le caractre optionnel de l'argument apparat dans son type par le symbole ?.

# let sp_incr ?(inc:x=1) y =  y := !y + x ;;
val sp_incr : ?inc:int -> int ref -> unit = <fun>
La fonction sp_incr se comporte comme la fonction incr du module Pervasives.

# let v = ref 4 in sp_incr v ; v ;;
- : int ref = {contents=5}
Mais on peut lui spcifier un incrment diffrent de celui par dfaut.

# let v = ref 4 in sp_incr inc:3 v ; v ;;
- : int ref = {contents=7}


L'application d'une fonction se fait en donnant la valeur par dfaut de tous les paramtres optionnels jusqu'au paramtre effectivement pass par l'application. Si l'argument de l'appel est donn sans label, il est considr comme tant le premier argument de la fonction qui ne soit pas optionnel.

# let f ?(:x1=0) ?(:x2=0) x3 x4 = 1000*x1+100*x2+10*x3+x4 ;;
val f : ?x1:int -> ?x2:int -> int -> int -> int = <fun>
# f 3 ;;
- : int -> int = <fun>
# f 3 4 ;;
- : int = 34
# f x1:1 3 4 ;;
- : int = 1034
# f x2:2 3 4 ;;
- : int = 234


Les arguments optionnels peuvent tre passs dans un ordre arbitraire quel que soit le mode en cours.

# #modern false ;;
# f x2:2 x1:1 3 4 ;;
- : int = 1234


Un argument optionnel peut tre donn sans valeur par dfaut, dans ce cas il est considr dans le corps de la fonction comme tant du type 'a option; None est sa valeur par dfaut.

Syntaxe


fun ?nom:p -> exp


# let print_entier ?file:x n = 
match x with
None -> print_int n
| Some f -> let fic = open_out f in
output_string fic (string_of_int n) ;
output_string fic "\n" ;
close_out fic ;;
val print_entier : ?file:string -> int -> unit = <fun>
Par dfaut, la fonction print_entier affiche son argument sur la sortie standard. Si on lui passe un nom de fichier avec le label file, la sortie se fait alors dans ce fichier.

Remarque


Si les derniers paramtres d'une fonction sont optionnels, il devront tre appliqus explicitement.

# let test ?:x ?:y n ?:a ?:b = n ;;
val test : ?x:'a -> ?y:'b -> 'c -> ?a:'d -> ?b:'e -> 'c = <fun>
# test 1 ;;
- : ?a:'_a -> ?b:'_b -> int = <fun>
# test 1 b:'x' ;;
- : ?a:'_a -> int = <fun>
# test 1 a:() b:'x' ;;
- : int = 1


Labels et objets

Les labels peuvent tre utiliss pour les paramtres d'une mthode ou pour ceux du constructeur d'un objet.

# class point ?(:x=0) ?(:y=0) (col : Graphics.color) = 
object
val pos = (x,y)
val couleur = col
method affiche ?(to:file=stdout) () =
output_string file "point (" ;
output_string file (string_of_int (fst pos)) ;
output_string file "," ;
output_string file (string_of_int (snd pos)) ;
output_string file ")\n"
end ;;
class point :
?x:int ->
?y:int ->
Graphics.color ->
object
val couleur : Graphics.color
val pos : int * int
method affiche : ?to:out_channel -> unit -> unit
end

# let obj1 = new point x:1 y:2 Graphics.white
in obj1#affiche () ;;
point (1,2)
- : unit = ()
# let obj2 = new point Graphics.black
in obj2#affiche () ;;
point (0,0)
- : unit = ()


Les labels et les arguments optionnels fournissent une alternative la surcharge des mthodes et des constructeurs qui sont classiques dans les langages objets et qu'Objective CAML ne peut accepter.

Cette mulation de la surcharge comporte quelques limitations, en particulier un des arguments devra ne pas tre optionnel; on peut toujours utiliser un argument de type unit.

# class nombre ?:entier ?:flottant () = 
object
val mutable valeur = 0.0
method print = print_float valeur
initializer
match (entier,flottant) with
(None,None) | (Some _,Some _) -> failwith "construction incorrecte"
| (None,Some f) -> valeur <- f
| (Some n,None) -> valeur <- float_of_int n
end ;;
class nombre :
?entier:int ->
?flottant:float ->
unit -> object val mutable valeur : float method print : unit end

# let n1 = new nombre entier:1 () ;;
val n1 : nombre = <obj>
# let n2 = new nombre flottant:1.0 () ;;
val n2 : nombre = <obj>


Constructeurs polymorphes

Les types sommes d'Objective CAML ont deux principales limitations. D'une part il n'est pas possible d'tendre un type somme avec un nouveau constructeur. D'autre part un constructeur ne peut appartenir qu' un seul type. Objective CAML 2.99 propose des constructeurs spciaux dit constructeurs polymorphes qui chappent ces deux contraintes.

Un constructeur polymorphe est bti avec un identificateur qui a la mme contrainte syntaxique que les autres constructeurs savoir qu'il doit tre prfix par une majuscule.

Syntaxe


`Nom
ou

Syntaxe


`Nom type


Un ensemble de constructeurs polymorphes forme un type, mais il n'est pas ncessaire de dfinir un type pour dfinir un constructeur polymorphe.

# let x =  `Entier 3 ;;
val x : [>`Entier int] = `Entier 3


Le type de x avec le symbole [> se lit comme le type qui contient au moins le constructeur `Entier int.

# let int_of = function 
`Entier n -> n
| `Reel r -> int_of_float r ;;
val int_of : [<`Entier int|`Reel float] -> int = <fun>


A l'inverse, le symbole [< indique que l'argument de int_of est du type qui contient au plus les constructeurs `Entier int et `Reel float.

Il est tout de mme possible de dfinir un type par numration de ses constructeurs polymorphes :

Syntaxe


type t = [ `Nom1 | `Nom2 | ... | `Nomn ]
ou pour des types paramtrs :

Syntaxe


type ('a,'b,...) t = [ `Nom1 | `Nom2 | ... | `Nomn ]


# type valeur = [ `Entier int | `Reel float ] ;;
type valeur = [`Entier int|`Reel float]


Les constructeurs polymorphes, comme leur nom l'indique, peuvent prendre des arguments de plusieurs valeurs.

# let v1 = `Nombre 2
and v2 = `Nombre 2.0 ;;
val v1 : [>`Nombre int] = `Nombre 2
val v2 : [>`Nombre float] = `Nombre 2
Mais pour autant, v1 et v2 ne sont pas du mme type.

# v1=v2 ;;
Characters 4-6:
This expression has type [>`Nombre float] but is here used with type
[>`Nombre int]


D'une manire plus gnrale, les contraintes sur le type des arguments des constructeurs polymorphes sont accumules dans leur type par l'annotation &.

# let test_nul_entier = function `Nombre n -> n=0 
and test_nul_reel = function `Nombre r -> r=0.0 ;;
val test_nul_entier : [<`Nombre int] -> bool = <fun>
val test_nul_reel : [<`Nombre float] -> bool = <fun>
# let test_nul x = (test_nul_entier x) || (test_nul_reel x) ;;
val test_nul : [<`Nombre int & float] -> bool = <fun>
Le type de test_nul indique que les seules valeurs acceptes par cette fonction sont celles avec le constructeur `Nombre et un argument qui est la fois de type int et de float. C'est dire que les seules valeurs acceptables sont de type 'a !

# let f () = test_nul (failwith "rend une valeur de type 'a") ;;
val f : unit -> bool = <fun>


Les types des constructeurs polymorphes sont eux-mmes susceptibles d'tre polymorphes.

# let id = function `Ctor -> `Ctor ;;
val id : [<`Ctor] -> [>`Ctor] = <fun>
Le type de la valeur de retour de id est << les ensembles de constructeurs qui contiennent au moins `Ctor >> donc c'est un type polymorphe qui peut s'instancier en un type plus prcis. De mme, l'argument de id est << les ensembles de constructeurs qui contiennent au plus `Ctor >> qui lui aussi est susceptible d'tre prcis. En consquence, ils suivent le mcanisme gnral des types polymorphes d'Objective CAML savoir qu'ils sont susceptibles d'tre affaiblis.

# let v = id `Ctor ;;
val v : _[>`Ctor] = `Ctor
v tant le rsultat d'une application, son type n'est pas polymorphe (d'o la prsence du caractre _).

# id v ;;
- : _[>`Ctor] = `Ctor
v est monomorphe et son type est un sous-type de << contient au moins le constructeur `Ctor >>. Le fait de l'appliquer id va forcer son type tre un sous-type de << contient au plus le constructeur `Ctor >>. Logiquement, il doit maintenant avoir le type << contient exactement `Ctor >>. Ce que nous vrifions.

# v ;;
- : [`Ctor] = `Ctor


la manire des classes, les types des constructeurs polymorphes peuvent tre ouverts.

# let is_entier = function
`Entier (n : int) -> true
| _ -> false ;;
val is_entier : [<`Entier int| ..] -> bool = <fun>
# is_entier (`Entier 3) ;;
- : bool = true
# is_entier `Autre ;;
- : bool = false
Tous les constructeurs sont accepts, mais le constructeur `Entier doit avoir un argument entier.

# is_entier (`Entier 3.0) ;;
Characters 12-23:
This expression has type [>`Entier float] but is here used with type
[<`Entier int| ..]


Toujours comme les classes, les types des constructeurs peuvent tre cycliques.

# let rec long = function `Rec x -> 1 + (long x) ;;
val long : ([<`Rec 'a] as 'a) -> int = <fun>


Pour finir, notons que le type peut tre en mme temps un sous-ensemble et un sur ensemble de constructeurs. Commenons par un exemple simple.

# let ex1 = function `C1 -> `C2 ;;
val ex1 : [<`C1] -> [>`C2] = <fun>
Maintenant nous identifions les types d'entre et de sortie de l'exemple par un second filtre.

# let ex2 = function `C1 -> `C2 | x -> x ;;
val ex2 : ([<`C2|`C1| .. >`C2] as 'a) -> 'a = <fun>
Nous obtenons donc le type ouvert qui contient au moins `C2 puisque le type de retour contient au moins `C2.

# ex2 ( `C1 : [> `C1 ] ) ;; (* est bien un sous type de [<`C2|`C1| .. >`C2] *)
- : _[>`C2|`C1] = `C2
# ex2 ( `C1 : [ `C1 ] ) ;; (* n'est pas un sous type de [<`C2|`C1| .. >`C2] *)
Characters 6-9:
This expression has type [`C1] but is here used with type [<`C2|`C1| .. >`C2]



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora215.html0000644000000000000000000000125107421273603014470 0ustar Notes
1
Nous conserverons l'usage du mot anglais.
ocaml-book-1.0/fr/html/book-ora214.html0000644000000000000000000000641407421273603014475 0ustar Navigateur OCamlBrowser Prcdent Index Suivant

Navigateur OCamlBrowser

OCamlBrowser est un environnement de dveloppement pour Objective CAML dvelopp en utilisant la bibliothque LablTk ce qui lui permet d'avoir une interface graphique. Il intgre un << navigateur >> permettant de se balader entre diffrents modules, de visualiser leur contenu (noms des valeurs et types) et de les diter.

Lorsqu'on lance OCamlBrowser par la commande ocamlbrowser, on obtient la liste de tous les modules compils disponibles (voir figure B.2). On peut en ajouter en donnant les chemins permettant de les trouver. Depuis le menu File, on peut lancer une boucle d'interaction ou un diteur dans une nouvelle fentre.




Figure B.2 : OCamlBrowser : la fentre principale


En cliquant sur un des modules, une nouvelle fentre s'ouvre affichant son contenu (voir figure B.3). En slectionnant une valeur, on voit son type apparatre en bas de la fentre.




Figure B.3 : OCamlBrowser : contenu d'un module


Depuis la fentre principale, on peut faire une recherche sur le nom d'une fonction. Le rsultat apparat dans une nouvelle fentre. La figure B.4 montre le rsultat de la recherche sur le mot create.




Figure B.4 : OCamlBrowser : recherche sur create


Il y a d'autres possibilits que nous laissons l'utilisateur dcouvrir.




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora100.html0000644000000000000000000004423007421273602014464 0ustar Exercices Prcdent Index Suivant

Exercices

Traces d'applications

Cet exercice montre l'valuation des arguments au moment de l'application.
  1. Activer la trace de la fonction List.fold_left et valuer l'expression suivante :

    List.fold_left (-) 1 [2; 3; 4; 5];;
    Que vous indique la trace?

    # #trace List.fold_left ;;
    List.fold_left is now traced.

    # List.fold_left (-) 1 [2; 3; 4; 5] ;;
    List.fold_left <-- <fun>
    List.fold_left --> <fun>
    List.fold_left* <-- <poly>
    List.fold_left* --> <fun>
    List.fold_left** <-- [<poly>; <poly>; <poly>; <poly>]
    List.fold_left <-- <fun>
    List.fold_left --> <fun>
    List.fold_left* <-- <poly>
    List.fold_left* --> <fun>
    List.fold_left** <-- [<poly>; <poly>; <poly>]
    List.fold_left <-- <fun>
    List.fold_left --> <fun>
    List.fold_left* <-- <poly>
    List.fold_left* --> <fun>
    List.fold_left** <-- [<poly>; <poly>]
    List.fold_left <-- <fun>
    List.fold_left --> <fun>
    List.fold_left* <-- <poly>
    List.fold_left* --> <fun>
    List.fold_left** <-- [<poly>]
    List.fold_left <-- <fun>
    List.fold_left --> <fun>
    List.fold_left* <-- <poly>
    List.fold_left* --> <fun>
    List.fold_left** <-- []
    List.fold_left** --> <poly>
    List.fold_left** --> <poly>
    List.fold_left** --> <poly>
    List.fold_left** --> <poly>
    List.fold_left** --> <poly>
    - : int = -13


    La trace indique seulement le passage des arguments la fonction List.fold_left, mais n'affiche pas les valeurs des arguments. La nature polymorphe des paramtres de List.fold_left empche la trace de pouvoir afficher les valeurs des arguments passs.


  2. Dfinir la fonction fold_left_int, identique List.fold_left, mais dont le type est : (int -> int -> int) -> int -> int list -> int.
    Tracer cette fonction. Pourquoi l'affichage de la trace est-il diffrent?

# let rec fold_left_int f (r : int) (l : int list) =
match l with
[] -> r
| t::q -> fold_left_int f (f r t) q ;;
val fold_left_int : (int -> int -> int) -> int -> int list -> int = <fun>

# #trace fold_left_int ;;
fold_left_int is now traced.
# fold_left_int (-) 1 [2; 3; 4; 5] ;;
fold_left_int <-- <fun>
fold_left_int --> <fun>
fold_left_int* <-- 1
fold_left_int* --> <fun>
fold_left_int** <-- [2; 3; 4; 5]
fold_left_int <-- <fun>
fold_left_int --> <fun>
fold_left_int* <-- -1
fold_left_int* --> <fun>
fold_left_int** <-- [3; 4; 5]
fold_left_int <-- <fun>
fold_left_int --> <fun>
fold_left_int* <-- -4
fold_left_int* --> <fun>
fold_left_int** <-- [4; 5]
fold_left_int <-- <fun>
fold_left_int --> <fun>
fold_left_int* <-- -8
fold_left_int* --> <fun>
fold_left_int** <-- [5]
fold_left_int <-- <fun>
fold_left_int --> <fun>
fold_left_int* <-- -13
fold_left_int* --> <fun>
fold_left_int** <-- []
fold_left_int** --> -13
fold_left_int** --> -13
fold_left_int** --> -13
fold_left_int** --> -13
fold_left_int** --> -13
- : int = -13
# #untrace_all ;;
fold_left_int is no longer traced.
List.fold_left is no longer traced.
La fonction fold_left_int est monomorphe. Le mcanisme de trace connat le type des arguments et peut alors les afficher lors de leur passage.

valuation des performances

On reprend l'exercice propos au chapitre 9, page ??, o l'on comparait l'volution du tas pour deux programmes (l'un rcursif terminal et l'autre non) de calcul de nombres premiers. On compare maintenant les temps passs dans chaque fonction en utilisant les outils de profiling et de temps d'excution. Cet exercice permet d'illustrer l'intrt de l'expansion en ligne (voir chapitre 7).

  1. Compiler avec l'option de profiling les deux programmes erart et eranrt en utilisant le compilateur de code-octet et le compilateur natif.
    $ ocamlcp -custom -o era_rt_bc unix.cma interval.ml trgc.ml eras2.ml era2_main.ml main_rt.ml -cclib -lunix
    $ ocamlcp -custom -o era_nrt unix.cma interval.ml trgc.ml eras.ml era2_main.ml main_nrt.ml -cclib -lunix
    $ ocamlopt -p -o era_rt_nat unix.cmxa interval.ml trgc.ml eras2.ml era2_main.ml main_rt.ml -cclib -lunix
    $  ocamlopt -p -o era_nrt_nat unix.cmxa interval.ml trgc.ml eras.ml era2_main.ml main_nrt.ml -cclib -lunix
    


  2. Excuter ces programmes en leur passant les nombres 3000 4000 5000 6000 sur la ligne de commande.
    $ era_rt_bc traceRT-BC 3000 4000 5000 6000
    $ era_nrt_bc traceNRT-BC 3000 4000 5000 6000
    $ era_rt_nat traceRT-NAT 3000 4000 5000 6000
    $ era_nrt_nat traceNRT-NAT 3000 4000 5000 6000
    


  3. Visualiser les rsultats par les commandes ocamlprof et grpof. Que pouvez-vous dire au vu des rsultats ? Compilateur de code-octet :

    La commande ocamlprof lit les informations du fichier camlprof.dump. Si plusieurs excutions diffrentes ont t lances, seule l'information de cette dernire est accessible.

    Voici les informations ressorties pour la version rcursive terminale :
    $ ocamlprof eras2.ml
    (* fichier eras2.ml *)
    
    let erart l  = 
      (* 4 *) let rec erart_aux l r = (* 2436 *) match l with 
        [] -> (* 4 *) List.rev r
      | p::q -> (* 2432 *) erart_aux  (List.filter ( fun x -> (* 808410 *) x mod p <> 0) q) (p::r) 
      in 
        erart_aux l [] ;;
    
    let erart_go n = 
      (* 4 *) erart (Interval.interval (<)  (fun x -> (* 17992 *) x + 1) 2 n) ;;
    
    et pour la version rcursive terminale :
    $ ocamlprof eras.ml
    (* fichier eras.ml *)
    
    let rec eras l  = (* 2436 *) match l with 
      [] -> (* 4 *) []
    | p::q -> (* 2432 *) p:: (eras (List.filter ( fun x -> (* 808410 *) x mod p <> 0) q)) ;;
    
    let era_go n = 
      (* 4 *) eras (Interval.interval (<)  (fun x -> (* 17992 *) x + 1) 2 n) ;;
    
    On s'aperoit que dans les deux cas, il y a 2436 appels la fonction principale, dont 4 avec une liste vide et 2432 dans l'autre cas.

    Attention le profilage rend invalide de nombreuses optimisations du compilateur.

    Compilateur natif :

    L'excution d'un excutable autonome natif compil en mode -p produit un fichier gmon.out.

    $ gprof era_rt_nat 
    
    affiche tout d'abord le temps pass dans chaque fonction :
    Flat profile:
    
    Each sample counts as 0.01 seconds.
      %   cumulative   self              self     total           
     time   seconds   seconds    calls  us/call  us/call  name    
     44.44      0.12     0.12   808410     0.15     0.15  Eras2_fun_112
     18.52      0.17     0.05     2436    20.53    32.67  List_rev_append_57
     11.11      0.20     0.03     2432    12.34    73.68  List_find_213
      7.41      0.22     0.02      114   175.44   175.44  sweep_slice
      7.41      0.24     0.02       34   588.24   588.24  mark_slice
      3.70      0.25     0.01    76203     0.13     0.13  allocate_block
      3.70      0.26     0.01    64466     0.16     0.31  oldify
    ...
    
    puis le graphe d'appel.

    $ gprof era_nrt_nat 
    
    affiche tout d'abord le temps pass dans chaque fonction :
    Flat profile:
    
    Each sample counts as 0.01 seconds.
      %   cumulative   self              self     total           
     time   seconds   seconds    calls  us/call  us/call  name    
     42.42      0.14     0.14   808410     0.17     0.17  Eras_code_begin
     15.15      0.19     0.05     2432    20.56    95.63  List_find_213
     15.15      0.24     0.05     2432    20.56    39.31  List_rev_append_57
      6.06      0.26     0.02    93936     0.21     0.53  oldify
      6.06      0.28     0.02    74519     0.27     0.27  allocate_block
      6.06      0.30     0.02       37   540.54   540.54  mark_slice
      3.03      0.31     0.01    74519     0.13     0.40  alloc_shr
    ...
    
    L'outil ocamlprof compte le nombre d'appels ou de passage dans certaines parties d'une fonction sans indication de temps.

    Par contre , l'outil gprof est plus prcis car il indique les temps passs dans chaque fonction.

    Attention les versions compiles avec l'option -p effectuent un travail supplmentaire qui est perceptible lors de mesures de temps. Les versions .exe sont compiles sans cette option.
    $ time era_rt.exe a1 3000 4000 5000 6000
    0.230u 0.010s 0:00.25 96.0%     0+0k 0+0io 129pf+0w
    $ time era_rt_nat a2 3000 4000 5000 6000
    0.510u 0.010s 0:00.52 100.0%    0+0k 0+0io 134pf+0w
    $ time era_nrt.exe a3 3000 4000 5000 6000
    0.220u 0.020s 0:00.24 100.0%    0+0k 0+0io 130pf+0w
    $ time era_nrt_nat a4 3000 4000 5000 6000
    0.520u 0.010s 0:00.53 100.0%    0+0k 0+0io 134pf+0w
    $ visu a1
    Nombre de Gc: mineur = 131, majeur = 20
    $ visu a2
    Nombre de Gc: mineur = 131, majeur = 20
    $ visu a3
    Nombre de Gc: mineur = 131, majeur = 23
    $ visu a4
    Nombre de Gc: mineur = 131, majeur = 23
    
    $ gprof era_rt_nat 
    

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora065.html0000644000000000000000000000320107421273601014466 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

Ce chapitre prsente les diffrents modes de compilation d'un programme Objective CAML et compare leurs caractristiques de portabilit et d'efficacit. La premire section explique les diffrentes tapes des compilateurs Objective CAML. La deuxime section dcrit les diffrents modes de compilation et leur syntaxe pour la production d'excutables. La troisime section montre la construction d'excutables autonomes, c'est--dire indpendants de l'installation du systme Objective CAML. Enfin la quatrime section compare ces diffrents modes de compilation par rapport aux caractristiques de portabilit et d'efficacit d'excution.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora045.gif0000644000000000000000000000460007073350152014271 0ustar GIF89aUUU!,ڋ޼H扦ʶ rJԌ7bш ̦ \B)j aܮ_иN5gg۽޼z}=G4H8Qx8)Ypbi@YR)))*i!   `jLAkb|@il }䅜gZ֣p@onOCEė2qW#V+k˨"%^pR- 9#Pnƙ4UfrxF!XQD0J( bui8:-EIi'lIr跁BiO1B]ذh|SS)KDY.y S%`@k\ XRpļSbb*W 'A^6}%37IMnonQȾQ[Pg'~V)L+r&s+CLպA*iuSU%QA'+%Q_? hVp u.x^Ճg0xa!X ̀$hF>.xp0Hc/c:c>dBIdFdJ.dN> %0bV^EaeG|^Ifi}eɦz[p2DmI5)uB'&f }U9h%j* D^jऄYih 飍@jiz.kȺP)] ,¶g^k,l>[dZKmvmv;*7{޹d)n&a/^ <| }( ;ȿHq_|1["Gqvp6л1)\2+`4i\901s+ fP=OsRGJ |Nát=u-'Z-FˆYXߴ`{FQ23m%KbSMڸ_xpFFU9`Q爽sAMKUa|}a2R5Ĺ#>h!$夑Yi:}u/5썇Me= n\^tk|}ԡz:Q~eaW];"c106W-vmkϿ4 gymzNJԆ7з 1}J3a ɠLtzc׿='uSPWjx.M6ʮBR*~˞g!A?L-/"1ǃ!"En\Evb+ɘ 1EgRD5CoQhqݑ_\2 pCHԖ!F |d"iI"I&I!}Re (GtJ@ "4c@8RUɊ\rhs#V([bUb,TQȸ gє&Pp$IN*dDk1vFm-١8!s<ʉ yHҝ+S_9|+2l'n$XԓKh<.Q 2J;hiv~Z1!3+5F9zlg'Byngĸˠ X0o(3QrQ MRH-"YZ-P+UeuucMKԳ6,j]DY'ֵb5$]V޵sݫ*W>udN_ +"c 2 KO&2MleXY ,ވVZ)Y0CE(Շ5k%ؒ6ES!ꕷkK▨l-hoGw"7}1\t:*f%t? &no!Cݢ*ֺ7j^B`O}Yos\ &\ AM#o.gjtvpE,"ssiQDWLFGF28Ey\ݏAfJN[+3 -mkbϢxh9vͫ7S=y9eK|Ylߺp:(5KMbc\xyp6dI)ܦ OɋNe>Ot&2ru:ЏMSd9@G aJeBk)3oƼ^pK] o7̞{}2H>lˌHV *x=j٨\.4By]즭;vkd|cGQkoA .BVÉdb+,ՉG\ 9;|z 9PK9_T<19Y>/bRa.Z7 s ~sQ Q2rD,OoW>uQ鳩֙+-z|:NfuSkO{R]p*]`3·;ocaml-book-1.0/fr/html/book-ora010.html0000644000000000000000000000425507421273601014466 0ustar Test de l'installation Prcdent Index Suivant

Test de l'installation

Une fois l'installation de l'environnement de dveloppement d'Objective CAML termine, il est ncessaire de la tester pour principalement vrifier les chemins d'accs aux commandes et aux bibliothques. Le plus simple est de lancer la boucle d'interaction du systme et d'crire le premier petit programme suivant :
String.concat "/" ["un"; "chemin"; "ici"] ;;
Cette expression concatne les diffrentes chanes de caractres en insrant le caractre <</>> entre chaque mot. La notation String.concat indique l'utilisation de la fonction concat de la bibliothque String. Si les chemins d'accs aux bibliothques ne sont pas bons, le systme affichera une erreur. On notera que le systme indique que le calcul retourne une chane de caractres et affiche le rsultat.

La documentation sur cette fonction String.concat peut tre trouve dans le manuel de rfrence en ligne en suivant les liens The standard library puis Module String: string operations.

Pour sortir de la boucle d'interaction, l'utilisateur doit crire la directive <<#quit ;;>>.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora085.html0000644000000000000000000000404307421273602014476 0ustar Mmoire et programme Prcdent Index Suivant

Mmoire et programme

Un programme en langage machine est une suite d'instructions manipulant des valeurs en mmoire. Cette mmoire est gnralement compose des lments suivants:
  • les registres (zone mmoire d'accs direct et rapide)
  • la pile (pile d'excution - stack)
  • zone d'allocation statique (segment de donnes ou data segment)
  • zone d'allocation dynamique (tas ou heap)
Seules les zones de la pile et d'allocation dynamique peuvent voluer en taille pendant l'excution d'un programme. Selon le langage de programmation utilis, un certain contrle de la gestion de ces lments mmoire est ou non prvu. Dans le cadre du chargement dynamique de code (voir page ??), celui-ci prend place dans une zone d'allocation dynamique.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora123.html0000644000000000000000000000250007421273603014464 0ustar Notes
1
Celle-ci diffre entre le compilateur de code-octet et le compilateur natif.
2
Rappelons qu'une fonction telle que fst, de type 'a * 'b -> 'a, ne prend pas deux arguments mais un seul qui est un couple.
3
Sous Unix, par dfaut il s'agit de /usr/local/lib/ocaml.
Sous Windows, par dfaut il s'agit de C:\OCAML\LIB.
4
Dans cette srie d'exemples le bit de tag est le bit de poids faible.
ocaml-book-1.0/fr/html/book-ora018.html0000644000000000000000000001273507421273601014500 0ustar Polymorphisme et valeurs de retour des fonctions Prcdent Index Suivant

Polymorphisme et valeurs de retour des fonctions

Le polymorphisme paramtrique d'Objective CAML permet de dfinir des fonctions dont le type de retour est compltement indtermin. Par exemple :

# let id x = x ;;
val id : 'a -> 'a = <fun>


Cependant, le type de retour dpend du type de l'argument. Ainsi, lorsque l'on applique la fonction id un argument, le mcanisme d'infrence de types sait instancier la variable de type 'a. Donc, pour chaque utilisation particulire, le type de id peut tre dtermin.

S'il n'en tait pas ainsi, l'utilisation du typage statique fort, charg d'assurer la sret d'excution, n'aurait plus de sens. En effet, une fonction d'un type entirement indtermin comme 'a -> 'b autoriserait n'importe quelle conversion de type qui amnerait invitablement une erreur durant l'excution puisque la reprsentation physique des valeurs de type diffrents ne sont pas les mmes.

Apparente contradiction

Cependant, il est possible de dfinir dans le langage Objective CAML des fonctions dont le type de retour contient une variable de type n'apparaissant pas dans le type de ses arguments. Nous allons en considrer quelques exemples et voir pourquoi une telle possibilit n'est pas contradictoire avec le typage statique fort.

Voici un premier exemple :

# let f x = [] ;;
val f : 'a -> 'b list = <fun>


Cette fonction permet de construire une valeur polymorphe partir de n'importe quoi :

# f () ;;
- : '_a list = []
# f "n'importe quoi" ;;
- : '_a list = []


Nanmoins, la valeur obtenue n'est pas entirement indtermine : il s'agit d'une liste. Elle ne pourra donc pas tre utilise n'importe o.

Voici trois exemples de fonctions dont le type est celui redout 'a -> 'b :

# let rec f1 x = f1 x ;;
val f1 : 'a -> 'b = <fun>
# let f2 x = failwith "n'importe quoi" ;;
val f2 : 'a -> 'b = <fun>
# let f3 x = List.hd [] ;;
val f3 : 'a -> 'b = <fun>


Ces fonctions ne sont, en fait pas dangereuses, vis--vis de la sret d'excution car il n'est pas possible de les utiliser pour construire une valeur : la premire boucle, les deux dernires dclenchent une exception qui interrompt le calcul.

C'est galement pour ne pas pouvoir dfinir de fonction de type 'a -> 'b que les nouveaux constructeurs d'exceptions ne peuvent pas avoir d'argument dont le type contient une variable.

En effet, si l'on pouvait dclarer une exception polymorphe Poly_exn de type 'a -> exn, on pourrait alors crire la fonction :

let f = function
0 -> raise (Poly_exn false)
| n -> n+1 ;;


La fonction f tant de type int -> int et Poly_exn tant de type 'a -> exn, on pourrait alors dfinir :

let g n = try f n with Poly_exn x -> x+1 ;;
Cette fonction est galement bien type (puisque l'argument de Poly_exn peut tre quelconque) et, ds lors, l'valuation de (g 0) aboutirait la tentative d'additionner un entier et un boolen !




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora030.gif0000644000000000000000000000415407073350152014267 0ustar GIF89a7 UUU!,7 ڋޜHBB LCk/ D"L*#srݜϪUAҎYwSL>øi~.n|8hGX!`x8E)iy *:JZjzz k' [;v{ƫJ,lel|L̬:]T m-ٽ)Cy^^~ɮg?/&/oؿO +x 50!$V( smd"ydI7@ReKd\%.7q'Μ@gBşG])Ȩ͢Jխ\z 6رd˚=Vjҵlۺ} 7.[$rڽ.޽|4Bts*p‹1PzWvD5Gfy9tfȤ|rjҮBdk;5a"|dk8`|尖y $7d@8,wxu3U]bN߈*0 Dq‹Edu="&fdNƘI8e"*/=&@F-ribvw/ gpgHsciglhs :tv駐o΁_hYhE&v H)~^i5ȇfZ]b=P ~z] ꪪ"w6 '<r,lXk.zZ5v,Vjh؜K b;(ri/G:+M{m7=p^4 1NE qZ_qoq 2D~2@]HrԗH(b2Qu3Fz=w44 #D)t tR<_}Qs=-=)7qֱP0ӖcݴO@FNmrgp^b-:Œ韻 뱟gx~ yo{ֶ$|+4]?Nz;ni%cz7W G7/~?zOwbʹ/[u@ʥ|ߨBd` V2:B~! c P~ TsyἘcpX9[0@GDbK(.'`#,zD҆:y9\8mh-)غi7kLJ/5G4@J 2Td_Eb>|D.Kd_#=Z?'o+Tr.Ld8ER"a(G8Д@e(AЕĂ-#yueR;iU8T1FUxY.$Y#%օuSZV\C߸j5%uuk6WKǮ{+! j<,^nAp,I2a*meYY 6eeuFdj; kmְ֝U0l+Gjv+lv+Y`@U;|`8p;[2|nu཮x}k:8UlyߋMVd֍rw[*/}ZVW'|;UWy^DKzl3 C-d;a_D \aX&$N|۞ŕJOX4>/0*=j=rCa F<4vPYڔ< c 0yWhձNYl㊵9hdsjTgX3WE=)#3hK6Omg5:&D,iDȊ(YI54 jɌTwZ՘3_};ocaml-book-1.0/fr/html/book-ora056.gif0000644000000000000000000001220207073350152014270 0ustar GIF89a?[}}}{{{yyywwwuuuqqqWWW333tttrrrnnnllljjj2P2!R,?[R*am)`(_&^%]$\#["!Y XWVUrH nXȰᔇŋ 㠣" Cl@2 0$IYɲ%0c I悛8sɳ@ ѣH"XʴSJիj꣫ׯŠۣٳҪUˣ۷ʝݻݫ߿La8+^)oHL9$PܠeK(]\dkIc~J}~x*1w. u뇳gg̽{bʔVnMHau]u! XY' -/lfRxq@a@<l@(dTDVeH dXLP2'sTJgeuXfv\~d`9&ze54nj xu¬ p[8km[["4ꨔJje^Xm*Y} j,侱F/xis+'εPcιć#}Jg8{~S'ӭ~,X`:>p``8׻^e9kg;vH]L>1&?~o{ /v mxvO^Mw}y||%;E< zn~z=M/wᬧ}m{ܯӖ}TOD&~ g A~~ҏ/uKwm}a?>!o~S߼w'_E^ߏ|Οwen'kd~%'vQmjGVɇdjHj!8d t%hI6Bv(sm28JvȀ7fʇx~GwHSzw:qWȄϖsօw]X`&rFfhqfs؅vUHPpn6LÆmb_X|f(bR8vkg4Hlhh(g{hkg+VO6Ė{b؊Xkg8vuƋLVٖW(dhh˸dgV|xeHhxj88(hѨ֌j֍M6kXf8g(|^ǎxz(VO6lgI֎Xe )g eِhIihYlyh h"kIe'"8,f2Yfe5 V֒Aj7w9Ye@zVVv.JigLٔ(Q攋xojZ 操HjCF jjfɕk6[pك&fwrIft f|q7i@}I8Kfg׈gcdi}g"|7v6H֙Ti9suyqM'sƚ,ٛiҘøsrݦKvD7si9z(iWƹɩ&l&29dy׉)fIsit)eidȹr镖 a7r3i3ɟ,%Z/B hrY99 eɇĩZpʡ|j9D f院*jz6 w4hN?ڣl雊i70*fZ6ǤP&fO ~)iVfjuiCNX}٥^Zfg%JlJOk:lG)@HqxDWڧ~kHifGp訏:ie㈨i:jyZJVYijGʩZVXM֩։|zke9j7wijwd:)ʓڪdګd鸝6JM8 _Ik dy*hwjd({*JIۜG|wyzh&|{"{ {ֱ췟'z{ꉤ-"#K55;h*{wʳ5X Y kh(}{}}GV{횈ƵzɖzGfˮm[iK8in[g]y{*g'92{8 [E{|k븞-Y˷6ˬ+t)[KшQ\ aڂ'jSY+7JgTj~뤻 H ȺV溯Kfțۤ`y;NjHi۽;ěBx9 K˽mʻu X ZGy+:{[o{\Fq׿ ڳ(+|Јh缳6Blkdˊ̿?x\1x5lwyycw*@m8wizyd>jU晶[ir6)`;,x5 Introduction Prcdent Index Suivant

Introduction

Le modle d'excution d'un programme sur un processeur correspond celui de la programmation imprative. Plus prcisment, un programme est une srie d'instructions dont l'excution modifie l'tat mmoire de la machine. Cette mmoire est constitue principalement des valeurs cres ou manipules par le programme. Or, comme toute ressource informatique, la mmoire disponible pour une application est une denre finie ; un programme tentant d'utiliser plus de ressources que le systme ne lui en attribue se retrouvera dans un tat incohrent. Pour cette raison, il est ncessaire d'optimiser l'utilisation de la mmoire en rutilisant la place occupe par des valeurs qui un instant donn ne sont plus ncessaires la suite de l'excution. Cette gestion mmoire a une influence importante sur l'excution du programmme et sur son efficacit.

Le fait de rserver une zone de la mmoire pour en faire un certain usage s'appelle l'allocation de mmoire. On distingue l'allocation statique qui est effectue au chargement du programme c'est--dire avant son excution proprement dite, et l'allocation dynamique qui intervient au cours mme de l'excution. Alors que la mmoire alloue statiquement le reste jusqu' la fin du programme, les zones alloues dynamiquement sont susceptibles d'tre libres, voire d'tre raffectes durant l'excution.

La rcupration d'une zone mmoire fait courir deux risques :
  • si une zone mmoire est libre alors qu'elle contient une valeur encore utilise, cette valeur risque d'tre corrompue lorsque le programme y accdera, on parle alors de pointeurs fantmes ;
  • si la localisation (l'adresse) d'une zone mmoire n'est plus connue alors celle-ci ne pourra plus tre libre avant la fin du programme. On parle alors de fuite de mmoire.
Lorsque la gestion de la mmoire est effectue par le programmeur, elle demande beaucoup de soins ce dernier pour viter ces deux cueils. Et cela peut s'avrer assez ardu quand les structures de donnes manipules par le programme sont complexes et en particulier quand elles partagent une mme zone mmoire.

Pour dcharger le programmeur de cet exercice difficile, des mcanismes de rcupration automatique de la mmoire ont t introduits dans de nombreux langages. L'ide matresse est qu' un moment quelconque de l'excution, les seules valeurs alloues dynamiquement susceptibles d'tre utiles sont celles dont le programme connat directement ou indirectement l'adresse. Toutes les valeurs qui ne peuvent plus tre atteintes ne le pourront pas plus dans le futur donc leur zone mmoire peut tre rcupre. Cette rcupration peut se faire immdiatement aprs qu'une valeur est devenue inutilisable, ou de manire retarde quand le programme a besoin de plus de place qu'il n'en dispose.

Objective CAML dispose d'un tel mcanisme, appel GC (Garbage Collector en anglais et Glaneur de Cellules en franais). La mmoire est alloue la construction d'une valeur (i.e. l'application d'un constructeur) et est libre implicitement. Dans la plupart des programmes, il n'est besoin de connatre du GC que son existence car il joue son rle de faon transparente. Par contre, ce mcanisme de gestion de mmoire peut avoir des consquences sur l'efficacit du programme pour des applications crant trs souvent de nouvelles valeurs. Dans de tels cas, il est utile d'avoir accs aux paramtres du GC, voire de pouvoir explicitement l'invoquer. De plus, il est ncessaire de comprendre ce qu'impose le GC la reprsentation des donnes pour interfacer Objective CAML avec un autre langage de programmation tel que cela est dcrit dans le chapitre 12.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora143.html0000644000000000000000000004565007421273602014502 0ustar Autres traits objet Prcdent Index Suivant

Autres traits objet

Rfrencements : self et super

Il est pratique, dans la dfinition d'une mthode d'une classe, de pouvoir invoquer une autre mthode de cette mme classe ou une mthode de la classe anctre. Pour cela Objective CAML autorise nommer l'objet lui-mme ou (les objets de) la classe anctre. Dans le premier cas, on indique le nom choisi aprs le mot cl object et dans le second cas, aprs la dclaration d'hritage.

Par exemple, pour dfinir la mthode to_string des points colors, il est prfrable d'invoquer la mthode to_string de la classe anctre et d'tendre son comportement en utilisant la nouvelle mthode get_color.

# class colored_point (x,y) c =
object (self)
inherit point (x,y) as super
val c = c
method get_color = c
method to_string () = super#to_string() ^ " [" ^ self#get_color ^ "] "
end ;;


Il est possible de donner des noms quelconques pour la classe anctre et sa sous-classe, mais autant utiliser la terminologie objet (self ou this pour soi-mme et super pour l'anctre). Choisir d'autres noms s'avre cependant utile dans le cas de l'hritage multiple pour diffrencier les anctres (voir page ??).

Warning


On ne peut pas rfrencer une variable d'instance anctre dans le cas o l'on dclare une nouvelle variable d'instance du mme nom qui masque la premire.


Liaison retarde

On appelle liaison retarde la dtermination l'excution de la mthode utiliser lors de l'envoi d'un message. La liaison retarde s'oppose la liaison statique qui est dtermine la compilation. En Objective CAML la liaison des mthodes est retarde. C'est donc le receveur d'un message qui sait quel est le code excuter.

La dclaration prcdente de la classe colored_point redfinit la mthode to_string. Cette nouvelle dfinition utilise la mthode get_color de la classe. Dfinissons prsent une nouvelle classe colored_point_bis hritire de colored_point, qui redfinit la mthode get_color (en testant la pertinence de la chane de caractres) mais qui ne redfinit pas to_string.

# class colored_point_bis coord c =
object
inherit colored_point coord c
val true_colors = ["blanc"; "noir"; "rouge"; "vert"; "bleu"; "jaune"]
method get_color = if List.mem c true_colors then c else "INCONNUE"
end ;;


La mthode to_string est la mme dans les deux classes de points colors, cependant, deux objets de chacune de ces classes n'auront pas le mme comportement.

# let p1 = new colored_point (1,1) "bleue comme une orange" ;;
val p1 : colored_point = <obj>
# p1#to_string();;
- : string = "( 1, 1) [bleue comme une orange] "
# let p2 = new colored_point_bis (1,1) "bleue comme une orange" ;;
val p2 : colored_point_bis = <obj>
# p2#to_string();;
- : string = "( 1, 1) [INCONNUE] "


La liaison de get_color dans le corps de to_string n'est donc pas fixe lors de la compilation de la classe colored_point. Le code excuter lors de l'invocation de la mthode get_color est dtermin en fonction des mthodes associes aux instances des classes colored_point et colored_point_bis. Ainsi, pour une instance de colored_point, l'envoi du message to_string provoque l'excution de get_color dfinie dans la classe colored_point. Alors que le mme envoi de message une instance de colored_point_bis invoque la mthode de la classe anctre, cette dernire active la mthode get_color de la classe fille qui contrle la pertinence de la chane reprsentant la couleur.

Reprsentation mmoire et envoi de messages

Un objet est dcoup en deux parties : l'une variable et l'autre fixe. La partie variable contient les variables d'instance, comme pour un enregistrement. La partie fixe correspond une table des mthodes qui est partage par toutes les instances de cette classe.

La table des mthodes est un tableau de fonctions et chaque mthode possde un numro qui est son indice dans ce tableau. On suppose qu'il existe une instruction machine GETMETHOD(o,n) qui prend en paramtre un objet o et un numro n. Elle retourne la fonction associe ce numro dans la table des mthodes. On note f_n le rsultat de l'appel GETMETHOD(o,n). La compilation de l'envoi de message o#m calcule le numro n de la mthode m et engendre le code de l'application GETMETHOD(o,n) l'objet o. Il correspond l'application de la fonction f_n l'objet receveur o. La liaison retarde est implante par l'appel GETMETHOD lors de l'excution.

L'envoi d'un message self dans le corps d'une mthode est lui aussi compil en une recherche du numro du message, suivi de l'application de la fonction trouve dans l'objet lui-mme.

Dans le cas d'un hritage, les mthodes, redfinies ou non, conservent le mme numro. Seule la table change pour les redfinitions. Ainsi l'envoi du message to_string sur une instance de la classe point appliquera la fonction de conversion d'un point alors que l'envoi du mme message sur une instance de colored_point trouvera au mme numro la fonction correspondant la mthode redfinie pour tenir compte du champ couleur.

C'est cette prservation des numros qui garantit que le sous-typage (voir page ??) est cohrent du point de vue de l'excution. En effet si un point color est explicitement contraint en point, l'envoi du message to_string calcule le numro de mthode de la classe point qui concide avec celui de la classe colored_point. La recherche de la mthode s'effectuera dans la table associe l'instance rceptrice, c'est--dire la table des colored_point.

L'implantation relle d'Objective CAML est lgrement plus complexe, mais le principe de recherche dynamique de la mthode employer reste le mme.

Initialisation

Il est possible d'indiquer par le mot cl initializer dans la dfinition de la classe, des traitements effectuer lors de la construction de l'objet. Cette initialisation peut effectuer tous les calculs et les accs aux champs ou aux mthodes de l'instance qui sont normalement autoriss dans une mthode.

Syntaxe


initializer expr


Reprenons l'exemple de la classe point pour dfinir un point bavard qui annonce sa cration.

# class verbose_point p =
object(self)
inherit point p
initializer
let xm = string_of_int x and ym = string_of_int y
in Printf.printf ">> Cration d'un point en (%s %s)\n" xm ym ;
Printf.printf " a distance %f de l'origine\n" (self#distance()) ;
end ;;

# new verbose_point (1,1);;
>> Cration d'un point en (1 1)
a distance 1.414214 de l'origine
- : verbose_point = <obj>


Une utilisation amusante et instructive des initialisateurs est de pouvoir suivre l'hritage des classes lors de la cration d'instances. En voici un exemple autant pdagogique que dpouill.

# class c1 =
object
initializer print_string "Cration d'une instance de c1\n"
end ;;

# class c2 =
object
inherit c1
initializer print_string "Cration d'une instance de c2\n"
end ;;

# new c1 ;;
Cration d'une instance de c1
- : c1 = <obj>
# new c2 ;;
Cration d'une instance de c1
Cration d'une instance de c2
- : c2 = <obj>
La construction d'une instance de c2 passe par la construction d'une instance de la classe anctre.

Mthodes prives

Une mthode peut tre dclare prive par le mot cl private. Elle apparatra dans l'interface de la classe mais pas dans le type des instances de cette classe. Une mthode prive peut tre invoque dans la dfinition des autres mthodes de la classe mais ne peut pas tre envoye une instance de cette classe. Par contre les mthodes prives sont hrites et pourront donc tre utilises dans les dfinitions de la hirarchie3.

Syntaxe


method private nom = expr


tendons la classe point en lui fournissant une mthode undo lui permettant de revenir sur son dernier mouvement. Pour raliser cela, nous devons nous souvenir de la position occupe avant d'effectuer un mouvement. Nous introduisons donc deux nouveaux champs old_x et old_y avec leur mthode de mise jour mem_pos. Nous ne voulons pas que l'utilisateur ait accs directement cette mthode aussi la dclarons nous prive. Il faut redfinir les mthodes moveto et rmoveto en mmorisant la position courante avant d'appeler les mthodes initiales de mouvement.

# class point_m1 (x0,y0) =
object(self)
inherit point (x0,y0) as super
val mutable old_x = x0
val mutable old_y = y0
method private mem_pos () = old_x <- x ; old_y <- y
method undo () = x <- old_x; y <- old_y
method moveto (x1, y1) = self#mem_pos () ; super#moveto (x1, y1)
method rmoveto (dx, dy) = self#mem_pos () ; super#rmoveto (dx, dy)
end ;;
class point_m1 :
int * int ->
object
val mutable old_x : int
val mutable old_y : int
val mutable x : int
val mutable y : int
method distance : unit -> float
method get_x : int
method get_y : int
method private mem_pos : unit -> unit
method moveto : int * int -> unit
method rmoveto : int * int -> unit
method to_string : unit -> string
method undo : unit -> unit
end


Remarquons que la mthode mem_pos apparat dans le type point_m1 prcde du mot cl private, elle ne pourra donc pas tre invoque par une instance de point alors que la mthode undo le pourra. C'est le mme cas de figure que pour les variables d'instance. Si les champs old_x et old_y figurent dans l'affichage rsultant de la compilation ils ne peuvent pas pour autant tre manipuls directement (voir ??).

# let p = new point_m1 (0, 0) ;;
val p : point_m1 = <obj>
# p#mem_pos() ;;
Characters 0-1:
This expression has type point_m1
It has no method mem_pos
# p#moveto(1, 1) ; p#to_string() ;;
- : string = "( 1, 1)"
# p#undo() ; p#to_string() ;;
- : string = "( 0, 0)"


Warning


Une contrainte de type peut rendre publique une mthode dclare avec l'attribut private.



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora007.html0000644000000000000000000000426507421273601014475 0ustar Description du Cdrom Prcdent Index Suivant

Description du Cdrom

Le cdrom est fourni sous forme d'une arborescence de fichiers. partir de la racine on trouve le fichier index.html de prsentation du cdrom ainsi que les cinq rpertoires catalogues suivants :
  • ouvrage : racine de la version HTML de l'ouvrage augment des solutions des exercices;
  • applis : applications dcrites dans l'ouvrage;
  • exercices : solutions autonomes des exercices proposs;
  • distrib : ensemble des distributions fournies par l'Inria, comme dcrit au paragraphe suivant;
  • outils : ensemble d'outils pour le dveloppement en Objective CAML;
  • docs : documentations en ligne de la distribution et des outils.
La lecture du cdrom passe par l'ouverture du fichier index.html de la racine partir de son navigateur prfr. Pour accder directement la version hypertexte de l'ouvrage, il suffit d'ouvrir le fichier ouvrage/index.html. On retrouve cette arborescence, mise jour en tenant compte des remarques des lecteurs, sur le site de l'diteur :

Lien


http://www.oreilly.fr



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora052.html0000644000000000000000000006753707421273601014510 0ustar Calculatrice graphique Prcdent Index Suivant

Calculatrice graphique

Nous reprenons l'exemple de la calculatrice dcrite dans le chapitre prcdent sur la programmation imprative (voir page ??). Nous la dotons d'une interface graphique la rendant plus facile tre utilise comme une calculette de bureau.

L'interface graphique matrialisera l'ensemble des touches (chiffres et fonctions) et une zone de visualisation des rsultats. Une touche pourra tre active soit via l'interface graphique (et la souris) soit directement au clavier. La figure 5.9 montre l'interface que l'on dsire construire.


Figure 5.9 : Calculatrice graphique


On rutilise les fonctions de trac de botes dcrites la page ??. On dfinit le type suivant :

# type etat_calc =
{ e : etat; t : (box_config * touche * string ) list; v : box_config } ;;
Il permet de conserver l'tat de la calculatrice, la liste des botes correspondant aux touches et la bote de visualisation. On dsire construire une calculatrice graphique facilement modifiable. On paramtre alors la construction de l'interface par une liste d'associations :

# let descr_calc =
[ (Chiffre 0,"0"); (Chiffre 1,"1"); (Chiffre 2,"2"); (Egal, "=");
(Chiffre 3,"3"); (Chiffre 4,"4"); (Chiffre 5,"5"); (Plus, "+");
(Chiffre 6,"6"); (Chiffre 7,"7"); (Chiffre 8,"8"); (Moins, "-");
(Chiffre 9,"9"); (MemoireOut,"RCL"); (Par, "/"); (Fois, "*");
(Off,"AC"); (MemoireIn, "STO"); (Clear,"CE/C")
] ;;


Gnration des botes des touches
partir de cette description on construit la liste des botes des touches. La fonction gen_boxes prend pour paramtres une description (descr), le nombre de colonnes (n), la sparation entre botes (wsep), la sparation entre le texte et les bords d'une bote (wsepint) et la taille des bords (wbord). Cette fonction retourne la liste des botes des touches ainsi que la bote de visualisation. Pour calculer ces placements, on dfinit les fonctions auxiliaires max_xy de calcul des tailles maximales d'une liste de couples d'entiers et max_lbox de calcul des positions maximales d'une liste de botes.

# let gen_xy vals comp o =
List.fold_left (fun a (x,y) -> comp (fst a) x,comp (snd a) y) o vals ;;
val gen_xy : ('a * 'a) list -> ('b -> 'a -> 'b) -> 'b * 'b -> 'b * 'b = <fun>
# let max_xy vals = gen_xy vals max (min_int,min_int);;
val max_xy : (int * int) list -> int * int = <fun>
# let max_boxl l =
let bmax (mx,my) b = max mx b.x, max my b.y
in List.fold_left bmax (min_int,min_int) l ;;
val max_boxl : box_config list -> int * int = <fun>


Voici la fonction principale gen_boxes de cration de l'interface.

# let gen_boxes descr n wsep wsepint wbord =
let l_l = List.length descr in
let nb_lig = if l_l mod n = 0 then l_l / n else l_l / n + 1 in
let ls = List.map (fun (x,y) -> Graphics.text_size y) descr in
let sx,sy = max_xy ls in
let sx,sy= sx+wsepint ,sy+wsepint in
let r = ref [] in
for i=0 to l_l-1 do
let px = i mod n and py = i / n in
let b = { x = wsep * (px+1) + (sx+2*wbord) * px ;
y = wsep * (py+1) + (sy+2*wbord) * py ;
w = sx; h = sy ; bw = wbord;
r=Top;
b1_col = grey1; b2_col = grey3; b_col =grey2}
in r:= b::!r
done;
let mpx,mpy = max_boxl !r in
let upx,upy = mpx+sx+wbord+wsep,mpy+sy+wbord+wsep in
let (wa,ha) = Graphics.text_size " 0" in
let v = { x=(upx-(wa+wsepint +wbord))/2 ; y= upy+ wsep;
w=wa+wsepint; h = ha +wsepint; bw = wbord *2; r=Flat ;
b1_col = grey1; b2_col = grey3; b_col =Graphics.black}
in
upx,(upy+wsep+ha+wsepint+wsep+2*wbord),v,
List.map2 (fun b (x,y) -> b,x,y ) (List.rev !r) descr;;
val gen_boxes :
('a * string) list ->
int ->
int ->
int -> int -> int * int * box_config * (box_config * 'a * string) list =
<fun>


Interaction
Comme l'on dsire aussi reprendre le squelette propos page ?? pour l'interaction, on dfinit les fonctions de gestion du clavier et de la souris qui s'intgrent ce squelette. La fonction de gestion du clavier est fort simple. Elle passe la traduction du caractre en valeur de type touche la fonction transition de la calculatrice, puis affiche le texte de l'tat de la calculatrice.

# let f_clavier ec c =
transition ec.e (traduction c);
draw_string_in_box Right (string_of_int ec.e.vaf) ec.v Graphics.white ;;
val f_clavier : etat_calc -> char -> unit = <fun>


La gestion de la souris est un peu plus complexe. Elle ncessite de vrifier que la position du clic souris est bien dans une des botes des touches. Pour cela on dfinit tout d'abord la fonction auxiliaire mem qui vrifie l'appartenance d'une position un rectangle.

# let mem (x,y) (x0,y0,w,h) =
(x >= x0) && (x< x0+w) && (y>=y0) && ( y<y0+h);;
val mem : int * int -> int * int * int * int -> bool = <fun>
# let f_souris ec x y =
try
let b,t,s =
List.find (fun (b,_,_) ->
mem (x,y) (b.x+b.bw,b.y+b.bw,b.w,b.h)) ec.t
in
transition ec.e t;
draw_string_in_box Right (string_of_int ec.e.vaf ) ec.v Graphics.white
with Not_found -> ();;
val f_souris : etat_calc -> int -> int -> unit = <fun>


La fonction f_souris cherche si la position de la souris lors du clic est bien dans une des botes correspondant une touche, si oui elle passe la touche correspondante la fonction de transition puis affiche le rsultat, sinon elle ne fait rien.

La fonction f_exc gre les exceptions pouvant se dclencher lors de l'excution de ce programme.

# let f_exc ec ex =
match ex with
Division_by_zero ->
transition ec.e Clear;
draw_string_in_box Right "Div 0" ec.v (Graphics.red)
| Touche_non_valide -> ()
| Touche_off -> raise Fin
| _ -> raise ex;;
val f_exc : etat_calc -> exn -> unit = <fun>


Si une division par zro survient, elle remet dans l'tat initial la calculatrice et affiche un message d'erreur l'cran de la calculatrice. Une touche non valide est simplement ignore. Enfin l'exception Touche_off dclenche l'exception Fin de sortie de la boucle du squelette.

Initialisation et sortie
L'initialisation de la calculatrice ncessite de calculer la taille de la fentre. La fonction suivante engendre les informations graphiques des botes partir d'une association touche-texte et retourne la taille de la fentre principale.

# let create_e t =
Graphics.close_graph ();
Graphics.open_graph " 10x10";
let mx,my,v,lb = gen_boxes t 4 4 5 2 in
let s = {dce=0; dta = false; doa = Egal; vaf = 0; mem = 0} in
mx,my,{e=s; t=lb;v=v};;
val create_e : (touche * string) list -> int * int * etat_calc = <fun>


La fonction d'initialisation utilise le rsultat de la fonction prcdente.

# let f_init mx my ec () =
Graphics.close_graph();
Graphics.open_graph (":0 "^(string_of_int mx)^"x"^(string_of_int my));
Graphics.set_color grey2;
Graphics.fill_rect 0 0 (mx+1) (my+1);
List.iter (fun (b,_,_) -> draw_box b) ec.t;
List.iter
(fun (b,_,s) -> draw_string_in_box Center s b Graphics.black) ec.t ;
draw_box ec.v;
draw_string_in_box Right "hello" ec.v (Graphics.white);;
val f_init : int -> int -> etat_calc -> unit -> unit = <fun>


Enfin la fonction de sortie ferme la fentre graphique.

# let f_fin e () = Graphics.close_graph();;
val f_fin : 'a -> unit -> unit = <fun>


La fonction go, paramtre par une description lance la boucle d'interaction.

# let go descr =
let mx,my,e = create_e descr in
squel (f_init mx my e) (f_fin e) (f_clavier e) (f_souris e) (f_exc e);;
val go : (touche * string) list -> unit = <fun>


L'appel go descr_calc correspond la figure 5.9.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora134.html0000644000000000000000000010073207421273602014473 0ustar Exemple : gestion de comptes bancaires Prcdent Index Suivant

Exemple : gestion de comptes bancaires

Nous terminons ce chapitre par la ralisation d'un petit exemple illustrant les principaux traits de l'utilisation d'une programmation modulaire : abstraction de type ; diverses vues d'un module ; rutilisation de code l'aide des foncteurs.

Le but de cet exemple est de fournir deux modules de gestion d'un compte bancaire. L'un est destin au banquier et l'autre au client. Le principe est de raliser un module paramtr gnral fournissant toutes les fonctionnalits souhaitables de gestion puis de l'appliquer certains paramtres en le contraignant avec la signature correspondant sa destination finale : le banquier ou le client.

Organisation de l'application




Figure 14.1 : graphe de dpendances des modules


Les deux modules finals BGestion et CGestion sont obtenus par contrainte sur la signature du module Gestion. Ce dernier est obtenu par application du foncteur FGestion aux modules Compte, Date et deux autres modules construits galement par application des foncteurs FHisto et FReleve. La figure 14.1 illustre ces dpendances.

Signatures des modules paramtres

Un module de gestion de compte bancaire est paramtr par quatre autres modules dont nous donnons et commentons ci-dessous les signatures.

Le compte lui-mme
Ce module fournit essentiellement les opration de calcul sur le contenu du compte.

# module type COMPTE = sig
type t
exception OperationImpossible
val creer : float -> float -> t
val depot : float -> t -> unit
val retrait : float -> t -> unit
val solde : t -> float
end ;;
Cet ensemble de fonctions fournit les oprations minimales de gestion du contenu du compte. L'opration de cration prend en argument le solde initial du compte et la valeur du dcouvert autoris. L'opration de retrait peut dclencher l'exception OperationImpossible.

Des cls ordonnes
Les oprations seront enregistres selon un historique dcrit au paragraphe suivant. On associera une cl chaque enregistrement. Les fonctions de gestion des cls sont donnes par la signature :

# module type OCLE =
sig
type t
val creer : unit -> t
val of_string : string -> t
val to_string : t -> string
val eq : t -> t -> bool
val lt : t -> t -> bool
val gt : t -> t -> bool
end ;;
La fonction creer permet d'engendrer une cl suppose unique. Les fonction of_string et to_string sont des oprations de conversion de et vers les chanes de caractres. Les trois autres fonctions sont des fonctions de comparaison.

Historique
Pour garder l'historique de gestion d'un compte, on se donne l'ensemble de donnes abstraites et de fonctions suivant :

# module type HISTO =
sig
type tcle
type tinfo
type t
val creer : unit -> t
val add : tcle -> tinfo -> t -> unit
val nth : int -> t -> tcle*tinfo
val get : (tcle -> bool) -> t -> (tcle*tinfo) list
end ;;


Nous laissons pour l'instant indtermin la nature des cls d'enregistrement (type tcle) la nature des informations (type tinfo) et la structure de stockage (type t). On supposera que l'ajout de nouvelles informations (fonction add) est squentiel. Le module fournit deux fonctions d'accs aux donnes archives : un accs selon leur ordre d'enregistrement (fonction nth) et un accs selon un critre dfini sur les cls (fonction get).

Les relevs de compte
Le dernier paramtre du module de gestion fournit deux fonctions d'dition d'un relev de compte :

# module type RELEVE =
sig
type tdata
type tinfo
val editB : tdata -> tinfo
val editC : tdata -> tinfo
end ;;
On laisse abstrait le type de donnes traiter (tdata) ainsi que le type des informations extraites (tinfo).

Module gnral paramtr de gestion

Munis des seules informations que fournissent les signatures ci-dessus, nous pouvons donner la structure du foncteur gnral de gestion d'un compte :

# module FGestion =
functor (C:COMPTE) ->
functor (K:OCLE) ->
functor (H:HISTO with type tcle=K.t and type tinfo=float) ->
functor (R:RELEVE with type tdata=H.t and type tinfo=(H.tcle*H.tinfo) list) ->
struct
type t = { mutable c : C.t; mutable h : H.t }
let creer s d = { c = C.creer s d; h = H.creer() }
let depot s g = C.depot s g.c ; H.add (K.creer()) s g.h
let retrait s g = C.retrait s g.c ; H.add (K.creer()) (-.s) g.h
let solde g = C.solde g.c
let releve edit g =
let f (d,i) = (K.to_string d) ^ ":" ^ (string_of_float i)
in List.map f (edit g.h)
let releveB = releve R.editB
let releveC = releve R.editC
end ;;
module FGestion :
functor(C : COMPTE) ->
functor(K : OCLE) ->
functor
(H : sig
type tcle = K.t
and tinfo = float
and t
val creer : unit -> t
val add : tcle -> tinfo -> t -> unit
val nth : int -> t -> tcle * tinfo
val get : (tcle -> bool) -> t -> (tcle * tinfo) list
end) ->
functor
(R : sig
type tdata = H.t
and tinfo = (H.tcle * H.tinfo) list
val editB : tdata -> tinfo
val editC : tdata -> tinfo
end) ->
sig
type t = { mutable c: C.t; mutable h: H.t }
val creer : float -> float -> t
val depot : H.tinfo -> t -> unit
val retrait : float -> t -> unit
val solde : t -> float
val releve : (H.t -> (K.t * float) list) -> t -> string list
val releveB : t -> string list
val releveC : t -> string list
end


Prcision et partage de types
La contrainte de type applique au paramtre H du foncteur FGestion prcise que les cls de l'historique sont celles fournies par le paramtre K et que les informations stockes sont simplement des flottants (le montant des transactions). La contrainte de type applique au paramtre R indique que les informations traites par le relev proviennent de l'historique (paramtre H).
La signature infre du foncteur FGestion prend en compte les contraintes de type dans la signature infre des arguments.

On dfinit le type d'un compte comme le contenu du compte (paramtre C) et son historique.

Oprations
Il est important de remarquer ici que toutes les oprations de ce foncteur sont dfinies en terme de fonctions fournies par les modules paramtres.

Les oprations de cration, retrait et dpot affectent le contenu du compte et son historique. Les deux oprations de consultation sont la lecture du contenu du compte (fonction solde) et la lecture d'une partie de l'historique (fonction releve).

Dfinition des paramtres

Il faut, avant de crer les modules finals, dfinir le contenu des paramtres du foncteur FGestion.

Contenu
Le contenu d'un compte est essentiellement un flottant reprsentant le solde courant. On rajoute l'information d'autorisation de dcouvert qui sert contrler l'opration de retrait.

# module Compte:COMPTE =
struct
type t = { mutable solde:float; decouvert:float }
exception OperationImpossible
let creer s d = { solde=s; decouvert=(-.d) }
let depot s c = c.solde <- c.solde+.s
let solde c = c.solde
let retrait s c =
let ss = c.solde -. s in
if ss < c.decouvert then raise OperationImpossible
else c.solde <- ss
end ;;
module Compte : COMPTE


Choix d'une cl
On dcide que la cl d'enregistrement est simplement la date de la transaction exprime en flottant telle que la fournit la fonction time du module Unix.

# module Date:OCLE =
struct
type t = float
let creer() = Unix.time()
let of_string = float_of_string
let to_string = string_of_float
let eq = (=)
let lt = (<)
let gt = (>)
end ;;
module Date : OCLE


L'historique
Nous l'avons vu, l'historique d'un compte dpend du type de cl choisi, c'est pourquoi nous dfinissons un foncteur prenant en argument un module fournissant le type des cls, ce qui permet de dfinir le type des cls d'enregistrement.

# module FHisto (K:OCLE) =
struct
type tcle = K.t
type tinfo = float
type t = { mutable content : (tcle*tinfo) list }
let creer() = { content = [] }
let add c i h = h.content <- (c,i)::h.content
let nth i h = List.nth h.content i
let get f h = List.filter (fun (c,_) -> (f c)) h.content
end ;;
module FHisto :
functor(K : OCLE) ->
sig
type tcle = K.t
and tinfo = float
and t = { mutable content: (tcle * tinfo) list }
val creer : unit -> t
val add : tcle -> tinfo -> t -> unit
val nth : int -> t -> tcle * tinfo
val get : (tcle -> bool) -> t -> (tcle * tinfo) list
end


Remarquons que le type des informations doit tre cohrent avec celui donn lors de la dfinition du foncteur de gestion.

Les relevs
Nous dfinissons deux types de relev. Le premier (editB) donne les cinq dernires transactions et est destin au banquier ; le second (editC) donne les transactions effectues les dix derniers jours partir de la date courante et est destin au client.

# module FReleve (K:OCLE) (H:HISTO with type tcle=K.t) =
struct
type tdata = H.t
type tinfo = (H.tcle*H.tinfo) list
let editB h =
List.map (fun i -> H.nth i h) [0;1;2;3;4]
let editC h =
let c0 = K.of_string (string_of_float ((Unix.time()) -. 864000.)) in
let f = K.lt c0 in
H.get f h
end ;;
module FReleve :
functor(K : OCLE) ->
functor
(H : sig
type tcle = K.t
and tinfo
and t
val creer : unit -> t
val add : tcle -> tinfo -> t -> unit
val nth : int -> t -> tcle * tinfo
val get : (tcle -> bool) -> t -> (tcle * tinfo) list
end) ->
sig
type tdata = H.t
and tinfo = (H.tcle * H.tinfo) list
val editB : H.t -> (H.tcle * H.tinfo) list
val editC : H.t -> (H.tcle * H.tinfo) list
end


Remarquons que pour dfinir le relev dcadaire, il nous faut connatre prcisment l'implantation des cls utilises. C'est sans doute l une entorse au principe des types abstraits. Cependant la cl correspondant une date antrieure de dix jours que nous utilisons est obtenue partir de sa reprsentation textuelle par appel la fonction of_string et non pas par calcul direct de la reprsentation interne de cette date (mme si notre exemple est trop pauvre pour rendre ce distingo trs manifeste).

Les modules finals
Pour construire les modules GBanque et GClient respectivement destins au banquier et au client nous procdons comme suit :
  1. dfinition d'une structure par application du foncteur FGestion aux paramtres.
  2. dclaration d'une signature indiquant les fonctions accessibles dans chacun des cas.
  3. dfinition du module final par contrainte de la structure obtenue en 1 avec la signature dclare en 2.

# module Gestion =
FGestion (Compte)
(Date)
(FHisto(Date))
(FReleve (Date) (FHisto(Date))) ;;
module Gestion :
sig
type t =
FGestion(Compte)(Date)(FHisto(Date))(FReleve(Date)(FHisto(Date))).t =
{ mutable c: Compte.t;
mutable h: FHisto(Date).t }
val creer : float -> float -> t
val depot : FHisto(Date).tinfo -> t -> unit
val retrait : float -> t -> unit
val solde : t -> float
val releve :
(FHisto(Date).t -> (Date.t * float) list) -> t -> string list
val releveB : t -> string list
val releveC : t -> string list
end

# module type GESTION_BANQUE =
sig
type t
val creer : float -> float -> t
val depot : float -> t -> unit
val retrait : float -> t -> unit
val solde : t -> float
val releveB : t -> string list
end ;;

# module GBanque = (Gestion:GESTION_BANQUE with type t=Gestion.t) ;;
module GBanque :
sig
type t = Gestion.t
val creer : float -> float -> t
val depot : float -> t -> unit
val retrait : float -> t -> unit
val solde : t -> float
val releveB : t -> string list
end

# module type GESTION_CLIENT =
sig
type t
val depot : float -> t -> unit
val retrait : float -> t -> unit
val solde : t -> float
val releveC : t -> string list
end ;;

# module GClient = (Gestion:GESTION_CLIENT with type t=Gestion.t) ;;
module GClient :
sig
type t = Gestion.t
val depot : float -> t -> unit
val retrait : float -> t -> unit
val solde : t -> float
val releveC : t -> string list
end


Pour que les comptes crs par un banquier puissent tre manipuls par un client nous avons utilis la contrainte de type Gestion.t dans la dfinition des modules GBanque et GClient.








Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora145.html0000644000000000000000000004603507421273602014502 0ustar Sous-typage et polymorphisme d'inclusion Prcdent Index Suivant

Sous-typage et polymorphisme d'inclusion

Le sous-typage est la possibilit pour un objet d'un certain type d'tre considr et utilis comme un objet d'un autre type. Un type d'objet ot2 pourra tre un sous-type de ot1 si
  1. il possde au moins toutes les mthodes de ot1,
  2. le type de chaque mthode de ot2 prsente dans ot1 est sous-type de celle de ot1.
La relation de sous-typage n'a de sens qu'entre objets. Elle ne devra donc tre exprime qu'entre objets. De plus, la relation de sous-typage devra toujours tre explicite. On peut indiquer soit qu'un type est sous-type d'un autre, soit qu'un objet doit tre considr comme objet d'un sur-type.

Syntaxe


(nom : sous_type :> sur_type)
(nom :> sur_type)

Exemple

Ainsi, on pourra indiquer qu'une instance de colored_point peut tre utilise comme une instance de point :

# let pc = new colored_point (4,5) "blanc";;
val pc : colored_point = <obj>
# let p1 = (pc : colored_point :> point);;
val p1 : point = <obj>
# let p2 = (pc :> point);;
val p2 : point = <obj>


Bien que connu comme un point, p1 n'en reste pas moins un point color et l'envoi de la mthode to_string dclenchera l'excution de la mthode attache aux points colors :

# p1#to_string();;
- : string = "( 4, 5) de couleur blanc"


On peut ainsi construire des listes contenant la fois des points et des points colors :

# let l = [new point (1,2) ; p1] ;;
val l : point list = [<obj>; <obj>]
# List.iter (fun x -> x#print(); print_newline()) l;;
( 1, 2)
( 4, 5) de couleur blanc
- : unit = ()


Bien entendu, les manipulations que l'on peut faire sur les objets d'une telle liste sont restreintes celles autorises sur les points.

# p1#get_color () ;;
Characters 1-3:
This expression has type point
It has no method get_color


Cette combinaison de liaison tardive et sous-typage autorise une nouvelle forme de polymorphisme : le polymorphisme d'inclusion. C'est--dire la possibilit de manipuler des valeurs de n'importe quel type en relation de sous-typage avec le type attendu. Ds lors, l'information de typage statique garantit que l'envoi d'un message trouvera toujours la mthode correspondante, mais le comportement de cette mthode dpendra de l'objet receveur effectif.

Sous-typage n'est pas hritage

Le sous-typage est une notion diffrente de celle d'hritage. Il y a deux arguments principaux cela.

Le premier est qu'une instance d'une classe c2 peut avoir comme type un sous-type du type objet c1 sans que c2 soit sous-classe de c1. En effet, on aurait pu dfinir la classe colored_point de manire indpendante de la classe point et contraindre le type de l'une de ses instances en type objet point.

Le second est qu'il est aussi possible d'avoir un hritage de classes sans pouvoir faire du sous-typage entre instances de ces classes. L'exemple ci-dessous illustre ce second point. Il utilise la possibilit de dfinir une mthode abstraite prenant en argument une instance (non encore dtermine) de la classe en cours de dfinition. Dans notre exemple, c'est la mthode eq de la classe equal.

# class virtual equal () =
object(self:'a)
method virtual eq : 'a -> bool
end;;
class virtual equal : unit -> object ('a) method virtual eq : 'a -> bool end
# class c1 (x0:int) =
object(self)
inherit equal ()
val x = x0
method get_x = x
method eq o = (self#get_x = o#get_x)
end;;
class c1 :
int ->
object ('a) val x : int method eq : 'a -> bool method get_x : int end
# class c2 (x0:int) (y0:int) =
object(self)
inherit equal ()
inherit c1 x0
val y = y0
method get_y = y
method eq o = (self#get_x = o#get_x) && (self#get_y = o#get_y)
end;;
class c2 :
int ->
int ->
object ('a)
val x : int
val y : int
method eq : 'a -> bool
method get_x : int
method get_y : int
end


On ne peut pas forcer une instance de c2 tre du type des instances de c1 :

# let a = ((new c2 0 0) :> c1) ;;
Characters 11-21:
This expression cannot be coerced to type
c1 = < eq : c1 -> bool; get_x : int >;
it has type c2 = < eq : c2 -> bool; get_x : int; get_y : int >
but is here used with type < eq : c1 -> bool; get_x : int; get_y : int >
Type c2 = < eq : c2 -> bool; get_x : int; get_y : int >
is not compatible with type c1 = < eq : c1 -> bool; get_x : int >
Only the first object type has a method get_y


L'incompatibilit entre les types c1 et c2 vient en fait de ce que le type de eq dans c2 n'est pas un sous-type du type de eq dans c1.

Pour montrer qu'il est bon qu'il en soit ainsi, comme dans nos bons vieux devoirs de mathmatiques : << raisonnons par l'absurde >>. Soient o1 une instance de c1 et o21 une instance de c2 sous-type en c1. Si nous supposons que le type de eq dans c2 est un sous-type du type de eq dans c1 alors l'expression o21#eq(o1) est correctement type (o21 et o1 sont toutes deux de type c1). Cependant, l'excution, c'est la mthode eq de c2 qui est dclenche (puisque o21 est une instance de c2). Cette mthode va donc tenter d'envoyer le message get_y o1 qui ne possde pas une telle mthode !

On aurait donc un systme de type qui ne remplirait plus son office. C'est pourquoi la relation de sous-typage entre types fonctionnels doit tre dfinie moins navement. C'est ce que nous proposons au paragraphe suivant.

Formalisation

Sous-typage entre objets
Soient t=<m1:t1; ... mn: tn> et t'=<m1:s1 ; ... ; mn:sn; mn+1:sn+1; etc...> on dit que t' est un sous-type de t, not t' t, si et seulement si si ti pour i {1,...,n}.

Appel de fonction
Si f : t s, si a:t' et t' t alors (f a) est bien typ et a le type s.

Intuitivement, une fonction f qui attend un argument de type t peut recevoir sans danger un argument d'un sous-type t' de t.

Sous-typage des types fonctionnels
Le type t' s' est un sous type de t s, not t' s' t s, si et seulement si
s' s et t t'
La relation s' s est appele covariance et la relation t t' est appele contravariance. Cette relation a priori surprenante entre les types fonctionnels peut facilement tre justifie dans le cadre des programmes objets avec liaison dynamique.

Supposons deux classes c1 et c2 possdant toutes deux une mthode m. La mthode m a le type t1 s1 dans c1 et le type t2 s2 dans c2. Pour plus de lisibilit, notons m(1) la mthode m de c1 et m(2) celle de c2. Supposons enfin c2 c1, c'est dire t2 s2 t1 s1, et voyons d'o viennent les relations de covariance et de contravariance sur un petit exemple.

Soit g : s1 a, posons h (o:c1) (x:t1) = g(o#m(x))

covariance
la fonction h attend comme premier argument un objet de type c1, comme c2 c1 on peut lui passer un objet de type c2. La mthode invoque par o#m(x) est alors m(2) qui retourne une valeur de type s2. Comme cette valeur est passe g qui attend un argument de type s1, il faut bien que s2 s1.
contravariance
la fonction h attend, comme second argument, une valeur de type t1. Si, comme prcdemment, nous passons h un premier argument de type c2, la mthode m(2) est invoque et elle attend un argument de type t2. Il faut donc qu'imprativement t1 t2.

Polymorphisme d'inclusion

On appelle << polymorphisme >> la possibilit d'appliquer une fonction des arguments de n'importe quelle << forme >> (type) ou d'envoyer un message des objets de formes diffrentes.

Dans le cadre du noyau fonctionnel/impratif du langage, nous avons dj rencontr le polymorphisme paramtrique qui permet d'appliquer une fonction des arguments de n'importe quel type. Le paramtre polymorphe de la fonction a un type contenant une variable de type. Une fonction polymorphe est une fonction qui excutera le mme code pour les diffrents types de paramtres. Pour cela elle n'explore pas la structure de l'argument.

La relation de sous-typage utilise avec la liaison retarde introduit un nouveau genre de polymorphisme pour les mthodes : le polymorphisme d'inclusion. Celui-ci autorise l'envoi d'un mme message, des instances de types diffrents, si celles-ci ont t contraintes vers le mme sur-type. On construit une liste de points, dont certaines valeurs sont en fait des points colors (vus comme des points). Le mme envoi de message entrane l'excution de mthodes diffrentes, slectionnes par l'instance rceptrice. Ce polymorphisme est appel d'inclusion car il accepte l'envoi d'un message, contenu dans la classe c, sur toute instance d'une classe sc, sous-type de c (sc :> c) qui est contrainte en c. On obtient alors un envoi de message polymorphe sur toutes les classes de l'arbre des sous-types de c. la diffrence du polymorphisme paramtrique le code excut peut tre diffrent pour ces instances.

Les deux formes de polymorphisme peuvent tre utilises conjointement grce aux classes paramtres.

galit entre objets

Nous pouvons maintenant expliquer le comportement surprenant de l'galit structurelle entre objets prsente la page ??. Un objet est gal structurellement un autre uniquement s'il est physiquement gal celui-ci.

# let p1 = new point (1,2);;
val p1 : point = <obj>
# p1 = new point (1,2);;
- : bool = false
# p1 = p1;;
- : bool = true


Cela provient de la relation de sous-typage. En effet une instance o2 d'une classe sc, sous-type de c, contrainte en c peut tre compare une instance o1 de la classe c. Si les champs communs ces deux instances sont gaux alors les deux objets seraient considrs comme gaux, ce qui est faux du point de vue structurel car o2 peut avoir des champs supplmentaires. Pour cela Objective CAML considre que deux objets physiquement diffrents sont structurellement diffrents.

# let pc1 = new colored_point (1,2) "rouge";;
val pc1 : colored_point = <obj>
# let q = (pc1 :> point);;
val q : point = <obj>
# p1 = q;;
- : bool = false
C'est une vision restrictive de l'galit qui garantit qu'une rponse true n'est pas errone; la rponse false ne garantit rien.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora191.html0000644000000000000000000000337307421273602014501 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a prsent les nouvelles possibilits offertes par la programmation rpartie. La communication entre programmes s'effectue partir du mcanisme de base que sont les sockets (prises de communication) issues des protocoles de bas niveau du rseau Internet. La manire la plus classique de construire une application distribue est le modle client-serveur. Les schmas d'actions entre le client et le serveur sont asymtriques. La communication repose sur un protocole le plus souvent en texte clair. La programmation fonctionnelle et la programmation par objets permettent de raliser facilement de telles applications rparties. Le modle client-serveur se prte diffrentes architectures logicielles, 2 ou 3 niveaux, selon la rpartition des tches entre eux.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora044.html0000644000000000000000000000130207421273603014465 0ustar Notes
1
Garbage Collector voir chapitre 9
ocaml-book-1.0/fr/html/book-ora050.gif0000644000000000000000000000276407073350152014276 0ustar GIF89ayyy!,*޼Hʶ jL ! flJKЩbS-C1355r'9.='8WhH)9IYiyi5 *:JZ*iں*;K +PKy L\l:l =,] MMz.JY ~x==N/^oJâq  /d5tP!-EduQ+ -V4.SF($yPɓzEÏ1'sRHI,q ejISNn\3@L3qUT[VZk$Nztf՞cB6ώfOL%mN1 k^j 3`~'v1`ƔE!ͺM [q@D;ά1M?3m:PJo|.LkZMjBId;idJ.9$L> e8)%n%xUDYwZzI:d?IenM")Igc 'si |6 _Yi-h& ,~v"je7A UjhBrঊi#G*fzuz$ f蘩~Joc:J߳ޚ|ܙʬJޕbX—nykǂ;-ؒ%T-Jj,{>F-yp;άv lok\/:0{pR0k 1(Lp\嵦6l"L&n턝=f@Y 4эf:J'H]5O]Z+sKg"6? 7]Ms׍wͷ{ N5ژW6DG䔯cdn#p>24 >sȩɴ.9 53nj)c=Xf&uŻVa*ms3o 5 i߬,{&'<~̷>(`U%~s[ ]m~1hw|ln@<܆D!*=1"y HeձˈB2"< Fom;ţWv= y*g7m8KɸGBqdhHb2$'/ D%JiJ42dX%+Wr!-ki[R ܥz0%&1ac*"ʄ0)gBҜfi3T37&7`ps,9P;ocaml-book-1.0/fr/html/book-ora019.gif0000644000000000000000000000150007073350152014266 0ustar GIF89a!,{޼oxb؈fLp-ݔC|}Pa<*el^~iCo_e#8Р6mcMUETtm!HG$&UBbʥ"yd͒:|3ѿ-*򨘟na*iáR ˇ5֭\I\jlؤTNaYg׶uیطpe.]vbJ| 7obË+nLrd/Wͅ'Mۛ۰G9Y|qWg@ZZ{r㥃orwߣ}?Z4_o;mG }5B~ӕVA.WSaN.ԃ$}!q.(c3Fa1Nx#\R)戝P= ފ;KBI*yɕhY]~ bz9fiehffjc)uVEN)U|Q~$ j苉ڄ>:*SF9KV"&]i|,jjv*r*h6f~v+:k_d +lK+*{,9J;-z+*U;ocaml-book-1.0/fr/html/book-ora032.gif0000644000000000000000000000416507073350152014273 0ustar GIF89aUUU!,ڋ޼H*& $4kfK hNȦSz]X+&rlPhD7}G"WX0qH&(Yu9j@  +Q;+xT \+lKl,=L -}YܝlM=#K~M}.m O:\oωL#*,Ç~"J$H`2Jx 2 щd(\ٱ呗bʜqM.4Q܉4@g:XBۙhTтIu!SVoDykW/*:և? ˬ[pe$xO7 >LqŌ7F8|RY.̜/ z[ФC.ZHϤ~}5j5]:6дԞM|C壍oGmp-Ng`G}a>[2۱V} w9$׏s^^~Qږ(awxBTp "]qF|!Wt@"$4H5b Jwc"L$գ҈ M8`1(2[&X ]pF\jx`*lV=B ``握$tyJ"'rʆ hqR#bA3)fN|Q uI'S,#x: @)Bj*ZYZjRS}z,P*j Z_s[ڥƸ2@PIآۭlh"k 箅Ŋ_~Θ` l+޸;z.I1ɪ%d(Yor2ˏ+dX8LԒSM9M (^j͝$H{^z޹Osp_GKm>Q7PS_]co+}9~ewƗ>~xiRG.*g_@PKkLbjEBIp&Ô%Yo UD:A!_('$A!XXB[KB*a(#b'P zw^> k8ఉ:'h^%2N-rV=;1_?,H 09`yܡmD*1ÈYS `i(b=E%=RSU$6!tJj|S /*򆚄/rlI<.5&+=yKEŮ Ȗ/sM&"l#IE)D2 D\,!ߦY.:B5BK3!J0Ks-t6N@Ra j$7T']D٣B+Gt~1^ӨfP;DSV^ W"X#M+L"-A7~%5K RFR%IUvUP5.Nj2^kjֳ^5j*[V.@=$sU>HԾu Bنևb*]P&/݀ANcdl0Yqpv3X,h/Z0Κ6lCqEVGLjijm;涱E-`|k{ݭ +OչB[\7.-ys {R|nOmvIv o6dkX..Mhoݎ7q%E0D]ݮ"枥)sgDHWQ$I9˰o-Hd;(;Xa͡[:ۂ~čd^AJkx hW6#{3crEa3y5ÐNIa̰;[zjN'P4 #@)f>xT}B":}^SLәȥNcl5.Va識ԁex]}^X# qP;ocaml-book-1.0/fr/html/book-ora062.html0000644000000000000000000001554607421273601014502 0ustar Prsentation de la partie II Prcdent Index Suivant

Prsentation de la partie II

L'ensemble des lments de l'environnement que nous dcrivons est inclus dans la distribution du langage. On y trouve les diffrents compilateurs, de nombreuses bibliothques, des outils d'analyse de programmes, des outils pour les analyses lexicale et syntaxique et une interface avec le langage C.

Objective CAML est un langage compil qui offre deux types de gnration de code :
  1. du code-octet (byte-code) destin tre excut par une machine virtuelle ;
  2. du code natif destin tre excut directement par un micro-processeur.
La boucle d'interaction d'Objective CAML utilise du code-octet pour excuter les phrases qui lui sont proposes. Elle constitue le premier outil d'aide au dveloppement en offrant la possibilit de typer, compiler et tester rapidement les dfinitions de fonctions. De plus, elle offre un mcanisme de trace visualisant les valeurs des arguments d'appel et les valeurs de retour des fonctions.

D'autres outils usuels de dveloppement sont galement fournis avec la distribution : calcul de dpendances entre fichiers, mise au point (debug) et analyse (profiling). L'outil de mise au point permet l'excution pas pas des programmes, l'utilisation de points d'arrt et l'inspection de valeurs. L'outil d'analyse donne des mesures sur le nombre d'appels ou le temps pass dans telle fonction ou telle partie de code. Ces deux outils ne sont disponibles que pour les plate-formes Unix.

La richesse d'un langage provient de son noyau mais aussi des bibliothques, ensemble de programmes rutilisables, qui viennent avec lui. Objective CAML n'chappe pas la rgle. Nous avons dj largement voqu la bibliothque graphique venant avec la distribution. Il en est bien d'autres que nous dcrirons. Les bibliothques apportent de nouvelles fonctionnalits au langage, mais elles ne sont pas sans contre-partie. En particulier, elles peuvent prsenter une difficult vis--vis de la discipline des types.

Quelque riche que soit l'ensemble des bibliothques d'un langage, il lui est toujours ncessaire de pouvoir communiquer avec un autre langage. La distribution d'Objective CAML contient une interface avec le langage C qui permet d'appeler des fonctions C partir d'Objective CAML ou d'tre appel par celui-ci. La difficult de comprhension et de mise en oeuvre de cette interface tient ce que les modles mmoire d'Objective CAML et C sont diffrents. La raison essentielle de cette diffrence est qu'un programme Objective CAML intgre un mcanisme de rcupration automatique de mmoire.

Aussi bien C qu'Objective CAML permettent l'allocation dynamique de mmoire donc une gestion fine de l'espace selon les besoins d'un programme. Cela n'a de sens que si l'on sait rcuprer l'espace inutilis pour un autre usage au cours d'une excution. La rcupration automatique dcharge le programmeur de la gestion de la rcupration, source frquente d'erreurs l'excution. Ce trait constitue un des lments de la sret du langage Objective CAML.

Cependant, ce mcanisme a une incidence sur la reprsentation des donnes. Aussi, pour utiliser correctement la communication entre le monde Objective CAML et le monde C, il est indispensable de connatre les principes guidant la gestion mmoire.

Le chapitre 7 prsente les lments de base du systme Objective CAML  : machine virtuelle, compilateurs et bibliothque d'excution. Il dcrit les diffrents modes de compilation du langage et compare la portabilit l'efficacit.

Le chapitre 8 donne une vision globale de l'ensemble des types, fonctions et exceptions prdfinis venant avec la distribution du systme. Il ne remplace pas la lecture du manuel de rfrence ([LRVD99]) qui dcrit trs bien ces bibliothques. Par contre il insiste sur les nouvelles fonctionalits apportes par certaines d'entres elles. En particulier on peut citer le formatage de sortie, la persistance des valeurs et l'interface avec le systme d'exploitation.

Le chapitre 9 prsente diffrentes mthodes de rcupration automatique de mmoire pour ensuite dcrire le mcanisme utilis par Objective CAML.

Le chapitre 10 prsente les outils de mise au point des programmes Objective CAML. Bien qu'encore assez frustes par certains cts, ces outils permettent bien souvent de comprendre les dysfonctionnements d'un programme.

Le chapitre 11 dcrit les diffrentes approches des problmes d'analyse lexicale et syntaxique du langage : bibliothque de gestion des expressions rgulires, outils ocamlex et ocamlyacc, mais aussi utilisation des flots (streams).

Le chapitre 12 dcrit l'interface avec le langage C. Il n'est plus possible pour un langage d'tre compltement isol des autres langages. Cette interface autorise un programme Objective CAML appeler une fonction C, en lui passant des valeurs du monde Objective CAML, et rciproquement. La principale difficult de cette interface provient du modle mmoire. Pour cela il est recommand de lire le chapitre 9 auparavant.

Le chapitre 13 propose deux applications : une bibliothque graphique enrichie reposant sur un modle hirarchique de composants graphiques inspir de l'AWT2 JAVA ; un programme classique de recherche de chemin de moindre cot dans un graphe utilisant notre nouvelle interface graphique ainsi qu'un mcanisme de mmoire-cache.




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora042.html0000644000000000000000000000361107421273601014466 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a compar les styles de programmation fonctionnel et impratif. Ils diffrent principalement sur le contrle de l'excution (implicite en fonctionnel et explicite en impratif) et sur la reprsentation mmoire des donnes (partage ou copie explicite en impratif, non crucial en fonctionnel). Les implantations d'algorithmes dans les styles fonctionnel et impratif tiennent compte de ces diffrences. Le choix entre ces deux styles amne en fait leur mlange. Celui-ci permet d'expliciter la reprsentation des fermetures, d'optimiser des parties cruciales d'applications et de crer des donnes fonctionnelles modifiables. La modification physique de valeurs dans l'environnement d'une fermeture permet de mieux comprendre ce qu'est une valeur fonctionnelle. Le mlange des deux styles donne des outils d'implantation puissants. Cela a t utilis pour la construction de valeurs potentiellement infinies.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora050.html0000644000000000000000000002615707421273601014477 0ustar Animation Prcdent Index Suivant

Animation

L'animation d'un graphisme l'cran reprend des techniques de dessins anims. La majeure partie du dessin ne change pas, seule la partie anime doit modifier la couleur des pixels qui la compose. Un des problmes immdiatement rencontr provient de la vitesse d'animation. Celle-ci peut varier selon la complexit du calcul et la vitesse d'excution du processeur. Ainsi une application graphique anime pour tre portable et donner le mme effet doit tenir compte de la rapidit du processeur. Pour avoir un rendu fluide il est prfrable d'afficher la nouvelle position de l'objet anim, puis d'effacer l'ancienne, en tenant compte de leur intersection.

Dplacement d'un objet
Nous simplifions le problme du dplacement d'un objet en choisissant des objets de forme simple que sont les rectangles. La difficult restante est de savoir rafficher le fond d'cran une fois l'objet dplac.

On cherche faire voluer un rectangle dans un espace clos. L'objet se dplace selon une vitesse en X et Y, quand il rencontre un bord de la fentre graphique, il rebondit selon son angle d'incidence. On se place dans une situation sans recouvrement entre la nouvelle et l'ancienne position de l'objet. La fonction calc_pv calcule, partir d'une position (x,y), de la taille de l'objet (sx,sy) et d'une vitesse (dx,dy), la nouvelle position et la nouvelle vitesse. Cette dernire tient compte des bords de la fentre.

# let calc_pv (x,y) (sx,sy) (dx,dy) =
let nx1 = x+dx and ny1 = y + dy
and nx2 = x+sx+dx and ny2 = y+sy+dy
and ndx = ref dx and ndy = ref dy
in
( if (nx1 < 0) || (nx2 >= Graphics.size_x()) then ndx := -dx ) ;
( if (ny1 < 0) || (ny2 >= Graphics.size_y()) then ndy := -dy ) ;
((x+ !ndx, y+ !ndy), (!ndx, !ndy)) ;;
val calc_pv :
int * int -> int * int -> int * int -> (int * int) * (int * int) = <fun>
La fonction roule_fond dplace n fois le rectangle donn par pos et taille selon la trajectoire indique par sa vitesse vit en tenant compte des bords comme dcrit par la fonction ci-dessus. La trace du dplacement que l'on obtient sur la figure 5.7 est obtenue par inversion du bitmap correspondant au rectangle dplac.

# let roule_fond pos taille vit n =
let (x, y) = pos and (sx,sy) = taille in
let mem = ref (Graphics.get_image x y sx sy) in
let rec roule_aux x y vit n =
if n = 0 then Graphics.moveto x y
else
let ((nx,ny),n_vit) = calc_pv (x,y) (sx,sy) vit
and old_mem = !mem in
mem := Graphics.get_image nx ny sx sy ;
Graphics.set_color Graphics.blue;
Graphics.fill_rect nx ny sx sy;
Graphics.draw_image (inv_image old_mem) x y;
roule_aux nx ny n_vit (n-1)
in roule_aux x y vit n ;;
val roule_fond : int * int -> int * int -> int * int -> int -> unit = <fun>


Le code suivant correspond aux dessins de la figure 5.7. Le premier est obtenu sur fond uniformment rouge, le second, en dplaant le rectangle sur l'image de Jussieu.

# let anim_rect () = 
Graphics.moveto 105 120 ;
Graphics.set_color Graphics.white;
Graphics.draw_string "Dbut" ;
roule_fond (140,120) (8,8) (8,4) 150 ;
let (x,y) = Graphics.current_point() in
Graphics.moveto (x+13) y ;
Graphics.set_color Graphics.white;
Graphics.draw_string "Fin" ;;
val anim_rect : unit -> unit = <fun>
# anim_rect();;
- : unit = ()




Figure 5.7 : Code du dplacement d'un objet


Le problme a t simplifi dans la mesure o il n'y a pas d'intersection entre deux positions successives de l'objet dplac. Si tel n'est pas le cas, il faut crire une fonction de calcul de cette intersection qui est plus ou moins complique selon la forme de l'objet. Dans le cas prsent d'un carr, l'intersection de deux carrs donne un rectangle. C'est celui-ci qu'il faut alors effacer.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora023.html0000644000000000000000000000354707421273603014477 0ustar Notes
1
Entre [-230,230-1] sur les machines 32bits et entre [-262,262-1] sur les machines 64 bits
2
Le nombre flottant qui m 10n est reprsent avec une mantisse m de 53 bits et un exposant n dans l'intervalle [-1022,1023].
3
Not a Number
4
Le module List est prsent page ??.
5
Heureusement puisque le nombre de types n'a de limitation que la mmoire de la machine
6
Certaines fonctions prdfinies ne respectent pas cette rgle, en particulier la fonction d'galit structurelle (=) qui est polymorphe (son type est 'a -> 'a -> bool) mais qui explore la structure de ses arguments pour tester leur galit.
ocaml-book-1.0/fr/html/book-ora206.html0000644000000000000000000000154107421273603014472 0ustar Notes
1
Le bootstrap est le fait pour un compilateur de se compiler lui-mme. Le fait d'arriver un point fixe, c'est dire que le compilateur et l'excutable gnr sont identiques, est un bon test de la correction du compilateur.
ocaml-book-1.0/fr/html/logocaml.gif0000644000000000000000000001241207073350147014130 0ustar GIF89aw`5m&C24w3113w4q4s3x3~12鲫I;q蓎I(<"7A > '< " -?JRc!Made with GIMP! ?,w`pH,Ȥrl:ШtJZجvzxL.zn|N~pf\XGIFEљTBa۲S]PRCYKOVD_gƝ#dX)^KCxژ\,lĎ?(z2="K|Jo4r̓[ϝq @2@fģz 'VIZQ췟d63R1;-0M v;%f2^7zPx.͂qʄZygF82KhnF>:ޡGbO ֪"قi(zW=!kG+PFjg sNqvR^]KZ(~{RV٪"+$J믁~Ժici&Loi(Ѷ#H-IUKTp ǰӱC*yWlsy3/p7#eaM, ^|UƳys϶LW]]OCm?)m9MRoT6cwg֬l,*"|O'veò+m8}D[i2wݲ-.{&'͙;V8|E`+W촋zŮ3N謹wK;āǝi|гZc['>M 50}ϳ춋odﳯqG.C~@{E> N=U $U88'd:ͣ` e<UB36yi8 ECΏ5T>(!Q *uH4"'=%jy`#b.b8mpD,xD¢-XB쬑m!(,: =.g`{HA}o4̘5Pj|d#)I.0w CEMB72Dt)I0EouemE%%!&]BA3&K}i ,1Cē#d#e)Mi$yG\R,`:37R5zy^gȁMȵ퓲5Zu_6WwNJֹed14`U]]Ůi+VOWD9?+?e=6  W(uO)xk~Q`(6q«wuq{XE6Nqjo{E oÿ1G:bṯQiR0gppsd wpXqAq` k/CdN..5|fש9OV̛} {68X4%]g;wwK7wcdqWSgo|ή:5mN>m0v?8%C=<=wrO]?:EI僯 Z<~Fg*W_V=W+>selp쓟g{k:x~?WȞ?SWaԖuyag+k7mKvHuv'v}w5xe{]恼GGt3*7g08W~i`{Ył׃䂕\AꐃƄMG(CR8dJ8MXhW5eb^hB(!(%'H"()6r5?i5wȆohi`-f:RgȇwsS< #nUX@}n؂?#4kȉ!㉇bĊ䊯-E(Ih2^'|8HH}(UA{ x PKX% Ȧ(SW~0h5?X{$jcU*HEm$jl& k=Zf$tH ƍ>n9j`rǁ6LV掘"En\Ē8Ɏ%uUBNE8. 2ِm;Eɒ:F0K{jƖCQxU) py(F5!Е^9 ԏ~@`i4bhuwƖ/hz ?au%i"i˜yxOp(x LtdْGB0J֚7 J zWrYYhy9v5)IXi rٓ?Iof癎y$IoنYyyT#gV1ɂțҩ)X'rxڛP(wܒᡟğy9->Iy"\#!$0 a!-J*zo"Pn'jC6&frPVn֎6@BV?FYEH%j 5"Rf"h3)! s T%VEdQ}l-R'tj4I:EcŤČ0Uڕ$uJ :Yg1<$2<(D|õX+TL+!MO,v 8qgJظMAe\˕K [\ j{n+{ul Bi1G¿v:̨ |4K+{0t]QN 򺵼CD54eƘp츊;M/5 wQ7VKR}̦Ss̐e\j@E0^/L=5Μ, )Růs 0J- dg:LJ@GMMzexɸ.\ 9]7|0jƑ*=DpdžqZJ;e+= LVcӺG:i[]5UsVsn o<'ݙQSW;zfggط9 )L FkTWБktm-C)|ěaԜ٠VڝM]o\<bIغMqiة=Ľ(*fܩΝ1$ݏ3mݥWj=a+}T=gRD&4]߷ h)m-And ޵';R~54^. =$^}(:"-> "(31N3#d2W2$Mb`J^@;Ni%Inw݆!8w?Ybh2 r4+f#\\,"nE Gx~w:ag}mW|iّn n"vC^=~:^ꨞ)>^~븞뺾>;ocaml-book-1.0/fr/html/book-ora017.gif0000644000000000000000000000412207073350152014267 0ustar GIF89aZ !,Z ڋ޼kH扦ʶ L ĐL*̦37xJԪj:bܮ҂y)>v84?8HX"h889IY&Yyu):IzJcڪ+ ;kZ{++ ; ыbd䟳O'ß8I tyvVn)(B8F1NaUq(荈BHb.4{Ë6ވXD>UFN>YTeV$%tJ\e(#^yexj>i&EUfEo i}`_ ꃣ^|(Iu*]PJ5hzz*HdꦌfB뎯bRjiKdZIU캃Tzl48;3.*mԆ .䞛"K3ĸ)n鮯NBBpY?0 p;q*_^lC; ,2ds-j|)lC|̝]Ј,ҥ݉\/ɦo:TRX1-0օXײs;'d\+,6 &@wJ[w6L+nwg"!}]4,n^C3޸ŏsmȹG鰖n9Sgz~yسfC踓~:{b{*w>|g';co7箾~SiB'<9/ _RUO~k~@\8 KzK} (π Bv~1,E8J d_ !8=p(GBO0\ gX&t\A1>t"(*zWŵ ׻%>70bf½1Rai)H h6qh#D92Q{q\$GH.1#3IDj$!Iv\& @DQby2eHFSZ)mIGPsaTf"YKAҘD&&]YJfM5or,,0##Y< xs=O{`c> Ѐ t>nt mCK̭VE/ьV5 Ґ4XBUgj3T9/ GU1)UJxRԎ%NRճ)Js:Jzu^F)Run3ijR *URV*Voç :jV}gcLղе++\Ɋд«Inb\"ԣT,c5bW]-bX)PE>'KW0]6;]PTv9<,53K.m dVhH R4jUej4w8r ݅2΍u{F¶ŮwKm6y[Wo _讗uk_7.y[w.j" ,C0+*laT`8ä0yK"K X'^1%Zbb8€1a#c:^0{9B2l #9^2;'X1`+W2/Wbfl0g^%fE9ν6l.9[3+i9Ѝ30C/UPf4 hz`Ҕ/LkzӜF@;ocaml-book-1.0/fr/html/book-ora103.html0000644000000000000000000000153607421273603014472 0ustar Notes
1
Les fichiers Makefile sont utiliss par la commande make pour la maintenance d'ensembles de programmes ou fichiers, dans le but de les mettre jour quand une modification apparat dans l'un d'entre eux.
ocaml-book-1.0/fr/html/book-ora014.html0000644000000000000000000000377007421273601014473 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

Ce chapitre prsente les lments de base de la partie fonctionnelle du langage Objective CAML, savoir ses lments syntaxiques, son langage de types et son mcanisme d'exceptions. Ceci nous amnera la ralisation d'un premier exemple de programme complet.

La premire section dcrit le noyau du langage en commenant par les valeurs de base et les fonctions qui les manipulent. Nous passons ensuite aux valeurs structures et aux valeurs fonctionnelles. Les structures de contrle de base sont introduites ainsi que les dclarations locales et globales de valeurs. La deuxime section s'intresse aux dfinitions de types pour la construction de valeurs structures et au filtrage de motifs pour l'accs ces structures. La troisime section compare le type infr des fonctions et leur domaine de dfinition, ce qui nous amne introduire le mcanisme des exceptions. La quatrime section illustre l'ensemble de ces notions en dcrivant une application simple : une calculatrice de bureau.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora114.html0000644000000000000000000000475407421273602014500 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

Ce chapitre prsente les outils de la distribution d'Objective CAML qui permettent l'interaction entre Objective CAML et C en crant des excutables contenant des parties dveloppes dans les deux langages. Ces outils contiennent les fonctions de conversion des valeurs des deux langages, permettent une allocation en C sre qui utilise le tas Objective CAML et son GC, et autorisent le dclenchement d'exceptions Objective CAML en C.

La premire section montre comment utiliser une fonction C en Objective CAML et comment construire des excutables et des toplevels incluant un programme C contenant cette fonction. La deuxime section explore la reprsentation des valeurs Objective CAML en C. La troisime section explique comment crer et modifier des valeurs Objective CAML en C. Elle dtaille d'autre part les problmes soulevs par l'allocation mmoire de C en prsence du GC d'Objective CAML et comment profiter de ce mcanisme pour avoir une allocation sre en C. La quatrime section dcrit la gestion des exceptions, leur dclenchement et leur rcupration selon le lieu de la rupture de calcul. La cinquime section inverse les rles en incluant du code Objective CAML dans un programme principal C.

Remarque


La lecture de ce chapitre ncessite de connatre un tant soit peu le langage C. De mme la lecture du chapitre 9 est utile pour la comprhension des difficults lies la rcupration automatique de mmoire.



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora138.html0000644000000000000000000000173107421273603014477 0ustar Notes
1
Il n'est pas ncessaire que les types soient gaux, il suffit qu'ils soient compatibles et que celui de l'interface ne soit pas plus gnral que celui de l'implantation.
2
Les fichiers nom.ml et Nom.ml produiront tous deux un module de mme nom.
ocaml-book-1.0/fr/html/book-ora004.gif0000644000000000000000000000173407073350152014271 0ustar GIF89aUUU!,ڋ޼gH扦ʶ xLCq}xS )ah\ %)tBO* ʰFՊc8xiP1QuhhA b#fIty) IJ0*hZZyӛk{;@ k+e,*̋}Ŭ=<] == ^uΎN(ؑ'~W7_?}C@Cȏ„C8D g͈b>A]Rȇ-~Rʕ%YD宓-աtʗ5mޔqF=MPfE9z 5ԩ0Zmխ_ $V$֬Z]}[tR7ζ|ԗ@%xỉcرdwOT(qă.osғN~ٝowOwn~y{O}oz RRW ` gbB( N2h}b|R衅bH/Vl3n'vr)fC=ָ99h#y8c1?[G)ʐDM6LpRWbWʶ[~ fbIfff\Rfn|.otzwrRn{{ҧmy>xZS(Vk\<:XV'|)8c41)n#8 +pca%"멪z,fZO1HfH6X6AV8゛] =#ko7vGD˭pR!jo{`qvʠ0${ ڮ+-,GZ;s:07?[sA*!N? u*jW;ocaml-book-1.0/fr/html/book-ora014.gif0000644000000000000000000000517707073350152014277 0ustar GIF89aaea!,80I8͙`(dihlp,(!`x<. #,G\:}4s>Ve2{rA P(:ؒ\~xyJnHw{ac6x}MyQ2&me%w$o"o }ǜbRпµعݣאۤΞ͉0+:tKxݿx3> %.B bP}?r|dKKL)bH钞͝8{NLTFo7FVIc|iQK{ *\vTFLL[iXD:b5Vsr/ɦ?~ڕqFQ2dձ ~ jX{}.Y|\:%n]}':ww놗=~ɍg)^_fчu'6v msBzW\{E|!*螆r؟%8!#uʋ0(#>3hcA78#g:b`>I~Bc:dG.iNx[Q2G7ڄWnc2^~aUf] sE=lgzØ~(2Q}26裐KVj. znʇɠꠣZ&zkͺ8SL h [$"gb(Wއ JxfYچb_n;.xv!Gn^'bI{b{2!K2jWqw\-@!plv׭N9 6ءm7߰WP?;~+^Dϋ?39ᴳ}ҟ}ǹp%C3#彜/}Xn"١N`2]Mf`6&aVz˳B&x&$jɉFdb@Mb6@U0T]8)Lp=e5iST:1d3ֱd%He YU!)eqFP#wICkn.T{XI0hzORrG L?(d @-m0҈^j ^r}#&+KX_⯘g.<. sׄe*O8Nb3*H:7'ussd35Mk;M` >C0ꑥ@~2q U(P 4 q(AE;u|HG`jh)Kwҡ(HRQϤd_4KSʽT2uC5ˡ )R}PԜijP)gVӊpxGjIPjӘDjR֘RwKPi/ J+d&~(O}*XP6TYz׋֮+9zثܳ=VB',;3ZYi9zL뛲 Zֶ­SC*MD,C!;]zHEnWGnv^j%vG@Oﭓ8GQ2h#L`t H{h0_GV-$$=a{upF&r{mSQͼvkZV!dc;7cƚ&[=lCy՛6W򓱊d6Fnke[9!h;2M}f8;_,g6{g;GUZ3 MheXB3Y Y+Vq:yӿ;dH_Ӆa 3҅tFhR\vˌ2Yߖ֜fk3kͦw[9+< ;Ԙ=j%Exi%R}EDukEZW"=;2h7lKJ pyudX/GseI9~c]E^њsfٵdzKum\N.7SQԉ}u3}\m۳Z8ѸVݾw>ɮtK}i o9_k;xW=u~wjgƀ.)t Typage, domaine de dfinition et exceptions Prcdent Index Suivant

Typage, domaine de dfinition et exceptions

Le type infr d'une fonction correspond un sur-ensemble de son domaine de dfinition. Ce n'est pas parce qu'une fonction prend un paramtre de type int qu'elle saura calculer une valeur pour tous les entiers passs en argument. On traite en gnral ce problme en utilisant le mcanisme d'exceptions d'Objective CAML. Le dclenchement d'une exception provoque une rupture du calcul qui peut tre intercepte et traite par le programme. Pour cela l'excution du programme doit avoir pos un rcuprateur d'exception avant le calcul de l'expression qui provoque le dclenchement de cette exception.

Fonctions partielles et exceptions

Le domaine de dfinition d'une fonction correspond l'ensemble des valeurs sur lesquelles la fonction effectue son calcul. Il existe de nombreuses fonctions mathmatiques partielles, on peut citer la division ou le logarithme nprien. Ce problme se pose aussi pour les fonctions manipulant des structures de donnes plus complexes. En effet quel est le rsultat du calcul du premier lment d'une liste vide ? De la mme manire le calcul de la fonction factorielle sur un entier ngatif peut entraner un calcul infini.

Un certain nombre de situations exceptionnelles peuvent se produire durant l'excution d'un programme, par exemple une tentative de division par zro. Tenter de diviser un nombre par zro provoquera au mieux l'arrt du programme, au pire un tat incohrent de la machine. La sret d'un langage de programmation passe par la garantie de ne pas se retrouver dans une telle situation pour ces cas particuliers. Les exceptions sont une manire d'y rpondre.

La division de 1 par 0 provoquera le dclenchement d'une exception spcifique :

# 1/0;;
Uncaught exception: Division_by_zero
Le message Uncaught exception: Division_by_zero indique d'une part que l'exception Division_by_zero a t dclenche, et que d'autre part elle n'a pas t rcupre. Cette exception fait partie des dclarations de base du langage.

Souvent, le type d'une fonction ne correspond pas son domaine de dfinition quand un filtrage de motif n'est pas exhaustif, c'est dire qu'il ne filtre pas tous les cas de l'expression donne. Pour prvenir une telle erreur, Objective CAML affiche un message dans un tel cas.

# let tete l = match l with t::q -> t ;;
Characters 14-36:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]
val tete : 'a list -> 'a = <fun>


Si nanmoins le programmeur maintient sa dfinition incomplte, Objective CAML utilisera le mcanisme d'exceptions en cas d'appel erron la fonction partielle :

# tete [] ;;
Uncaught exception: Match_failure("", 14, 36)


Enfin, nous avons dj rencontr une autre exception prdfinie : Failure. Elle prend un argument de type string. On peut dclencher cette exception en utilisant la fonction failwith. On pourra ainsi l'utiliser pour complter notre dfinition de la fonction tete :

# let tete = function
[] -> failwith "Liste vide"
| h::t -> h;;
val tete : 'a list -> 'a = <fun>
# tete [] ;;
Uncaught exception: Failure("Liste vide")


Dfinition d'une exception

En Objective CAML, les exceptions appartiennent un type prdfini exn. Ce type est trs particulier puisque c'est un type somme extensible : on peut tendre l'ensemble des valeurs du type en dclarant de nouveaux constructeurs. Cette particularit permet l'utilisateur de dfinir ses propres exceptions en ajoutant au type exn des nouveaux constructeurs.

La syntaxe de la dclaration d'une exception est la suivante :

Syntaxe


exception Nom ;;
ou

Syntaxe


exception Nom of t ;;
Voici des exemples de dclarations d'exceptions :

# exception A_MOI;;
exception A_MOI
# A_MOI;;
- : exn = A_MOI
# exception Depth of int;;
exception Depth of int
# Depth 4;;
- : exn = Depth(4)
Une exception est donc une valeur du langage part entire.

Warning


Les noms d'exceptions sont des constructeurs. Ils commencent donc obligatoirement par une majuscule.

# exception minuscule ;;
Characters 11-20:
Syntax error


Warning


Les exceptions sont monomorphes : elles n'ont pas de paramtre de type dans la dclaration du type de leur argument.

# exception Value of 'a ;;
Characters 20-22:
Unbound type parameter 'a
Une exception polymorphe autoriserait la dfinition de fonctions avec un type de retour quelconque comme nous le verrons plus loin, page ??.

Dclenchement d'une exception

La fonction raise est une fonction primitive du langage. Elle prend une exception comme argument et possde un type de retour entirement polymorphe.

# raise ;;
- : exn -> 'a = <fun>
# raise A_MOI;;
Uncaught exception: A_MOI
# 1+(raise A_MOI);;
Uncaught exception: A_MOI
# raise (Depth 4);;
Uncaught exception: Depth(4)
Il n'est pas possible d'crire en Objective CAML la fonction raise. Elle doit tre prdfinie.

Rcupration d'une exception

Tout l'intrt de dclencher des exceptions rside dans la capacit de les rcuprer et d'orienter la suite du calcul selon la valeur de l'exception dclenche. L'ordre de calcul d'une expression prend alors de l'importance pour dterminer quelle exception est dclenche. On sort du cadre purement fonctionnel, o l'ordre d'valuation des arguments peut changer le rsultat d'un calcul comme il sera discut au chapitre suivant (voir page ??).

La construction syntaxique suivante, qui calcule la valeur d'une expression, permet la rcupration d'une exception dclenche lors de ce calcul :

Syntaxe


try expr with
| p1 -> expr1
:
| pn -> exprn

Si le calcul de expr ne dclenche pas d'exception, alors le rsultat est celui du calcul de expr. Sinon, la valeur de l'exception dclenche est filtre; la valeur de l'expression correspondante la premire branche du filtrage correct est retourne. Si aucune branche du filtrage ne correspond la valeur de l'exception alors celle-ci se propage jusqu'au prcdent try-with pos durant l'excution du programme. Donc le filtrage d'une exception est toujours considr comme exhaustif. Implicitement, le dernier filtre est | e -> raise e . Si aucun rcuprateur d'exception n'est rencontr dans le programme, le systme lui-mme se charge d'intercepter l'exception et termine le programme en affichant un message d'erreur.

Il ne faut pas confondre calculer une exception (c'est--dire une valeur de type exn) et dclencher une exception qui effectue une rupture de calcul. Une exception tant une valeur comme les autres elle peut tre rendue comme rsultat d'une fonction.

# let rendre x = Failure x ;;
val rendre : string -> exn = <fun>
# rendre "test" ;;
- : exn = Failure("test")
# let declencher x = raise (Failure x) ;;
val declencher : string -> 'a = <fun>
# declencher "test" ;;
Uncaught exception: Failure("test")
On remarque que l'application de declencher ne rend pas de valeur alors que celle de rendre en rend une de type exn.

Calculer avec des exceptions

Outre leur utilisation pour le traitement de valeurs non dsires, les exceptions permettent aussi un style de programmation et peuvent tre source d'optimisations. L'exemple suivant effectue le produit de tous les lments d'une liste d'entiers. On utilise une exception pour interrompre le parcours de la liste et retourner la valeur 0 ds qu'on la rencontre.

# exception Found_zero ;;
exception Found_zero
# let rec mult_rec l = match l with
[] -> 1
| 0 :: _ -> raise Found_zero
| n :: x -> n * (mult_rec x) ;;
val mult_rec : int list -> int = <fun>
# let mult_list l =
try mult_rec l with Found_zero -> 0 ;;
val mult_list : int list -> int = <fun>
# mult_list [1;2;3;0;5;6] ;;
- : int = 0
Ainsi tous les calculs restant en attente, savoir les multiplications par n qui suivent chacun des appels rcursifs, sont abandonnes. Aprs avoir rencontr le raise, le calcul reprend partir du filtrage du with.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora037.html0000644000000000000000000004275507421273601014506 0ustar Comparaison entre fonctionnel et impratif Prcdent Index Suivant

Comparaison entre fonctionnel et impratif

Les chanes de caractres (string) et les listes chanes ('a list) vont nous servir d'exemple pour illustrer les diffrences entre << fonctionnel >> et << impratif >>.

Du ct du fonctionnel

La fonction map (voir page ??) est l'une des plus classiques des langages fonctionnels. Dans un style fonctionnel pur, elle s'crit :

# let rec map f l = match l with
[] -> []
| t::q -> (f t) :: (map f q) ;;
val map : ('a -> 'b) -> 'a list -> 'b list = <fun>


La liste rsultat de l'application de f aux lments de la liste passe en argument est construite rcursivement en spcifiant indpendamment sa tte (f t) et sa queue (map f q). En particulier, le programme ne prcise pas lequel des deux sera calcul en premier.

De plus, la reprsentation physique des listes n'a pas besoin d'tre connue du programmeur pour crire une telle fonction. En particulier, les problmes d'allocation et de partage des donnes sont grs implicitement par le systme et non par le programmeur. L'exemple ci-dessous en est une illustration :

# let exemple = [ "un" ; "deux" ; "trois" ] ;;
val exemple : string list = ["un"; "deux"; "trois"]
# let resultat = map (function x -> x) exemple ;;
val resultat : string list = ["un"; "deux"; "trois"]


Les listes exemple et resultat contiennent des valeurs gales :

# exemple = resultat ;;
- : bool = true


Ces deux valeurs ont exactement la mme structure bien que leur reprsentation en mmoire diffre, comme on s'en rend compte en utilisant le test d'galit physique :

# exemple == resultat ;;
- : bool = false
# (List.tl exemple) == (List.tl resultat) ;;
- : bool = false


Du ct impratif

Reprenons l'exemple prcdent, et modifions une chane de la liste resultat.

# (List.hd resultat).[1] <- 's' ;;
- : unit = ()
# resultat ;;
- : string list = ["us"; "deux"; "trois"]
# exemple ;;
- : string list = ["us"; "deux"; "trois"]
Manifestement, cette opration a modifi la liste exemple. Donc, il est indispensable de connatre la structure physique des deux listes manipules ds que nous utilisons des traits impratifs.

Voyons maintenant comment l'ordre d'valuation des arguments d'une fonction peut constituer un pige dans un programme impratif. Nous dfinissons une structure de liste modifiable avec les fonctions de base de cration , rajout et accs :

# type 'a ilist = { mutable c : 'a list } ;;
type 'a ilist = { mutable c: 'a list }
# let icreate () = { c = [] }
let iempty l = (l.c = [])
let icons x y = y.c <- x::y.c ; y
let ihd x = List.hd x.c
let itl x = x.c <- List.tl x.c ; x ;;
val icreate : unit -> 'a ilist = <fun>
val iempty : 'a ilist -> bool = <fun>
val icons : 'a -> 'a ilist -> 'a ilist = <fun>
val ihd : 'a ilist -> 'a = <fun>
val itl : 'a ilist -> 'a ilist = <fun>
# let rec imap f l =
if iempty l then icreate()
else icons (f (ihd l)) (imap f (itl l)) ;;
val imap : ('a -> 'b) -> 'a ilist -> 'b ilist = <fun>


Bien qu'ayant repris l'architecture gnrale de la fonction map du paragraphe prcdent, nous obtenons avec, imap, un rsultat sensiblement diffrent :

# let exemple = icons "un" (icons "deux" (icons "trois" (icreate()))) ;;
val exemple : string ilist = {c=["un"; "deux"; "trois"]}
# imap (function x -> x) exemple ;;
Uncaught exception: Failure("hd")


Que s'est-il pass ? Simplement, l'valuation de (itl l) a eu lieu avant celle de (ihd l) de sorte que, la dernire itration de imap, la liste rfrence par l devient la liste vide avant que l'on n'examine sa tte. La liste exemple est dsormais dfinitivement vide bien que nous n'ayons obtenu aucun rsultat :

# exemple ;;
- : string ilist = {c=[]}


Le dfaut de la fonction imap provient d'un mlange insuffisamment contrl des genres : nous avons laiss au systme le choix de l'ordre d'valuation. Nous pouvons reformuler la fonction imap en explicitant l'ordre d'valuation l'aide de la construction syntaxique let .. in ..

# let rec imap f l =
if iempty l then icreate()
else let h = ihd l in icons (f h) (imap f (itl l)) ;;
val imap : ('a -> 'b) -> 'a ilist -> 'b ilist = <fun>
# let exemple = icons "un" (icons "deux" (icons "trois" (icreate()))) ;;
val exemple : string ilist = {c=["un"; "deux"; "trois"]}
# imap (function x -> x) exemple ;;
- : string ilist = {c=["un"; "deux"; "trois"]}


Cependant, la liste initiale a ici encore t perdue :

# exemple ;;
- : string ilist = {c=[]}


Une seconde faon de rendre explicite l'ordre d'valuation est d'utiliser l'oprateur de mise en squence et une structure de boucle.

# let imap f l =
let l_res = icreate ()
in while not (iempty l) do
ignore (icons (f (ihd l)) l_res) ;
ignore (itl l)
done ;
{ l_res with c = List.rev l_res.c } ;;
val imap : ('a -> 'b) -> 'a ilist -> 'b ilist = <fun>
# let exemple = icons "un" (icons "deux" (icons "trois" (icreate()))) ;;
val exemple : string ilist = {c=["un"; "deux"; "trois"]}
# imap (function x -> x) exemple ;;
- : string ilist = {c=["un"; "deux"; "trois"]}
La prsence de ignore illustre bien le fait que ce n'est pas le rsultat des fonctions qui nous importe mais leurs effets de bord sur leur argument. Il a de plus t ncessaire de remettre dans le bon ordre (par la fonction List.rev) les lments du rsultat.

Rcursif ou itratif

On associe souvent tort rcursif avec fonctionnel et itratif avec impratif. Un programme purement fonctionnel ne pourra pas tre itratif car la valeur de la condition d'une boucle ne varie jamais. Par contre un programme impratif peut tre rcursif : la fonction imap en est un exemple.

L'appel d'une fonction conserve les valeurs de ses arguments le temps de son calcul. Si elle appelle une autre fonction, celle-ci conservera en plus ses propres arguments. Ces valeurs sont conserves dans la pile d'excution. Au retour de l'appel ces valeurs sont dpiles. Cet espace mmoire tant born, il est possible d'en atteindre la limite en utilisant une fonction rcursive dont la profondeur des appels serait trop importante. Dans ce cas Objective CAML dclenche l'exception Stack_overflow.

# let rec succ n = if n = 0 then 1 else 1 + succ (n-1) ;;
val succ : int -> int = <fun>
# succ 100000 ;;
Stack overflow during evaluation (looping recursion?).


Dans la version itrative l'occupation de la pile par l'appel de succ_iter ne dpend pas de son argument.

# let succ_iter n =
let i = ref 0 in
for j=0 to n do incr i done ;
!i ;;
val succ_iter : int -> int = <fun>
# succ_iter 100000 ;;
- : int = 100001


La version rcursive suivante a a priori la mme profondeur d'appels et pourtant russit s'excuter avec le mme argument.

# let succ_rt n =
let rec succ_aux n accu =
if n = 0 then accu else succ_aux (n-1) (accu+1)
in
succ_aux 1 n ;;
val succ_rt : int -> int = <fun>
# succ_rt 100000 ;;
- : int = 100001


Cette fonction a une forme particulire d'appel rcursif, dit rcursif terminal qui veut que le rsultat de l'appel soit le rsultat de la fonction sans autre calcul. Il n'est donc pas ncessaire d'avoir conserv les valeurs des arguments de la fonction pendant le calcul de l'appel rcursif. Objective CAML quand il sait reconnatre une fonction rcursive terminale libre les arguments empils avant d'effectuer l'appel. Cette optimisation permet d'avoir des fonctions rcursives ne faisant pas grossir la pile.

La dtection des fonctions rcursives terminales existe dans beaucoup de langages, mais elle est indispensable dans un langage fonctionnel o par nature on utilise beaucoup de fonctions rcursives terminales.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora033.gif0000644000000000000000000000430207073350152014265 0ustar GIF89aUUU!,ڋ޼扒ʶ Mm6àL*i JTjܮ{0f>5O7GX7h8hP#)Xqi%z :몐: Z;;* 0(`IY:k'<( <@ L= mpJ+vm(`Y3mlH^NLoU]ZS֙HA>T51EM ERLʓ,ak J1kIӦN-8sСz$Q6Kƫ5ԩ6m5֭\z 6رpcՔNrplێ`v /\<RrF`7Jbʅ _lA^lUӛi6Xnײΰ4vCjb mn%wg^:yx?}i*UշoGQ(yCjD"@* B&Y0SgHb+|(*H cxc6-c?EVx5WAd&#(BE">WԓIFC`K\VHNi!euএi}F CP޵(I(vɥF9FS:%a{,ת6*|7I+ޢqHZRϦjn쫼6ZalS*jD8glv^q~ ;喘(infg̣r/'~*f;TK*,R-bq+w qʔ;}ɨBIb-ɡKN ]INmOm^)4M_sc/ -ӚdXKMwvߍwz jX*338Mv~s6ͅ8Gj ~`qٗN=ެ䐿Z4S#:iN;ȉ׶nz3KORonu$\1X5֓̌Rvmoە kn[p7h]nd\:WIt6.']Wwb仨%oG(ތc^w"dWhbI1oKƼ.wۗrC0$f9xO0&\aX8.UDO)-`KvSBM1qO{lh.fJqxq^Tٸ>pl#$7C)b2,'c tsed޲}^*Cv^{f枹nK{lT8YΫ-r|.Yt֮ e@,#4 =B,9ۙC- ґƮ1MHԝ.BR ՘apꓸmxmMkںp `PcIȶqf#I⡶ bmgGڑOmDET}Tެ[^w[;ocaml-book-1.0/fr/html/book-ora049.gif0000644000000000000000000000274707073350152014307 0ustar GIF89ayyy!,*޼Hʶ jL ! flJKЩbS-C1355r'9.='8WhH)9IYiyi5 *:JZ*iں*;K +PKy L\l:l =,] MMz.JY ~x==N/^oJâq  /d5tP!-EduQ+ -V4.SF($yPɓzEÏ1'sRHI,q ejISNn\3@L3qUT[VZk$Nztf՞cB6ώfOL%mN1 k^j 3`~'v1`ƔE!ͺM [q@D;ά1M?3m:PJo|.LkZMl1)7*i<,+ !@Ki m44H'LWSv,NWMX/ ߧ^ vb}>dvjgn mMwvz\!ww6 87mx߈-uKؔcg揲-zZl랜Q/lH#:(7{dxƚڌyssO,mK[8so}C{ٻO{_|:9{}>5;Nۓ/ȍ.qc+ 7gRIH:8od HAs˒3N+ ᪾wp$\ S03`='>op.Daw(}@,OW(K1!, y2ȸr7+5Yɖ7ƽ|aT^ScFt|1 ȷ2D" /wI "l$'O!)KɅS \ZJ)2N%-@["</{_iL 3i&3ϰg lljs|@;ocaml-book-1.0/fr/html/book-ora043.html0000644000000000000000000000652107421273601014472 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

Les principales consquences de l'ajout des traits impratifs dans un langage fonctionnel sont :
  • de fixer la stratgie d'valuation (valuation stricte);
  • d'ajouter des contraintes d'implantation, en particulier pour le GC (voir chapitre 9);
  • pour les langages typs statiquement, de complexifier leur systme de types;
  • d'offrir diffrents styles de programmation dans le mme langage, permettant ainsi l'criture dans le style appropri ou possiblement mixte d'algorithmes.
Ce dernier point est important en Objective CAML o l'on dsire avoir le mme polymorphisme paramtrique pour les fonctions crites dans un style ou dans l'autre. Pour cela, certains programmes purement fonctionnels ne sont plus typables avec cet ajout. L'article de Wright ([Wri93]) explique les difficults du polymorphisme dans les langages munis de traits impratifs. Objective CAML adopte la solution qu'il prconise. La classification des diffrents types de polymorphisme en prsence de modification physique est bien dcrite dans la thse d'Emmanuel Engel ([Eng98]).

Ces consquences rendent la tche des programmeurs un peu plus ardue et l'apprentissage du langage un peu plus difficile, mais comme le langage est plus riche et surtout laisse le choix du style, le jeu en vaut la chandelle. Par exemple l'valuation stricte est de rgle, mais il est possible d'implanter les mcanismes de base pour l'valuation paresseuse grce au mlange des deux styles. La plupart des langages fonctionnels purs utilisent cette valuation. Proches de ML on peut citer les langages Miranda, LazyML et Haskell. Les deux premiers sont utiliss l'universit (enseignement et recherche). Par contre, il existe des applications consquentes crites en Haskell. L'absence d'effets de bord contrlables ncessite une abstraction supplmentaire pour les entres-sorties, appele monade. On peut lire les ouvrages sur Haskell ([Tho99]) pour approfondir le sujet. Les flots reprsentent bien ce mlange de style fonctionnel et impratif. Leur utilisation pour les analyses lexicale et syntaxique est dtaille au chapitre 11.




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora043.gif0000644000000000000000000001147407073350152014276 0ustar GIF89arUUUrrr!,rH0I8ͻ`(dih"p,tmx|ﬠpH,rl:PrJZبvzkذxL.[^:^-_~o+~& uaAs.neƨYC!/T؀ F%  }ݨG@7i1(`nPQpG@^` N@|%2ė D_ˌ7Irf—B k:#4N̨J5JfL@gK0#µ7S{סvlnMXΛeܳzvVa gR&>LDŽ%sUxf`.hϟHzbћN aVgks\qRb^6ܷmv{oh J197N+}Kw{}#Aq_l7 AgR E8 DAI4VWXXIHe ]a bu{]WcPRnG@4"{8e%ʂh0i$+ EoXiGk\5DSbni 8D\#tp>t:s \ق~aebXMd}idhCŨq䘁gC|@ب1*K)  -W*wu!Ίq|bVԂgȢj%\@b!@$Fжh@"Y-ܲCoE#=heul>.mu cd mE,\q,!̽&,*SqZ4l 0<3:WFlH41xbts8~W/-[DS! J;j2%k=Awۮv-:DɁ ߙ^-GSk#ZͲ c_ȹ?Յxk>/ۺg~>Ωgc0c{MW~2tR+3=PbY6=dʳOx~ee GStpI;ZA xV)ʷQ&6SʢF52,18@! 5&&$QhJSXt0xݤa"9YF^SvR]oaDL]$AE)vJ 5 .UicbdX0Nj#UħFlN ψkmz2(,︦hRҗR!oSB4ѵHz|0ld^*Z$ҔK}#ܶ!VE$@ٵql{:qDJYq2 aԡ7)b0]F_&S9lDICsM+z!I!6zQ&ŲmzT dp33.쳎u%@ .Z="#\7zz^֠<  Z=4yDj=/Z1-57/,#OzBħ!u ӥ}?ɇv|Hv~7zwv W~X3 xT~ zHX Hy{j&($({)-+34V1H/XT#x{9[7wBxw;(u8HtEt? XROwT+X(Z[8]&_)\8^H`hbXjxklKs(hNvHmxauo膀ȇy臁h qss(Ȉ؇8؈~xo؉XXȉ艡؊.Ȋ花Â秊苺؋ȋEwиh،xrLj؍h⸆hrHȍX׸׎騏%bYiXp ) p)II)oɑ$%'IBI_Y)(i2+y- .99< 57BّD6:L9FHYoJٓMYK HWiT_ Z}\Oy]h[#s[{2 "^|Yjf@zn >{TyY} ~Օ,;E{l`{>1 Mzii{{ص]E-SSC*؅EJTeLATDlC$k0)9|YX._KKVb(`?=HT,rMAet@jYyi,a)a|Kb)kKX&J5g2͹&c ĉzjs}LTAH&VL"$LlK  "**i6NZr!G9:fC3$@%J{&B!PaCP &Pە~D`+9eLڙq"c U&!*#9rfQR*Ed5+UEզg2ef'QUgkDhT ?*o ,`:A*na<4!:;HX.ө c9bؒzȨ Z o ʩZ髦Zz92z: ϚjњPX:ʄڭ䪮4YlM8HBsuZʮ*kYX dm$9{: dD{BEF #@24wbg@뚰ٰ GGRcQ)B)({)Ӻ IՀ^$.cI/zaJ[":["NJFg*+cI-K{6BWbPQ4eQeL[;MzO; (vXZ{4-{YʷO@kp۵q봔 $r K{@<:z;<>,ƋF+{L\i|n<2 z;AXOJ."TkXa=_؃Qs0#Q}:$SE?R%l\$}Wc--b3ڢL\ʦ}ƴ ǽ]Ýܵ ݻ,Al ]ܽvL ޷mT XJ] ;ocaml-book-1.0/fr/html/book-ora064.html0000644000000000000000000000516107421273601014474 0ustar Introduction Prcdent Index Suivant

Introduction

La transformation du texte d'un programme vers une forme excutable ncessite plusieurs tapes de transformation. Le regroupement de celles-ci constitue le processus dit de compilation. Celui-ci produit un arbre de syntaxe abstraite (comme pour l'interprte BASIC, page ??) puis une suite d'instructions pour une machine relle ou virtuelle. Dans ce dernier cas, il est ncessaire d'utiliser un interprte des instructions de cette machine virtuelle. Dans tous les cas, ce que produit le compilateur doit tre reli avec la bibliothque d'excution qui est fournie avec la distribution du compilateur et qui peut varier selon les processeurs et les systmes d'exploitation. Cette bibliothque d'excution comprend les fonctions primitives (comme les oprations sur les nombres, l'interface avec le systme d'exploitation) et le gestionnaire de mmoire.

Le langage Objective CAML possde deux compilateurs. Le premier est un compilateur vers une machine virtuelle, il produit du code-octet (byte-code). Le deuxime compilateur est un compilateur vers du code natif qui produit des instructions de processeurs rels, comme les machines Intel, Motorola, SPARC, HP-PA, Power-PC et Alpha. Le compilateur de code-octet privilgie la portabilit alors que le compilateur natif augmente la rapidit d'excution. La boucle d'interaction, vue dans la premire partie de cet ouvrage, utilise le compilateur de code-octet : chaque phrase crite est compile puis excute dans l'environnement des symboles dj dfinis lors d'une session d'interaction.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora057.html0000644000000000000000000000711407421273601014476 0ustar Introduction Prcdent Index Suivant

Introduction

L'intrt d'un langage informatique par rapport un autre provient de la facilit de dvelopper des applications dont l'excution est sre et dont la maintenance est aise. La premire partie de cet ouvrage consacre la prsentation du langage Objective CAML va logiquement se clore avec la ralisation de plusieurs applications.

La premire application propose l'implantation de quelques fonctions permettant l'expression de requtes sur une base de donnes. L'accent est mis sur l'utilisation d'un style de programmation fonctionnel et sur les manipulations de listes. On peut ainsi fournir l'utilisateur un ensemble de fonctions permettant d'exprimer et d'valuer facilement ses requtes en utilisant directement le langage Objective CAML. On montre au programmeur comment il peut facilement fournir l'utilisateur la plupart des outils d'interrogation dont ce dernier peut avoir besoin.

La deuxime application est un interprte pour un petit BASIC1. Il est noter que ce type de langage impratif a fait le succs des premiers micro-ordinateurs. Vingt ans aprs, cela semble fort simple raliser. Bien que BASIC soit un langage impratif, l'implantation de cet interprte utilisera plutt la partie fonctionnelle d'Objective CAML, en particulier pour l'valuation des instructions. Nanmoins les parties d'analyses lexicale et syntaxique du langage utilisent une structure physiquement modifiable.

La troisime application est un jeu un joueur, le Dmineur, bien connu car il est incorpor l'installation standard des systmes Windows. Le but du jeu est de dcouvrir un ensemble de mines caches en envoyant des sondes qui indiquent le nombre de mines autour d'une case dtermine. L'implantation utilise plutt la partie imprative du langage, car la structure de donnes sous-jacente est un tableau deux dimensions qui sera physiquement modifi chaque coup jou. Cette application utilise le module Graphics pour le dessin du jeu et pour grer l'interaction avec le joueur. Nanmoins la dtection des cases dcouvrir sera ralise dans un style plus fonctionnel.
Cette dernire application fera appel des fonctions du module Graphics dcrit au chapitre 5 (voir page ??) ainsi qu' quelques fonctions des modules Random et Sys (voir chapitre 8, pages ?? et ??).


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora149.html0000644000000000000000000000332207421273602014476 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a dcrit l'extension objet du langage Objective CAML. La structuration en classes est une alternative aux modules qui permet d'une part la modlisation objet d'une application et d'autre part la rutilisation et la modifiabilit des programmes grce l'hritage et la liaison retarde. Cette extension s'intgre au systme de types d'Objective CAML et lui ajoute la notion de sous-type, permettant ainsi d'utiliser une instance d'un sous-type en tout endroit o une valeur du type est attendue. En combinant le sous-typage et la liaison retarde, on obtient un polymorphisme d'inclusion permettant, par exemple, de construire des listes homognes du point de vue des types, mais htrognes du point de vue des comportements.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora034.html0000644000000000000000000000150307421273603014467 0ustar Notes
1
Un mot de 32 bits contient quatre caractres cods chacun sur un octet.
2
avec les droits en lecture si il y a lieu.
ocaml-book-1.0/fr/html/book-ora183.html0000644000000000000000000000202207421273603014471 0ustar Notes
1
Dans ce cas, les compilateurs Objective CAML ont d tre construits en indiquant qu'ils utilisaient la bibliothque fournie par le systme d'exploitation et non celle fournie par la distribution.
2
De faon plus gnrale, on peut tre en concurrence pour d'autres ressources telles que les priphriques d'entres-sorties
ocaml-book-1.0/fr/html/book-ora034.gif0000644000000000000000000000462407073350152014275 0ustar GIF89aUUUrrr!,X0I8ͻ`(dihlj,tmx/PpH,ȇ+K:ШtkԬv*wL.qՇnp#xxv}_{SN = KPl l +Q xnTՀ-ïd֨͏hɓ rԙ m$ >H1ED3jpƏ 7 I$YP\ NG0|掙7p.SΝ=AѣmBʴӧPJJբz"sX↡cF?TK+4[&Gt5oիf-ͣm[~e=*RI@0*wMXCx,W-!/J& Ћco*%̔_{,;h‘ 8AXAGaޤc:?w^\)֩6G^4P p|ޱ6{mހe{ǟa)C8Ws 27+pIٲap,'X ,a 2HČmp^~>2HҐiD i1190Bɓw =a)UUdc^]xYX\Zh9^>IPOxL䩧vޗٕU&#{٧R r6!(f9h+vZ.ؓ>zkN*j*\;zjPJ6, X)*:g Vfdժp,kYREVnf7JmA[x,l_t闱=Lv/| r&wK*¹:1 :.#),] پ[K#L1lY)=.q*?|k}(r5'Y4k|$ۿ3H3_`~F .X3 vB]Ҧi^z.cx˝^hhS$-CS..M }iUw(vY ]:oc޹03yYc4 =|s߼X<^t5}ʋc?@25-}7mfA*W=|C d#Dυ6bg 6M~9( rЈx6GGBY+^Prkp^C%>.tFJ.Fo!-ԡApAA.c\A1QBBFt9%ȦDrg[k8F~Ƒ#xvYrX_Z OTgG+'BHWk gػxH,4%@ml8hB\ TNN'gORSc+罀ЎA Ɩi[fsP`qFblGoK^ѿ5d(`?.i`5w}p#.F0&aIi;6xI:1_8.ś.^qZa ه2;p,k8Ǐc CxGJ/(ȩV\ZH,~f\ Z5mA4]0kc0jnaw~z0ʹ29:R)DG>, _Y kZ> RætBLem5l:vvo/n>#uK|SX&kbd'[ҳ51m\GW6FDa8ưN3p;!޶N{ݨywY £{"?20}S.0gum%pT}rrXtޏx.pgRi܄EMxr1Qᦢ"ʎ+A;^t;f ;ocaml-book-1.0/fr/html/book-ora072.gif0000644000000000000000000002302007073350152014266 0ustar GIF89at*}}}{{{yyywwwuuusssWWW333pppnnnllljjjĊ!!Q,tQ*ma)`(_&^%]$\#["!Y X (zXȰ!C+#Jءŋhq# CBɒR\r˗0ajI  㠧O"@BhH"´Ӧ J*UիX.ʵ+ `Ê ٳhӢM۷p"Kݻvw/6@?+^ǐL2e3kܣϞMz4ӨSk;b˞M6ms{w .$Q+WysQP<*ɗY }`Պ_6m_?{=L0#wlbڀfh%xk fۃۄF܅܆9 $ljp ,GbvXmywz@ސyח|G~ h 6`\Vᗺe(fqr%)"l0FƜtIՎM@)hDG"1ɘAiQ a v`ØPg~fm .)5Z+Ʈ f 'o+l\v kh' z2A*Qʙajq\jcaJbmꬲ檯/ ,pm\Q m @ #E찄"puбs9+r{Ek& +2 kiy+  {.%b!;/n"v\:+,mkj!%_@&{Ϭ .P'.'0N= tB] 2Aݜ&Rob][w/-ٴ;;Q v|̄U/F 8Ox/~f߇>&,BM4k z!Ɖ>:7~:걪N+r`G0ىv˝(:wi @º ,{ >}|=8A UBvV E#C '!sCˇ[rm7!]~8_ 5ЀC= -2 hL61 @:1BfN( `Ͱbx1Bp![#,@N0IIV]"boH$*kmIVTņi1b]HKލfK4s0<082if"@P$PN  x~l"FY7V*[WN\e-o9F]沗>c0~.l@:=h28uD jvi TȤt8]8Er\eJw3#=h3Sf? ОLP ѢFTHhJ:B |$I?3nP) XWt1L%X29 NCQԾ&}j Kv Mlb IJ౐, | (ᲘU6YHhGKҚHHjWֲlgKᶸͭnw"OK\FQr2:ХQVrP5$iZpL3bҀ|Kͯ~L턂;'L [ΰ7{ GL(NW0gL8αw@L"HN&;PL*[Xβ.{`L2hN6pL:xs>πMBЈNF;ѐ'MJ[ϊ7N{ӠG LԨNWVЦ~gMZ֪5w^׸5Mb؍6f;~Mj_:ζ>cqnuv[~MoX'η}7Npj O;<~'kS u/.kseC|-?ˆ4_|)9K|}^:FЇs3zL9ГoSP:͋kW:"¾k|SGN9{v]t.E{ݹnhp:U=so6W?}#|7?χ>?sw}?^9=G_׻O|ygGyxy|hu H~ QwHzXiv{i"z]|$Xy؁sOvwƁ68i1t]˱3KK; ytl`wR;MrM˴Xm;bx$8Nn+^;/ 6ej=ʳ\y~Hs禷 Ǔ7@utٸ>w;nkk빦ɊCgjxɾtkʝfql+rݛ{ w;6KJػɺ( gJO)ÍI[~Q), zOˮg鋝뫿Չ8|͆!_0KU|E|K v׋xW (|@kȻZ ;u jKkrܚ*Y˥Qt囼Vlۼn<y G6ez| eJbJfށLՀkءӦX,&9]wh}]qكwγڸ,oy[ܺ;ε n79X|f ky|̐L;䋺ɔ=>nVY|r͜<^M^x_܅vs{X.ۊ\kZl9ڰΝ«f|~y^ƹޝIڄ΢[Pj_l.z'ԛ.|<ؓ^挷#˭p_nNNs]٧&{~TjqfKv.n`.P䯖}皊~{?^/,OŌ gI~{쀮ε*-/_^n$^I].ǹ&>UFj5N"P`>N.mj{=\AC, WJK. ݃3N~{V񈵬K->j:튖 pOL|B?xǘΎVԷ+xՄl*߾;ߏӆkʿg[?k8ǝ0 za]ۦՀ %EXhx((ȨyiHy9ye * Pjz0J 0K[kk˚ 9K)˩I\xl< ]: {z L>.h>IM+}K 9եcwPFL0&{{Ŋ--|PRH \ȇFvGqŋ2r{3J* N'Iќ0yYTCW\Z𪮡Uj ԰d\5`RYnMiٴkݼ{_Mo߸y λxY:7)~H9ᇏ|,ަޱrݾv'4|(Kz&  ap@Q(n^B' 48*^=dȢF+;""c?fWi6.2!A#7EX =_Z`)P&$Qif^J&/82%eqD"|b & ii Mign類&㦿Xz}˜NBa4[R`Jnڢ=QԤzᩥh#eВ:+"J;.ˬq>bҦºy-E箺b-+uwn8Bf\"Lf>msܮ;/E&FQ2;,< ì: ,^L3mL[Xm6'|04tMN!1Ul(Kn%+R<^Ԏj6sY1 I"MmYݱa[Nl-Dm.8nxȈ)[r_vb>N8|zK]2ob7i༲Mvz+{56` _*j-Ch +ҖUoaM1EIFce&xRN2eE_Kә1*uh!6bY!L-FR7Ԧ>l@eIb#ht\RfR0J]Bi8U)kc"''iYj:@iA詗DS %L*b\WH}F*:})"jx|GOC,}wO~JVxlXXj@hz#^(a?B(=f({nlyՅ SvAs8]/eGvz8Wj {Hg\6!ĖxxaWۦgMH芭h(Šh$Hܶ7X:xP{kwtŒHkyHYh'؍öe?#H&}רmtS8uW.$ yF(Pw|혐(/ǃyx?88XnXXmrx`ȋbؒ1II)BA9(o"iXeJI[chm,ԔN9rPIWqEd9~qUwȀ_ ٘DCg@L8I` D|yB8ݥvg ?1xY3(6FiQv8Yx(nwv1C ko J񚨙|i)ɛP H)ljyhyZWs؜ ׹HKi~/ّ4W0Y2 IG♗hᒟ8(iwnsS1gm*II }h*(02jʡKJ:1ʩ!Xډĕlg+ʢ- f)h=: zQZp>JZzWK:%i9L*C:Y8NܩZ \ڥ̙,a1ZyZ:zs;ئn)r:t9չyڣiԩjZ ʨ 8J1pʩZ J/1ʪ IGJvZʠɫ ʩjJP *Jj׊٪ʭ *Jj犮骮ʮ *Jjʯ +K؀ Q +Kk˱ !+#[ &K+˲-/ 1+3K{5˳=? A+/[CK˴MO۱I[KUkWP;ocaml-book-1.0/fr/html/book-ora101.html0000644000000000000000000000427007421273602014465 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a prsent les diffrents outils d'aide la ralisation de programmes que fournit la distribution d'Objective CAML.

Le premier outil effectue une analyse statique pour dterminer les dpendances d'un ensemble d'units de compilation. Ces informations sont ensuite intgrables dans un fichier Makefile permettant de ne compiler que le strict ncessaire suite une modification dans un ou plusieurs fichiers.

D'autres outils donnent des informations sur l'excution du programme. En premier lieu, la boucle d'interaction offre une trace de l'excution ; mais nous avons vu que le polymorphisme imposait des restrictions assez lourdes quant aux valeurs qui sont observables. En fait seules les dclarations globales de valeurs monomorphes sont visibles, ce qui inclut tout de mme les paramtres des fonctions monomorphes et permet de tracer l'excution des fonctions rcursives.

Les derniers outils sont ceux de la tradition des langages de dveloppement sous Unix, savoir un debugger et un profiler. Le premier permet d'excuter pas pas un programme et le second fournit des informations sur ses performances. Tous deux ne sont pleinement utilisables que sous Unix.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora107.html0000644000000000000000000014751307421273602014503 0ustar Syntaxe Prcdent Index Suivant

Syntaxe

Grce l'analyse lexicale, nous sommes mme de dcouper notre flot d'entre en units plus structures : les units lexicales. Il faut encore savoir assembler ces units de faon former des phrases syntaxiquement correctes d'un langage donn. Les rgles d'assemblage syntaxique sont dfinies au moyen de rgles de grammaires. C'est un formalisme originaire de la linguistique que les mathmaticiens thoriciens des langages et les informaticiens ont su reprendre avec profit. Nous avons dj vu par anticipation page ??, un exemple de grammaire pour le langage Basic. Nous allons reprendre cet exemple afin d'introduire les concepts de base des grammaires.

Grammaire

Formellement, une grammaire est constitue de quatre lments :
  1. Un ensemble de symboles dits terminaux. Ces symboles reprsentent les units lexicales du langage. En Basic, on y trouve les symboles oprateurs et de relations arithmtiques et logiques (+, &, <, <=, ..) ; les mots cls du langage (GOTO, PRINT, IF, THEN, ..) ; les entiers (unit entier) et les variables (unit variable).
  2. Un ensemble de symboles dits non terminaux. Ces symboles reprsentent les composantes syntaxique du langage. Par exemple, un programme Basic est compos de lignes, on a donc la composante Ligne ; une ligne peut contenir une Expression, etc.
  3. Un ensemble de rgles dites de production. Elles dcrivent comment s'articulent les symboles terminaux et ceux non terminaux pour produire une composante syntaxique. Une ligne en Basic, est forme de son numro suivi d'une instruction. C'est le sens de la rgle suivante :
    Ligne ::= entier Instruction
    Une mme composante peut tre produite de plusieurs faons. On spare les alternatives par le symbole | comme dans
    Instruction ::= LET variable = Expression
      | GOTO entier
      | PRINT Expression
    etc.
  4. Enfin, parmi tous les non terminaux, on en distingue un que l'on appelle axiome. C'est sa rgle de production qui dnote la rgle de production du langage dans son entier.

Production et reconnaissance

Les rgles de production peuvent servir reconnatre qu'une suite de lexmes appartient un langage.

Considrons, par exemple un langage simple d'expressions arithmtiques :
Exp ::= entier (R1)
  | Exp + Exp (R2)
  | Exp * Exp (R3)
  | ( Exp ) (R4)
o (R1) (R2) (R3) et (R4) sont les noms que nous donnons nos rgles. Aprs passage par l'analyse lexicale, l'expression 1*(2+3) devient la suite de lexmes :
entier * ( entier + entier )
Pour analyser cette phrase et reconnatre qu'elle appartient bien au langage des expressions arithmtiques, nous allons utiliser nos rgles de droite gauche : si une sous-expression correspond un membre droit de l'une des rgles, nous la remplaons par le membre gauche correspondant et nous recommenons jusqu' avoir rduit notre expression au non terminal axiome (ici, Exp). Voici les tapes d'une telle analyse3.
entier * ( entier + entier ) (R1) Exp * ( entier + entier )
  (R1) Exp * ( Exp + entier )
  (R1) Exp * ( Exp + Exp )
  (R2) Exp * ( Exp )
  (R4) Exp * Exp
  (R3) Exp
En partant de la dernire ligne qui ne contient plus que Exp et en remontant les flches, on lit comment a pu tre produite notre expression en partant de la rgle axiome Exp : c'est donc une phrase bien forme du langage dfini par la grammaire.

La traduction des grammaires en programmes capables de reconnatre qu'une suite de lexmes appartient au langage dfini par une grammaire est un problme beaucoup plus complexe que celui de l'utilisation des expressions rationnelles. En effet, un rsultat mathmatique nous dit que tout ensemble (de mots) dfini au moyen du formalisme des expressions rationnelles peut aussi tre dfini dans un autre formalisme : les automates finis dterministes. Et ces derniers sont faciles exprimer comme des programmes prenant en entre une suite de caractres. On ne dispose pas d'un tel rsultat pour le cas des grammaires en gnral. Cependant, on dispose de rsultats plus faibles tablissant l'quivalence entre certaines classes de grammaires et des automates un peu plus riches : les automates pile. Nous ne voulons pas entrer dans les dtails de ces rsultats ni donner une dfinition exacte de ce que sont les automates. Cependant, nous ne pourrons pas faire l'conomie d'une petite tude des grammaires afin d'identifier quelle classe de grammaire peut tre utilise dans les outils de gnration d'analyseurs syntaxiques ou pour implanter directement un analyseur.

Analyse descendante

La dcomposition de l'expression 1*(2+3) propose au paragraphe prcdent n'est pas unique : on aurait pu tout aussi bien commencer par rduire les entiers de droite gauche, ce qui aurait permis d'utiliser plutt la rgle (R2) pour rduire d'abord 2+3. Ces deux faons de faire constituent deux types d'analyses : l'analyse ascendante (de droite gauche) et l'analyse descendante (de gauche droite). Cette dernire est facilement ralisable partir de flots de lexmes en utilisant le module Stream. L'analyse ascendante est celle ralise par l'outil ocamlyacc, elle met en jeu un mcanisme explicite de pile comme cela a dj t illustr pour l'analyse syntaxique des programmes Basic. Le choix du type d'analyse n'est pas indiffrent car suivant la forme de grammaire utilise pour spcifier un langage, on peut ou non utiliser l'analyse descendante.

Le cas simple

Le cas d'cole de l'analyse descendante est la notation prfixe des expressions arithmtiques dfinie par :
Expr ::= entier
  | + Expr Expr
  | * Expr Expr
Dans un tel cas, il suffit de connatre le premier lexme pour savoir quelle rgle de production a pu tre utilise. Cette prdictibilit immdiate permet de ne pas grer explicitement la pile de l'analyse et de s'en remettre l'empilement des appels rcursifs de l'analyseur. Il est alors trs facile d'crire un programme implantant l'analyse descendante en utilisant les fonctionnalits des modules Genlex et Stream. La fonction infix_of ainsi obtenue prend une expression prfixe et retourne son quivalent infix.

# let lexer s =
let ll = Genlex.make_lexer ["+";"*"]
in ll (Stream.of_string s) ;;
val lexer : string -> Genlex.token Stream.t = <fun>
# let rec stream_parse s =
match s with parser
[<'Genlex.Ident x>] -> x
| [<'Genlex.Int n>] -> string_of_int n
| [<'Genlex.Kwd "+"; e1=stream_parse; e2=stream_parse>] -> "("^e1^"+"^e2^")"
| [<'Genlex.Kwd "*"; e1=stream_parse; e2=stream_parse>] -> "("^e1^"*"^e2^")"
| [<>] -> failwith "Parse error"
;;
val stream_parse : Genlex.token Stream.t -> string = <fun>
# let infix_of s = stream_parse (lexer s) ;;
val infix_of : string -> string = <fun>
# infix_of "* +3 11 22";;
- : string = "((3+11)*22)"


Il faut prendre garde ce que l'analyse lexicale est assez fruste. Il est conseill d'intercaler systmatiquement un espace entre les diverses units lexicales de la chane d'entre.

# infix_of "*+3 11 22";;
- : string = "*+"


Un cas moins simple

L'analyse syntaxique utilisant les flots est prdictive. Elle impose deux conditions sur les grammaires.
  1. Il ne doit pas y avoir de rcursivit gauche dans les rgles de grammaire. Une rgle est rcursive gauche lorsque que le membre droit commence par le non-terminal qui est le membre gauche de la rgle, comme dans Exp ::= Exp + Exp ;
  2. Il ne doit pas y avoir deux rgles commenant par la mme expression.
La grammaire usuelle des expressions arithmtiques donne page ?? ne convient pas directement l'analyse descendante : elle ne satisfait aucun des deux critres ci-dessus. Pour pouvoir utiliser une analyse descendante, il faut reformuler la grammaire de faon supprimer la rcursion gauche et le non dterminisme des rgles. Pour les expressions arithmtiques, on peut donner, par exemple :
Expr ::= Atom SuiteExpr
SuiteExpr ::= + Atom
  | - Atom
  | * Atom
  | / Atom
  | e
Atom ::= entier
  | ( Expr )
Remarquons l'usage du mot vide e dans la dfinition de SuiteExp. Il est ncessaire si nous voulons qu'un entier tout seul soit une expression.

Notre grammaire permet l'implantation de l'analyseur suivant qui n'est qu'une traduction simple des rgles de production. Cet analyseur produit l'arbre de syntaxe abstraite des expressions arithmtiques.

# let rec suite = parser
[< 'Lsymbol "+"; e2 = atom >] -> Some (PLUS,e2)
| [< 'Lsymbol "-"; e2 = atom >] -> Some (MOINS,e2)
| [< 'Lsymbol "*"; e2 = atom >] -> Some (MULT,e2)
| [< 'Lsymbol "/"; e2 = atom >] -> Some (DIV,e2)
| [< >] -> None
and atom = parser
[< 'Lint i >] -> ExpInt i
| [< 'Lsymbol "("; e = expr ; 'Lsymbol ")" >] -> e
and expr s =
match s with parser
[< e1 = atom >] ->
match suite s with
None -> e1
| Some (op,e2) -> ExpBin(e1,op,e2) ;;
val suite : lexeme Stream.t -> (op_bin * expression) option = <fun>
val atom : lexeme Stream.t -> expression = <fun>
val expr : lexeme Stream.t -> expression = <fun>


La difficult d'utilisation de l'analyse descendante vient de la ncessit d'avoir des grammaires d'une forme trs restreinte. Et lorsque le langage vis se formule naturellement, comme dans le cas des expressions infixes, avec une grammaire rcursive gauche, il n'est pas toujours trivial de trouver une grammaire quivalente (i.e. qui dfinit le mme langage) qui satisfasse aux exigences de l'analyse descendante. C'est pourquoi les outils yacc et ocamlyacc mettent plutt en oeuvre un mcanisme d'analyse ascendante qui autorise la dfinition de grammaires plus naturelles. Nous allons cependant voir que l encore, tout ne sera pas possible.

Analyse ascendante

Page ??, nous avons prsent intuitivement les principes de l'analyse ascendante : avancer (shift) ou rduire (reduce). chacune de ces actions l'tat de la pile est modifi. On peut dduire dette suite d'actions des rgles de grammaire, si toutefois, comme dans le cas de l'analyse descendante, la grammaire s'y prte ! Ici encore, la difficult viendra du non dterminisme des rgles qui ne permettra pas de choisir entre avancer et rduire. Nous allons illustrer le fonctionnement de l'analyse ascendente et ses causes d'chec en considrant les sempiternelles expressions arithmtiques en notation postfixe et en notation infixe.

Pour le meilleur
La grammaire simplifie des expressions arithmtiques postfixes est :
Expr ::= chiffre (R1)
  | Expr Expr + (R2)
  | Expr Expr * (R3)
Cette grammaire est duale de celle des expressions prfixes : il faut attendre la fin de l'analyse pour savoir, en fin de compte, quelle rgle a t utilise, mais une fois cette fin atteinte, on sait exactement comment faire. En fait, l'analyse ascendente de telles expressions est extrmement proche d'un mcanisme d'valuation avec une pile. Simplement, au lieu d'empiler les valeurs rsultant du calcul, on empile les symboles de la grammaire. L'ide tant que si l'on dmarre avec une pile vide il faut obtenir une pile ne contenant que le non terminal axiome lorsque l'entre est puise. Les modifications de la pile sont les suivantes : si l'on avance, on empile le non terminal courant ; si l'on rduit, c'est que les premiers lments de la pile correspondent un membre droit (invers) de rgle, auquel cas on remplace ces lments par le membre gauche correspondant.

La figure 11.2 illustre comment l'analyse ascendante traite l'expression : 1 2 + 3 * 4 +. L'unit lexicale lue est souligne. La fin de l'entre est marque par un $.


Action Entre Pile
  12+3*4+$ []
Avancer  
  2+3*4+$ [1]
Rduire (R1)  
  2+3*4+$ [Expr]
Avancer    
  +3*4+$ [2Expr]
Rduire (R1)  
  +3*4+$ [Expr Expr]
Avancer, Rduire (R2)  
  3*4+$ [Expr]
Avancer, Rduire (R1)  
  *4+$ [Expr Expr]
Avancer, Rduire (R3)  
  4+$ [Expr]
Avancer, Rduire (R1)  
  +$ [Expr Expr]
Avancer, Rduire (R2)  
  $ [Expr]

Figure 11.2 : Exemple d'analyse ascendante


Pour le pire
Toute la difficult du passage de la grammaire au programme de reconnaissance du langage est de dterminer quel type d'action appliquer. Nous allons illustrer cette difficult sur trois exemples provoquant trois genres d'indtermination.

Le premier exemple est une grammaire pour les expressions n'utilisant que l'addition :
E0 ::= entier (R1)
  | E0 + E0 (R2)
L'indcision de cette grammaire vient de la rgle (R2). Supposons la situation suivante:
Action Entre Pile
:    
  +... [E0 + E0 ...]
:    
Dans un tel cas, il n'est pas possible de dterminer s'il faut avancer et empiler le + ou rduire selon (R2) les deux E0 et le + prsents dans la pile. Nous sommes en prsence d'un conflit avancer-rduire (shift/reduce). Il provient du fait que l'expression entier + entier + entier peut tre produite, par drivation droite, de deux faons.

Premire faon : E0 (R2) E0 + E0
    (R1) E0 + entier
    (R2) E0 + E0 + entier
  etc.

Seconde faon : E0 (R2) E0 + E0
    (R2) E0 + E0 + E0
    (R1) E0 + E0 + entier
  etc.

Les expressions obtenues par les deux drivations peuvent sembler similaire du point de vue de l'valuation d'une expression :
(entier + entier) + entier et entier + (entier + entier)
mais diffrent pour la construction d'un arbre syntaxique (voir figure 6.3 page ??).

Le deuxime exemple de grammaire engendrant un conflit entre avancer et rduire relve du mme genre d'ambigit : un parenthsage implicite. Mais contrairement au cas prcdent, le choix en avancer et rduire modifie le sens de l'expression analyse. Soit donc la grammaire :
E1 ::= entier (R1)
  | E1 + E1 (R2)
  | E1 * E1 (R3)
On retrouve avec cette grammaire le mme conflit que ci-dessus la fois pour + et *. Mais s'y rajoute un conflit entre + et *. Ici encore, une mme expression peut tre produite de deux faons. Il existe deux drivations droite de

entier + entier * entier

Premire faon : E1 (R3) E1 * E1
    (R1) E1 * entier
    (R2) E1 + E1 * entier
  etc.

Seconde faon : E1 (R2) E1 + E1
    (R3) E1 + E1 * E1
    (R1) E1 + E1 * entier
  etc.

Ici, les deux paranthsages (implicites) ne sont pas quivalents :
(entier + entier) * entier entier + (entier * entier)

Ce problme a dj t rencontr pour les expressions Basic (voir page ??). Il a t rsolu en attribuant des priorits aux oprateurs : on rduit (R3) avant (R2). Ce qui revient parenthser les produits.

On peut galement rsoudre le problme du choix entre + et * en modifiant la grammaire. On introduit deux nouveaux terminaux : T, pour terme, et F, pour facteur. Ce qui donne :
E ::= E + T (R1)
  | T (R2)
T ::= T * F (R3)
  | F (R4)
F ::= entier (R5)
Il n'y a maintenant plus qu'une seule faon de dmarrer la squence de production de entier + entier * entier : en utilisant la rgle (R1).

Le troisime et dernier exemple concerne les instructions conditionnelles des langages de programmation. Un langage comme Pascal offre deux conditionnelles : le if .. then et le if .. then .. else. Imaginons la grammaire suivante :
Instr ::= if Exp then Instr (R1)
  | if Exp then Instr else Instr (R2)
  | etc...
Et dans la situation suivante :
Action Entre Pile
:    
  else... [Instr then Exp if...]
:    
On ne peut pas dterminer si les premiers lments de la pile correspondent la conditionnelle de (R1), au quel cas il faut rduire, ou au premier Instr de la rgle (R2), auquel cas il faut avancer.

Outre les conflits avancer-rduire, l'analyse ascendante provoque galement des conflits rduire-rduire.

Nous prsentons maintenant l'outil ocamlyacc qui utilise cette technique et peut rencontrer ces conflits.

L'outil ocamlyacc

L'outil ocamlyacc est construit sur le mme principe que ocamllex : il prend en entre un fichier de description d'une grammaire dont chaque rgle est attache une action smantique et il engendre deux fichiers Objective CAML d'extension .ml et .mli contenant la fonction d'analyse syntaxique et son interface.

Format gnral
Les fichiers de description syntaxique pour ocamlyacc utilisent par convention l'extension .mly et ont le format suivant :

%{  
  en-tte
}%  
  dclarations
%%  
  rgles
%%  
  suite-et-fin

Le format d'une rgle est :
non-terminal : symbole...symbole { action smantique }
  | ...
  | symbole...symbole { action smantique }
  ;

Un symbole est soit un terminal, soit un non terminal. Les sections << en-tte >> et << suite-et-fin >> jouent le mme rle qu'en ocamllex la diffrence prs que l'en-tte n'est visible que des rgles et non des dclarations. En particulier, cela entrane que les ouvertures de module (open) ne sont pas prises en compte dans la partie dclarative et donc que les types doivent tre qualifis.

Actions smantiques
Les actions smantiques sont des morceaux de code Objective CAML excuts lorsque l'analyseur rduit la rgle laquelle elles sont associes. Le corps d'une action smantique peut faire rfrence aux composants du membre droit de la rgle. Ceux-ci sont numrots de gauche droite en commenant par 1. Le premier composant est rfrenc par $1, le deuxime par $2, etc.

Axiomes
On peut dclarer plusieurs non terminaux axiomes de la grammaire en crivant, dans la partie dclaration :
 %start non-terminal .. non-terminal
Pour chacun d'eux sera engendr une fonction d'analyse. Il faut prciser, toujours dans la partie dclaration, quel sera le type de retour de ces fonctions.
 %type <type-retour> non-terminal
Le type-retour doit tre qualifi.

Warning


Les symboles non terminaux deviennent les noms des fonctions d'analyse. Ils ne doivent donc pas commencer par une majuscule qui est l'initiale rserve aux constructeurs.


Units lexicales
Les rgles de grammaire font rfrence aux units lexicales qui sont les symboles terminaux des rgles.

Un (ou des) lexme(s) se dclare(nt) de la faon suivante :
 %token PLUS MOINS MULT DIV MOD
Certaines units lexicales, comme les identificateurs, reprsentent un ensemble de chanes de caractres. Lorsque l'on rencontre un identificateur on veut pouvoir rcuprer cette chane. On indique l'analyseur lexical que ces lexmes ont une valeur associe en donnant entre < et > le type de cette valeur :
 %token <string> IDENT

Warning


Aprs traitement par ocamlyacc toutes ces dclarations sont transformes en constructeurs du type token. Ils doivent donc imprativement commencer par une majuscule.


On peut utiliser des chanes de caractres comme terminaux implicites comme dans :
expr : expr "+" expr   { ... }
     | expr "*" expr   { ... }
     | ...             
     ;
auquel cas, il est inutile de dclarer un symbole les reprsentant : ils sont directement traits par l'analyseur syntaxique sans avoir passer par l'analyseur lexical. Par soucis d'uniformit, nous ne conseillons cependant pas cette manire de faire.

Priorit, associativit
Nous avons vu que nombre de conflits de l'analyse ascendante proviennent de rgles implicites d'association d'un oprateur ou de conflits de priorit entre oprateurs. Pour liminer ces conflits, on peut dclarer des rgles d'associativit par dfaut ( gauche ou droite ou aucune) pour les oprateurs ainsi que des rgles de priorit. La dclaration suivante indique que les oprateurs + (lexme PLUS) et * (lexme MULT) sont associatifs droite par dfaut et que * a une priorit suprieure celle de + car MULT est dclar aprs PLUS.
 %left PLUS
 %left MULT
Deux oprateurs dclars sur une mme ligne ont mme priorit.

Options de la commande
ocamlyacc possde deux options :
  • -b nom : les fichiers Objective CAML engendrs sont nom.ml et nom.mli;
  • -v : cre un fichier d'extension .output contenant la numrotation des rgles, les tats de l'automate reconnaissant la grammaire et les sources de conflits.
Utilisation conjointe avec ocamllex
On peut composer les deux outils ocamllex et ocamlyacc de sorte que la transformation du flot de caractres en flot de lexmes soit l'entre de l'analyseur syntaxique. Pour se faire, Le type lexeme doit tre commun aux deux. Ce type est dfini dans les fichiers d'extension .mli et .ml engendrs par ocamlyacc partir des dclarations des token du fichier d'extension .mly correspondant. Le fichier .mll importe ce type; ocamllex traduit ce fichier en une fonction Objective CAML de type Lexing.lexbuf -> lexeme. L'exemple page ?? illustre cette interaction et dcrit les diffrentes phases de compilation.

Grammaires contextuelles

Les analyseurs engendrs par ocamlyacc traitent de langages engendrs par des grammaires dites non contextuelles. La poursuite de l'analyse d'un flot de lexme ne dpend pas des valeurs syntaxiques dj traites. Ce n'est pas le cas du langage L dcrit par la formule suivante :
L ::= wCw | w avec w (A|B)*
o A, B et C sont des symboles terminaux. On a crit wCw (avec w (A|B)*) et non simplement (A|B)*C(A|B)* car nous voulons avoir le mme mot gauche et droite du C mdian.

Pour analyser les mots de L, il faut garder en mmoire ce qui a t rencontr avant la lettre C pour vrifier que l'on retrouve bien la mme chose aprs. Voici une solution ce problme qui utilise le parcours d'un flot. L'ide gnrale de l'algorithme est de construire une fonction d'analyse de flot qui reconnatra exactement le sous-mot figurant avant l'ventuelle occurrence de C :

On se donne le type :

# type token = A | B | C ;;


La fonction parse_w1 construit la fonction de mmorisation du premier w sous forme d'une liste de fonctions d'analyse de flot atomique (i.e. pour un seul token) :

# let rec parse_w1 s =
match s with parser
[<'A; l = parse_w1 >] -> (parser [<'A >] -> "a")::l
| [<'B; l = parse_w1 >] -> (parser [<'B >] -> "b")::l
| [< >] -> [] ;;
val parse_w1 : token Stream.t -> (token Stream.t -> string) list = <fun>


Le rsultat des fonctions construites par parse_w1 est simplement la chane de caractres contenant l'unit lexicale analyse.

La fonction parse_w2 prend en argument une liste construite par parse_w1 pour composer chacun de ses lments en une seule fonction d'analyse :

# let rec parse_w2 l =
match l with
p::pl -> (parser [< x = p; l = (parse_w2 pl) >] -> x^l)
| [] -> parser [<>] -> "" ;;
val parse_w2 : ('a Stream.t -> string) list -> 'a Stream.t -> string = <fun>


Le rsultat de l'application de parse_w2 sera la chane reprsentant le sous-mot w. Par construction, la fonction parse_w2 ne pourra reconnatre que le sous-mot parcouru par parse_w1.

En utilisant la possibilit de nommer dans les flots les rsultats intermdiaires, on crit la fonction de reconnaissance des mots du langage L :

# let parse_L = parser [< l = parse_w1 ; 'C; r = (parse_w2 l) >] -> r ;;
val parse_L : token Stream.t -> string = <fun>


Voici deux petits exemples. Le premier donne en rsultat la chane contenant le mot entourant C, le second choue car les deux mots entourant C sont diffrents :

# parse_L [< 'A; 'B; 'B; 'C; 'A; 'B; 'B >];;
- : string = "abb"
# parse_L [< 'A; 'B; 'C; 'B; 'A >];;
Uncaught exception: Stream.Error("")



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora086.gif0000644000000000000000000000454507073350152014306 0ustar GIF89a֖!,H0I8c`qR`lp,tmJm~pv& 0Y ШtJZ4)YRejaNzn|Ngh#.go)w|lxp)e&g~dzG+jkl(m<jb /i˓'K&;(}ԵΈҳğś,ʁLNJW4{܃C|жoQuQ@Qa5vҦMGQ泌fXLIӟ}Tehƴ8bYnk7匢l2FӆhVJ7CÙ;# =Wҵ>vu:.+~˸K`I0R"3kޜϠC67ӨS^ͺװc˞MɺsͻoطNM_μKN]6tn vSN~߽sO={7}֯=x6x\}e |._.!ZzWl R hr'ᅴ(ءx8("8#8!:cL6ڒ#[NVyVf9ZvI^bYf܊ p)tixzng砄j衈g)jFʚ ЈY5ǣvV-鄞 jVXuaaZ fG MV&),뮐`:맱I*l i޾ .^K*:kl0[]v1@nz/^ I CpSp,( O% k\%vmJ}k_.kL2kn~kJ)i3V3ʲ80&ZsA{2M,8]\1SL^!.vӞF릭.X!5Hۨ ph hˮ=lKʷ6#L.nCn6Yk9o]~-4 sNz~kԚ{z|sHO4ʾaӦ<Nc6dٻ}Āϝ7O:ۮ;p|l8???o߶я9sH6b 6g[ؖƢB4yȂ48B%jK =og?B g axX@=gCr 7HH<9"RE8\k(6 }ZdbX03cqXC5+fJ"pL[G-:φvc1S% CFj4+i;z #)OG߬q+ ̲%.w]ʒR#WwHF2-]7/W< 7v=@cx1'!GvRz`i|'= iπ¦=?Wτo46cѡ)srL4-o%5IkKġԨ1t56ijrӌO]EP u?  M&U5DfScVd88Nb-Hfqbe!YEq-[*ô6e)9 Wծm<*R T VUaW5qi캺j r" 9PdYڕjڼ ꬼWN_2DaFSJvir [v֘aI6CBQے@b6R<.%Im<-l4d6[zy˲l-L]/}DݥkDi9ᗊwop׃+8 jTf,[ػ5 0PJ`8αw6lzo"HNP-!˒ɰtLK)V%52L*~b>ˬ暦͟z3&9{c3γ c&5huLЈfuG+ X#hE:ȌNUui2eZn6 ƿti>/I7*Lej٨7 <-7Q[;³qM7Nuͮ:/sSZֱIrMiMkqV؆mek :@"yI(_B 4аBc=q1Q&!Ti@G-$2rc%3JM*CSBΐ1Iv%R&-A BEn\`ZtS*hiihRȑJVa]~R/4)L0Vd]8ŋYkB3bַߖH-hӑQ?n5I,~:3wg8P{~'`5쩬aR5uʾS|nqˉg6OfMu<crekͲRfLgpO|dOAh|Da`Vzexn]%Wl ^wlETxa1`y-w J~5UPq8,)ɢԡ}:&)_P bOeX i!b$Fvp׭ZgBCE'}reKrcDGPr 7JဃW]tk%)>{"@d`)U5lwY.3M *RZH{tȝM Ͻ&ۑ:+k˗,31׬8!s*]FBH{b!:G-T[ .u@Mw3N4ؚ%Djv wr)w$d=~}ě}x9xw_1b3=oӏk}6,pl h@Ox Dr!Š ęr"|Ywq})TX•3OHhC0_ y8?ݎH7h q5b.~XD' R!h+bхZb 1#h30j ۈ7Q蘣h1#?V/! C"yX$A2$%MgK.<:=NrreiA2\%+W2,^)kXRv%zK2&1cVr̜1&Jsl 4kbsQc7_9s,[6q :NR+iϜq3{'?1> :ƒ"4b(- ؑ1L4jq7slTG N(V0>ģYHҡFԁ+HE<\Ga Ni[)N}o(8 q(Sf9ds q;5Xi%*BK.Dԋ kDMe,vvY"̓(V/\)Z,g] :PکգJsv}KtܠTM:+`ͨDܜ2\*)EOY bjke]l%")^Q#9aJC&{/6޴ f51j[d0. |9Bp jb8#5w"1K| #l;ocaml-book-1.0/fr/html/book-ora006.gif0000644000000000000000000000367607073350152014302 0ustar GIF89a:UUUrrr!,:X0I8ͻ`(F@i(0lp,W)s|?'G,H.l:PZXXzN -vnjoMm}}nd$o#%% t c$y Sǡ ɿ_*vj8>sz,lDz&& pLZʡ׏̿~ *pPa…E8/jqGClqIE%Or0cʜI38sܙ%EK+gE}T|TjDO2ʠh)Z-0baTu5ѳV|PS7pLکF& /ԽX.,E@E߸@&lcXy`E02\J&u`y1*_Kr4+[ؠAZu+;{|U9bB~:[C]$<}\CEcvuwP\2 z5@~$ )](Ov &v'ۉ(` "k&'RrݥY]a1h$C.K>^XqA*EU!(daU>y%9Ҹ%]>%KD:cFk9T WQޑJMURVJel JJ}f(Vfz駔* jjlzFN:U e,k&DKIdpkBy3l{ Bb'RKvaڪ-5`dž@"i۠sٲ],՗&Rۂ eB uëM[,-Y2oLk rȩL& *X.2Y6Ds<%-Dm2otHCskM'4QduՋ\lM*^1uae{iJb vpAϝFM6!^fi(W!v>v- ;F*3UKN7^՝Od?M[g!À!r¯]P`-:3rj'YO}K H'䯀; [ρ<7b eI38%dN@,ppk jRaB3/uf.>թOU~ӡ<Њ24L턨'yCPCĨ]tT]u1iHQ&IGB˛eŢJuC)ET#SS˙]d0xfUkuՋOnGCq)x#׊#`]OXC`@qU%0-+,(!`4&1jmuH/B!AWJ4Ւz` [y9Ǹኂ Calculatrice de bureau Prcdent Index Suivant

Calculatrice de bureau

Pour comprendre comment se btit un programme en Objective CAML, il est ncessaire d'en raliser un. L'exemple choisi est une calculatrice de bureau, le modle le plus simple, c'est--dire qui ne fonctionne que sur des nombres entiers et ne ralise que les quatre oprations standard de l'arithmtique.

Pour commencer, on dfinit le type touche pour reprsenter les touches d'une calculette. Celle-ci possde quinze touches, savoir : une pour chaque opration, une pour chaque chiffre et la touche =.

# type touche = Plus | Moins | Fois | Par | Egal | Chiffre of int ;;
On remarque que les touches numriques sont regroups sous un unique constructeur Chiffre prenant un entier comme argument. De ce fait, certaines valeurs de type touche ne reprsentent pas rellement une touche. Par exemple, (Chiffre 32) est une valeur possible du type touche, mais ne reprsente aucune touche de la calculatrice.

On crit donc une fonction valide qui vrifie que son argument correspond une touche de la calculatrice. Le type de cette fonction est touche -> bool, c'est--dire qu'elle prend comme argument une valeur de type touche et rend une valeur de type bool.

La premire tape est de dfinir une fonction qui vrifie qu'un entier est compris entre 0 et 9. Nous dclarons cette fonction sous le nom est_chiffre :

# let est_chiffre = function x -> (x>=0) && (x<=9) ;;
val est_chiffre : int -> bool = <fun>


On dfinit ensuite la fonction valide par filtrage sur son argument de type touche :

# let valide tch = match tch with
Chiffre n -> est_chiffre n
| _ -> true ;;
val valide : touche -> bool = <fun>
Le premier filtre s'applique quand l'argument de valide est une valeur construite avec le constructeur Chiffre; dans ce cas, l'argument de Chiffre est test par la fonction est_chiffre. Le second filtre s'applique dans tous les autre cas de valeur du type touche. On rappelle que grce au typage, la valeur filtre est obligatoirement de type touche.

Avant d'entamer le codage du mcanisme de la calculatrice, nous allons dterminer un modle permettant de dcrire d'un point de vue formel la raction l'activation d'une des touches de la machine. On considre qu'une calculette possde quatre mmoires o sont respectivement conservs le dernier calcul effectu, la dernire touche actionne, le dernier oprateur actionn et le nombre affich l'cran. L'ensemble de ces quatre mmoires est appel l'tat de la calculatrice, il est modifi par chaque appui sur une touche du clavier. Cette modification s'appelle une transition et la thorie gouvernant ce genre de mcanismes est celle des automates. Un tat sera reprsent dans notre programme par un type enregistrement :

# type etat = {
dce : int; (* dernier calcul effectu *)
dta : touche; (* dernire touche actionne *)
doa : touche; (* dernier oprateur actionn *)
vaf : int (* valeur affiche *)
} ;;


La figure 2.6 donne un exemple d'une suite de transitions.


  tat touche
  (0,=,=,0) 3
(0,3,=,3) +
(3,+,+,3) 2
(3,2,+,2) 1
(3,1,+,21)
(24,*,*,24) 2
(24,2,*,2) =
(48,=,=,48)  

Figure 2.6 : transitions pour 3+21*2=


On a besoin par la suite d'une fonction evalue qui prend deux entiers et une valeur de type touche contenant un oprateur et qui renvoie le rsultat de l'opration correspondant la touche applique aux deux entiers. Cette fonction est dfinie par filtrage sur son dernier argument, de type touche :

# let evalue x y tch = match tch with
Plus -> x + y
| Moins -> x - y
| Fois -> x * y
| Par -> x / y
| Egal -> y
| Chiffre _ -> failwith "evalue : no op";;
val evalue : int -> int -> touche -> int = <fun>


On donne maintenant la dfinition de la fonction de transition en numrant tous les cas possibles. On suppose que l'tat courant est le quadruplet (a,b,,d) :
  • une touche avec le chiffre x est enfonce, alors deux cas sont envisager :
    • la dernire touche enfonce tait aussi un chiffre. C'est donc un nombre que l'utilisateur de la calculette est en train de composer, il faut en consquence ajouter le chiffre x la valeur affiche, c'est dire le remplacer par d10+x. Le nouvel tat est :
      (a,(Chiffre x),,d10+x)

    • la dernire touche enfonce n'tait pas un chiffre. C'est donc le dbut d'un nouveau nombre qui est compos. Le nouvel tat est :
      (a,(Chiffre x),,x)
  • une touche avec l'oprateur a t enfonce, le deuxime oprande d'une opration a donc t entirement compos et il s'agit pour la calculatrice d'effectuer cette opration. C'est cette fin qu'est conserve la dernire opration (ici ). Le nouvel tat est :
    (a d,,,a d)
Pour crire la fonction transition, il suffit de traduire mot mot en Objective CAML la dfinition prcdente : la dfinition par cas devient une dfinition par filtrage sur la touche passe en argument. Le cas d'une touche qui comporte lui mme deux cas, est trait par la fonction locale transition_chiffre par filtrage sur la dernire touche actionne.

# let transition et tou =
let transition_chiffre n = function
Chiffre _ -> { et with dta=tou; vaf=et.vaf*10+n }
| _ -> { et with dta=tou; vaf=n }
in
match tou with
Chiffre p -> transition_chiffre p et.dta
| _ -> let res = evalue et.dce et.vaf et.doa
in { dce=res; dta=tou; doa=tou; vaf=res } ;;
val transition : etat -> touche -> etat = <fun>
Cette fonction prend un etat, une touche et calcule le nouvel etat.

On peut maintenant tester ce programme avec l'exemple prcdent :

# let etat_initial = { dce=0; dta=Egal; doa=Egal; vaf=0 } ;;
val etat_initial : etat = {dce=0; dta=Egal; doa=Egal; vaf=0}
# let etat2 = transition etat_initial (Chiffre 3) ;;
val etat2 : etat = {dce=0; dta=Chiffre 3; doa=Egal; vaf=3}
# let etat3 = transition etat2 Plus ;;
val etat3 : etat = {dce=3; dta=Plus; doa=Plus; vaf=3}
# let etat4 = transition etat3 (Chiffre 2) ;;
val etat4 : etat = {dce=3; dta=Chiffre 2; doa=Plus; vaf=2}
# let etat5 = transition etat4 (Chiffre 1) ;;
val etat5 : etat = {dce=3; dta=Chiffre 1; doa=Plus; vaf=21}
# let etat6 = transition etat5 Fois ;;
val etat6 : etat = {dce=24; dta=Fois; doa=Fois; vaf=24}
# let etat7 = transition etat6 (Chiffre 2) ;;
val etat7 : etat = {dce=24; dta=Chiffre 2; doa=Fois; vaf=2}
# let etat8 = transition etat7 Egal ;;
val etat8 : etat = {dce=48; dta=Egal; doa=Egal; vaf=48}


Cette excution peut s'crire de manire plus concise en utilisant une fonction appliquant une suite de transitions correspondant une liste de touches passe en argument.

# let transition_liste et lt = List.fold_left transition et lt ;;
val transition_liste : etat -> touche list -> etat = <fun>
# let exemple = [ Chiffre 3; Plus; Chiffre 2; Chiffre 1; Fois; Chiffre 2; Egal ]
in transition_liste etat_initial exemple ;;
- : etat = {dce=48; dta=Egal; doa=Egal; vaf=48}







Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora073.gif0000644000000000000000000002240407073350152014274 0ustar GIF89at*}}}{{{yyywwwuuusssWWW333pppnnnllljjjĊ!!Q,tQ*ma)`(_&^%]$\#["!Y X (zXȰ!C+#Jءŋhq# CBɒR\r˗0ajI  㠧O"@BhH"´Ӧ J*UիX.ʵ+ `Ê ٳhӢM۷p"Kݻvw/6@?+^ǐL2e3kܣϞMz4ӨSk;b˞M6ms{w .$Q+WysQP<*ɗY }`Պ_6m_?{=L0#wlbڀfh%xk fۃۄF܅܆9 $ljp ,GbvXmywz@ސyח|G~ h 6`\Vᗺe(fqr%)"l0FƜtIՎM@)hDG"1ɘAiQ a v`ØPg~fm .)5Z+Ʈ f 'o+l\v kh' z2A*Qʙajq\jcaJbmꬲ檯/ ,pm\Q m @ #E찄"puбs9+r{Ek& +2 kiy+  {.%b!;/n"v\:+,mkj!%_@&{Ϭ .P'.'0N= tB] 2Aݜ&Rob][w/-ٴ;;Q v|̄U/F 8Ox/~f߇>&,BM4k z!Ɖ>:7~:걪N+r`G0ىv˝(:wi @º ,{ >}|=8A UBvV E#C '!sCˇ[rm7!]~8_ 5ЀC= -2 hL61 @:1BfN( `Ͱbx1Bp![#,@N0IIV]"boH$*kmIVTņi1b]HKލfK4s0<082if"@P$PN  x~l"FY7V*[WN\e-o9F]沗>c0~.l@:=h28uD jvi TȤt8]8Er\eJw3#=h3Sf? ОLP ѢFTHhJ:B |$I?3nP) XWt1L%X29 NCQԾ&}j Kv Mlb IJ౐, | (ᲘU6YHhGKҚHHjWֲlgKᶸͭnw"OK\FQr2:ХQVrP5$iZpL3bҀ|Kͯ~L턂;'L [ΰ7{ GL(NW0gL8αw@L"HN&;PL*[Xβ.{`L2hN6pL:xs>πMBЈNF;ѐ'MJ[ϊ7N{ӠG LԨNWVЦ~gMZ֪5w^׸5Mb؍6f;~Mj_:ζ>cqnuv[~MoX'η}7Npj O;<~'kS u/.kseC|-?ˆ4_|)9K|}^:FЇs3zL9ГoSP:͋kW:"¾k|SGN9{v]t.E{ݹnhp:U=so6W?}#|7?χ>?sw}?^9=G_׻O|ygGyxy|hu H~ QwHzXiv{i"z]|$Xy؁sOvwƁ68i1t]!5V&ܽ9^-^Wm(g i4 7O!j-/-|NNFz`= @~XnyZ^S<^Ky=>Xdwj洜D2>u@jmW<)؀~ʂ~Ci:>&|֑'jG}МlyN拎ZmқzNx=" וծƹ֣=WCH=?^k-쥼5 $A-ێ՗6턮N{ G]h&|R^]Э~uȐ>A,ć;]mkNYGNbJixȊ&fz.ӊ,σ νmL?/k-n#κkθ/=M j%'_ǩϺ_)l?w hBY .r8OM+iް4HXhXȘx(yhYy9Ij9Z*y +;Kku[K 6L\ll+: L iX=8 ZofG('cirꧧШVD0t_n6^)Zff\NۮFׂ@J>pėl!O QŻYm,xpH4\S5u2u1ƨjsp;^'v%SchKMg#mjXvCuG=b A@ݵp`*N@i]ۅ^txrx).NwOTE&fWⵈ 鳘~ 걨미"D{=W^>:+;0w:X3ˢKyW~[/~ X@&0s4oD(ap`~U۠GB̈́_$+ hv*d"C0]9"8max qQ!J%pEa^,)\/2 ыڕ-2튙c"e1Y#֤ rU*c~jǹl(Kvp}a:Y&'#;ĕHJKlw9&OCdእ Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

Les rsultats produits par la commande ocamldep peuvent tre visualiss sous forme graphique par l'utilitaire ocamldot que l'on trouve la page suivante :

Lien


http://www.cis.upenn.edu/~tjim/ocamldot/index.html
ocamldot se sert d'un programme indpendant (dot), lui aussi tlchargeable :

Lien


http://www.research.att.com/sw/tools/graphviz/


Plusieurs fichiers Makefile gnriques pour Objective CAML ont t proposs pour simplifier la tche de gestion de projets :

Lien


http://caml.inria.fr/FAQ/Makefile_ocaml-eng.html


Lien


http://miss.wu-wien.ac.at/~mottl/ocaml_sources/intro.html
Ceux-ci intgrent la sortie d'ocamldep.

On trouve dans [HFa96] une valuation de performance d'une vingtaine d'implantations de langages fonctionnels, dont de nombreux ML, sur un exemple de calcul numrique manipulant de grandes structures de donnes.








Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora092.html0000644000000000000000000000341507421273602014476 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a prsent les principales familles d'algorithmes de rcupration automatique de mmoire dans le but de dtailler celui utilis en Objective CAML. Le GC d'Objective CAML est un GC deux gnrations, incrmentiel, qui utilise un Mark&Sweep pour la gnration ancienne et un Stop&Copy pour la jeune gnration. Deux modules en liaison directe avec le GC permettent de contrler l'volution du tas. Le module Gc permet d'analyser le comportement du GC et de modifier certains paramtres dans le but de l'optimiser pour des applications spcifiques. Avec le module Weak on peut conserver dans des tableaux des valeurs potentiellement rcuprables, mais qui sont encore accessibles. Il est utile pour l'implantation d'un cache mmoire.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora187.html0000644000000000000000000002610407421273602014503 0ustar Prises de communication Prcdent Index Suivant

Prises de communication

Nous avons vus aux chapitres 18 et 19 deux moyens de communication entre processus, respectivement : les tubes et les canaux. Ces deux premires mthodes utilisent un modle logique de concurrence. Elles n'induisent en gnral pas d'amlioration de performances dans la mesure o les processus communicants partagent les mmes ressources ; en particulier, le mme processeur. La troisime possibilit, que nous prsentons dans ce paragraphe, utilise les prises de communication. Cette possibilit vient du monde Unix. Elle permet la communication entre processus s'excutant soit sur une mme machine, soit sur des machines diffrentes.

Description et cration

Une prise de communication a pour rle d'tablir une communication avec une autre prise dans le but de transfrer des informations. Nous numrons les diffrentes situations que l'on peut rencontrer ainsi que les commandes et types qui seront utiliss par les sockets TCP/IP. La mtaphore classique est de comparer les sockets aux postes tlphoniques.
  • Pour fonctionner ils doivent tre raccords au rseau (socket).
  • Pour recevoir un appel ils doivent possder un numro de type sock_addr (bind).
  • Pendant un appel, il est possible de recevoir un autre appel si la configuration le permet (listen).
  • Il n'est pas ncessaire d'avoir soi-mme un numro pour appeler un autre poste, une fois la connexion tablie la communication est dans les deux sens (connect).
Domaine
Suivant qu'une prise est destine la communication interne ou externe elle appartient un domaine diffrent. La bibliothque Unix dfinit deux domaines possibles correspondant aux constructeurs du type :

# type socket_domain = PF_UNIX | PF_INET;;
Le premier domaine correspond la communication locale et le second, la communication transitant sur le rseau Internet. Ce sont l les principaux domaines d'application des sockets.

Nous n'utiliserons dans la suite que les sockets du domaine Internet.

Type et protocole
Quel que soit leur domaine, les sockets dfinissent certaines proprits de communication (fiabilit, ordonnancement, etc.) reprsentes par les constructeurs du type :

# type socket_type = SOCK_STREAM | SOCK_DGRAM | SOCK_SEQPACKET | SOCK_RAW ;;
Suivant le type de socket utilis, le protocole sous-jacent la communication obira aux caractristiques dfinies. chaque type de communication est associ un protocole par dfaut.

En fait, nous n'utiliserons ici que le premier type de communication : SOCK_STREAM avec le protocole par dfaut TCP. Il garantit fiabilit, ordonnancement et non duplication des messages changs et fonctionne en mode connect.

Pour le reste, nous renvoyons le lecteur la littrature sur Unix, par exemple [Rif90].

Cration
La fonction de cration d'une socket est :

# Unix.socket ;;
- : Unix.socket_domain -> Unix.socket_type -> int -> Unix.file_descr = <fun>
Le troisime argument permet de prciser le protocole associ la communication. La valeur 0 est interprte comme << le protocole par dfaut >> associ au couple (domaine, type), argument de la cration de la socket. La valeur de retour de cette fonction est un descripteur de fichier. Ainsi les changes pourront se faire en utilisant les fonctions standard du module Unix d'entres-sorties.

Crons une socket TCP/IP :

# let s_descr = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 ;;
val s_descr : Unix.file_descr = <abstr>


Warning


Bien que la fonction socket retourne une valeur de type file_descr, le systme fait la diffrence entre un descripteur de fichier classique et celui attribu une socket.
On peut utiliser les fonctions sur les fichiers du module Unix avec les sockets ; mais une exception est dclenche dans le cas o un descripteur classique est pass une fonction attendant une socket.


Fermeture
Comme tout descripteur de fichier, une socket se ferme par la fonction :

# Unix.close ;;
- : Unix.file_descr -> unit = <fun>
La sortie par exit d'un processus ferme les descripteurs de fichier encore ouverts.

Adresses et connexions

Une socket que l'on vient de crer ne possde pas d'adresse. Pour qu'une connexion puisse s'tablir entre deux sockets, l'appelant doit connatre l'adresse du rcepteur.

L'adresse d'une socket (TCP/IP) est constitue d'une adresse IP et d'un numro de port. Une socket du domaine Unix est simplement constitue d'un nom de fichier.

# type sockaddr =
ADDR_UNIX of string | ADDR_INET of inet_addr * int ;;


Attribution d'une adresse
La premire chose faire pour pouvoir recevoir des appels aprs la cration d'une socket est de lui attribuer (de la lier ) une adresse. C'est le rle de la fonction :

# Unix.bind ;;
- : Unix.file_descr -> Unix.sockaddr -> unit = <fun>


En effet, nous avons dj un descripteur de socket, mais l'adresse qui lui est attache la cration ne peut gure nous tre utile comme le montre l'exemple suivant :

# let (addr_in, p_num) =
match Unix.getsockname s_descr with
Unix.ADDR_INET (a,n) -> (a,n)
| _ -> failwith "pas INET";;
val addr_in : Unix.inet_addr = <abstr>
val p_num : int = 0
# Unix.string_of_inet_addr addr_in;;
- : string = "0.0.0.0"


Reste donc fabriquer une adresse utile et l'associer notre socket. Nous reprenons notre adresse locale my_addr obtenue page ?? et choisissons le port 12345 qui, en gnral, n'est pas attribu.

# Unix.bind s_descr (Unix.ADDR_INET(my_addr, 12345)) ;;
- : unit = ()


Capacit d'coute et rception
Il faut encore procder deux oprations avant que notre socket soit compltement oprationnelle pour recevoir des appels : dfinir sa capacit d'coute et se mettre effectivement l'coute. C'est le rle respectif des deux fonctions :

# Unix.listen ;;
- : Unix.file_descr -> int -> unit = <fun>
# Unix.accept ;;
- : Unix.file_descr -> Unix.file_descr * Unix.sockaddr = <fun>


Le second argument de la fonction listen indique le nombre maximal de connexions tolr en attente. L'appel de la fonction accept attend une demande de connexion. Quand celle-ci survient la fonction accept se termine et retourne l'adresse de la socket ayant appel ainsi qu'un nouveau descripteur de socket, dite socket de service. Cette socket de service est automatiquement lie une adresse. La fonction accept ne s'applique qu'aux sockets ayant excut un listen, c'est--dire aux sockets ayant paramtr la file d'attente des demandes de connexion.

Demande de connexion
La fonction duale de accept est ;

# Unix.connect ;;
- : Unix.file_descr -> Unix.sockaddr -> unit = <fun>


Un appel Unix.connect s_descr s_addr tablit un connexion entre la socket locale s_descr (qui est automatiquement lie) et la socket d'adresse s_addr (qui doit exister).

Communication
partir du moment o une connexion est tablie entre deux sockets, les processus propritaires de ces dernires peuvent communiquer dans les deux sens. Les fonctions d'entres-sorties sont celles du module Unix dcrites au chapitre 18.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora069.html0000644000000000000000000001201407421273601014474 0ustar Portabilit et efficacit Prcdent Index Suivant

Portabilit et efficacit

L'intrt de compiler vers une machine abstraite est de produire un excutable indpendant de l'architecture de la machine relle o il tourne. Son principal inconvnient provient de l'interprtation de ces instructions virtuelles. Un compilateur natif produira un code plus efficace, mais ne pouvant s'excuter que sur un seul type d'architecture. Il est donc souhaitable d'avoir le choix entre ces deux types de compilation selon le type d'application dveloppe. L'autonomie d'un excutable, c'est--dire l'indpendance de celui-ci de l'installation de la distribution d'Objective CAML, fait aussi perdre la proprit de portabilit.

Autonomie et portabilit

Pour produire un excutable autonome, le compilateur de code-octet a effectu une dition de liens du code-octet t.cmo avec la bibliothque d'excution, l'interprte de code-octet et une amorce de programme crite en C. Cela sous-entend qu'il y avait un compilateur C sur la machine hte. ce moment l mme si la partie importante de l'excutable contient les instructions en code-octet, cet excutable n'est plus portable sur d'autres systmes ou d'autres architectures machine.

Cela n'tait pas le cas pour la version non autonome. En effet les instructions de code-octet ne diffrent pas d'une machine une autre, car la machine Zinc n'existant pas, seul son interprte compte. Si l'interprte de code-octet existe sur des machines d'architecture ou de systme diffrents, rien ne les empche d'excuter les instructions de code-octet produites par d'autres compilateurs Objective CAML en utilisant la commande ocamlrun. Celle-ci fait partie des diffrentes distributions d'Objective CAML pour Sparc sous Solaris, Intel sous Windows, etc. Cependant, il est toujours prfrable d'utiliser des versions de l'interprte de code-octet et du compilateur de mme numro.

La portabilit des fichiers objets en code-octet autorise la diffusion de bibliothques directement sous cette forme et immdiatement utilisables sur les machines possdant l'interprte de code-octet.

Efficacit d'excution

Le compilateur de code-octet produit une suite d'instructions de la machine Zinc, qui, au moment de l'excution, seront interprtes par ocamlrun. Cette phase d'interprtation a un certain cot pour la rapidit d'excution du programme. On peut se reprsenter l'interprte de code-octet comme une grande structure d'aiguillage (filtrage match ... with) o chaque instruction est un motif et les branches de calcul modifient la pile et le compteur ordinal (adresse de la prochaine instruction). Ce filtrage, bien qu'optimis, induit une perte de performance.

Bien que ne testant pas toutes les parties du langage, le petit exemple suivant qui calcule la fonction de Fibonnacci montre les diffrences de temps d'excution entre le compilateur de code-octet et le compilateur natif. Soit le programme fib.ml suivant :
let rec fib n = 
  if n < 2 then 1
  else (fib (n-1)) + (fib(n-2));;
et le programme main.ml suivant :
for i = 1 to 10 do 
  print_int (Fib.fib 30 );
  print_newline()
done;;
et leurs compilations suivantes :
$ ocamlc -o fib.exe fib.ml main.ml
$ ocamlopt -o fibopt.exe fib.ml main.ml
produisant les excutables fib.exe et fibopt.exe. On obtient avec la commande Unix time sur un Pentium 350 sous Linux les temps suivants :
fib.exe (code-octet) fibopt.exe (natif)
7 s 1 s
Ce qui correspond un facteur 7 entre les deux versions du mme programme. Ce programme ne teste pas toutes les caractristiques du langage. Le gain dpend fortement du type d'application.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora038.gif0000644000000000000000000000630407073350152014276 0ustar GIF89aUUU!,ڋ޼H9 G@>A hj J%" ` Ԯ #VU /k,4k`7HXhSuuv) ax2 *:JZjz +;K[k{ ,ոv .߾Pѻ*ᢆ Nt1n۰ re6<|om>:W0=:?|ޡ>7!gW;ZRlδO'Ҽk}^*{k=75:2P&>wZ-G^>Z(z -֝z}ųTf7Vٷ_T9PtDRA]v _ Jp_]( ^6" GXYaI\0[v)@ԌXcmbC#iʹ ##(ov`˘^T*pHbinQtű2ygE}鴤רZ,)qWH2"&Q(,b.$-Y2%QDP4`0=KK57GOfd3 .bȡ ըJuR |ԘΪ;))*UNAbSY}гzF$[V++k;ՌCj&k8~WS+Ul`eĺtwy :JVb4e#)v$V*KPVJIYE[[lɫ>]Aq""Mo`(r'\6 nnW+VSVwkt]v@y;Wp[wYzݻ(ܑө.e/U0167a5QOk\ÔE?r;pf?tʕ@Dg}X\"֧:4w]/j_;¼}q^|N(7<. ;y[f6xW+9RW𤼃-xp7<nL m×~qO3Qrg4V*)GtD,d^ꤩ;"GUg+4d}-=E=b5~zwlRZ/Pdz~7D5x{T}D<&{>TzV{}{%66)6BWI b|IL #Ez6`M8#Gpuw-#;T>xwbv@F;}w|bdCA'X-T=ątwi(xwxN5(ń..p@a}R[8M(bS(؈gjy艟(HhױV~xVcvh׋q7j1TlFnXqH1qȌШc7ݸnXxahG|HouȎ(u'LVo98dI![ 9l ,Iw0^+8/"/\g{`C|I3t+ 12,&YGMǂ5)hsRӡ'@`A`}D(56bs?ɓlT3ΐ tdaOJSR%ב"Y P7w~SFՔ>$IpRm~Y+ x ( _8A%ȑ3T0TADuA:d=9 E`KP;ocaml-book-1.0/fr/html/book-ora131.html0000644000000000000000000004400407421273602014467 0ustar Modules simples Prcdent Index Suivant

Modules simples

Le langage Objective CAML est distribu avec un certain nombre de modules dj dfinis. Nous avons vu leur utilisation au chapitre 8. Nous allons voir, dans ce paragraphe, comment raliser de tels modules.

Interface et implantation

La distribution d'Objective CAML fournit le module Stack implantant les fonctions principales pour utiliser une pile (LIFO).

# let queue = Stack.create () ;;
val queue : '_a Stack.t = <abstr>
# Stack.push 1 queue ; Stack.push 2 queue ; Stack.push 3 queue ;;
- : unit = ()
# Stack.iter (fun n -> Printf.printf "%d " n) queue ;;
3 2 1 - : unit = ()


Objective CAML tant distribu avec ses sources, nous pouvons regarder comment les piles ont t implantes.

ocaml-2.04/stdlib/stack.ml
type 'a t = { mutable c : 'a list }
exception Empty
let create () = { c = [] }
let clear s = s.c <- []
let push x s = s.c <- x :: s.c
let pop s = match s.c with hd::tl -> s.c <- tl; hd | [] -> raise Empty
let length s = List.length s.c
let iter f s = List.iter f s.c


Nous nous apercevons que le type des piles connu sous le nom de Stack.t est un enregistrement constitu d'un champ modifiable contenant une liste. Les oprations sur la pile sont ralises par les oprations classiques sur les listes appliques l'unique champ de l'enregistrement.

Fort de cette connaissance, rien ne nous interdit a priori d'accder directement la liste constituant la queue; cela n'est en fait pas le cas.

# let liste = queue.c ;;
Characters 13-18:
This expression has type int Stack.t but is here used with type 'a vm
Le compilateur proteste comme s'il ne connaissait pas l'identificateur du champ du type Stack.t. C'est en fait le cas, comme nous nous en apercevons en regardant l'interface du module Stack.

ocaml-2.04/stdlib/stack.mli
(* Module [Stack]: last-in first-out stacks *)
(* This module implements stacks (LIFOs), with in-place modification. *)

type 'a t (* The type of stacks containing elements of type ['a]. *)

exception Empty (* Raised when [pop] is applied to an empty stack. *)

val create: unit -> 'a t
(* Return a new stack, initially empty. *)
val push: 'a -> 'a t -> unit
(* [push x s] adds the element [x] at the top of stack [s]. *)
val pop: 'a t -> 'a
(* [pop s] removes and returns the topmost element in stack [s],
or raises [Empty] if the stack is empty. *)
val clear : 'a t -> unit
(* Discard all elements from a stack. *)
val length: 'a t -> int
(* Return the number of elements in a stack. *)
val iter: ('a -> unit) -> 'a t -> unit
(* [iter f s] applies [f] in turn to all elements of [s],
from the element at the top of the stack to the element at the
bottom of the stack. The stack itself is unchanged. *)


Outre des commentaires expliquant comment utiliser les fonctions du module Stack, ce fichier explicite quels sont les valeurs, les types et les exceptions dfinis dans le fichier stack.ml. Plus prcisment, l'interface fournit les noms des valeurs et leur type. En particulier, le nom du type t est donn, mais son implantation (c'est--dire l'identificateur du champ : c) n'est pas fournie. Ceci explique que l'utilisateur du module Stack ne puisse accder directement ce champ. Le type Stack.t est dit abstrait.

Les fonctions de manipulation des piles sont elles aussi simplement dclares sans tre dfinies. Il faut seulement, pour que le mcanisme d'infrence de types puisse juger de leur utilisation lgitime, indiquer le type de chacune d'elles. Ceci est fait grce un nouveau mot cl :

Syntaxe


val nom : type


Relation entre interface et implantation

Ainsi le module Stack est en fait constitu de deux entits : une interface et une implantation. Pour construire un module, il faut que tous les lments dclars dans l'interface soient effectivement dfinis dans l'implantation. Il est galement ncessaire que les dfinitions des fonctions satisfassent la dclaration de type donne dans l'interface1.

La relation entre interface et implantation n'est pas symtrique. L'implantation peut trs bien contenir plus de dfinitions que n'en demande l'interface. Typiquement, la dfinition d'une fonction un peu complexe pourra utiliser des fonctions auxiliaires dont le nom n'apparat pas dans l'interface. Dans ce cas, le programmeur utilisant un tel module, tout comme il ne peut faire rfrence au champ c de la structure Stack.t, ne peut faire appel la fonction auxiliaire non dclare dans l'interface. De mme il est possible dans l'interface d'apporter des restrictions sur le type des valeurs. Imaginons qu'un module dfinisse la fonction d'identit (let id x = x) mais que son interface dclare la valeur id de type int -> nt; alors les modules utilisant la valeur id ne pourront le faire que sur des entiers.

La dualit de constitution d'un module permet de rendre la dclaration des entits composant un module indpendante de leur implantation. Ainsi, on peut substituer au fichier d'implantation stack.ml de la distribution, un autre fichier, que l'on appelle toujours stack.ml et qui contient une implantation diffrente des piles reposant, par exemple, sur des tableaux :

type 'a t = { mutable sp : int; mutable c : 'a array }
exception Empty
let create () = { sp=0 ; c = [||] }
let clear s = s.sp <- 0; s.c <- [||]
let size = 5
let increase s = s.c <- Array.append s.c (Array.create size s.c.(0))

let push x s =
if s.c = [||] then ( s.c <- Array.create size x ; s.sp <- succ s.sp )
else ( (if s.sp = Array.length s.c then increase s) ;
s.c.(s.sp) <- x ;
s.sp <- succ s.sp )

let pop s = if s.sp = 0 then raise Empty
else let x = s.c.(s.sp) in s.sp <- pred s.sp ; x

let length s = Array.length s.c
let iter f s = Array.iter f s.c


Cet ensemble de fonctions satisfait le requisit du fichier d'interface stack.mli. Le nouveau couple de fichiers stack.mli et stack.ml (nouvelle mouture) fournit une alternative au module Stack de la distribution d'Objective CAML.

Compilation spare

l'instar des autres langages de programmation modernes, Objective CAML permet de dcouper un programme en plusieurs units de compilation. Une unit de compilation est constitue de deux fichiers, le fichier d'implantation (d'extension .ml) et le fichier d'interface (d'extension .mli). Chaque unit de compilation est vue comme un module. Ainsi la compilation du fichier nom.ml produit le module Nom2. Les valeurs, types et exceptions dfinis dans un module peuvent tre rfrencs soit en utilisant la notation pointe (Module.identificateur), soit en utilisant le mot cl open.

      nom1.ml        nom2.ml
type t = { x:int ; y:int } ;; let val = { Nom1.x = 1 ; Nom1.y = 2 } ;;
let f c = c.x + c.y ;; Nom1.f val ;;
open Nom1 ;;
f val ;;

Le fichier d'interface, portant l'extension .mli, doit tre compil en utilisant la commande ocamlc -c avant la compilation des modules qui en dpendent dont en particulier le fichier d'implantation de ce mme module. En cas d'absence d'un fichier d'interface, Objective CAML considre que la totalit du module est exporte; c'est--dire que toutes les dclarations de l'implantation sont prsentes dans l'interface implicite avec leur type le plus gnral.

L'dition de liens pour engendrer l'excutable se fait comme dcrit au chapitre 7 avec la commande ocamlc (sans l'option -c) suivi des fichiers objets. Attention, sur la ligne de commande les fichiers doivent tre dans l'ordre de leur dpendance; ceci interdit donc les dpendances croises entre modules.

Pour engendrer un excutable partir des fichiers nom1.ml et nom2.ml, nous utiliserons les commandes suivantes :
> ocamlc -c nom1.ml
> ocamlc -c nom2.ml 
> ocamlc nom1.cmo nom2.cmo 
Cette faon de procder par fichier d'interface et fichier d'implantation pour constituer les modules permet la compilation spare, mais les possibilits de structuration logique qu'elle offre sont faibles. En particulier, le nom des modules est implicite. Il est dduit du nom des fichiers constituant le module. L'amalgame entre module et fichier empche d'avoir en mme temps deux implantations d'une mme interface, voire deux interfaces pour une mme implantation. Pour pallier ce dfaut, le mcanisme de module a t intgr la syntaxe du langage de programmation. C'est ce que nous prsentons dans la suite de ce chapitre.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora135.html0000644000000000000000000013070707421273602014501 0ustar Exercices Prcdent Index Suivant

Exercices

Listes d'association

Voici un premier petit exercice simple dans lequel il vous est demand d'implanter un type abstrait polymorphe : les listes d'association. Et d'en donner deux vues diffrentes.

  1. Dfinir une signature ALIST dclarant un type (abstrait) paramtr par deux variables de type (une pour la cl, l'autre pour la valeur stocke), une fonction de cration, une fonction d'ajout, une fonction d'accs, un test d'appartenance et une fonction de suppression.

    # module type ALIST =
    sig
    type ('a,'b) t
    val create : unit -> ('a,'b) t
    val add : 'a -> 'b -> ('a,'b) t -> ('a,'b) t
    val get : 'a -> ('a,'b) t -> 'b
    val mem : 'a -> ('a,'b) t -> bool
    val rem : 'a -> ('a,'b) t -> ('a,'b) t
    end ;;
    module type ALIST =
    sig
    type ('a, 'b) t
    val create : unit -> ('a, 'b) t
    val add : 'a -> 'b -> ('a, 'b) t -> ('a, 'b) t
    val get : 'a -> ('a, 'b) t -> 'b
    val mem : 'a -> ('a, 'b) t -> bool
    val rem : 'a -> ('a, 'b) t -> ('a, 'b) t
    end
    On choisira une implantation fonctionnelle (ie : sans effet de bord sur la donne).

  2. Dfinir le module Alist respectant la signature ALIST

    # module Alist:ALIST =
    struct
    type ('a,'b) t = ('a*'b) list
    let create () = []
    let add k v al = (k,v)::al
    let get = List.assoc
    let mem = List.mem_assoc
    let rem = List.remove_assoc
    end ;;
    module Alist : ALIST


  3. Dfinir une signature ADM_ALIST pour l'administrateur des listes d'association. Il pourra uniquement crer une liste, y rajouter et en retirer une entre.

    # module type ADM_ALIST =
    sig
    type ('a,'b) t
    val create : unit -> ('a,'b) t
    val add : 'a -> 'b -> ('a,'b) t -> ('a,'b) t
    val rem : 'a -> ('a,'b) t -> ('a,'b) t
    end ;;
    module type ADM_ALIST =
    sig
    type ('a, 'b) t
    val create : unit -> ('a, 'b) t
    val add : 'a -> 'b -> ('a, 'b) t -> ('a, 'b) t
    val rem : 'a -> ('a, 'b) t -> ('a, 'b) t
    end


  4. Dfinir une signature USER_ALIST pour les utilisateurs des listes qui n'auront droit qu' l'accs et au test d'appartenance.

    # module type USER_ALIST =
    sig
    type ('a,'b) t
    val get : 'a -> ('a,'b) t -> 'b
    val mem : 'a -> ('a,'b) t -> bool
    end ;;
    module type USER_ALIST =
    sig
    type ('a, 'b) t
    val get : 'a -> ('a, 'b) t -> 'b
    val mem : 'a -> ('a, 'b) t -> bool
    end


  5. Crer deux modules AdmAlist et UserAlist pour les administrateurs et les utilisateurs. Penser qu'un utilisateur doit pouvoir manipuler une liste cre par un administrateur.

    # module AdmAlist = (Alist:ADM_ALIST with type ('a,'b) t = ('a,'b) Alist.t) ;;
    module AdmAlist :
    sig
    type ('a, 'b) t = ('a, 'b) Alist.t
    val create : unit -> ('a, 'b) t
    val add : 'a -> 'b -> ('a, 'b) t -> ('a, 'b) t
    val rem : 'a -> ('a, 'b) t -> ('a, 'b) t
    end
    # module UserAlist = (Alist:USER_ALIST with type ('a,'b) t = ('a,'b) Alist.t) ;;
    module UserAlist :
    sig
    type ('a, 'b) t = ('a, 'b) Alist.t
    val get : 'a -> ('a, 'b) t -> 'b
    val mem : 'a -> ('a, 'b) t -> bool
    end

Vecteurs paramtrs

On illustre, dans ce deuxime exercice, comment les modules paramtrs permettent la gnricit et la rutilisation de code en dfinissant un foncteur de manipulation de vecteurs que l'on pourra instancier selon plusieurs types de nombres.

On se donne la signature suivante :

# module type NOMBRE =
sig
type a
type t
val create : a -> t
val add : t -> t -> t
val string_of : t -> string
end ;;


  1. Dfinir le foncteur FVecteur paramtr par un module de signature NOMBRE qui dfinit un type t, une fonction de cration, l'addition (i.e. la translation) et la conversion en chane de caractres.

    # module FVecteur (N:NOMBRE) =
    struct
    type e = N.t
    type t = { mutable vx:e; mutable vy:e }
    let create x0 y0 = { vx=x0; vy=y0 }
    let add v1 v2= {vx = N.add v1.vx v2.vx; vy = N.add v1.vy v2.vy}
    let string_of v= "("^N.string_of v.vx^" , "^N.string_of v.vy^")"
    end ;;
    module FVecteur :
    functor(N : NOMBRE) ->
    sig
    type e = N.t
    and t = { mutable vx: e; mutable vy: e }
    val create : e -> e -> t
    val add : t -> t -> t
    val string_of : t -> string
    end


  2. Dfinir une signature VECTEUR , non paramtre, o les types des nombres et des vecteurs sont abstraits.

    # module type VECTEUR =
    sig
    type e
    type t
    val create : e -> e -> t
    val add :t -> t -> t
    val string_of : t -> string
    end ;;
    module type VECTEUR =
    sig
    type e
    and t
    val create : e -> e -> t
    val add : t -> t -> t
    val string_of : t -> string
    end


  3. Dfinir trois structures Rationnel, Flottant et Complexe implantant la signature NOMBRE.

    # module Rationnel:NOMBRE =
    struct
    type a = int*int
    type t = { p:int; q:int }
    let create (p0,q0) = { p=p0; q=q0 }
    let add r1 r2 = { p = r1.p*r2.q + r2.p*r1.q; q = r1.q*r2.q }
    let string_of r = (string_of_int r.p)^"/"^(string_of_int r.q)
    end ;;
    module Rationnel : NOMBRE

    # module Flottant:NOMBRE =
    struct
    type a = float
    type t = a
    let create x = x
    let add = (+.)
    let string_of = string_of_float
    end ;;
    module Flottant : NOMBRE

    # module Complexe:NOMBRE =
    struct
    type a = float*float
    type t = { r:float; i:float }
    let create (r0, i0) = { r=r0; i=i0 }
    let add c1 c2 = { r = c1.r+.c2.r; i = c1.i+.c2.i }
    let string_of c =
    (string_of_float c.r)^"+"^(string_of_float c.r)^"*i"
    end ;;
    module Complexe : NOMBRE


  4. Utiliser ces structures pour dfinir, par application du foncteur FVecteur, trois modules pour les rationnels, les rels et les complexes.

    # module RatVect:VECTEUR = FVecteur(Rationnel) ;;
    module RatVect : VECTEUR
    # module FloVect:VECTEUR = FVecteur(Flottant) ;;
    module FloVect : VECTEUR
    # module ComVect:VECTEUR = FVecteur(Complexe) ;;
    module ComVect : VECTEUR

Arbres lexicaux

Cet exercice reprend les arbres lexicaux vus au chapitre 2, page ??, pour obtenir un module gnrique de gestion des arbres lexicaux paramtrs par un type abstrait pour les mots.

  1. Dfinir la signature MOT contenant la dclaration d'un type abstrait alpha pour l'alphabet et t pour les mots sur cet alphabet. On dclarera le mot vide, l'opration de conversion d'un lment de l'alphabet en un mot, l'accs un lment d'un mot, l'extraction d'un sous-mot, la longueur d'un mot et la concatnation de deux mots.

    # module type MOT =
    sig
    type alpha
    type t
    val null : t
    val of_alpha : alpha -> t
    val get : t -> int -> alpha
    val sub : t -> int -> int -> t
    val length : t -> int
    val concat : t -> t -> t
    end ;;
    module type MOT =
    sig
    type alpha
    and t
    val null : t
    val of_alpha : alpha -> t
    val get : t -> int -> alpha
    val sub : t -> int -> int -> t
    val length : t -> int
    val concat : t -> t -> t
    end


  2. Dfinir le foncteur ArbLex, paramtr par un module de signature MOT, qui redfinit (en fonction des types et oprations sur les mots) le type des arbres lexicaux et les fonctions existe, ajoute et selecte de l'exercice du chapitre 2, page ??.

    # module ArbLex (M:MOT) =
    struct
    type node = Lettre of M.alpha * bool * t
    and t = node list

    let rec existe m d =
    let aux sm i n =
    match d with
    [] -> false
    | (Lettre (a,b,l))::q when a = M.get sm i ->
    if n = 1 then b else existe (M.sub sm (i+1) (n-1)) l
    | (Lettre (a,b,l))::q when a = M.get sm i ->
    existe sm q
    in aux m 0 (M.length m)

    let rec ajoute m d =
    let aux sm i n =
    if n = 0 then d
    else
    match d with
    [] ->
    let aux = ajoute (M.sub sm (i+1) (n-1)) [] in
    [ Lettre (M.get sm i, n = 1, aux) ]
    | (Lettre(a,b,l))::q when a = M.get sm i ->
    if n = 1 then (Lettre(a,true,l))::q
    else Lettre(a,b,ajoute (M.sub sm (i+1) (n-1)) l)::q
    | (Lettre(a,b,l))::q -> (Lettre(a,b,l))::(ajoute sm q)
    in aux m 0 (M.length m)

    let rec selecte n d = match d with
    [] -> []
    | (Lettre(a,b,l))::q when n=1 ->
    let f (Lettre(a,b,_)) = if b then M.of_alpha a else M.null in
    List.filter ((<>) M.null) (List.map f d)
    | (Lettre(a,b,l))::q ->
    let r = selecte (n-1) l and r2 = selecte n q in
    let pr = List.map (function s -> M.concat (M.of_alpha a) s) r in
    pr@r2
    end ;;
    Characters 161-409:
    Warning: this pattern-matching is not exhaustive.
    Here is an example of a value that is not matched:
    _::_
    module ArbLex :
    functor(M : MOT) ->
    sig
    type node = | Lettre of M.alpha * bool * t
    and t = node list
    val existe : M.t -> t -> bool
    val ajoute : M.t -> t -> t
    val selecte : int -> t -> M.t list
    end


  3. Dfinir le module Chars qui implante les oprations de la signature MOT avec alpha = char et t = string. En dduire un module CharsDic pour les dictionnaires de chanes de caractres.

    # module Chars =
    struct
    type alpha = char
    type t = string
    let null = ""
    let of_alpha c = String.make 1 c
    let get s i =
    try s.[i]
    with Invalid_argument(_) -> raise (Invalid_argument "Chars.get")
    let sub s i1 i2 =
    try String.sub s i1 i2
    with Invalid_argument(_) -> raise (Invalid_argument "Chars.sub")
    let length = String.length
    let concat = (^)
    end ;;
    module Chars :
    sig
    type alpha = char
    and t = string
    val null : string
    val of_alpha : char -> string
    val get : string -> int -> char
    val sub : string -> int -> int -> string
    val length : string -> int
    val concat : string -> string -> string
    end

    # module CharsDic = ArbLex(Chars) ;;
    module CharsDic :
    sig
    type node = ArbLex(Chars).node = | Lettre of Chars.alpha * bool * t
    and t = node list
    val existe : Chars.t -> t -> bool
    val ajoute : Chars.t -> t -> t
    val selecte : int -> t -> Chars.t list
    end

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora197.html0000644000000000000000000017675607421273602014527 0ustar Servlets HTTP Prcdent Index Suivant

Servlets HTTP

Un servlet est un << module >> intgrer dans une application serveur pour rpondre aux requtes des clients. Bien qu'un servlet ne soit pas spcifique un protocole, on utilisera le protocole HTTP pour la communication (voir la figure 21.1). Dans la pratique le terme servlet correspond un servlet HTTP.

Le moyen classique de construire des pages HTML dynamiques sur un serveur HTTP est d'utiliser des commandes CGI (Common Gateway Interface). Celles-ci prennent en argument une URL pouvant contenir des donnes provenant d'un formulaire. L'excution produit alors une page HTML qui est envoye au client. On trouvera aux liens suivants la description du protocole HTTP et des CGI.

Lien


http://www.eisti.fr/eistiweb/docs/normes/rfc1945/1945tm.htm

Lien


http://hoohoo.ncsa.uiuc.edu/docs/cgi/overview.html
C'est un mcanisme un peu lourd car il lance chaque requte un nouveau programme.

Les servlets HTTP sont lancs une fois pour toutes, et peuvent dcoder les arguments du format CGI pour excuter une requte. Les servlets permettent de profiter des possibilits des navigateurs WEB pour construire l'interface graphique d'une application.



Figure 21.1 : communication entre un navigateur et un serveur Objective CAML


Nous dfinissons dans la suite un serveur pour le protocole HTTP. Nous ne traiterons pas l'ensemble des spcifications de ce protocole, mais nous nous limiterons aux quelques fonctions ncessaires l'implantation d'un serveur mimant le comportement d'une application CGI.

Dans un premier temps, nous dfinissons un module gnrique de serveur Gsd. Puis nous donnons les fonctions utiles la dfinition d'une application de ce serveur gnrique pour traiter une partie du protocole HTTP.

Formats HTTP et CGI

Nous voulons obtenir un serveur qui imite le comportement d'une application CGI. Une des premires tches raliser est de dcoder le format des requtes HTTP avec les extensions CGI pour le passage des arguments.

Les clients de ce serveur pourront donc tre des navigateurs tels Netscape ou Windows Explorer.

Acquisition des requtes

Les requtes qui transitent selon le protocole HTTP ont essentiellement trois composantes : une mthode, une URL et des donnes. Les donnes rpondent un format particulier.

Nous ralisons dans ce paragraphe un ensemble de fonctions permettant la lecture, le dcoupage et le dcodage des composantes d'une requte. Ces fonctions pourront dclencher l'exception :

# exception Http_error of string ;;
exception Http_error of string


Dcodage
La fonction decode, qui utilise l'auxiliaire rep_xcode, a pour but de rtablir les caractres qui ont t encods par le client HTTP : les espaces (qui ont t remplacs par +) et certains caractres rservs qui ont t remplacs par leur code hexadcimal.

# let rec rep_xcode s i =
let xs = "0x00" in
String.blit s (i+1) xs 2 2;
String.set s i (char_of_int (int_of_string xs));
String.blit s (i+3) s (i+1) ((String.length s)-(i+3));
String.set s ((String.length s)-2) '\000';
Printf.printf"rep_xcode1(%s)\n" s ;;
val rep_xcode : string -> int -> unit = <fun>

# exception End_of_decode of string ;;
exception End_of_decode of string

# let decode s =
try
for i=0 to pred(String.length s) do
match s.[i] with
'+' -> s.[i] <- ' '
| '%' -> rep_xcode s i
| '\000' -> raise (End_of_decode (String.sub s 0 i))
| _ -> ()
done;
s
with
End_of_decode s -> s ;;
val decode : string -> string = <fun>


Fonctions de manipulation de chanes
On dfinit dans le module String_plus les fonctions de dcoupage de chanes de caractres :
  • prefix et suffix qui extraient une sous-chane partir d'un index;
  • split qui retourne la sous-chane jusqu'au caractre sparateur;
  • unsplit qui concatne deux chanes en ajoutant un caractre sparateur entre elles.
# module String_plus =
struct
let prefix s n =
try String.sub s 0 n
with Invalid_argument("String.sub") -> s

let suffix s i =
try String.sub s i ((String.length s)-i)
with Invalid_argument("String.sub") -> ""

let rec split c s =
try
let i = String.index s c in
let s1, s2 = prefix s i, suffix s (i+1) in
s1::(split c s2)
with
Not_found -> [s]

let unsplit c ss =
let f s1 s2 = match s2 with "" -> s1 | _ -> s1^(Char.escaped c)^s2 in
List.fold_right f ss ""
end ;;


Dcoupage des donnes d'un formulaire
Les requtes sont souvent mises depuis une page HTML contenant un formulaire. Le contenu d'un formulaire est transmis comme une chane de caractres contenant les noms et les valeurs associes aux champs du formulaire. La fonction get_field_pair transforme cette chane en une liste d'association.

# let get_field_pair s =
match String_plus.split '=' s with
[n;v] -> n,v
| _ -> raise (Http_error ("Bad field format : "^s)) ;;
val get_field_pair : string -> string * string = <fun>

# let get_form_content s =
let ss = String_plus.split '&' s in
List.map get_field_pair ss ;;
val get_form_content : string -> (string * string) list = <fun>


Lecture et dcoupage
La fonction get_query extrait de la requte la mthode dsigne ainsi que l'URL associe qu'elle stocke dans un tableau de chanes de caractres. On peut ainsi utiliser une application CGI qui rcupre ces arguments dans le tableau des arguments de la ligne de commande. La fonction get_query utilise l'auxiliaire get. La taille maximale d'une requte est arbitrairement limite, par nous, 2555 caractres.

# let get =
let buff_size = 2555 in
let buff = String.create buff_size in
(fun ic -> String.sub buff 0 (input ic buff 0 buff_size)) ;;
val get : in_channel -> string = <fun>

# let query_string http_frame =
try
let i0 = String.index http_frame ' ' in
let q0 = String_plus.prefix http_frame i0 in
match q0 with
"GET"
-> begin
let i1 = succ i0 in
let i2 = String.index_from http_frame i1 ' ' in
let q = String.sub http_frame i1 (i2-i1) in
try
let i = String.index q '?' in
let q1 = String_plus.prefix q i in
let q = String_plus.suffix q (succ i) in
Array.of_list (q0::q1::(String_plus.split ' ' (decode q)))
with
Not_found -> [|q0;q|]
end
| _ -> raise (Http_error ("Non supported method: "^q0))
with e -> raise (Http_error ("Unknown request: "^http_frame)) ;;
val query_string : string -> string array = <fun>

# let get_query_string ic =
let http_frame = get ic in
query_string http_frame;;
val get_query_string : in_channel -> string array = <fun>


Le serveur

Pour obtenir un pseudo-serveur CGI, qui ne sait en l'tat traiter que la mthode GET, on crit la fonction http_service dont l'argument fun_serv est une fonction de traitement des requtes HTTP telle qu'elle aurait pu tre crite pour une application CGI.

# module Text_Server = Server (struct type t = string
let to_string x = x
let of_string x = x
end);;

# module P_Text_Server (P : PROTOCOL) =
struct
module Internal_Server = Server (P)

class http_servlet n np fun_serv =
object(self)
inherit [P.t] Internal_Server.server n np

method receive_h fd =
let ic = Unix.in_channel_of_descr fd in
input_line ic

method treat fd =
let oc = Unix.out_channel_of_descr fd in (
try
let request = self#receive_h fd in
let args = query_string request in
fun_serv oc args;
with
Http_error s -> Printf.fprintf oc "HTTP error : %s <BR>" s
| _ -> Printf.fprintf oc "Unknown error <BR>" );
flush oc;
Unix.shutdown fd Unix.SHUTDOWN_ALL
end
end;;


Comme il n'est pas prvu de faire communiquer, via le servlet, de valeurs internes Objective CAML spciales, on choisit le type string comme type du protocole. Les fonctions of_string et to_string ne font rien.

# module Simple_http_server =
P_Text_Server (struct type t = string
let of_string x = x
let to_string x = x
end);;
On construit alors la fonction principale de lancement du service en construisant une instance de la classe http_servlet.

# let cgi_like_server port_num fun_serv =
let sv = new Simple_http_server.http_servlet port_num 3 fun_serv
in sv#start;;
val cgi_like_server : int -> (out_channel -> string array -> unit) -> unit =
<fun>


Test du servlet

Il est toujours utile en cours de dveloppement de pouvoir tester les parties dj ralises. Pour cela on ralise un petit serveur HTTP qui envoie tel quel le fichier inscrit dans la requte HTTP qui lui a t adresse. La fonction simple_serv envoie le fichier dont le nom suit la requte GET (deuxime lment du tableau des arguments). La fonction simple_serv trace les diffrents arguments passs dans la requte.

# let send_file oc f =
let ic = open_in_bin f in
try
while true do
output_byte oc (input_byte ic)
done
with End_of_file -> close_in ic;;
val send_file : out_channel -> string -> unit = <fun>

# let simple_serv oc args =
try
Array.iter (fun x -> print_string (x^" ")) args;
print_newline();
send_file oc args.(1)
with _ -> Printf.printf "erreur\n";;
val simple_serv : out_channel -> string array -> unit = <fun>

# let run n = cgi_like_server n simple_serv;;
val run : int -> unit = <fun>


L'appel run 4003 lance ce servlet sur le port 4003. Par ailleurs, on lance un navigateur effectuant la requte de chargement de la page baro.html sur le port 4003. La figure 21.2 montre l'affichage du contenu de cette page dans le navigateur.



Figure 21.2 : requte HTTP sur un servlet Objective CAML


Le navigateur a envoy la requte GET /baro.html pour le chargement de la page, et ensuite la requte de chargement de l'image GET /canard.gif.



Interface HTML pour un servlet

Nous utilisons le serveur la CGI pour construire une interface HTML pour la base de donnes du chapitre 6 (voir page ??).

Le menu de la fonction main est ici affich sous forme d'une page HTML proposant les mmes choix. Les rponses aux requtes sont aussi des pages HTML dynamiquement construites par le servlet. La construction dynamique de pages fait appel l'utilitaire que nous dfinissons ci-dessous.

Protocole de l'application

Nous utilisons dans notre application plusieurs lments de plusieurs protocoles :
  1. Les requtes transitent entre un navigateur WEB et notre serveur d'application selon le format des requtes HTTP.
  2. Les donnes constituant les requtes obissent au format de codage des applications CGI.
  3. Le contenu des rponses est donn selon le format des pages HTML.
  4. Enfin, la nature des requtes est donne selon un format spcifique cette application.
Nous voulons rpondre trois requtes : demande de la liste des adresses postales, demande de la liste des adresses lectroniques et demande de l'tat des cotisations entre deux dates donnes. chacune d'elles, nous associons respectivement les noms :
postal_addr, email_addr et etat_cotis. Dans ce dernier cas, nous transmettrons galement deux chanes de caractres contenant les dates souhaites. Ces deux dates correspondent aux valeurs des champs debut et fin d'un formulaire HTML.

la premire connexion d'un client la page de garde suivante est envoye. Les noms des requtes y sont cods sous forme d'ancres HTML.
<HTML>
<TITLE> association </TITLE>
<BODY>
<HR>
<H1 ALIGN=CENTER> L'association</H1>
<P>
<HR>
<UL>
<LI> Liste des 
<A HREF="http://freres-gras.ufr-info-p6.jussieu.fr:12345/postal_addr">
adresses postales
</A>
<LI> Liste des 
<A HREF="http://freres-gras.ufr-info-p6.jussieu.fr:12345/email_addr">
adresses &eacute;lectroniques
</A>
<LI> &Eacute;tat des cotisations<BR>
<FORM 
 method="GET" 
 action="http://freres-gras.ufr-info-p6.jussieu.fr:12345/etat_cotis">
Date de d&eacute;but : <INPUT type="text" name="debut" value="">
Date de fin : <INPUT type="text" name="fin" value="">
<INPUT name="action" type="submit" value="Envoyer">
</FORM>
</UL>
<HR>
</BODY>
</HTML>
Nous supposerons que cette page est contenue dans le fichier assoc.html.

Primitives pour HTML

Les fonctionnalits de l'utilitaire HTML sont runies au sein de la seule classe print. Elle possde un champ indiquant le canal de sortie. Elle peut donc aussi bien tre utilise dans le cadre d'une application CGI (o le canal de sortie est la sortie standard) que d'une application utilisant le serveur HTTP dfini au paragraphe prcdent (o le canal de sortie est une socket de service).

Les mthodes proposes permettent essentiellement d'encapsuler du texte dans des balises HTML. Ce texte est, soit pass directement en argument aux mthodes sous forme de chane de caractres, soit produit par une fonction. Par exemple, la mthode principale page prend en premier argument une chane correspondant l'en-tte de la page1, et en second argument une fonction affichant le contenu de la page. La mthode page produit les balises correspondantes du protocole HTML.

Le nom des mthodes reprend le nom des balises HTML correspondantes en y ajoutant parfois quelques options.

# class print (oc0:out_channel) =
object(self)
val oc = oc0
method flush () = flush oc
method str =
Printf.fprintf oc "%s"
method page header (body:unit -> unit) =
Printf.fprintf oc "<HTML><HEAD><TITLE>%s</TITLE></HEAD>\n<BODY>" header;
body();
Printf.fprintf oc "</BODY>\n</HTML>\n"
method p () =
Printf.fprintf oc "\n<P>\n"
method br () =
Printf.fprintf oc "<BR>\n"
method hr () =
Printf.fprintf oc "<HR>\n"
method hr () =
Printf.fprintf oc "\n<HR>\n"
method h i s =
Printf.fprintf oc "<H%d>%s</H%d>" i s i
method h_center i s =
Printf.fprintf oc "<H%d ALIGN=\"CENTER\">%s</H%d>" i s i
method form url (form_content:unit -> unit) =
Printf.fprintf oc "<FORM method=\"post\" action=\"%s\">\n" url;
form_content ();
Printf.fprintf oc "</FORM>"
method input_text =
Printf.fprintf oc
"<INPUT type=\"text\" name=\"%s\" size=\"%d\" value=\"%s\">\n"
method input_hidden_text =
Printf.fprintf oc "<INPUT type=\"hidden\" name=\"%s\" value=\"%s\">\n"
method input_submit =
Printf.fprintf oc "<INPUT name=\"%s\" type=\"submit\" value=\"%s\">"
method input_radio =
Printf.fprintf oc "<INPUT type=\"radio\" name=\"%s\" value=\"%s\">\n"
method input_radio_checked =
Printf.fprintf oc
"<INPUT type=\"radio\" name=\"%s\" value=\"%s\" CHECKED>\n"
method option =
Printf.fprintf oc "<OPTION> %s\n"
method option_selected opt =
Printf.fprintf oc "<OPTION SELECTED> %s" opt
method select name options selected =
Printf.fprintf oc "<SELECT name=\"%s\">\n" name;
List.iter
(fun s -> if s=selected then self#option_selected s else self#option s)
options;
Printf.fprintf oc "</SELECT>\n"
method options selected =
List.iter
(fun s -> if s=selected then self#option_selected s else self#option s)
end ;;
Nous supposerons que cet utilitaire est fourni par le module Html_frame.

Pages dynamiques pour la gestion d'associations

Pour chacune des trois requtes de l'application, il faut construire une page en rponse. Nous utilisons pour cela l'utilitaire Html_frame donn ci-dessus. Ce qui signifie que les pages ne sont pas rellement construites, mais que leurs diffrents composants sont mis squentiellement sur le canal de sortie.
Nous ajoutons une page (virtuelle) supplmentaire qui est retourne en rponse une requte errone ou incomprise.

Page d'erreur
La fonction print_error prend en argument une fonction d'mission de page HTML (i.e. : une instance de la classe print) et une chane de caractres contenant le message d'erreur.

# let print_error (print:Html_frame.print) s =
let print_body() =
print#str s; print#br()
in
print#page "Erreur" print_body ;;
val print_error : Html_frame.print -> string -> unit = <fun>


Toutes nos fonctions d'mission d'une rponse une requte auront en paramtre un premier argument contenant la fonction d'mission d'une page HTML.

Liste des adresses postales
La page composant la rponse la demande de la liste des adresses postales est obtenue en formatant la liste des chanes de caractres obtenue par la fonction adresses_postales dfinie pour la base d'adhrents (voir page ??). Nous supposons que cette fonction, et toute autre concernant directement les requtes sur la base de donnes, ont t dfinies dans un module nomm Assoc.

Pour mettre cette liste, nous utilisons une fonction de sortie de lignes simples :

# let print_lines (print:Html_frame.print) ls =
let print_line l = print#str l; print#br() in
List.iter print_line ls ;;
val print_lines : Html_frame.print -> string list -> unit = <fun>


La fonction de rponse la demande des adresses postales est :

# let print_adresses_postales print db =
print#page "Adresses postales"
(fun () -> print_lines print (Assoc.adresses_postales db))
;;
val print_adresses_postales : Html_frame.print -> Assoc.data_base -> unit =
<fun>


Outre la fonction d'mission de page, la fonction print_adresses_postales prend en second paramtre la base de donnes.

Liste des adresses lectroniques
Cette fonction est construite sur le mme principe que celle donnant la liste des adresses postales sauf qu'elle fait appel la fonction adresses_electroniques du module Assoc :

# let print_adresses_electroniques print db =
print#page "Adresses &eacute;lectroniques"
(fun () -> print_lines print (Assoc.adresses_electroniques db)) ;;
val print_adresses_electroniques :
Html_frame.print -> Assoc.data_base -> unit = <fun>


tat des cotisations
C'est encore le mme principe qui gouverne la dfinition de cette fonction : rcuprer les donnes correspondant la requte (qui ici est un couple), puis mettre les chanes de caractres correspondantes.

# let print_etat_cotisations print db d1 d2 =
let ls, t = Assoc.etat_cotisations db d1 d2 in
let page_body() =
print_lines print ls;
print#str ("Total : "^(string_of_float t));
print#br()
in
print#page "&Eacute;tat des cotisations" page_body ;;
val print_etat_cotisations :
Html_frame.print -> Assoc.data_base -> string -> string -> unit = <fun>


Analyse des requtes et rponse

Nous dfinissons deux fonctions de production de rponse en fonction d'une requte HTTP. La premire (print_get_answer) rpond une requte suppose formule par une mthode GET du protocole HTTP. La seconde aiguille la production de la rponse selon la mthode de requte utilise.

Ces deux fonctions prennent en second argument un tableau de chanes de caractres contenant les lments de la requte HTTP tels qu'ils ont t analyss par la fonction get_query_string (voir page ??). Le premier lment du tableau contient la mthode et le second le nom de la requte sur la base.
Dans le cas d'une demande d'tat des cotisations, les dates de dbut et de fin composant la requte sont contenues dans les deux champs du formulaire associ cette demande. Les donnes du formulaire sont contenues dans le troisime champ du tableau qui doit tre dcompos par la fonction get_form_content (voir page ??).

# let print_get_answer print q db =
match q.(1) with
| "/postal_addr" -> print_adresses_postales print db
| "/email_addr" -> print_adresses_electroniques print db
| "/etat_cotis"
-> let nvs = get_form_content q.(2) in
let d1 = List.assoc "debut" nvs
and d2 = List.assoc "fin" nvs in
print_etat_cotisations print db d1 d2
| _ -> print_error print ("Unknown request: "^q.(1)) ;;
val print_get_answer :
Html_frame.print -> string array -> Assoc.data_base -> unit = <fun>

# let print_answer print q db =
try
match q.(0) with
"GET" -> print_get_answer print q db
| _ -> print_error print ("Unsupported method : "^q.(0))
with
e
-> let s = Array.fold_right (^) q "" in
print_error print ("Some thing wrong with request: "^s) ;;
val print_answer :
Html_frame.print -> string array -> Assoc.data_base -> unit = <fun>


Programme principal et application

Le programme principal de l'application est un excutable autonome paramtr par le numro de port du service. La base de donnes est lue avant le lancement du serveur. La fonction de service est obtenue partir de la fonction print_answer dfinie ci-dessus et de la fonction gnrique de serveur HTTP cgi_like_server dfinie au paragraphe prcdent (voir page ??). Cette dernire est donne par le module Servlet.

# let get_port_num() =
if (Array.length Sys.argv) < 2 then 12345
else
try int_of_string Sys.argv.(1)
with _ -> 12345 ;;
val get_port_num : unit -> int = <fun>

# let main() =
let db = Assoc.read_base "assoc.dat" in
let assoc_answer oc q = print_answer (new Html_frame.print oc) q db in
Servlet.cgi_like_server (get_port_num()) assoc_answer ;;
val main : unit -> unit = <fun>


Pour obtenir l'application complte, nous rassemblons dans un fichier httpassoc.ml les dfinitions des fonctions d'affichage. Ce fichier se termine par un appel la fonction main :
main() ;;
Nous pouvons alors produire un excutable nomm assocd par la commande de compilation :
ocamlc -thread -custom -o assocd unix.cma threads.cma \
       gsd.cmo servlet.cmo html_frame.cmo string_plus.cmo assoc.cmo \
       httpassoc.ml -cclib -lunix -cclib -lthreads
Ne reste plus alors qu' lancer le serveur, charger la page HTML2 contenue dans le fichier assoc.html donn au dbut de ce paragraphe (page ??) et cliquer.

La figure 21.3 montre un exemple d'utilisation.


Figure 21.3 : requte HTTP sur un servlet Objective CAML


Le navigateur effectue une premire connexion sur le servlet qui lui envoie la page de menu. Une fois les champs de saisie remplis, l'utilisateur envoie une nouvelle requte qui contient les champs saisis. Ceux-ci sont dcods, et le serveur fait un accs la base de donnes de l'association pour rcuprer l'information demande qui est traduite en HTML, envoye au client qui affiche cette nouvelle page.

Pour en faire plus

Cette application a de nombreux prolongements. Tout d'abord le protocole HTTP utilis est trop simple par rapport aux nouvelles versions qui ajoutent un en-tte informatif sur le type, le contenu et la longueur de la page envoye. De mme la mthode POST, qui autorise une modification sur le serveur, n'est pas intgre3

Pour pouvoir dcrire le type et le contenu des pages renvoyes, il est ncessaire d'intgrer dans les servlets la convention MIME utilise pour la description des documents comme pour les documents attachs dans les courriers lectroniques.

La transmission d'images, comme la figure 21.2, permet aussi de construire des interfaces pour les jeux 2 joueurs (voir chapitre 17), o l'on associe des liens aux dessins des cases jouer. Comme l'arbitre du jeu connat les coups lgaux, seules les cases valides sont associes un lien.

L'extension MIME autorise aussi dfinir de nouveaux types de donnes. On intgre alors le protocole interne des valeurs Objective CAML comme un nouveau type MIME. Ces valeurs ne sont rellement comprhensibles que par un programme Objective CAML utilisant le mme protocole interne. Ainsi une requte d'un client sur une valeur Objective CAML distante est effectue via une requte HTTP. On peut ainsi passer dans la page HTML une fermeture srialise qui sera passe en tant qu'argument de la requte. Celle-ci, une fois reconstruite du ct du serveur s'excute pour fournir le rsultat escompt.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora137.html0000644000000000000000000000425607421273602014502 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

Pour avoir plus de dtails sur l'implantation des modules dans Objective CAML, on peut consulter le rapport de recherche de Franois Pottier qui dcrit les motivations et l'implantation du systme de modules tel qu'il a t primitivement dfini pour le langage Caml-Light. Ce document est un peu technique. Il s'intitule << Implantation d'un systme de modules volus en Caml-Light >>. Il est accessible partir de la page du projet Cristal de l'Inria :

Lien


http://www.inria.fr/Equipes/CRISTAL-fra.html


Le systme de modules d'Objective CAML reprend les principes dfinis pour le langage SML, cousin d'Objective CAML. La chapitre 22 revient sur la comparaison de ces deux langages et donne les pointeurs bibliographiques ncessaires au lecteur curieux.

D'autres langages offrent des systmes de modules volus. Citons, par exemple, Modula-3 (et ses diffrents numros) ainsi que ADA. Ils offrent la possibilit de dfinir des modules paramtrs par des types ou des valeurs.










Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora160.html0000644000000000000000000000433607421273602014475 0ustar Introduction Prcdent Index Suivant

Introduction

Les deux applications de ce chapitre illustrent la structuration de programmes selon le modle modulaire pour la premire et objet pour la deuxime.

La premire application propose un ensemble de modules paramtrs pour les jeux deux joueurs. Un premier foncteur implante l'algorithme minimax-ab pour l'lagage de l'arbre de recherche. Un deuxime foncteur permet de changer l'interface homme-machine du jeu. Ces modules paramtrs sont ensuite appliqus deux jeux : un morpion gravitationnel et un jeu de construction de lignes de force.

La deuxime application construit un monde o voluent des robots. Le monde et les robots sont structurs en classes. Les diffrents comportements des robots sont obtenus par hritage d'une classe abstraite commune. Il est donc facile de dfinir de nouveaux comportements. L aussi l'interface homme-machine peut tre modifie.

Chacune de ces deux applications, de part sa structuration, contient des composants qui sont rutilisables. Il est ais de construire un nouveau jeu deux joueurs, de mme le mcanisme gnral de dplacement d'un robot dans un monde reste valable pour d'ventuels nouveaux robots.




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora194.html0000644000000000000000000000451107421273602014477 0ustar Introduction Prcdent Index Suivant

Introduction

La premire application est en fait une bote outils qui permet de raliser rapidement des applications client-serveur pour communiquer des valeurs Objective CAML. Il suffit en fait d'implanter les fonctions de srialisation de ces valeurs, puis d'appliquer un foncteur pour obtenir une classe abstraite du serveur dont on pourra implanter la fonction de traitement via un hritage.

La deuxime application reprend la simulation des robots, prsente la page ??, pour l'tendre au modle client-serveur. Le monde devient le serveur o voluent les robots clients. On simule ainsi une mmoire distribue partage par l'ensemble des clients et se trouvant potentiellement sur diffrentes machines du rseau.

La troisime application est une implantation de petits serveurs HTTP (appels servlets). Un serveur sait rpondre une requte HTTP comme l'accs une page HTML. De plus il est possible de passer des valeurs ces requtes, respectant le format CGI des serveurs HTTP. On utilise immdiatement cette fonctionnalit pour construire un serveur de requtes pour la gestion d'associations, dcrite la page ??. On utilise comme client un navigateur WEB qui est transmis une premire page contenant le formulaire d'interrogation.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora028.gif0000644000000000000000000000655507073350152014305 0ustar GIF89aʽ}}}{}{{y{y}׮!B,BBKC7&D E!F"H#IIA$HpXȐ!HbSȑ ?"I䊓(Q&X2A0aIs&8q2sg@6Jt腣HXʔ)P*JjX.u`2Kv쌳h6X˶A|iHGKӤ3AOB{ $)b @H2If:әAf43Xt6Mx|89Ń:׹N:p<9 > $@J=B8D'*QXF7xHG:&JWR .Lg*S7Nw?=PzAHMRԦ:TJժZXͪVծz` XJֲhEmZYpmZZ x+X>¯x Z-H]DbؽnbdDD֨!8Yu V?KYfdkTF,ֳmhgVҢu!8(pj[mlAZ&wЭjq1U]a+[: Tyⶼ,zoj|Ɋ\wͯ~L誗5k ,Vw+`Ŧ5}',V7~.fZV09 WzĻ]q7\ 8'.YLrq}`C;k1,cF>dL!ri3ZcJx V/όeՂB^/:կq>πW\aYЁ-n3*7'>-=iʖΰ]N۷5wM1r3mҦti/CմɫdCYֹt ЛsGdgus#i:¶6Wy[X$kW/lA\͂Ԅmlfzm\5S;ڿ7%pXw; ZKi\7X=^WTvm=gjO¶}]G|^nGnsI`[BotiɽLzoqtȭ-SwrMGy{n$o y췮qlguMwg=- p?^d[|?^xCU; [z۷9Y55{U]n8߷Gr5}xg~[mvz~Ym{VyƁF~urivn#&xG|x7fWl؂B~X6xwU{*v^W6w÷xyFswb Ha_|Z{HsyWzlprgZh5\'fxDVi׃c}8.H|؁]gac'uvvcȈfq ʇnaanduo0XpHsX8Xxؘڸ؍8Xx蘎긎؎h،Ja(Hz}8d؏V7Xvc☃G8((iHy_FfxUj{Ж/wՈQX]X(dȊ5*@B9ɖDa GI\-K)&vN)VV%YY)Qٕu|\ xdy;ocaml-book-1.0/fr/html/book-ora076.gif0000644000000000000000000000477607073350152014313 0ustar GIF89a/UUUrrr!,/H0I8ͻ@(dihl_,txCpHȤl"h*nް,.9^ 7\,v->oރ!;C~|a< TF q >DQI\q  B 6GP ϡϹع@ˌ4dѯ Ţ =PyGo=t"f4n>S Иp.ڗ /h,6r+1bB#2 %=)}2C.GΨK-Iƈ*hUWPͪa+H_x +f;g[Z/ʝKlnK~'.:e᥇ 'Vxo1s6;o尗f溹m竟ӂ@)CSV$TCFm"_a}FJ.GCl̼ &-@_^q\ U|@%#t b5"psaI0X Nub|_%vU/~c,3X|7#B;أ}?cU[4"qPFi8h$TT^9pٓiPddi]fi&h `U[pbI@, Qwj&(Bc{"^}TB1i1`i巤g~:;=Z "=)NZ*m,NZwh} gVepC;lBꢵ,"tኀ CL~-n62JG!ҩ.o-=s\GScKN*rKT7lE &'J\hE2d4w,O]ɌBX) Ed :PJxLB!|oo58dΨ `muSU` ְ?S 4WqknL:%B2V6_LwW$;# ѩ}-3+i-vyWHiqKXQ` dn""w:>fy67< t=b_+𴿎zJ7-D nNgw/~* 6`]`wy!ۼ H[dCb‡9xg @%N4ܖo%BavS9ls ZZ "(E9֌6Oثl ]11 8đsO]cX9(dkZJI 3ĮpCRUvկ "c(Yo|:JvR=әy"* 2bT=?CPxzSMC$N.82JZ Qz"DIS:ecJR!8]VP-.kPIQ#P".R4*􉘤))e.5p!9f^U2BdtYDc㭹0&|1+WKµjo#Ȧ3V.UF/MBaы52ֲ58/zDwI˳J&bp>:q Ӱ+6`▘ W6HIDa:QaLӞŔ?,$B֧WtkUN7r';[bW}w8ޚJzފ׻{_P@`5%bf%U=GU۳/2J%5 %2C  ')ew ~݋H^7#t_WmXƫ8a|Exv&*Q 24dXrlA}׶MVSTT>i9cH,:[,kײvb\MkbجFjaٍa6RjtlnSۻw=IrܸFwսnv7EMolrޟ&j?oOߝw }_Ynd2 g4XYTkXeBEzk ɺ`77tnOA;R؇rMhk_F-,3&4w\@ljtnCJ.kĥqit׊V"ϩ8IJ+?::8+CZ#ll.L㍮M掩UXXV@ ~GS[{l y[VL$\}䙟_^ُ}?x/w௩ȧ;ocaml-book-1.0/fr/html/book-ora010.gif0000644000000000000000000010536507073350152014273 0ustar GIF89a Ƕy aH 00(ǖaaL7(( 77(Yae0YU(quaצhyhyyhY;QaQνQH0aqyHyYYH7A7Q(yaU(aH7 }YDzˎǮ7H0eauYyY߽׽qHUha(7qa0qlQνӶy ߦםǢyh ׮yyyHD(ΝaqQeQq}7,Y,(0 }׽ǎ7HHǺ   ǮQqQAHAǝyHQAyqa˝΢hqaHHlA߮YhhqU7heY7ν!h, H*\ȰÇ#Jŋ#D@B#I$  (SR’-RI@%$(~!F#\QQcGDչSU"I:ŦByM:*tmƧ@4Mq#b hIS(QB^fHؤ\&Q:LW"=ti۾FjRn$MDr96Ǯ#;(ku)TdNKضcNv v.(jw><Z`xL- hhX-Z4oپ=uTQUH$YrgT[W`|UNet2w|DJ(%FAGL2 MtSIT#-ǡ|5Ruk%1B_oDem[!\[H 蔎lXf_ Mh&ISJ1ƒ3ZtY%V]QjQ`95f 9kFf7:E)IptHtmuvYak9&GM$dkL4M)eҗXRQ`ZnuH[+PZNicuE %IhiIVwE`&`qt(oUiwwM(]fHxZjnWゴ]U5HRSQzEm{&ʾ +kq]؟{iZ(go,hSh8h{8hQ]h̕6 kO12Z$?yFlmOUmZebUZ.Å6UH!آ^V!AYK4sQD5n{ jf'P2 7{`N#GtopHgV҆ d_ Ig:smΆ>z\JlQ8]2Kyp&erYVf)xY6nE!`Yݣ!S~.㩒uafºh0&4*bɝV݄j9HCZpEZukdDsZ8S'ܚbZAK=CK̭|䌦T/0܉wꧯT2"k@bBI(*bft!TXZXx08\[zJL=>l#LnYQ_-F XӴYXd Eo;,ɍ5eő,}+dN2)t`! yqi5-rK7l zie&d >wb+7VW(JP p$[/TB\v57ŝoҗ3&Eɧ:USәzJ̫\J(`qII:,@a0 `=‚%^T"@Sz4{rt1u #J2sn&FdN`avd_#E7|$pPf8F->UltKƆgsW6?Rp!n@>fEu7+1gf/s' DZ9uc(@U"?h\qB >A3NvUv{⦪/`A6FLW7fjvأ'r%MAbB^PpPk0;J<(R` @zCP 6ĝ4qNWΦNjGh(Q828.2E^a3idji%QyȈRwy0% nk ye%J7:%\3k*Du3Yo rk;ZVfuC5r@ D@0R$pr` u @Ϸ. %М @ SzL9>&WR&eK4#ccT*".7gN[ppp-@9o`%w}% uIR@$npa.f)4/EoDc湯>~NɍTU& ٪gB,8w@ ЏcpooPW j }е;IuqȳX[#"[lKk;܈Qoc]VpFb[4"*ۑR>0Cn\XBL0@P$8PE G]q%<}¡$R )})m',\Im6PdDp u dМ؜Y (M opkaо `'PGxI! @ XlhJL NʽCAz$v4/e`q)],,CA{p 0 p[PVǎR0Gx֌h̿k m/I0.@?R, D٥]uw-S1S 5VN,iMƒn,{jZ>7X8;\qͽʲS|vgZSs-@BqS 9>h0,67l€5HAG<#'npiâNlF0X>`0CxXC  uJm|ZiR3jLq _% pX VV׹0*:@ )X007nH@Ђ 8IMMda8BLJ&o°0@ġ P!FvҚ0ۑfNOQb㥴\I\:>j]S0ǝ *` j.Г/\@ T >O& @Kր gSB ,8@IVs@DdBZ A #.v{ᴘ" -i Ls4K\5INLzs+hB*l ` x ]$թU@Q DrV~2`&Pt3@߈ [ j r(p 4DdKX9+dR<&lfJKc82s@a , 5R,ްE%S K,H u~t$9PDmn $X ԤHBz(ۉYifpV+KZ"Ny^x*A5MuVK3\A 8` 3p PT@$.z\b)MIhņ.m9kH-_: 'S<+ ̀fC)f6 F*hfRK1di4"AI|\otF`F Z"< J @CH$ >`mj5(HkE lp"z %Jd9U2ܠua 85M@UzBl)ib{mes<2A)+s`?|` LX@ lha5! pW}X(Bh!WȄo0Ȕ3׵-0@>bt([A3+Od첖 2{ <>Y$ S%% 0 @ x@ :qӕQLB7VeW Xvѹ sxbM\3P$J">"3\kf+Ǒ[ldV+sfR2ЦMr} ByKS& }(Ba%H7 >7aqƃU ;(vXqooI7\eD3D9JU~ct4Je[i@]w[R/^cP6w 8A Mp$IH* +ss8R X o^c`m6#&>L@tkVXd]Sf(je`rN;zk,cI#wpA vl{ 3_⌈O5md.l%Rp0r[Xڀ "1 xor*Us)-(Y8/Z R; z"<=*x6PP`3>{!Zـ A6m!.4.)%_*ƒ )7 %C9Q fI2 .[ j^c ̣; : Ђ:0*p6!'a6(6%X%M)/ඐ-P.7[*07 " q؉}1,hs5:(;);f I=̟cbəD_p\( 39/`|b؂k+#Z! ؀yꀞqëNQ5,耊:N&2X 0 `yI?\Puip I,( (P: X?0 579Nh )ԛxJv{>PX"q?'N: he27C2)srb &8 z\!HH(-pX78’`0"04lyUS%8ƾb 蜝].x+ y m zAxDd; @+ p?>pU7YH)8׼X2!a<.$.E8)€#/SJ xIuY882.N@kE3Њ̂Ʊ0 =\!Pp?89`2H  ( "X7 [Z7e\5.!H- +s()AjxłВ̒;kY3;ì92[8 (6M)$P$h2,-؀* 7@yKЛU@X͌JSDK׸+%pZ8-I38IE6؂ @!td-TQ(2q=) \1a ."8 Qp18(843I : I3P1)8vqM FB@u ,AZcW@;ҽ* ȝ @ = 1ad:-\ wB<X,<( ((Qwd V$%_|>`-<1]@+C7zlMFp1yT@Rdv;3p' lh7)ט"82 S؂Ѓ `()<ț8󑑹JFx<Mv_f_=_tUPC!ƓD;z?0h/H/Ș7T!> ,P%2-3@< S%+f=c`J2*.=`:e1*m%+D֡|N;ɸ+Ν!. !# 5  p  H) >p;p͆R%؃4B:Wh  Kfx[7A昐 ԛ)j&뼙$gG GGA/j)]z.%h&x`&(OR%i`cP9A dQY 8].E.БnP0h\B`etA>e`9 8P/%`#px>> H@+\eV#5$f~$N!~yV_D b &@DwZq#<-`*l.`i ?p|3("ChဃE ޢ9ž XP|RXp% d__tA\[f 2휦;l/ ԃ 9ɹ)HQ&4*%,#S< #n;VH*H|MɈ-âeK̥5mAlVTbJ@ATwP=;+PDـ&c @@1ЃƻnUH5ۼ!SIsV[ `_N̘A4e jmA #f0HY)(m2Xӿ $:`DEB^!C7<N?ļk3 + 0'=hEXjCu,+d*Cy -p#( '5U h;}Д ɼq41۰DB ykHǐS4@[7؂)j  ad,#W|oP;z;zg|>H.Z:H" >/k8!X*MyP`@{(X` d*pc"}𘨠 `ys"$a[H4`, 2pAP?JQF&EiP?"E& VL,(Jv=L*XHA UXsˉ>!1`>,>$(I`dnrC 1c̃X.<@ 6<޸y8d`\0< 2T(ҥAe8TҬa~U:h~gP9  9kDÅopcތXXTRdtZYA@>(pJ|P6Df'l#X8tS!\Y'qAQ0Pl\VZ ЁduǕZA 8 pXh`ذq,F E@ogXA$u.pC Й@Po`R0%hQb, EC4A@g> vDuqQ>$E%Va!erAuTDE`!6 R@e(0 ˇ@d[XglaљH2@A RJ7gP#-I%HQ0I4xGqMi5eVBh eUQ(* 1-X` #E$4|~@ wt$8Чi04gHDxm` nxiD  mH ,sbI2]qrHA_y\w#]R9A-@(HUtA|]%@BU7G,,@F9al!F '5=FQ0 V>\Xle6w9ŏ0 @X$7!bTFF)`DCjyϐ50 `ADrFqb3N!(+0|=4p*|` .؂@h@0p( { j84" ` !,`v@ DHAtaRAlADX ZpG< AHL+ةp G*ou $A@KAn5 o b| DB܄v)`_3T;Arz%.@C  ")  66AF @ Lجf%;gIǑ tAX8J7 a^0 $HZ 0׆`  [Al@@@ "" D"4;4T9DW> ~EIhTa6@*P/(@Ls D`a5 ^T @1dpL i EX@ : JA !^`lEHed 2"Ma Ĵ',>"; IB* TL&A $@i78IAn ;a"ڇ`!7{@X1B =RopPFD٢64ybrJGk,"E@= ?0:P a |0dP N%-҄}JhCZ)#ԑa@'7 //` p'*` #[#&h (:t 8Iw-)kcսZj@L(8 8 Ҁ'C ,^JN#W )y4RAX*w!t;ڧhp!A]CUKhlNTBts] 8o8``BբOj0-xzh t|O`ÃPa5A PHu9xm> 6&CT ٗ6"2Te;2`4CO؈pdHQ8HuT`AdAL,@`@<|Gݽ %lAċA 2@n0I8F $FHdA! `ME^t1JPZx 4X T`І@  HAeyD lI=F(@}1DhPhl|b@DAjʁ<^er!Vؕ)M`ե0 8A @\w`,@@@K8aILlP1D4`~Hb}@EcƆM 2.#BM @! Oatء|<@-T UـYA` `AXYK8d<$AH$m`\䴌@eI)@t8IDL\ I`($@>#U5npǀD}i A 8@ ' -K@n@<DCb\ 0Dc/xA h D !A @L&NΧTfe># `Os@t!ԁQ"AW@TWAM  Mt@ L$JA Trn؀lQD@LLFdJT4э@v]2D 8}AU A$4'~ Rc]瑊Y c*ɀc!xzI AKB ě$@N ,d|Y/ "])@H <p=@]*x( NBMN~pd56X؀zN L ,,<lD A`K0@h1JP|QleH_I hD ًIˍ ܁2^f}2|2A5pݐeVtD h8^QxHx|(@H DѐĕM!iHDBVFR ߹يle @bH\m"BTB l4|^&2*`qv]XQ(QXirlVށT A A M  AB9 ϙ| F\cD/YּR&]DX mi:MUIHtKg$z!5b5[h(F hP G`  A MRD e]UV @|y/~8} yR5 HN,!A@XĪv.nAN%JTǬ)0U @IPhiL |H8_/R X^Me BOI$=yc@Ed& y lN5 ۮj1Um*u& `@ XDMtRFd(X @\dm%Il=*ֽHĆAy-@FE%2* +$gnYc C5rvxT-A |A8 tAA T} ^.f]*`F l[$91rlNIWJF$f)AZH ʔrf ` %rXe9UA^Th@B\, I 0\~XKKX-rΪEs;A.!m֚AX4Kt-@FAlAdMw![o3cuf0^t SpwkT ă} @@@ Ƕ\聹6kygcz'6zs6FpIMp*$nT < XlF@~@IFMXAl$/k G6@F,i,GNdnHpywMg#S)Dw,)M@DT:  `R܀i`KTA4{JrNNT5%M蒁(R THE(@[lTm @k攎tx2Aqm, 8m``F|(`WDt@~@̉;VuAY/|inݽF&`3ynp |'d" )F~ AwN^}BYD`A|@ @8iD D,H@} 8Ңp/dudl1nFMD Ńd&-m`$,Xy'ԧ>@La`  Ha)wNA&IE@\1cǍ@9ŜJ>E oYq%$`rAX  e9<ȐAfP*X >*QԇQ2 \JFQm@7 .cRP7Xبv: H`CPf!B 3LqtMLB΂$ 2@O6H;tB僂7 $BJ [N2 xx1@ QJ԰n5%TvH=]4b %7~ *s@X #(32#H'6$"JA)8c%FC:v 8 (( ,r=!"  Xԋj-<ޠ!) *;H&+ B%Pj,, .9@ 2BH22{4Ã@L&TO C- $@ XA -xC C: 2 p!; aȜ J>" "Hw"pr[  5p2/ltB!I=4J("@ .8446#8 6b&pb#Ï ؂ *X`7x!06)F(OAZ 2`B/3 *(w`5b`L p--/\ %R 젻5;S1oT*:rB>:  @Ï p/+,8*Y,&iWۥ` YؘJ+ \`  f2J1;.Ex rU4+NHC7cI(b Fl4Lg)@DT[F` ,PB Œ %5= n 8pq Q,3088[`$J B0 ;wŃ ' jޤIl N`p,QSeDƨ ‰L\A*#K\@PA5Ѐ*Ї%Q =pހ oP*HA9VN-2|@ x|(*ӊ 4'0Q@ i'X⣔[Rڔ7$ A6 ) ^d`ڣ@ (P!B/=tfCN$EՀMx`U[؀] Ђve@LЇ7 `FRmgF -`IZV'%@Z_b6T*bq@3E Ch @aA$7!{ޓ%d$d;шG!CaPp*@oCЇ`Xa*K |`<4AiAe 4>?^JVʖإPz4vYt3I YK [BdSgb#Hrj10 Be  ACÅ Hei% ʢpER !*;L([`펂&(A-\z"P,[&y(.J@ , l!@< B sn-mY-S7]}$`7`AHPF D4@ 0b7<`iT ސ ;t%weDJY9 e@zI3 0 rbP80R9ؐ8n4_!/MU i!R9%5A ` !{C`X,} ( 8Dx$=@X0@BtAa 4tA=_Á6 5XT FX0^߈lkH&D[2D0bIѤFbަptU C! z:A/ B @H,HAL8-XI o(/&1t6Ȓ$p$dO(!CY1k} =JG>w6n:gf Ë< @L 8 .h1*5MH ]0 p,8J`WkiV>|-X|[M@Z0"ZI: ` >n` +$$vlBZ:C,"v>S @ ` &K .d6g`@6hZ¾-X<:Ѓ@ _hl`-T`6n R> F ޠZ@'t` `L (.6dJd ϭl &+ e* D.l>& R`<*b'L 4!N4e  _ HH > `` >6  Z *JVDH =.@< *!ɠ@=`ؚd<6 ] k ƃM* j * K GUoC.k [O oN  `LR@q` f @. vcR @ZV,b.dǁҔljn Z̪Ⓘv=1k B dd2#,am62?e# L`@JJ<` @  ޠ n)#x`<̂Rb$ `:M (  FOt<.)Zҥs$ i*1ǣl -6 4_j̄(<%5`# *` `: ``X@$n  X| `u*\@Wm@%&*CE X+: ZhX  N$x ZB0B-@d @@E5-P T*:#"T),E gC` k6@W` lk9x"Rcr E `@hgdOIƖF`0rԄJ( .W'CK*j.vb̐ @ A1/xI!2K4t5,z8$663 `" O7m 4怑J@ h  4~#Z6 *:HZŁ":`B(X n &Ikð2)) nY/H+j  g^@uQQJs!eqL\@Ƞ ` .g -@`9C@HOjgK(8S-wo]I@6u+jb,.@0B ni l( *)K:@B8bɿp|.("Ʊu*5LqR -`ޠn܀~9 `(v ΋!'(Xh.bX >JȠ"U C*D钤M jlޠ ,@-"+.D+``|EEqLZ/l{2a8)A%brZt b *@ *l@h ŀ( )jh  i~9@wMX ;ժ`LfkI :`;d, +2i喫*,xbv0di'3пYa!TEI7@ /\_F` ..]`bgEjI!YŤJ\fEvƦ-Gx̨tF+H!TBYk@@հ+` `O`b:")] JD R.+ l&(@r(("0|X )ZbE fdӬ4)Cd $0 fecSfS ľp %r Dٚ 4 j . HH@9 &xCx o+E.&\ ʏ+@ 2R|1B+ɏcBK(v>@wT'zixyQAB>8` j 2*@@ qT ^2M䪲 @Ԋ./y Y IzIk|M j`LJti,Q]+,f *`4gJ|.#4IW) <^Kդ*JwICp -.9 & \c,سB"ibeJCX:>e K+{Q/O)`  X`jpF`2gQB`z<#Z 1ڠ  x` %T@%@@VpŔDx Ө $oH[ Gyx#`-Pj(d ʠ @lԭ/ZfI ތ7+c"X we\c5 H؀z!9`.@oDy<`?FX `揜cQ˕ F @U8$3*Ȁ ryD+ g*o]SlfCZM Z@` TNj @ @ `(XL`,.,hWZ0=^=*ѵB rl"`(Yba+E/A2Lf޹"esS >X``0p@ 6@̻*i%I&;@ ޸x`@ U1[x(ɀc#.M$sv6N 6< Ǭ a?ڃ˺ep¬q@LC&A&XYe z`YbO5l # ~QpC,C,C6lp$3Ņx:12m\49b6ȐCFKG xГ7(@JHq2oP:͛ < ,hU` SR n Nwo߹%  @301c0>ȁAI .0zj@:ul`?@YX#EJ>6zu#_ t% di$wG,I = ?e(Xpaxsb[dvge#NKG 7 @ <|E[#RRxÁ"a'w&!Vu<9`(! AEHqF=!n<=DԐECt@9ԝ7RVМKؘ`<@w@A$l 82_`JH@ _|XTA)xQ Ar,0f->07I-CPd s',Ѐ O}udg<sQRK-bY>la@ Dz^2`珩hjbh>/`P7AP3|pC0 `` ޠ !CE w`szb# A:Il:b! ކPEsȁ!6KB̷/i fx6 n)O01JЂv% A fJ2`p* Xz` ax H|Xa 8LbET@ K!g`*xDZ`4.(יc-cKn`odH8M)/ 2mP1ܠb@ 0a ؐ ԓ n`Ttu@ %tXĴ@V;)K(@ hb8$>Yd.8 ` h2lIO9 .5 X l\GJGNSjLWxnJ2I @ %|yRPzÈp* AA` Bli= x-`ʊiz} 6ԀH> Jp%A _R\Ї|D pPr7%NE65Z hb{!<(\;L njP?lA3gb` J 4U `A H*t9ePR)7(*u+zA L“8#a@6ԡ )P{`q6`+-T`_  -@O0eP!iBDgaK^Qg#_YQidr4qvuxC&e"'zE7kz0/a656%3AtJP~w~ 5`(0p14 00J!r ,v0-X7P5PMH8C{(~w b@,p0p @x7qA`2e"~70A`g A(& u1)+lqc$J愁S;]wIHP#h`zc04!h1[r GNF7!;f!zBZf!&"*p4`dP8VD{99_@Nn'0_k!0]GǞ Qy٥vT P0`=PbE4Rpl@_R{mOM͖4 ;] 1jF`A%(~!dE.bB[hGN8_C6.rbB?4esPI yw8rP7 79pn&˓o0XJ]%Apl0Z,P+0EY{AYYeOza1^'bmPgݔ'pEdBI' гk[?G7!CKN ?u~;}0 ~pnO_1+X}@:QC`-;þ8&qnȚLTZ[%EvyxEGT<56jA7@(VIX; `dc"X>Q0NX "G9T5od*@y`. K0@1:CJq pjb,k;>A_i pT:\axpJ[#o.. Z&w7vig HP;TT+/KXQPqE`>]G;h6v8ʞ( @- 0|8*X}0_#-G6}@|u!\J,!fDw$5Ȗ`Mj(nB[ 60&XfX q#@6fapG#=H8sbAs4,$+bT!kv8UpUBT9^Ch5D`XtP$[0dO%UETbp25].XM3+$,GXV A< /'+%'vQ`~~@i>V7S@RoH2`&@O@lO @;LR<pKkǩC11wRc#ֲ$A}{.b/FAv@mbK1Y6`%inpe@ܐxgxU*b0'KcpdpyyntPBpk| V|t|rsLOXp SeOPD5!;66oP IRܽ6}aryR.D<..#-A@}BNM\Rs`&1Y` o[oҕa%9MbwsB-.wt]P^fZgCp+8 _R0Up {-k^R@'KƷ.Vpz6AP}"E]9a Ɨ?Ȑ2dė6`BX`%CLnY”)2d)s͙Q 0h H6 *Q %Xـc,oIeA %n葀K!)| 5t)H!5.>r @_ .cމd(p,T|l02%w XXu<(ra XV`%[&b x(I׀ِOyPkS`K5Z,.BA. R *3AL.&d %pȈ z"h52p5S$2 PC|@ `6c-p`:覘i:i@ nA6 6h@À3px h?Xࡊ.F`X`a ;.5 !6#̋=Z:TH<2%KT U\\SR7*PRI26 BlC `T Φ&(h.p9Rc/ިa"B-6h?R n` $6ajȆ`k#ɐb TP).@ :0a%R౏`T b`;;`!$ja Z"t-X0ஔoxõ:`pckq2 x);< 18Al#>*,Hh( X5Vh0ۼa \ M,$2 K( R9rjj@))Kp1dbo.P>\M@(݂\(.C. 5Є/THpP7 =C|!\l@87k y%q,0?:IL%X00Ѓl.APoH@ "Ѐ +`P ;a?8۵LY<^`QŖO 4kC z-t _Qp )BD8B c$ $xH\`/H{f XA)(F4 :-Hy/Ъ  8FvypJ` Op BGVL bӃT i*03n JanZa2|@mv;!!ᨭ#0sg(ܑ_`ld ~^h_(XC,&A_{1J k JgPY^C:$@.Jb $kuܒ 㲦_`<(6Ay(/xkZ"xX] (1ᑏ@׫x-iÚ x </v%.؂h6 =Hw@5  h21Hp+[ x.[B !Y!?i6C Y K7x "# qʐ[6p*vD`v2h؂*( #X(0i;)<05&H! [00$X] C*!R[Xc` #3929A)(Ǻ ð 96@` (8X=j32a-уXÃ& xEh8+@R!2ƾ2E[ Ю= *<cO0tC̐" FiI=n %(? ljJ3t-Ѝ& C ,1D318\ @#j)h+<(06K#X{9:#BdCZ? ؁8 (XŒM" *Z(%ȁS =#Y%?pN N2@`2 bx +:+ETXliҀcZ@{K-=pLRh!%jc2#09<5 H*Ã]+xF - 4*C!3P$X1P*eX%@ -ѩDЃ@92?XX!)_[8; $=CCI4̂h&C3$E @" D%  b#16%%Ȟ2: 9L @-`P:D1%h5ȋ2pSL 8HsVK!h5ا@X+ɾY5X P ֡h%(c (3 i$ X` $(;dutkA) )Dz?X |:yE%0 6 @LZ>Đ7*)1TC+̾(ăLd[01B8.Lf%; JZِ, ’({f4': (98D vN1A eQP/8c&L7Hx#)ۉHؚ 00H$`2]\2O<p<-(1S2' a1Go $ꙅl"0B|WG4h1H/x.%m B70Ha5!22Mؾ؃2E ޓ|t#%[)1:3p%N P=8.(nJ0L%!@2 MD:w/(,h>)``1&Y588 2pOD5(D1#+-@R.h"&,XCJ X!V 5p$`B$8!@0` -h%-phI x9c9;cM `N9ĮRP;Oy a1C%( TAE[90 ߌ \],%h#0Sfc 0H#$E%!ݠ H1$Vh+*=fkf>c8x:. # 0 ;h'ncU\5yOVR"X!. hm#C~R6Ӄ?ؠ6!0k( {8fc`9 -0)(@O_7P;X2۸ݡ#B,#6h${&++Z[2 +/9؜!b]2Y# @.Ur.U 듮@ )i(j>Z4XP*88=-%@p,> ! tZLm.#shr$)XK`$@EI{".Wcaw ijc$h "+8 d$\2H&[?7`Y/ .k2 ؃&KI%Q&HX2(L%.Pʙ9*vdƂUڈ.h ċ `ql~l#G7JH#?DpY5BB€[ŻQ=Zl`j\^ЖE*EݱhK~4IYK⁷$~k6=?c@#K%8: (ƒW?I2Ч )u8$Rx%S%X09xnes_q(p2$X[Oi'ĸ^ 'Mb _REBfP(RKx8H&`۟q.3d8dゐR`opܮYx@?@_‡29GR8.ꋩ~y,#@,Fwc NTfܘMDH%˵EJ[2w& IX`MFH0ᅄe"4\ȣ I(*CQ1G4r  !`En qaM0 Da,X)aj$Q&J(aɅmq t x LIMBn dH%^EIM2[x(P% `#īaŒ-[V;lf#fx D&$!È (F+u&SdmFp`4 H!L]҇4D3դae점H-iO \u! P 0@4~ OQ(KePPma(B, AS ĆO*!M[1G @C\@וQl!maWuACK{` p * X#pF( Zj%x!etq.f"*G=DH690c 6 1$TJm1LB),Xd@Y]8ߠp!řЄ! bp`p wHJAcI&1SkM4 WGm! Fk VJuCeH`,${He(R v[Q0%T< !Hc-+DDˎl@@BMaC>AAd\.pUfǫw*J#JF VIbP 0U Pba@ YT ).HCbUXD@QOO[@#P% P0 $`@d&5KMNwQ b L3 k.5 %0Xk4"AτP7=a  ng %nX#[Pϝ X 6>O2h Jpd~Լ@ H Q j!Hssx))Lo dE Ӯ Da{Swo<Cl1̡ BD|`l`Y j(ív0&0D.p ^ON:J p$\-C1 rPd +QS S &'B0@}cS@`@`H+Х L@Zf*b le$, j@`t P2iX #DJ4 (…{= "#gЄhxI@ ^`P8sAb+A8Cq!?7{DUAg郘0@I%oSr5 ÚjYYᬆrCd Bj (T$5I53L곚 Ӕp`l: U-"p8`nwɠE#2 Mq]H`(e  YJ46^J(:uadOcM({%e[cv JLۄ|dRxU!L\ a jP@hd諸E< o#>_<x;ocaml-book-1.0/fr/html/book-ora075.html0000644000000000000000000000642107421273601014476 0ustar Dcoupage et utilisation des bibliothques Prcdent Index Suivant

Dcoupage et utilisation des bibliothques

On dcoupe en trois parties l'ensemble des bibliothques de la distribution d'Objective CAML. La premire contient les dclarations globales prcharges. La deuxime, appele bibliothque standard, possde un bon degr de stabilit. Elle est subdivise en quatre parties  :
  • structures de donnes
  • entres/sorties
  • interface avec le systme
  • analyses lexicale et syntaxique
Enfin viennent les bibliothques du troisime groupe qui apportent le plus souvent une extension au langage comme la bibliothque Graphics (voir chapitre 5). Dans ce dernier ensemble, sont regroupes des bibliothques portant sur les aspects suivants : expressions rgulires (Str), arithmtique exacte (Num), appels au systme Unix (Unix), processus lgers (Threads) et chargement dynamique de code-octet (Dynlink).

Les entres-sorties et l'interface avec le systme de la bibliothque standard sont compatibles sur diffrents systmes d'exploitation comme Unix, Windows et MacOS. Ceci n'est pas toujours le cas des bibliothques du troisime groupe. Il existe par ailleurs de nombreuses bibliothques distribues de manire indpendante de la distribution Objective CAML.

Usage et nommage
Pour utiliser une dclaration globale f d'un module simple de nom de fichier nom.ml et apparaissant dans le fichier nom.mli s'il existe, on la qualifie par Nom.f. Pour viter cela, il est possible d'ouvrir la bibliothque pour nommer directement f sans le faire prcder par le nom de sa bibliothque.

Syntaxe


open Nom


partir de l, toutes les dclarations globales de la bibliothque Nom seront considres comme appartenant l'environnement global. Si deux dclarations portent le mme nom dans deux bibliothques distinctes ouvertes, alors seule la dernire dclaration est visible. Pour pouvoir appeler la premire, il sera ncessaire d'utiliser la notation pointe pour faire rfrence celle-ci.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora048.html0000644000000000000000000000673007421273601014501 0ustar Notions de base Prcdent Index Suivant

Notions de base

La programmation graphique est intimement lie l'volution technologique du matriel, en particulier celle des crans et des cartes graphiques. Pour que le rendu d'une image soit d'une qualit suffisante, il est ncessaire que le dessin soit rafrachi (redessin) intervalles rguliers et courts, un peu comme au cinma. Il y a principalement deux techniques pour dessiner l'cran : soit partir d'une liste de segments visibles (cran cavalier) o seule la partie utile du dessin est dessine, soit en affichant tous les points de l'cran (cran bitmap). C'est cette dernire technologie qui est utilise sur les micro-ordinateurs du march.

Les crans bitmap peuvent tre vus comme un rectangle de points accessibles, c'est dire affichables. On appelle ces points des pixels, pour picture element, en anglais. Ce sont les lments de base pour construire des images. On appelle dfinition d'un cran la hauteur et la largeur du bitmap principal. La taille de ce bitmap dpend donc de la taille mmoire de chaque pixel. En noir et blanc, un pixel peut tre cod par un bit. Pour les crans niveau de gris ou en couleur, la taille d'un pixel dpend alors du nombre de teintes diffrentes pouvant tre donnes un pixel. Pour un bitmap de 320x640 pixels avec 256 couleurs par pixel, il sera donc ncessaire de coder un pixel sur 8 bits, ce qui demandera une mmoire vido de : 480 * 640 octets = 307200 octets ~ 300 ko. Cette rsolution est encore utilise par certains programmes MS-DOS.

Les oprations de base sur un bitmap que l'on retrouve dans la bibliothque Graphics sont :
  • la coloration d'un pixel,
  • le trac d'un segment,
  • le trac d'une forme : rectangle, ellipse,
  • le remplissage d'une forme ferme : rectangle, ellipse, polygone,
  • l'affichage d'un texte : bitmap ou vectoriel,
  • la manipulation ou le dplacement d'une partie de l'image.
Toutes ces oprations s'effectuent dans un repre, celui du bitmap. Un certain nombre de caractristiques des oprations graphiques, comme la largeur du trait, la jointure des lignes, le choix de la police de caractres, le style et le motif de remplissage dfinissent ce que l'on appelle un contexte graphique. Une opration graphique s'effectue toujours dans un contexte graphique particulier et son rsultat en dpend. Le contexte graphique de la bibliothque Graphics ne contient que le point courant, la couleur courante, la police courante et sa taille.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora023.gif0000644000000000000000000001272507073350152014274 0ustar GIF89a YUUU!, Yڋ޼H>@wGf19_xW9ʿS[0r,jD )Nod[۴Uu_ZxWkb tNAxj' Hxgض$6ey9ɈI:F(ʉ*ɪ*+SzuZ 9{ uW]{΍l_.vV `Sq=ڼ}a1-Vdq&Cy1"L *=4i廁9uL?1iP DAA >zUOYiTVSmjڞk&E{÷2EoבnWo[}VݙWm`ĉܘm\&paseɗ~rjͫ;~`gg\vnһCf]kQvoOf~tpþzvo>]xeOzx6E: }Z~tWv퉗q r7 yz}Yx!1 :יx"}!_́ע#"5ꗢ@9ڴ!@ǡwX7H$ `*lY.Lfi.z R"(d2!ddYIGF%:&wڹfo'pgT*Y=vifa 蟂蕆cxh Z%N驦楃jjxZfbj%UΙ\Հj+k> ߠX:몣>KꙧZ{Ίo2k@{f[-ڊ'ˎm Nk/fjk.[kL+"lrR*,qK16qn,0\.v{誼W1$[L3 3HR ;l?'[t-<1!G#A6C-VtQi?g[2%-wMs:2_-8$=xkk\w6~5gKxrCyFvdk߈9ުa-頻wO>'> osjg}=m9=͇~~~򺫿~e{Mǧ}G?M|s> $ H˟@=xERKUBipP\8rHfP- # H>!DPRb8xUS="c E\P4Qdk_Cr4QX!=ҋ]dF2Z;(3>>  HFZAT'|t$)H>?BP!iS2*{BÊIҌd LҘ%2 @e0*o6sE^Sv$&g9Msr3{c'€N^ '%OB~? O-Bs (@SdhM\P64_A N~է-+OùLJY*m(59ἩKsLue LVTIBԚr!{Uyӫ hWłV7tcT*T.Uj}jZϺV"խ4kE}╬--+IE׍UB5i` 8S{DQsk]jvlciY~ְ*ÒK,:ك2vUhWڒL=[.\D+o_( 6-pEZF!nrc͐ wݺ3Vָsu{d{+7@VWeiK^VF}$/˥Lwut=iB,;ҷ` r[ "pa I4GrmfG0ߜA9Ȅqd):pf :XȂw%Ж׼eg:guTzլf-Z XXg\y3'zv뵧f;Ծle`wzϟ6vHG洢mD/{ۧ}iN?o۶]I\kO\N[mxk۶ʆ6n{ػn/nnSSZ} uKn#mõiEO.C\6W<(O%~_E9p5;89Хs~gLts{hE^fm|7:w蜷fi'/u+;1r.z.=5-uAϞBu݉ڗ5?=|ӫ<__|~e{|w{N|'|׀(2"|IV~s(lgQqg~zC} ~x({y!&+.84؂3(ã@BTvė719XDFGǂJH8PBvEugHMx| Ho o* tN,IBqǘ3NIĔ8̂b,֓:`LJa0E]1B6Ԗ"Ȏނ7Qv;d8:"C;^V4E)K6SDZ—2,_mIrEIyR{)}$,Pj4MD;0?Q>qY ИE.R/4J"Iʩ3)yi)!,umIٜI}baYXI ډ-"G:ӛ?>1t(5KI ىi MY(9w9ZiH HiAt8<])4z9J٠2O )xhء(ڍD)9ɀ1:i'ih59C٣&:3ڗNY7z،DiG?MY,ڢ)$H  Yf*dzb*\#sJujwsKyʧ} *WkJʨꨏ *Jjʩ꩟ *Jjʪꪯ *Jjʫ꫿ *JjNJɪʬ *Jj׊٪ʭ *Jj犮骮ʮm*jʯ +6k ˰  +Kk˱ !+#K%k')+˲-/ 1+3K5k79;˳=? A+CKEkGIK۲=XAiIBt PHUWa{`ZۋJ#vcfKm[r 눈-Ij*^n۷|Z+Z{Z|t링JP7ɵvڸ%;[lzXeV۹;l[ [b]ƹk빷K ʺ*gkWz`K˽xۼzNR۽+;zWJٻ+ +{K+z˾w ۾y;;ᛥ!&0 ‚;+;\)|;< \ ; Z\"LΚđK\ :LPQJLd4U$'SNŮO\4G|k p,x}.-A~n!II.!׹A%nخsin.]Gй-YB2%>ڶ:y%ڔމ|ٟd=B..%G9n:넙nX>*_,M<.EvK="  Requtes sur une base de donnes Prcdent Index Suivant

Requtes sur une base de donnes

La ralisation d'une base de donnes, de son interface et de son langage de requtes est un projet trop ambitieux pour le cadre de ce livre et pour les connaissances en Objective CAML du lecteur rendu ce point. Pourtant, en restreignant le problme et en profitant au mieux des possibilits offertes par la programmation fonctionnelle, nous allons pouvoir raliser un outil intressant pour le traitement des requtes. Nous verrons, en particulier, comment utiliser les itrateurs ainsi que l'application partielle pour formuler et excuter des requtes. Nous montrerons galement l'utilisation d'un type de donnes encapsulant des valeurs fonctionnelles.

Tout au long de cette application, nous utiliserons en guise d'exemple une base de donnes rassemblant les informations sur les adhrents d'une association. Elle est suppose tre contenue dans le fichier association.dat.

Format des donnes

La plupart des logiciels classiques de base de donnes utilisent un format dit << propritaire >> pour stocker les informations qu'ils manipulent. Cependant, il est gnralement possible de les sauver sous un format texte auquel on peut donner la forme suivante :
  • la base est une suite de fiches spares par des retour-chariot ;
  • chaque fiche est une suite de champs spars par un caractre quelconque que nous supposerons ici tre le caractre ':' ;
  • un champ est une chane de caractres qui ne contient ni retour-chariot, ni le caractre ':' ;
  • la premire fiche est la liste des noms associs aux champs spars par '|'.
Le fichier de l'association dbute par :
Num|Nom|Prenom|Adresse|Tel|Email|Pref|Date|Montant
0:Chailloux:Emmanuel:Universit P6:0144274427:ec@lip6.fr:mail:25.12.1998:100.00
1:Manoury:Pascal:Laboratoire PPS::pm@lip6.fr:adr:03.03.1997:150.00
2:Pagano:Bruno:Cristal:0139633963::adr:25.12.1998:150.00
3:Baro:Sylvain::0144274427:baro@pps.fr:mail:01.03.1999:50.00
La signification des champs est la suivante :
  • Num est le numro d'adhrent ;
  • Nom, Prenom, Adresse, Tel et Email parlent d'eux-mmes ;
  • Pref indique comment l'adhrent dsire recevoir les informations : par courrier postal (adr), par courrier lectronique (mail) ou par tlphone (tel) ;
  • Date et Montant sont respectivement la date et le montant de la dernire cotisation perue.
Il nous faut ds prsent choisir la reprsentation que le programme adoptera pour stocker une base de donnes. Une alternative s'offre : soit une liste de fiches, soit un tableau de fiches. La liste prsente l'avantage d'tre aisment modifiable : l'ajout et la suppression d'une fiche sont des oprations simples. Le tableau, pour sa part, offre un accs en temps constant n'importe quelle fiche. Comme notre but est de travailler sur l'ensemble des fiches et non pas sur certaines en particulier, chaque requte devra traiter la totalit des fiches. On peut donc choisir une structure de liste. La mme alternative se pose pour les fiches : listes ou tableaux de chanes de caractres ? Cette fois-ci, la rponse est inverse car, d'une part le format d'une fiche est fig pour toute la base, il n'est donc pas question de rajouter de nouveaux champs ; d'autre part, selon les traitements envisags, seuls certains champs seront utiles, il est donc important d'y accder rapidement.

La solution la plus naturelle pour une fiche serait d'avoir recours un tableau index par le nom des champs. Un tel type n'tant pas possible en Objective CAML, nous allons le remplacer par un tableau classique (index par des entiers) et une fonction associant un champ, l'indice du tableau lui correspondant.

# type data_card = string array ;;
# type data_base = { card_index : string -> int ; data : data_card list } ;;


L'accs au champ de nom n d'une fiche dc de la base db est ralis par la fonction :

# let field db n (dc:data_card) = dc.(db.card_index n) ;;
val field : data_base -> string -> data_card -> string = <fun>
Le type de dc a t forc data_card pour contraindre la fonction field n'accepter que des tableaux de chanes de caractres et non des tableaux quelconques.

Voici un petit exemple en guise d'illustration :

# let base_ex =
{ data = [ [|"Chailloux"; "Emmanuel"|] ; [|"Manoury";"Pascal"|] ] ;
card_index = function "Nom"->0 | "Prenom"->1 | _->raise Not_found } ;;
val base_ex : data_base =
{card_index=<fun>;
data=[[|"Chailloux"; "Emmanuel"|]; [|"Manoury"; "Pascal"|]]}
# List.map (field base_ex "Nom") base_ex.data ;;
- : string list = ["Chailloux"; "Manoury"]


L'expression field base_ex "Nom" s'value comme une fonction qui prend une fiche et renvoie son champ "Nom". Par l'utilisation de List.map, cette fonction est excute sur chacune des fiches de la base base_ex et on obtient la liste des rsultats : soit la liste des champs "Nom" de la base.

Cet exemple nous permet de voir comment nous souhaitons utiliser la fonctionnalit dans notre programme. Ici, l'application partielle de field nous permet de dfinir une fonction d'accs un champ prcis, utilisable sur un nombre quelconque de fiches. Cela nous permet aussi de voir que notre implantation de cette mme fonction field souffre d'un lger dfaut car, alors que nous accdons toujours au mme champ, son indice est tout de mme recalcul chaque fois. Pour cette raison, nous lui prfrons l'implantation suivante :

# let field base name =
let i = base.card_index name in fun (card:data_card) -> card.(i) ;;
val field : data_base -> string -> data_card -> string = <fun>
Ici, aprs application de deux arguments, l'indice du champ est effectivement calcul et sert pour toutes les applications ultrieures.

Lecture d'une base depuis un fichier

Depuis Objective CAML, un fichier contenant une base est une suite de lignes. Notre travail va consister lire chacune d'entre elles comme une chane de caractres puis la dcouper en reprant les sparateurs et construire d'une part les donnes et d'autre part la fonction d'indexation des champs.

Utilitaires de traitement de ligne

Nous nous dotons d'une fonction split qui dcoupe une chane de caractres selon un caractre sparateur. Cette fonction utilise la fonction suffix retournant le suffixe d'une chane s partir d'une position i. On utilise pour cela trois fonctions prdfinies :
  • String.length donne la longueur d'une chane;
  • String.sub retourne la sous-chane de s partir de la position i de longueur l;
  • String.index_from calcule dans la chane s, partir de la position n, la position de la premire occurrence du caractre c.
# let suffix s i = try String.sub s i ((String.length s)-i) 
with Invalid_argument("String.sub") -> "" ;;
val suffix : string -> int -> string = <fun>
# let split c s =
let rec split_from n =
try let p = String.index_from s n c
in (String.sub s n (p-n)) :: (split_from (p+1))
with Not_found -> [ suffix s n ]
in if s="" then [] else split_from 0 ;;
val split : char -> string -> string list = <fun>


La seule chose noter dans l'implantation de ces fonctions est la gestion des exceptions, en particulier, l'exception Not_found.

Calcul de la structure data_base
Obtenir un tableau de chanes partir d'une liste ne pose pas de problme : le module Array fournit la fonction ncessaire (of_list). Le calcul de la fonction d'indice partir d'une liste de noms de champs pourrait paratre plus compliqu mais le module List fournit tous les outils dont nous avons besoin.

Nous partons d'une liste de chanes et nous devons obtenir une fonction qui une chane associe un indice qui correspond sa place dans la liste.

# let mk_index list_names =
let rec make_enum a b = if a > b then [] else a::(make_enum (a+1) b) in
let list_index = (make_enum 0 ((List.length list_names) - 1)) in
let assoc_index_name = List.combine list_names list_index in
function name -> List.assoc name assoc_index_name ;;
val mk_index : 'a list -> 'a -> int = <fun>
Pour obtenir la fonction d'association entre noms de champ et indices, nous construisons la liste des indices que nous combinons avec la liste de noms. Nous obtenons une liste d'association de type string * int list. Pour chercher l'indice associ un nom nous avons recours la fonction assoc de la bibliothque List qui est ddie cette tche. Le rsultat de make_index est une fonction qui prend un nom et appelle assoc avec ce nom sur la liste d'association prcdemment construite.

Nous pouvons maintenant dfinir la fonction de lecture des fichiers contenant une base au format impos.

# let read_base name_file =
let channel = open_in name_file in
let split_line = split ':' in
let list_names = split '|' (input_line channel) in
let rec read_file () =
try
let data = Array.of_list (split_line (input_line channel )) in
data :: (read_file ())
with End_of_file -> close_in channel ; []
in
{ card_index = mk_index list_names ; data = read_file () } ;;
val read_base : string -> data_base = <fun>
La lecture des enregistrements du fichier est ralise par la fonction auxiliaire read_file qui opre rcursivement sur le canal d'entre. Le cas de base de la rcurrence correspond la fin du fichier signale par l'exception End_of_file. Notons que nous profitons de ce cas retournant la liste vide pour refermer le canal.

Nous pouvons maintenant charger le fichier de notre association :

# let base_ex = read_base "association.dat" ;;
val base_ex : data_base =
{card_index=<fun>;
data=
[[|"0"; "Chailloux"; "Emmanuel"; "Universit\233 P6"; "0144274427";
"ec@lip6.fr"; "mail"; "25.12.1998"; "100.00"|];
[|"1"; "Manoury"; "Pascal"; "Laboratoire PPS"; ...|]; ...]}


Principes gnraux des traitements

La richesse et la complexit du traitement de l'ensemble des donnes d'une base sont proportionnelles la richesse et la complexit du langage de requtes utilis. Puisqu'ici, notre volont est d'utiliser Objective CAML comme langage de requtes, il n'y a a priori pas de limite l'expression des requtes ! Cependant, nous souhaitons galement fournir quelques outils simples de manipulation des fiches et de leurs donnes. Pour obtenir cette simplicit, il nous faut ncessairement limiter la trop grande richesse du langage Objective CAML en posant quelques objectifs et principes gnraux de traitement.

L'objectif d'un traitement de donnes est d'obtenir ce que l'on appelle un tat de la base. On peut dcomposer la construction d'un tat en trois tapes :
  1. slection, selon un critre donn, d'un ensemble des fiches ;
  2. traitement individuel de chacune des fiches slectionnes ;
  3. traitement de l'ensemble des donnes collectes sur chacune des fiches.
Cette dcomposition est illustre la figure 6.1.


Figure 6.1 : dcomposition d'une requte


Suivant cette dcomposition, nous avons besoin de trois fonctions possdant les types suivants :
  1. (data_card -> bool) -> data_card list -> data_card list
  2. (data_card -> 'a) -> data_card list -> 'a list
  3. ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b
Objective CAML nous fournit trois fonctions d'ordre suprieur, ou itrateurs, prsents la page ??, qui rpondent notre spcification :

# List.find_all ;;
- : ('a -> bool) -> 'a list -> 'a list = <fun>
# List.map ;;
- : ('a -> 'b) -> 'a list -> 'b list = <fun>
# List.fold_right ;;
- : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b = <fun>
Nous pourrons les utiliser pour raliser les trois tapes de construction d'un tat lorsque nous aurons fix la valeur de leur argument fonctionnel.

Pour certaines requtes particulires, on utilisera galement :

# List.iter ;;
- : ('a -> unit) -> 'a list -> unit = <fun>
En effet, si le traitement d'un ensemble d'informations se limite leur affichage, il n'y a pas rellement de valeur calculer.

Nous allons, dans les paragraphes suivants, voir comment dfinir des fonctions exprimant les critres de slection ainsi que quelques traitements simples. Nous conclurons ce chapitre par un petit exemple mettant en oeuvre ces fonctions selon le principe nonc ci-dessus.

Critres de slection

Dans la pratique, la fonction boolenne qui exprime le critre de slection d'une fiche est obtenue par combinaison boolenne de proprits donnes sur tout ou partie des champs de la fiche. Chaque champ d'une fiche, quoique donn comme une chane de caractres, peut tre porteur d'une information d'un autre type : un flottant, une date, etc.

Critres de slection sur un champ

La composante d'un critre de slection concernant un champ particulier sera, en gnral, obtenue partir d'une fonction de type data_base -> 'a -> string -> data_card -> bool. Le paramtre de type 'a correspond au type de l'information contenue dans le champ. L'argument de type string correspond au nom du champ.

Informations de type chanes de caractres
Nous dfinissons deux tests simples sur ces types de champ : l'galit avec une autre chane et la non vacuit.

# let eq_sfield db s n dc = (s = (field db n dc)) ;;
val eq_sfield : data_base -> string -> string -> data_card -> bool = <fun>
# let nonempty_sfield db n dc = ("" <> (field db n dc)) ;;
val nonempty_sfield : data_base -> string -> data_card -> bool = <fun>


Informations de type flottant
Pour raliser des tests sur des informations de type flottant, il suffit simplement d'oprer la conversion d'une valeur de type string reprsentant un nombre dcimal en une valeur de type float. Voici quelques exemples obtenus partir d'une fonction gnrique tst_ffield :

# let tst_ffield r db v n dc = r v (float_of_string (field db n dc)) ;;
val tst_ffield :
('a -> float -> 'b) -> data_base -> 'a -> string -> data_card -> 'b = <fun>
# let eq_ffield = tst_ffield (=) ;;
# let lt_ffield = tst_ffield (<) ;;
# let le_ffield = tst_ffield (<=) ;;
(* etc. *)
Ces trois fonctions sont de type :

data_base -> float -> string -> data_card -> bool.

Les dates
Ce type d'information est un peu plus complexe traiter. Il dpend de la reprsentation de la date dans la base et rclame de dfinir un codage des comparaisons de dates.

Nous fixons qu'une date est reprsente dans une fiche par une chane de caractres au format jj.mm.aaaa. Pour les besoins des comparaisons que nous souhaitons obtenir,
nous enrichissons ce format en autorisant le remplacement du jour, du mois ou de l'anne par le caractre soulign ('_'). Les dates sont compares selon l'ordre lexicographique sur des triplets d'entiers de la forme (anne, mois, jour). Pour pouvoir exprimer des requtes telles : << est antrieure juillet 1998 >>, on utilisera le motif de date : "_.07.1998". La comparaison d'une date avec un motif sera ralise par la fonction tst_dfield qui analysera le motif pour en extraire la fonction de comparaison ad hoc. La dfinition de cette fonction gnrique de test sur les dates ncessite un certain nombre de fonctions auxiliaires.

On se donne deux fonctions de conversion de dates (ints_of_string) et de motifs de dates (ints_of_dpat) en triplets d'entiers. Le caractre '_' d'un motif sera remplac par l'entier 0 :

# let split_date = split '.' ;;
val split_date : string -> string list = <fun>
# let ints_of_string d =
try match split_date d with
[j;m;a] -> [int_of_string a; int_of_string m; int_of_string j]
| _ -> failwith "Bad date format"
with Failure("int_of_string") -> failwith "Bad date format" ;;
val ints_of_string : string -> int list = <fun>

# let ints_of_dpat d =
let int_of_stringpat = function "_" -> 0 | s -> int_of_string s
in try match split_date d with
[j;m;a] -> [ int_of_stringpat a; int_of_stringpat m;
int_of_stringpat j ]
| _ -> failwith "Bad date format"
with Failure("int_of_string") -> failwith "Bad date pattern" ;;
val ints_of_dpat : string -> int list = <fun>


tant donne une relation r sur les entiers, on crit la fonction d'application du test. C'est un codage de l'ordre lexicographique prenant en compte la valeur particulire 0 que l'on ignore :

# let rec app_dtst r d1 d2 = match d1, d2 with
[] , [] -> false
| (0::d1) , (_::d2) -> app_dtst r d1 d2
| (n1::d1) , (n2::d2) -> (r n1 n2) || ((n1 = n2) && (app_dtst r d1 d2))
| _, _ -> failwith "Bad date pattern or format" ;;
val app_dtst : (int -> int -> bool) -> int list -> int list -> bool = <fun>


On dfinit enfin la fonction gnrique tst_dfield qui prend comme arguments une relation r, une base de donnes db, un motif dp, un nom de champ nm et une fiche dc. Cette fonction vrifie que le motif et le champ extrait de la fiche satisfont la relation.

# let tst_dfield r db dp nm dc =
r (ints_of_dpat dp) (ints_of_string (field db nm dc)) ;;
val tst_dfield :
(int list -> int list -> 'a) ->
data_base -> string -> string -> data_card -> 'a = <fun>


On l'applique alors trois relations.

# let eq_dfield = tst_dfield (=) ;;
# let le_dfield = tst_dfield (<=) ;;
# let ge_dfield = tst_dfield (>=) ;;
Ces trois fonctions sont de type :
data_base -> string -> string -> data_card -> bool.

Composition de critres

Les tests que nous avons dfinis ci-dessus ont pour trois premiers arguments une base de donnes, une valeur et le nom d'un champ. Lorsque nous formulons des requtes particulires, les valeurs de ces trois arguments sont connues. Lorsque l'on travaille sur la base base_ex, le test << est antrieure juillet 1998 >> s'crit

# ge_dfield base_ex "_.07.1998" "Date" ;;
- : data_card -> bool = <fun>


En pratique, un test est donc une fonction de type data_card -> bool. Nous voulons obtenir des combinaisons boolennes des rsultats de telles fonctions appliques une mme fiche. Nous nous donnons cette fin l'itrateur  ;

# let fold_funs b c fs dc =
List.fold_right (fun f -> fun r -> c (f dc) r) fs b ;;
val fold_funs : 'a -> ('b -> 'a -> 'a) -> ('c -> 'b) list -> 'c -> 'a = <fun>
O b est la valeur de base, la fonction c sera le connecteur boolen, fs la liste des fonctions de test sur un champ et dc une fiche.

On obtient la conjonction et la disjonction d'une liste de tests par :

# let and_fold fs = fold_funs true (&) fs ;;
val and_fold : ('a -> bool) list -> 'a -> bool = <fun>
# let or_fold fs = fold_funs false (or) fs ;;
val or_fold : ('a -> bool) list -> 'a -> bool = <fun>


Par commodit, on dfinit galement la ngation d'un test :

# let not_fun f dc = not (f dc) ;;
val not_fun : ('a -> bool) -> 'a -> bool = <fun>


On peut, par exemple, utiliser ces combinateurs pour dfinir une fonction de slection d'une fiche dont le champ date est compris dans un intervalle donn :

# let date_interval db d1 d2 =
and_fold [(le_dfield db d1 "Date"); (ge_dfield db d2 "Date")] ;;
val date_interval : data_base -> string -> string -> data_card -> bool =
<fun>


Traitements et calculs

Il est difficile de prvoir ce que peut tre a priori le traitement d'une fiche ou de l'ensemble des donnes issues de ce traitement. Nanmoins, on peut considrer deux grandes classes de tels traitements : un calcul numrique ou le formatage de donnes pour impression. Prenons un exemple de chacun des cas envisags.

Formatage

On dsire prparer pour l'impression une ligne contenant l'identit d'un adhrent suivie d'un certain nombre d'informations.

On commence par se donner l'opration inverse du dcoupage d'une chane selon un caractre donn :

# let format_list c =
let s = String.make 1 c in
List.fold_left (fun x y -> if x="" then y else x^s^y) "" ;;
val format_list : char -> string list -> string = <fun>


Afin de construire la liste des champs d'information, on se donne la fonction extract qui extrait d'une fiche un ensemble de champs dfinis par une liste de noms :

# let extract db ns dc =
List.map (fun n -> field db n dc) ns ;;
val extract : data_base -> string list -> data_card -> string list = <fun>


On crit enfin la fonction de formatage d'une ligne :

# let format_line db ns dc =
(String.uppercase (field db "Nom" dc))
^" "^(field db "Prenom" dc)
^"\t"^(format_list '\t' (extract db ns dc))
^"\n" ;;
val format_line : data_base -> string list -> data_card -> string = <fun>
L'argument ns est la liste des champs d'information dsirs. Les champs d'information sont spars par le caractre de tabulation ('\t') et la ligne se termine par un retour-chariot.

On obtient l'affichage de la liste des noms et prnoms des adhrents par :

# List.iter print_string (List.map (format_line base_ex []) base_ex.data) ;;
CHAILLOUX Emmanuel
MANOURY Pascal
PAGANO Bruno
BARO Sylvain
- : unit = ()


Calcul numrique

On veut calculer la somme des cotisations perues pour un ensemble de fiches. On obtient facilement ce chiffre en composant l'extraction et la conversion du champ correspondant avec une fonction d'addition. Pour allger l'criture, on dfinit un oprateur infixe de composition :

# let (++) f g x = g (f x) ;;
val ++ : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c = <fun>
On utilise cet oprateur de composition dans la dfinition suivante :

# let total db dcs =
List.fold_right ((field db "Montant") ++ float_of_string ++ (+.)) dcs 0.0 ;;
val total : data_base -> data_card list -> float = <fun>
On peut l'appliquer la totalit de la base :

# total base_ex base_ex.data ;;
- : float = 450


Un exemple

Nous donnons, pour conclure ce chapitre un petit exemple d'application des principes exposs dans les paragraphes prcdents.

Nous envisageons deux types de requtes sur notre base :
  • l'dition de deux listes contenant chacune l'identit de l'adhrent, puis, selon ses prfrences, son adresse postale ou son adresse lectronique.
  • l'dition d'un tat des cotisations entre deux dates donnes. Cet tat contiendra la liste des nom, prnom, date et montant des cotisations ainsi que le total de ces dernires.

Listes d'adresses

Pour obtenir ces listes, on slectionne d'abord les fiches pertinentes selon la valeur du champ "Pref" puis on utilise la fonction de formatage format_line :

# let adresses_postales db =
let dcs = List.find_all (eq_sfield db "adr" "Pref") db.data in
List.map (format_line db ["Adresse"]) dcs ;;
val adresses_postales : data_base -> string list = <fun>

# let adresses_electroniques db =
let dcs = List.find_all (eq_sfield db "mail" "Pref") db.data in
List.map (format_line db ["Email"]) dcs ;;
val adresses_electroniques : data_base -> string list = <fun>


tat des cotisations

Le calcul de l'tat des cotisations procde toujours selon le mme esprit : slection puis traitement. Mais le traitement est ddoubl en formatage des lignes puis calcul du total.

# let etat_cotisations db d1 d2 =
let dcs = List.find_all (date_interval db d1 d2) db.data in
let ls = List.map (format_line db ["Date";"Montant"]) dcs in
let t = total db dcs in
ls, t ;;
val etat_cotisations : data_base -> string -> string -> string list * float =
<fun>
Le rsultat de cette requte est un couple contenant la liste des chanes d'information et le montant cumul des cotisations.

Programme principal

Le programme principal de l'application est essentiellement une boucle d'interaction avec l'utilisateur qui affiche le rsultat de la requte demande par menu. On y retrouve naturellement un style de programmation impratif, mme si l'affichage des rsultats fait appel un itrateur.

# let main() =
let db = read_base "association.dat" in
let fin = ref false in
while not !fin do
print_string" 1: liste des adresses postales\n";
print_string" 2: liste des adresses lectroniques\n";
print_string" 3: cotisations\n";
print_string" 0 sortie\n";
print_string"Votre choix : ";
match read_int() with
0 -> fin := true
| 1 -> (List.iter print_string (adresses_postales db))
| 2 -> (List.iter print_string (adresses_electroniques db))
| 3
-> (let d1 = print_string"Date de dbut : "; read_line() in
let d2 = print_string"Date de fin : "; read_line() in
let ls, t = etat_cotisations db d1 d2 in
List.iter print_string ls;
print_string"Total : "; print_float t; print_newline())
| _ -> ()
done;
print_string"bye\n" ;;
val main : unit -> unit = <fun>


Cet exemple sera repris au chapitre 21 pour le munir d'une interface utilisant un navigateur WEB.

Pour en faire plus

Une extension naturelle de notre exemple serait de munir la base d'une indication de type associe chaque champ de la base. Cette information pourrait alors tre exploite pour dfinir des oprateurs de comparaison gnraux de type data_base -> 'a -> string -> data_card -> bool o le nom du champ (troisime argument) permettrait d'aiguiller sur les bonnes fonctions de conversion et de test.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora186.html0000644000000000000000000003010007421273602014471 0ustar Rseau Internet Prcdent Index Suivant

Rseau Internet

Internet est un rseau de rseaux (Inter Networking). L'interconnexion est hirarchise en domaines, sous-domaines, etc. jusqu'aux interfaces. Une interface est l'quipement matriel d'une machine permettant sa connexion (typiquement : une carte ethernet). Certaines machines peuvent possder plusieurs interfaces. Chaque interface possde une adresse IP unique qui respecte, en gnral, la hirarchie d'interconnexion. L'acheminement, ou routage des messages peut ainsi tre lui-mme hirarchis : de domaine domaine ; puis de domaine sous domaines, etc. jusqu' l'interface ultime. Outre l'adresse de leur(s) interface(s), les machines ont la plupart du temps un nom, de mme que les domaines, sous-domaines, etc.
Certaines machines ont dans ce mta rseau un rle particulier :
les passerelles
oprent le passage d'un rseau un autre ;
les routeurs
connaissent, en partie, la topologie d'Internet et oprent l'acheminement des donnes ;
les serveurs de noms
connaissent la correspondance entre noms de machines et adresses rseau.
L'intrt d'Internet (i.e. : du protocole IP) est que le rseau de rseaux devient une seule entit. C'est pourquoi on peut parler du rseau Internet. Deux machines quelconques connectes sur Internet peuvent communiquer. Diffrents types de machines et de systmes cohabitent sur Internet. Elles parlent toutes les protocoles IP et, majoritairement, ses surcouches UDP et TCP.

Les diffrents protocoles et services du rseau Internet sont dcrits dans les RFC (Request For Comments), autrement dit <<appels commentaires>>, que l'on trouve sur le site miroir de Jussieu :

Lien


ftp://ftp.lip6.fr/pub/rfc


Protocoles et services Internet

L'unit de transfert du protocole IP est le datagramme ou paquet IP. C'est un protocole non fiable : il n'assure ni le bon ordre, ni le bon port, ni la non duplication des datagrammes transmis. Il traite juste le routage correct des paquets et la signalisation d'erreurs lorsqu'un datagramme n'a pu arriver destination. Un datagramme contient un en-tte et des donnes. Dans l'en-tte apparaissent les adresses du destinataire et de l'expditeur du datagramme. Les adresses sont codes sur 32 bits dans la version courante du protocole : IPv4. Ces 32 bits sont scinds en 4 champs comportant une valeur comprise entre 0 et 255. On note ces adresses sous forme de quatre entiers spars par un point comme par exemple : 132.227.60.30.

Le protocole IP connat l'heure actuelle une rforme importante rendue ncessaire par l'puisement de l'espace d'adressage et la complexit croissante des problmes de routage ds l'expansion d'Internet. La nouvelle version du protocole IP est IPv6 dont on peut trouver une prsentation dans [Ciz98].

Au dessus d'IP, deux protocoles permettent des transmissions de plus haut niveau : UDP (User Datagram Protocol) et TCP (Transfert Control Protocol). Ces deux protocoles qui utilisent IP pour la communication entre machines permettent en plus la communication entre des applications (ou programmes) tournant sur ces machines. Ils s'occupent de la transmission correcte de l'information indpendamment du contenu. L'identification des applications sur une machine est faite en utilisant un numro de port.

UDP est un protocole sans connexion et non fiable, il est aux applications ce qu'IP est aux interfaces. TCP est un protocole orient connexion et fiable : il gre l'acquittement, la retransmission et l'ordonnancement des paquets. De plus, il est capable d'optimiser la transmission par une technique de fentrage.

Les services standard (applications) d'Internet utilisent le plus souvent le modle client-serveur. Le serveur gre les requtes des clients qui il offre un service spcifique. Il y a une asymtrie entre le client et le serveur. Ces services implantent des protocoles de plus haut niveau tenant compte cette fois du contenu de l'information transmise. Parmi les services standard, on peut citer :
  • FTP (File Transfert Protocol)
  • TELNET (Terminal Transfert Protocol)
  • SMTP (Simple Mail Transfert Protocol)
  • HTTP (Hyper Text Transfert Protocol)
D'autres services utilisent ce modle client-serveur :
  • NFS (Network File System)
  • X-Windows
  • les services Unix : rlogin, rwho ...
La communication entre applications s'effectue via des prises de communication. Elles permettent la communication entre des processus ne rsidant pas forcment sur une mme machine. Diffrents processus peuvent lire et crire dans cette voie de communication.

Module Unix et adressage IP

Le module Unix fournit le type abstrait inet_addr reprsentant les adresses Internet ainsi que deux fonctions de conversion entre reprsentation interne des adresses et chanes de caractres :

# Unix.inet_addr_of_string ;;
- : string -> Unix.inet_addr = <fun>
# Unix.string_of_inet_addr ;;
- : Unix.inet_addr -> string = <fun>


Dans les applications, les adresses Internet et les numros de port de services (ou numros de services) sont souvent remplacs par des noms. La correspondance entre noms et adresse ou numro est gre partir de bases de donnes. Le module Unix fournit des fonctions de requte sur ces bases et les types de donnes permettant le stockage des informations obtenues. Nous les dcrivons brivement ci-dessous.

Base d'adresses
La base d'adresses (hosts database) contient l'association entre nom(s) de machine et adresse(s) d'interface(s). La structure des entres de la base d'adresses est reprsente par :

# type host_entry =
{ h_name : string;
h_aliases : string array;
h_addrtype : socket_domain;
h_addr_list : inet_addr array } ;;
Les deux premiers champs contiennent le nom de la machine et ses alias, le troisime, le type d'adresse (voir page ??) et le dernier, la liste des adresses des interfaces de la machine.

On obtient le nom de sa machine par la fonction :

# Unix.gethostname ;;
- : unit -> string = <fun>
# let my_name = Unix.gethostname() ;;
val my_name : string = "zinc.pps.jussieu.fr"


Les fonctions d'interrogation de la base d'adresses ncessitent en entre soit le nom, soit l'adresse de la machine.

# Unix.gethostbyname ;;
- : string -> Unix.host_entry = <fun>
# Unix.gethostbyaddr ;;
- : Unix.inet_addr -> Unix.host_entry = <fun>
# let my_entrie_byname = Unix.gethostbyname my_name ;;
val my_entrie_byname : Unix.host_entry =
{Unix.h_name="zinc.pps.jussieu.fr"; Unix.h_aliases=[|"zinc"; "zn"|];
Unix.h_addrtype=Unix.PF_INET; Unix.h_addr_list=[|<abstr>|]}
# let my_addr = my_entrie_byname.Unix.h_addr_list.(0) ;;
val my_addr : Unix.inet_addr = <abstr>

# let my_entrie_byaddr = Unix.gethostbyaddr my_addr ;;
val my_entrie_byaddr : Unix.host_entry =
{Unix.h_name="zinc.pps.jussieu.fr"; Unix.h_aliases=[|"zinc"; "zn"|];
Unix.h_addrtype=Unix.PF_INET; Unix.h_addr_list=[|<abstr>|]}

# let my_full_name = my_entrie_byaddr.Unix.h_name ;;
val my_full_name : string = "zinc.pps.jussieu.fr"
Ces fonctions dclenchent l'exception Not_found en cas d'chec de la requte.

Base de services
La base de services contient la correspondance entre noms de service et numros de port. La plupart des services d'Internet sont standardiss. La structure des entres de la base de services est :

# type service_entry =
{ s_name : string;
s_aliases : string array;
s_port : int;
s_proto : string } ;;
Les premiers champs sont le nom du service et ses ventuels alias, le troisime contient le numro de port du service et le dernier, le nom du protocole utilis.

Un service est en fait caractris par son numro de port et son protocole. Les fonctions d'interrogation sont :

# Unix.getservbyname ;;
- : string -> string -> Unix.service_entry = <fun>
# Unix.getservbyport ;;
- : int -> string -> Unix.service_entry = <fun>
# Unix.getservbyport 80 "tcp" ;;
- : Unix.service_entry =
{Unix.s_name="http"; Unix.s_aliases=[||]; Unix.s_port=80; Unix.s_proto="tcp"}
# Unix.getservbyname "ftp" "tcp" ;;
- : Unix.service_entry =
{Unix.s_name="ftp"; Unix.s_aliases=[||]; Unix.s_port=21; Unix.s_proto="tcp"}
Ces fonctions dclenchent l'exception Not_found si elles ne trouvent pas le service demand.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora040.gif0000644000000000000000000000611107073350152014263 0ustar GIF89a UUU!, ڋ޼HN° L׶ nĢL*1-@Npj.TN13k%E_0(8HX㶣5Vav'08g0c8y +;K[k{ ,z&2+TL|QL;,absN5nL{Х;4 OIjխX̨ױ^rK6mllb6tUk$J9Y'7CfCoZU6ܘFH)T86~)3V*aLaHِ՝[D\Q頳ۅ;'7ܖ6㒕R%Gr۸/D9{DKSwѫ[?@!/q2 ))h']| Y̓۱f{ 5l`f?F#1'9Ʋ8.ԣ^ogH(]NZc" ONIeV^eZn>Х8` !(@f eKiDpOd΋7&l9,Y6&T$Z( iv6JhZxx{i)] ʤZ$EIid|i*ZQk:AkΗ~[{NDzB]>!rr9_O@a ښ&/zh؆;gŠ&Wy M[u BS4,p7{> |vǏv< +1\|س[5)|ϒ,͸MR.3Jl)#ɉ ?_K-e}7Al;aWvcWlqdDƊ 1^p߅{7&z#!Ą>vgIZϾUmzPםN`6Аd[<w-q[\"w sHBwŬ.u©bwݮw)׸.y{󢗻]/s7Dž/};׾o~[ZxplF7`/g5*+ cFV;a8Q19+G* [ø72jjl871uB ##Y*J^2S@9;2 +?9ZʋY\slf29)2%73rs,89 tsaF!k1@ЈQ3F;d/wgҒtU7=i umIR jV~ae=[O&fu]k=׿vD[bAm 9L$WCۮXHlDflru܌ۭ xûv Uak魐a{4K~/Bie#/oGu)cc\vqn䱶I^lBF =T\Nsi~0AuQɒ ;IP+*RuLv?p k<7U`Y,+lpWFd҂ *Is,"{>tw )B۝07_잍C`t2D X[ȅMH[acH_hrIB ,; ~ev!kguRxqtX((!vn8htpP2nMDm #Cu؆RFh}Ȅp8 F]V8'X%iwX/ȌϸZ=0VRhA?v rX pW6s6ю"Tx3VُH ɐ  w`}\'PI M>*0;;IGc?UEzo&Yl!B|q2Y2&5E<9z&>EzC~II}DDeG>>^X;ocaml-book-1.0/fr/html/book-ora141.html0000644000000000000000000004263307421273602014476 0ustar Classes, objets et mthodes Prcdent Index Suivant

Classes, objets et mthodes

L'extension objet d'Objective CAML s'intgre au noyau fonctionnel et au noyau impratif du langage, mais aussi son systme de types. C'est ce dernier point qui en fait son originalit. On obtient ainsi un langage objet, typ statiquement, avec infrence de types. Cette extension permet de dfinir des classes et des instances, autorise l'hritage entre classes (y compris l'hritage multiple), accepte les classes paramtres et les classes abstraites. Les interfaces de classes sont engendres par leur dfinition mais peuvent tre prcises par une signature, l'instar de ce qui est fait pour les modules.

Terminologie objet

Nous dcrivons brivement dans ce paragraphe les principaux lments du glossaire de la programmation par objet.
classe
une classe est un ensemble agrg de champs de donnes (appels des variables d'instance) et de traitements (appels mthodes).
objet
un objet est un lment (ou instance) d'une classe. Un objet possde les comportements de la classe laquelle il appartient. L'objet est le composant effectif des programmes (c'est lui qui calcule) alors que la classe est plutt une dfinition ou une spcification pour l'ensemble des instances venir.
mthode
une mthode est une action qu'un objet est mme d'effectuer.
envoi de message
un envoi de message un objet est la demande faite ce dernier d'excuter une de ses mthodes. On pourra galement dire que l'on invoque une mthode.

Dclaration d'une classe

La syntaxe la plus simple pour dfinir une classe est la suivante. Nous enrichirons cette dfinition tout au long du chapitre.

Syntaxe


class nom p1 ...pn =
  object
      :
    variables d'instance
      :
    mthodes
      :
  end

p1, ..., pn sont les paramtres que prendra le constructeur de cette classe. Une classe peut n'avoir aucun paramtre.

Une variable d'instance se dclare par :

Syntaxe


val nom = expr
ou
val mutable nom = expr

Dans la mesure o un champ de donnes est dclar mutable, il est possible d'en modifier sa valeur. Dans le cas contraire, la valeur est celle calcule la cration de l'objet par l'valuation de expr.

Une mthode se dclare par :

Syntaxe


method nom p1 ...pn = expr


Il existe d'autres clauses que val et method dans la dclaration d'une classe. Nous les introduirons au fur et mesure des besoins.

Une premire classe
Commenons par l'invitable classe point; elle contient :
  • deux champs de donnes x et y contenant les coordonnes du point,
  • six mthodes :
    • deux mthodes d'accs aux champs de donnes (get_x et get_y),
    • deux procdures de dplacement absolu (moveto) et relatif (rmoveto),
    • une mthode de prsentation des donnes sous forme de string (to_string),
    • une mthode de calcul de la distance du point par rapport l'origine (distance).
# class point (x_init,y_init) =
object
val mutable x = x_init
val mutable y = y_init
method get_x = x
method get_y = y
method moveto (a,b) = x <- a ; y <- b
method rmoveto (dx,dy) = x <- x + dx ; y <- y + dy
method to_string () =
"( " ^ (string_of_int x) ^ ", " ^ (string_of_int y) ^")"
method distance () = sqrt (float(x*x + y*y))
end ;;
Remarquons que les mthodes peuvent ne pas avoir de paramtre. C'est le cas de get_x et de get_y. Nous prendrons comme convention de dfinir les accesseurs aux variables d'instance par des mthodes sans paramtre.

Aprs la dclaration de la classe point, le systme affiche le texte suivant :

class point :
int * int ->
object
val mutable x : int
val mutable y : int
method distance : unit -> float
method get_x : int
method get_y : int
method moveto : int * int -> unit
method rmoveto : int * int -> unit
method to_string : unit -> string
end


Ce texte contient deux informations. D'une part, un type pour les objets de cette classe. Ce type sera abrg par le nom point. Le type d'un objet est la liste des noms des mthodes de sa classe avec leur type. Dans notre exemple, point est une abrviation pour :
  
< distance : unit -> unit; get_x : int; get_y : int;
moveto : int * int -> unit; rmoveto : int * int -> unit;
to_string : unit -> unit >
D'autre part, un constructeur d'instances de la classe point, de type int*int -> oint, qui permet de construire un objet point partir des valeurs initiales fournies en argument. Ici, on construit un point partir d'un couple d'entiers (sa position initiale). La fonction de construction point sera utilise avec le mot rserv new.

Il est possible de dfinir des types de classe :

# type simple_point = < get_x : int; get_y : int; to_string : unit -> unit > ;;
type simple_point = < get_x : int; get_y : int; to_string : unit -> unit >


Remarque


Le type point ne reprend pas la totalit des informations affiches aprs la dclaration de la classe. Les variables d'instance n'apparaissent pas dans le type. On ne peut accder ces valeurs que par l'entremise d'une mthode.


Warning


Une dclaration de classe est considre comme une dclaration de type. Elle ne peut donc pas contenir de variable de type libre.


Nous reviendrons sur ce point quand nous aborderons les contraintes de type (page ??) et les classes paramtres (page ??).

Notation graphique des classes

On adapte la notation UML la syntaxe des types d'Objective CAML. Les classes se notent par un rectangle constitu de trois parties :
  • une partie portant le nom de la classe,
  • une autre o figurent les attributs (champs de donnes) d'une instance de la classe,
  • une dernire o sont inscrites les mthodes d'une instance de la classe.
La figure 15.1 donne un exemple de reprsentation graphique de la classe chameau.



Figure 15.1 : reprsentation graphique d'une classe


On peut ajouter l'information de types pour les champs d'une classe.

Cration d'une instance

Un objet est une valeur d'une classe, appele instance de cette classe. Cette instance est cre par la primitive de construction gnrique new laquelle on indique la classe et les valeurs d'initialisation.

Syntaxe


new nom espr1 ...exprn
L'exemple suivant construit plusieurs instances de la classe point, partir de valeurs initiales diffrentes.

# let p1 = new point (0,0);;
val p1 : point = <obj>
# let p2 = new point (3,4);;
val p2 : point = <obj>
# let coord = (3,0);;
val coord : int * int = 3, 0
# let p3 = new point coord;;
val p3 : point = <obj>


En Objective CAML, le constructeur d'une classe est unique mais rien ne nous empche de dclarer une fonction spcifique instancie_point de cration de points :

# let instancie_point x = new point (x,x) ;;
val instancie_point : int -> point = <fun>
# instancie_point 1 ;;
- : point = <obj>


Envoi d'un message

L'envoi d'un message un objet s'effectue par la notation # 2.

Syntaxe


nom1#nom2 p1 ...pn
Le message (la mthode) nom2 est envoy l'objet nom1. Les arguments p1, ..., pn sont ceux attendus par la mthode nom2. Elle doit tre connue dans la classe de l'objet, c'est--dire visible dans son type. Les types des paramtres d'appel doivent satisfaire les types des paramtres formels. L'exemple suivant montre diffrentes requtes effectues sur des objets de la classe point.

# p1#get_x;;
- : int = 0
# p2#get_y;;
- : int = 4
# p1#to_string();;
- : string = "( 0, 0)"
# p2#to_string();;
- : string = "( 3, 4)"
# if (p1#distance()) = (p2#distance())
then print_string ("c'est le hasard\n")
else print_string ("on pouvait parier\n");;
on pouvait parier
- : unit = ()


Du point de vue des types, les objets de type point peuvent tre manipuls par les fonctions polymorphes d'Objective CAML comme n'importe quelle valeur du langage :

# p1 = p1 ;;
- : bool = true
# p1 = p2;;
- : bool = false
# let l = p1::[];;
val l : point list = [<obj>]
# List.hd l;;
- : point = <obj>


Warning


L'galit entre deux objets est vrifie uniquement dans le cas o ils sont physiquement identiques.


Ce point sera dtaill lors de l'tude de la relation de sous-typage (page ??).


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora033.html0000644000000000000000000000361607421273601014473 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

La programmation imprative est le style de programmation qui a t le plus utilis depuis les premiers langages informatiques comme Fortran, C ou Pascal. Pour cette raison de nombreux algorithmes sont dcrits dans ce style, souvent en utilisant un pseudo-Pascal. Bien qu'ils soient implantables dans le style fonctionnel, l'utilisation de vecteurs incite utiliser le style impratif. Les structures de donnes et les algorithmes prsents dans les ouvrages classiques d'algorithmique, comme [AHU85] et [FGS90], sont directement transposables dans le style appropri. Un intrt supplmentaire de l'inclusion de ces deux styles dans un mme langage est de pouvoir dfinir de nouveaux modles de programmation par mlange des deux. C'est justement le propos du prochain chapitre.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora015.html0000644000000000000000000024674707421273601014511 0ustar Noyau fonctionnel d'Objective CAML Prcdent Index Suivant

Noyau fonctionnel d'Objective CAML

Comme tout langage fonctionnel, Objective CAML est un langage d'expressions comprenant principalement la cration de fonctions et l'application de celles-ci. Le rsultat du calcul d'une de ces expressions est une valeur du langage et l'excution d'un programme est le calcul de toutes les expressions le constituant.

Valeurs, fonctions et types de base

Les nombres entiers et flottants, les caractres, les chanes de caractres et les boolens sont prdfinis en Objective CAML.

Nombres

On distingue les nombres entiers1 de type int et les nombres flottants de type float. Objective CAML suit la norme IEEE 7542 pour reprsenter les nombres flottants en double prcision. Les oprations sur les entiers et sur les flottants sont dcrites la figure 2.1. Notons que lorsque le rsultat d'une opration entire sort de l'intervalle de dfinition des valeurs de type int, cela ne produit pas une erreur mais le rsultat est un entier dans l'intervalle des entiers du systme. En d'autres termes, toutes les oprations sur les entiers sont des oprations modulo les bornes de l'intervalle.


nombres entiers nombres flottants
+ addition
- soustraction et moins unaire
* multiplication
/ division entire
mod reste de la division entire
+. addition
-. soustraction et moins unaire
*. multiplication
/. division
** exponentiation

# 1 ;;
- : int = 1
# 1 + 2 ;;
- : int = 3
# 9 / 2 ;;
- : int = 4
# 11 mod 3 ;;
- : int = 2
(* limites de la reprsentation *)
(* des entiers *)
# 2147483650 ;;
- : int = 2
 

# 2.0 ;;
- : float = 2
# 1.1 +. 2.2 ;;
- : float = 3.3
# 9.1 /. 2.2 ;;
- : float = 4.13636363636
# 1. /. 0. ;;
- : float = Inf
(* limites de la reprsentation *)
(* des flottants *)
# 222222222222.11111 ;;
- : float = 222222222222
 

Figure 2.1 : oprations sur les nombres


Diffrence entre nombres entiers et flottants
Des valeurs ayant des types diffrents comme float et int ne peuvent pas tre directement compares. Mais il existe des fonctions de conversion (float_of_int et int_of_float) de l'un vers l'autre.

# 2 = 2.0 ;;
Characters 5-8:
This expression has type float but is here used with type int
# 3.0 = float_of_int 3 ;;
- : bool = true
De mme, les oprateurs sur les flottants sont distincts de ceux sur les entiers.

# 3 + 2 ;;
- : int = 5
# 3.0 +. 2.0 ;;
- : float = 5
# 3.0 + 2.0 ;;
Characters 0-3:
This expression has type float but is here used with type int
# sin 3.14159 ;;
- : float = 2.65358979335e-06


Un calcul non dfini, comme une division entire par zro, dclenchera une exception (voir page ??) qui interrompt le calcul. Les nombres flottants ont une reprsentation pour les valeurs infinies (affiches comme Inf) et les calculs non dfinis (affichs comme NaN3). Les principales fonctions sur les nombres flottants sont dcrites la figure 2.2.




fonctions sur les flottants fonctions trigonomtriques
ceil  
floor  
sqrt racine carre
exp exponentielle
log log nprien
log10 log en base 10
cos cosinus
sin sinus
tan tangente
acos arccosinus
asin arcsinus
atan arctangente

# ceil 3.4 ;;
- : float = 4
# floor 3.4 ;;
- : float = 3
# ceil (-.3.4) ;;
- : float = -3
# floor (-.3.4) ;;
- : float = -4
 

# sin 1.57078 ;;
- : float = 0.999999999867
# sin (asin 0.707) ;;
- : float = 0.707
# acos 0.0 ;;
- : float = 1.57079632679
# asin 3.14 ;;
- : float = NaN
 

Figure 2.2 : fonctions sur les flottants


Caractres et Chanes

Les caractres, type char, correspondent aux entiers compris entre 0 et 255 selon le codage ASCII pour les 128 premiers. Les fonctions char_of_int et int_of_char permettent les conversions entre entiers et caractres. Les chanes de caractres, type string, sont des suites de caractres de longueur connue (infrieure 224-6). L'oprateur de concatnation est  . Les fonctions int_of_string, string_of_int, string_of_float et float_of_string effectuent les diffrentes conversions entre nombres et chanes de caractres.

# 'B' ;;
- : char = 'B'
# int_of_char 'B' ;;
- : int = 66
# "est une chane" ;;
- : string = "est une cha\238ne"
# (string_of_int 1987) ^ " est l'anne de la cration de CAML" ;;
- : string = "1987 est l'ann\233e de la cr\233ation de CAML"


Mme si une chane contient les caractres d'un nombre, il ne sera pas possible de l'utiliser dans des oprations sur les nombres sans effectuer une conversion explicite.

# "1999" + 1 ;;
Characters 1-7:
This expression has type string but is here used with type int
# (int_of_string "1999") + 1 ;;
- : int = 2000
Les nombreuses fonctions sur les chanes de caractres sont regroupes dans le module String (voir page ??).

Boolens

Un boolen appartient un ensemble form de deux valeurs : true et false. Les oprateurs de base sont dcrits la figure 2.3. Pour des raisons historiques, les oprateurs << et >> et << ou >> possdent deux formes.

not ngation
&& et squentiel
|| ou squentiel
 
& synonyme pour &&
or synonyme pour ||
 

Figure 2.3 : oprateurs sur les boolens



# true ;;
- : bool = true
# not true ;;
- : bool = false
# true && false ;;
- : bool = false
Les oprateurs && et ||, ou leurs synonymes, valuent leur argument de gauche et, selon sa valeur, valuent leur argument droite. Ils peuvent se rcrire sous forme de structures conditionnelles (voir page ??).


= galit structurelle
== galit physique
<> ngation de =
!= ngation de ==
< infrieur
> suprieur
<= infrieur ou gal
>= suprieur ou gal

Figure 2.4 : oprateurs d'galit et de comparaison


Les oprateurs d'galit et de comparaison sont dcrits la figure 2.4. Ils sont polymorphes, c'est--dire qu'ils sont aussi bien utilisables pour comparer deux entiers que deux chanes de caractres. La seule contrainte reste que leurs deux oprandes doivent tre du mme type (voir page ??).

# 1<=118 && (1=2 || not(1=2)) ;;
- : bool = true
# 1.0 <= 118.0 && (1.0 = 2.0 || not (1.0 = 2.0)) ;;
- : bool = true
# "un" < "deux" ;;
- : bool = false
# 0 < '0' ;;
Characters 4-7:
This expression has type char but is here used with type int


L'galit structurelle teste l'galit de deux valeurs en explorant leur structure, alors que l'galit physique teste si les deux valeurs occupent la mme zone mmoire. Ces deux galits retournent le mme rsultat pour les valeurs simples : boolens, caractres, entiers et constructeurs constants (page ??).

Warning


Les nombres flottants et les chanes de caractres sont considrs comme des valeurs structures.


Unit

Le type unit dcrit un ensemble ne possdant qu'une seule valeur note : ().

# () ;;
- : unit = ()
Cette valeur sera plus particulirement utilise dans les programmes impratifs (3, page ??) pour les fonctions qui effectuent des effets de bord. Les fonctions dont le rsultat est la valeur () simulent la notion de procdure qui n'existe pas en Objective CAML comme le fait le type void dans le langage C.

Produit cartsien, n-uplet

Des valeurs de types diffrents peuvent tre regroupes en couple ou plus gnralement en n-uplets. Les valeurs constituant un n-uplet sont spares par une virgule. Le constructeur de type * indique un n-uplet. Le type int * string est le type des couples dont le premier lment est un entier (de type int) et le second une chane de caractres (de type string).

# ( 12 , "octobre" ) ;;
- : int * string = 12, "octobre"
Lorsqu'il n'y a pas ambigut, on crit plus simplement :

# 12 , "octobre" ;;
- : int * string = 12, "octobre"
Les fonctions fst et snd permettent d'accder au premier et au second lment d'un couple.

# fst ( 12 , "octobre" ) ;;
- : int = 12
# snd ( 12 , "octobre" ) ;;
- : string = "octobre"
Ces deux fonctions acceptent des couples dont les composants sont de type quelconque. Elles sont polymorphes, l'instar de l'galit.

# fst;;
- : 'a * 'b -> 'a = <fun>
# fst ( "octobre", 12 ) ;;
- : string = "octobre"
Le type int * char * string est celui des triplets dont le premier lment est de type int, le deuxime de type char et le troisime de type string. Ses valeurs s'crivent

# ( 65 , 'B' , "ascii" ) ;;
- : int * char * string = 65, 'B', "ascii"


Warning


Les fonctions fst et snd appliques un n-uplet, autre qu'un couple, provoquent une erreur de typage.

# snd ( 65 , 'B' , "ascii" ) ;;
Characters 7-25:
This expression has type int * char * string but is here used with type
'a * 'b
Il y a effectivement une diffrence entre le type d'un couple et celui d'un triplet. Le type int * int * int est diffrent des types (int * int) * int et int * (int * int). Les accesseurs d'un triplet (et des autres n-uplets) ne sont pas dfinis dans la bibliothque de base. On utilisera le filtrage de motifs pour les dfinir si besoin est (voir page ??).

Listes

Des valeurs d'un mme type peuvent tre regroupes en liste. Une liste peut tre soit vide soit constitue d'lments du mme type.

# [] ;;
- : 'a list = []
# [ 1 ; 2 ; 3 ] ;;
- : int list = [1; 2; 3]
# [ 1 ; "deux" ; 3 ] ;;
Characters 15-18:
This expression has type int list but is here used with type string list


La fonction qui ajoute un lment en tte de liste est l'oprateur infixe :: . C'est l'analogue du cons de Lisp.

# 1 :: 2 :: 3 :: [] ;;
- : int list = [1; 2; 3]


La concatnation de deux listes est aussi un oprateur infixe @.

# [ 1 ] @ [ 2 ; 3 ] ;;
- : int list = [1; 2; 3]
# [ 1 ; 2 ] @ [ 3 ] ;;
- : int list = [1; 2; 3]


Les autres fonctions de manipulation des listes sont dfinies dans la bibliothque List. Les fonctions hd et tl de cette bibliothque donnent respectivement la tte et la queue d'une liste quand ces valeurs existent. On note ces fonctions List.hd et List.tl pour indiquer au systme qu'il doit les trouver dans le module List4.

# List.hd [ 1 ; 2 ; 3 ] ;;
- : int = 1
# List.hd [] ;;
Uncaught exception: Failure("hd")
Dans ce dernier exemple, il est effectivement problmatique de vouloir rcuprer le premier lment d'une liste vide. C'est pour cela que le systme dclenche une exception (voir page ??).

Structure de contrle conditionnelle

Une des structures de contrle indispensable pour tout langage de programmation est la structure dite conditionnelle (ou alternative) qui oriente le calcul en fonction d'une condition.

Syntaxe


if expr1 then expr2 else expr3
L'expression expr1 est de type bool. Les expressions expr2 et expr3 doivent tre du mme type, quel qu'il soit.

# if 3=4 then 0 else 4 ;;
- : int = 4
# if 3=4 then "0" else "4" ;;
- : string = "4"
# if 3=4 then 0 else "4";;
Characters 20-23:
This expression has type string but is here used with type int


Une expression conditionnelle est, elle aussi, une expression et son calcul retourne une valeur.

# (if 3=5 then 8 else 10) + 5 ;;
- : int = 15


Remarque


La branche else peut tre omise, mais, dans ce cas elle est implicitement remplace par else (). En consquence le type de l'expression expr2 doit tre unit (voir page ??).


Dclarations de valeurs

Une dclaration associe un nom une valeur. On distingue les dclarations globales des dclarations locales. Dans le premier cas, les noms dclars sont connus de toutes les expressions suivant la dclaration ; dans le second, les noms dclars ne sont connus que d'une expression. Il est galement possible de dclarer simultanment plusieurs associations nom-valeur.

Dclarations globales

Syntaxe


let nom = expr ;;
Une dclaration globale dfinit l'association du nom nom la valeur de l'expression expr qui sera connue de toutes les expressions ultrieures.

# let an = "1999" ;;
val an : string = "1999"
# let x = int_of_string(an) ;;
val x : int = 1999
# x ;;
- : int = 1999
# x + 1 ;;
- : int = 2000
# let nouvel_an = string_of_int (x + 1) ;;
val nouvel_an : string = "2000"


Dclarations globales simultanes

Syntaxe


let nom1 = expr1
and nom2 = expr2
:
and nomn = exprn ;;



Une dclaration simultane dclare au mme niveau diffrents symboles. Ils ne seront connus qu' la fin de toutes les dclarations.

# let x = 1 and y = 2 ;;
val x : int = 1
val y : int = 2
# x + y ;;
- : int = 3
# let z = 3 and t = z + 2 ;;
Characters 18-19:
Unbound value z


Il est possible de regrouper plusieurs dclarations globales dans une mme phrase, l'affichage de leur type et de leur valeur n'intervient alors qu' la fin de la phrase marque par le double ;; . Ces dclarations sont values squentiellement la diffrence d'une dclaration combine.

# let x = 2
let y = x + 3 ;;
val x : int = 2
val y : int = 5
Une dclaration globale peut tre masque par une nouvelle dclaration du mme nom (voir page ??).

Dclarations locales

Syntaxe


let nom = expr1 in expr2;;
Le nom nom n'est connu que pour le calcul de expr2. La dclaration locale lui associe la valeur de expr1.

# let xl = 3 in xl * xl ;;
- : int = 9
La dclaration locale liant xl la valeur 3 n'est conserve que pour le temps du calcul de xl * xl.

# xl ;;
Characters 1-3:
Unbound value xl
Une dclaration locale masque toute dclaration antrieure du mme nom, mais l'ancienne valeur est retrouve lorsque l'on sort de la porte de la dclaration locale :

# let x = 2 ;;
val x : int = 2
# let x = 3 in x * x ;;
- : int = 9
# x * x ;;
- : int = 4
Une dclaration locale est une expression et peut donc tre utilise pour construire d'autres expressions :

# (let x = 3 in x * x) + 1 ;;
- : int = 10


Les dclarations locales peuvent aussi tre simultanes.

Syntaxe


let nom1 = expr1
and nom2 = expr2
:
and nomn = exprn
in expr ;;



# let a = 3.0 and b = 4.0 in sqrt (a*.a +. b*.b) ;;
- : float = 5
# b ;;
Characters 0-1:
Unbound value b


Expressions fonctionnelles, fonctions

Une expression fonctionnelle est constitue d'un paramtre et d'un corps. Le paramtre formel est un nom de variable et le corps une expression. On dit que le paramtre est abstrait. C'est pour cela qu'une expression fonctionnelle est aussi appele abstraction.

Syntaxe


function p -> expr
Ainsi la fonction qui lve au carr son argument s'crit :

# function x -> x*x ;;
- : int -> int = <fun>
Le systme Objective CAML dduit son type. Le type fonctionnel int -> int indique une fonction attendant un paramtre de type int et retournant une valeur de type int.

L'application d'une fonction un argument s'crit comme la fonction suivie par l'argument.

# (function x -> x * x) 5 ;;
- : int = 25
Le calcul d'une application revient calculer le corps de la fonction, ici x * x, o le paramtre formel, x, est remplac par la valeur de l'argument (ou paramtre d'appel, ou encore paramtre effectif), ici 5.

Dans la construction d'une expression fonctionnelle, expr est une expression quelconque. En particulier, expr peut elle-mme tre une expression fonctionnelle.

# function x -> (function y -> 3*x + y) ;;
- : int -> int -> int = <fun>


Les parenthses ne sont pas obligatoires. On peut crire plus simplement :

# function x -> function y -> 3*x + y ;;
- : int -> int -> int = <fun>
Le type de cette expression peut tre lu de la faon usuelle comme le type d'une fonction qui attend deux entiers et retourne une valeur entire. Mais dans le cadre d'un langage fonctionnel comme Objective CAML il s'agit plus exactement du type d'une fonction qui attend un entier et retourne une valeur fonctionnelle de type int -> int :

# (function x -> function y -> 3*x + y) 5 ;;
- : int -> int = <fun>


On peut, bien entendu, utiliser l'expression fonctionnelle de faon usuelle en l'appliquant deux arguments. On crit :

# (function x -> function y -> 3*x + y) 4 5 ;;
- : int = 17
Lorsque l'on crit f a b, il y a un parenthsage implicite gauche ce qui rend cette expression quivalente : (f a) b.

Revenons sur le dtail de l'application
(function x -> function y -> 3*x + y) 4 5
Pour calculer la valeur de cette expression, il faut calculer la valeur de
(function x -> function y -> 3*x + y) 4
qui est une expression fonctionnelle quivalente
function y -> 3*4 + y
obtenue en remplaant x par 4 dans 3*x + y. En appliquant cette valeur (qui est une fonction) 5 on obtient la valeur finale 3*4+5 = 17 :

# (function x -> function y -> 3*x + y) 4 5 ;;
- : int = 17


Arit d'une fonction

On appelle arit d'une fonction le nombre de ces arguments. L'usage, hrit des mathmatiques veut que les arguments d'une fonction soient donns entre parenthse aprs le nom de la fonction. On crit : f(4,5). Nous venons de voir qu'en Objective CAML, on crit plutt : f 4 5. On peut, bien entendu, en Objective CAML crire une expression fonctionnelle que l'on applique (4,5) :

# function (x,y) -> 3*x + y ;;
- : int * int -> int = <fun>
Mais, comme l'indique son type, cette dernire expression, n'attend pas deux, mais un seul argument : un couple d'entiers. Tenter d'appliquer deux argument une fonction qui attend un couple ou tenter d'appliquer un couple une fonction qui attend deux arguments provoquent une erreur de typage :

# (function (x,y) -> 3*x + y) 4 5 ;;
Characters 29-30:
This expression has type int but is here used with type int * int
# (function x -> function y -> 3*x + y) (4, 5) ;;
Characters 39-43:
This expression has type int * int but is here used with type int


Syntaxe alternative

Il existe une manire plus compacte d'crire des expressions fonctionnelles plusieurs paramtres. C'est une survivance des anciennes versions du langage Caml. Sa forme est la suivante :

Syntaxe


fun p1 ...pn -> expr
Elle permet de ne pas rpter le mot cl function ni les flches. Elle est quivalente la traduction suivante :
function p1 -> -> function pn -> expr

# fun x y -> 3*x + y ;;
- : int -> int -> int = <fun>
# (fun x y -> 3*x + y) 4 5 ;;
- : int = 17
On la rencontre encore souvent, en particulier dans les bibliothques fournies avec la distribution d'Objective CAML.

Fermeture

Objective CAML considre une expression fonctionnelle comme toute autre expression et sait calculer sa valeur. La valeur retourne par le calcul d'une expression fonctionnelle est appele une fermeture. Toute expression Objective CAML est value dans un environnement constitu des associations noms-valeurs provenant des dclarations prcdant l'expression calculer. On peut dcrire une fermeture comme le triplet constitu du nom du paramtre formel, du corps de la fonction et de l'environnement de l'expression. On a besoin de conserver cet environnement car le corps d'une expression fonctionnelle peut utiliser, en plus des paramtres formels, toute autre variable dclare prcdemment. Ces variables sont dites libres dans l'expression fonctionnelle. On aura besoin de leur valeur lorsque l'expression fonctionnelle sera applique.

# let m = 3 ;;
val m : int = 3
# function x -> x + m ;;
- : int -> int = <fun>
# (function x -> x + m) 5 ;;
- : int = 8


Lorsque l'application d'une fermeture un argument retourne une nouvelle fermeture, celle-ci possde dans son environnement toutes les associations ncessaires une future application. Le paragraphe sur la porte des variables (voir page ??) dtaille cette notion. Nous reviendrons sur le reprsentation mmoire d'une fermeture au chapitre 4 (page ??) ainsi qu'au chapitre 12 (page ??).

Les expressions fonctionnelles utilises jusqu' prsent sont anonymes. Il est assez pratique de pouvoir les nommer.

Dclarations de valeurs fonctionnelles

On dclare une valeur fonctionnelle de la mme manire que les autres valeurs du langage par la construction let.

# let succ = function x -> x + 1 ;;
val succ : int -> int = <fun>
# succ 420 ;;
- : int = 421
# let g = function x -> function y -> 2*x + 3*y ;;
val g : int -> int -> int = <fun>
# g 1 2;;
- : int = 8


Pour simplifier l'criture la notation suivante est accepte :

Syntaxe


let nom p1 ... pn = expr
qui est quivalente la forme suivante :
let nom = function p1 -> -> function pn -> expr

Les dclarations suivantes de succ et de g sont quivalentes leur dclaration prcdente.

# let succ x = x + 1 ;;
val succ : int -> int = <fun>
# let g x y = 2*x + 3*y ;;
val g : int -> int -> int = <fun>


On dcouvre le caractre pleinement fonctionnel d'Objective CAML dans l'exemple suivant o la fonction h1 est obtenue par application de g un seul entier. On parle alors d'application partielle :

# let h1 = g 1 ;;
val h1 : int -> int = <fun>
# h1 2 ;;
- : int = 8


On peut aussi, partir de g, dfinir une fonction h2 en fixant la valeur du second paramtre (y) de g :

# let h2 = function x -> g x 2 ;;
val h2 : int -> int = <fun>
# h2 1 ;;
- : int = 8


Dclaration de fonctions infixes

Certaines fonctions deux arguments peuvent tre appliques sous forme infixe. C'est le cas de l'addition sur les entiers. On crit 3 + 5 pour l'application de + 3 et 5. Pour utiliser le symbole + comme valeur fonctionnelle classique, il faut syntaxiquement l'indiquer en entourant ce symbole infixe par des parenthses. La syntaxe est la suivante :

Syntaxe


(op)


L'exemple suivant dfinit la fonction succ en utilisant (+).

# (+) ;;
- : int -> int -> int = <fun>
# let succ = (+) 1 ;;
val succ : int -> int = <fun>
# succ 3 ;;
- : int = 4


Il est aussi possible de dfinir de nouveaux oprateurs. On dfinit un oprateur ++ d'addition de couples d'entiers

# let (++) c1 c2 = (fst c1)+(fst c2), (snd c1)+(snd c2) ;;
val ++ : int * int -> int * int -> int * int = <fun>
# let c = (2,3) ;;
val c : int * int = 2, 3
# c ++ c ;;
- : int * int = 4, 6


Il y a une limitation importante sur les oprateurs possibles. Ils ne doivent contenir que des symboles (tels *, +, $, etc. ) l'exclusion des lettres et des chiffres. Font exception la rgle certaines fonctions prdfinies comme infixes dont la liste suit : or mod land lor lxor lsl lsr asr.

Fonctions d'ordre suprieur

Une valeur fonctionnelle (une fermeture) peut tre retourne comme rsultat. Elle peut galement tre prise comme argument d'une fonction. Les fonctions prenant en argument ou retournant des valeurs fonctionnelles sont dites d'ordre suprieur.

# let h = function f -> function y -> (f y) + y ;;
val h : (int -> int) -> int -> int = <fun>


Remarque


L'application est parenthse gauche, mais les types fonctionnels sont parenthss droite. Ainsi le type de la fonction h peut s'crire
(int -> int) -> int -> int  ou  (int -> int) -> (int -> int)



Les fonctions d'ordre suprieur offrent une possibilit lgante de traiter les listes. Par exemple la fonction List.map permet d'appliquer une fonction tous les lments d'une liste et de retourner la liste des rsultats.

# List.map ;;
- : ('a -> 'b) -> 'a list -> 'b list = <fun>
# let square x = string_of_int (x*x) ;;
val square : int -> string = <fun>
# List.map square [1; 2; 3; 4] ;;
- : string list = ["1"; "4"; "9"; "16"]


Autre exemple, la fonction List.for_all permet de savoir si tous les lments d'une liste satisfont un certain critre.

# List.for_all ;;
- : ('a -> bool) -> 'a list -> bool = <fun>
# List.for_all (function n -> n<>0) [-3; -2; -1; 1; 2; 3] ;;
- : bool = true
# List.for_all (function n -> n<>0) [-3; -2; 0; 1; 2; 3] ;;
- : bool = false


Porte des variables

Pour que l'valuation d'une expression soit possible, il faut que toutes les variables qui y apparaissent soient dfinies. C'est en particulier le cas pour l'expression e de la dclaration let p = e. Or p n'tant pas encore connue dans cette expression, cette variable ne peut tre prsente que si elle rfrence une autre valeur issue d'une dclaration antrieure.

# let p = p ^ "-suffixe" ;;
Characters 9-10:
Unbound value p
# let p = "prfixe" ;;
val p : string = "pr\233fixe"
# let p = p ^ "-suffixe" ;;
val p : string = "pr\233fixe-suffixe"


En Objective CAML, les variables sont lies statiquement. L'environnement utilis pour excuter l'application d'une fermeture est celui de sa dclaration (porte statique) et non celui courant au moment de l'application (porte dynamique).

# let p = 10 ;;
val p : int = 10
# let k x = (x, p, x+p) ;;
val k : int -> int * int * int = <fun>
# k p ;;
- : int * int * int = 10, 10, 20
# let p = 1000 ;;
val p : int = 1000
# k p ;;
- : int * int * int = 1000, 10, 1010
La fonction k possde une variable libre : p. Celle-ci tant dfinie dans l'environnement global, la dfinition de k est accepte. La liaison entre le nom p et la valeur 10 dans l'environnement de la fermeture k est statique, c'est--dire ne dpend pas de la dernire dfinition de p.

Dclarations rcursives

Une dclaration de variable est dite rcursive si elle utilise son propre identificateur dans sa dfinition. Cette possibilit est principalement utilise pour les fonctions, notamment pour simuler une dfinition par rcurrence. Nous venons de voir que la dclaration let ne le permet pas. Pour dclarer une fonction rcursive on utilisera une construction syntaxique ddie.

Syntaxe


let rec nom = expr ;;
On peut galement utiliser la facilit syntaxique des dfinitions de valeurs fonctionnelles en indiquant les paramtres des fonctions :

Syntaxe


let rec nom p1 ...pn = expr ;;


En guise d'exemple, voici la fonction sigma qui calcule la somme des entiers (positifs) compris entre 0 et la valeur de son argument.

# let rec sigma x = if x = 0 then 0 else x + sigma (x-1) ;;
val sigma : int -> int = <fun>
# sigma 10 ;;
- : int = 55
On peut remarquer que cette fonction risque de ne pas terminer si son argument est strictement ngatif.

Une valeur rcursive est en gnral une fonction. Et le compilateur rejette certaines dclarations rcursives dont la valeur n'est pas fonctionnelle :

# let rec x = x + 1 ;;
Characters 13-18:
This kind of expression is not allowed as right-hand side of `let rec'
Nous verrons cependant que dans certains cas de telles dclarations sont admises (voir page ??).

La dclaration let rec peut tre combine avec la construction and. Dans ce cas, toutes les fonctions dfinies au mme niveau sont connues dans le corps de toutes les autres. Cela permet, entre autres, des dclarations de fonctions mutuellement rcursives.

# let rec pair n = (n<>1) && ((n=0) or (impair (n-1)))
and impair n = (n<>0) && ((n=1) or (pair (n-1))) ;;
val pair : int -> bool = <fun>
val impair : int -> bool = <fun>
# pair 4 ;;
- : bool = true
# impair 5 ;;
- : bool = true


De mme, les dclarations locales peuvent tre rcursives. Cette nouvelle dfinition de sigma teste la validit de l'argument avant d'effectuer le calcul de la somme dfinie par une fonction locale sigma_rec.

# let sigma x =
let rec sigma_rec x = if x = 0 then 0 else x + sigma_rec (x-1) in
if (x<0) then "erreur : argument ngatif"
else "sigma = " ^ (string_of_int (sigma_rec x)) ;;
val sigma : int -> string = <fun>


Remarque


La ncessit de donner une valeur de retour qui soit de mme type, que l'argument soit ngatif ou non, nous a pouss rendre le rsultat sous forme d'une chane de caractres. Car quelle valeur doit rendre sigma quand son argument est ngatif ? Nous verrons la bonne faon de rgler ce problme en utilisant les exceptions (voir page ??).


Polymorphisme et contrainte de type

Un certain nombre de fonctions excutent le mme code pour des arguments ayant des types diffrents. Par exemple, la cration d'un couple partir de deux valeurs ne ncessite pas d'avoir des fonctions diffrentes pour chaque type connu du systme5. De mme, l'accs au premier champ d'un couple n'a pas tre diffrenci selon le type de la valeur de ce premier champ.

# let make_pair a b = (a,b) ;;
val make_pair : 'a -> 'b -> 'a * 'b = <fun>
# let p = make_pair "papier" 451 ;;
val p : string * int = "papier", 451
# let a = make_pair 'B' 65 ;;
val a : char * int = 'B', 65
# fst p ;;
- : string = "papier"
# fst a ;;
- : char = 'B'


Les fonctions dont l'un des paramtres ou la valeur de retour est d'un type qu'il n'est pas ncessaire de prciser sont dites polymorphes. Le synthtiseur de types contenu dans le compilateur d'Objective CAML trouve le type le plus gnral pour chaque expression. Dans ce cas, Objective CAML utilise des variables, ici 'a et 'b, pour dsigner ces types gnraux. Ces variables sont instancies par le type de l'argument lors de l'application de la fonction.

Avec les fonctions polymorphes d'Objective CAML nous cumulons les avantages de pouvoir crire un code gnrique utilisable pour des valeurs de tout type, tout en conservant la sret d'excution du typage statique. En effet, bien que make_pair soit polymorphe, la valeur cre par (make_pair 'B' 65) possde un type bien prcis qui est diffrent de celui de (make_pair "papier" 451). En outre, la vrification des types est effectue la compilation, donc la gnricit du code ne nuit pas l'efficacit du programme.

Exemples de fonctions et valeurs polymorphes

Les exemples suivants de fonctions polymorphes possdent des paramtres fonctionnels dont le type est paramtr.

La fonction app applique une fonction un argument.

# let app = function f -> function x -> f x ;;
val app : ('a -> 'b) -> 'a -> 'b = <fun>
On peut donc l'appliquer la fonction impair dfinie prcdemment :

# app impair 2;;
- : bool = false


La fonction identit (id ) prend un paramtre et le retourne tel quel.

# let id x = x ;;
val id : 'a -> 'a = <fun>
# app id 1 ;;
- : int = 1


La fonction compose prend deux fonctions et une autre valeur pour composer ces l'application de ces deux fonctions sur cette valeur.

# let compose f g x = f (g x) ;;
val compose : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun>
# let add1 x = x+1 and mul5 x = x*5 in compose mul5 add1 9 ;;
- : int = 50
On voit que le rsultat de g doit tre de mme type que le paramtre de f.

Les valeurs non fonctionnelles peuvent elles aussi tre polymorphes. C'est le cas par exemple de la liste vide :

# let l = [] ;;
val l : 'a list = []


L'exemple suivant montre que la synthse du type provient bien de la rsolution des contraintes d'une application et non de la valeur obtenue l'excution.

# let q = List.tl [2] ;;
val q : int list = []
Le type de List.tl est 'a list -> 'a list, donc cette fonction applique une liste d'entiers retourne une liste d'entiers. Le fait qu' l'excution ce soit la liste vide qui est obtenue ne change rien son type.

Objective CAML engendre des types paramtrs pour toute fonction qui n'utilise pas la forme de ses arguments. Ce polymorphisme est appel paramtrique6.

Contrainte de type

Comme le synthtiseur de types en Caml engendre le type le plus gnral, il peut tre utile ou ncessaire de prciser le type d'une expression.

La forme syntaxique d'une contrainte de type est la suivante :

Syntaxe


( expr : t )
Lorsqu'il rencontre une telle contrainte, le synthtiseur de type en tiendra compte pour la construction du type de l'expression. L'usage des contraintes de type permet :
  • de rendre visible le type des paramtres d'une fonction ;
  • d'interdire l'utilisation d'une fonction en dehors de son cadre ;
  • de spcifier le type d'une expression, ce qui sera particulirement pratique pour les valeurs physiquement modifiables (voir page ??).
Les exemples suivants montrent l'utilisation de telles contraintes de type

# let add (x:int) (y:int) = x + y ;;
val add : int -> int -> int = <fun>
# let make_pair_int (x:int) (y:int) = x,y;;
val make_pair_int : int -> int -> int * int = <fun>
# let compose_fn_int (f : int -> int) (g : int -> int) (x:int) =
compose f g x;;
val compose_fn_int : (int -> int) -> (int -> int) -> int -> int = <fun>
# let nil = ([] : string list);;
val nil : string list = []
# 'H'::nil;;
Characters 5-8:
This expression has type string list but is here used with type char list


Cette restriction du polymorphisme permet de mieux contrler le type des expressions en restreignant le polymorphisme du type dduit par le systme. On peut utiliser n'importe quel type dfini, pouvant contenir des variables de type, comme le montre l'exemple suivant :

# let llnil = ([] : 'a list list) ;;
val llnil : 'a list list = []
# [1;2;3]:: llnil ;;
- : int list list = [[1; 2; 3]]
Le symbole llnil est une liste de listes de n'importe quel type.

Il s'agit ici de contraintes et non d'un typage explicite remplaant la synthse de type d'Objective CAML. En particulier, on ne peut pas gnraliser le type plus que ne le permet l'infrence.

# let add_general (x:'a) (y:'b) = add x y ;;
val add_general : int -> int -> int = <fun>


Les contraintes de type seront utilises dans les interfaces de modules (14) ainsi que dans les dclarations de classes (15).

Exemples

Nous donnons dans ce paragraphe quelques exemples un peu labors de fonctions. La plupart de ces fonctions sont prdfinies en Objective CAML. Nous les redfinissons titre << pdagogique >>.

Ici, le test du cas d'arrt des fonctions rcursives est ralis par une conditionnelle. D'o un style de programmation plus proche de Lisp. Nous verrons comment donner un caractre plus ML ces dfinitions lorsque nous prsenterons une autre faon de dfinir des fonctions par cas (voir page ??).

Longueur d'une liste

Commenons par la fonction null qui teste si une liste est vide.

# let null l = (l = []) ;;
val null : 'a list -> bool = <fun>
On dfinit alors la fonction size de calcul de la longueur d'une liste (i.e. le nombre de ses lments).

# let rec size l =
if null l then 0
else 1 + (size (List.tl l)) ;;
val size : 'a list -> int = <fun>
# size [] ;;
- : int = 0
# size [1;2;18;22] ;;
- : int = 4
La fonction size teste si la liste argument est vide, si oui elle retourne 0, sinon elle retourne 1 ajout la valeur rsultant du calcul de la longueur de la queue de la liste.

Itration de composition

L'expression iterate n f calcule la valeur fn qui correspond l'application de f itre n fois.

# let rec iterate n f =
if n = 0 then (function x -> x)
else compose f (iterate (n-1) f) ;;
val iterate : int -> ('a -> 'a) -> 'a -> 'a = <fun>
La fonction iterate teste si n vaut 0, si oui elle retourne la fonction identit, sinon elle compose f avec l'itration de f n-1 fois.

On peut, en utilisant iterate, dfinir l'lvation la puissance comme itration de la multiplication.
 
# let rec power i n =
let i_times = ( * ) i in
iterate n i_times 1 ;;
val power : int -> int -> int = <fun>
# power 2 8 ;;
- : int = 256
La fonction power itre n fois l'expression fonctionnelle i_times, puis applique ce rsultat 1, ce qui calcule bien la puissance n-ime d'un nombre entier.

Table de multiplication

On veut crire la fonction multab qui calcule la table de multiplication d'un entier pass en argument.

On dfinit dans un premier temps la fonction apply_fun_list telle que, si f_list est une liste de fonctions, apply_fun_list x f_list retourne la liste des rsultats de l'application de chaque lment de f_list x.

# let rec apply_fun_list x f_list =
if null f_list then []
else ((List.hd f_list) x)::(apply_fun_list x (List.tl f_list)) ;;
val apply_fun_list : 'a -> ('a -> 'b) list -> 'b list = <fun>
# apply_fun_list 1 [(+) 1;(+) 2;(+) 3] ;;
- : int list = [2; 3; 4]


La fonction mk_mult_fun_list retourne la liste des fonctions multipliant leur argument par i, pour i variant de 0 n.

# let mk_mult_fun_list n =
let rec mmfl_aux p =
if p = n then [ ( * ) n ]
else (( * ) p) :: (mmfl_aux (p+1))
in (mmfl_aux 1) ;;
val mk_mult_fun_list : int -> (int -> int) list = <fun>


On obtient la table de multiplication de 7 par :

# let multab n = apply_fun_list n (mk_mult_fun_list 10) ;;
val multab : int -> int list = <fun>
# multab 7 ;;
- : int list = [7; 14; 21; 28; 35; 42; 49; 56; 63; 70]


Itration sur les listes

L'appel de la fonction fold_left f a [e1; e2; ... ; en] retourne f ... (f (f a e1) e2) ... en. Il y a donc n applications.

# let rec fold_left f a l =
if null l then a
else fold_left f ( f a (List.hd l)) (List.tl l) ;;
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a = <fun>


La fonction fold_left permet la dfinition compacte d'une fonction de calcul de la somme des lments d'une liste d'entiers :

# let sum_list = fold_left (+) 0 ;;
val sum_list : int list -> int = <fun>
# sum_list [2;4;7] ;;
- : int = 13


Ou encore, la concatnation des lments d'une liste de chanes :

# let concat_list = fold_left (^) "" ;;
val concat_list : string list -> string = <fun>
# concat_list ["Hello "; "world" ; " "; "!"] ;;
- : string = "Hello world !"



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora066.gif0000644000000000000000000000256207073350152014301 0ustar GIF89aUUU!,ڋYq扦~%`CRpoĢ# /&j pGNB\ENqm(8HXhxH96)qYY ZCڀjʢk@+{{rǫ` |Xq9Lm1p]Y pI{lEn>{^_o* 0MB<B0[x˹~cX<{3nrbfGgҰI_(qe WV,)'͛:ɳ(R*D*]ZUdOqՑQ\1 lU[VͪܨapCgIb(dKׯA<¶ 5#OʉWF5k8O`E\HaYG73r _ܨ,/Z罣?tw|Ńխ9F;ڭO#_Qlyn/z* nvU_nWYc@ZW8Bǎ~U`hF}'Kc R$?֬Յ,bOS 2@Q?:ވ#4>c AATc6AVN6$n,Qy-0%1")el^bVT0H f,u /;zzV hFg{~ DxWި(Y 0zVQ懝8= lep3GHj򔥀QP+g>+`CF)+( *l& e0c+,Q+N$rxۭ.-z,6{ 7+r#Jދ# )o24 P$:P.$Đd*."k<2l$[,r μ2&7l:sB2℮4XYJ#vEӠ> +on^-rJff2"u`4CJMK0uiQwуӗ7ҵ:'0XڤAS/*x}S'0uhBO w:PtJ^4X@aZ8<,F|Ƿ23A_G?4σK?~@#_{/@[,_lTl 0vZX4p}XiC(h Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

La communication entre programmes Objective CAML distants peut tre riche. L'utilisation de protocoles texte est grandement facilite par les outils d'analyse syntaxique du langage (voir chapitre 11). Le mcanisme de persistance apport par le module Marshal (voir chapitre 8) permet de transporter en format interne des donnes complexes y compris des valeurs fonctionnelles si les deux programmes communicants sont les mmes. Il manque principalement la transmission d'instances de classe. Une solution ce problme est d'utiliser un ORB (Object Request Broker) pour transmettre des objets ou invoquer des mthodes distantes. Cette architecture existe dj pour de nombreux langages objets sous la forme de la norme CORBA (Common ORB Architecture). Cette norme, du groupe OMG, dont la premire mouture date de de 1990, permet d'utiliser des objets distants en tant indpendants du langage d'implantation des classes.

Lien


http://www.omg.org
Les deux fonctions principales sont de pouvoir envoyer des objets distance et surtout de pouvoir rfrencer de plusieurs endroits du rseau un mme objet, pour invoquer ses mthodes permettant de modifier ses variables d'instances. De plus cette norme est indpendante des langages d'implantation de ces objets distants. Pour cela, un ORB fournit un langage de description des interfaces appel IDL (Interface Declaration Language), la manire de CAMLIDL pour l'interface entre Objective CAML et C. Pour l'instant, il n'y a pas d'ORB intgrant le langage Objective CAML, mais il n'y a pas d'impossibilit de ralisation car le langage IDL est un sous-ensemble des langages objets classes. Pour simplifier CORBA fournit un bus logiciel (IIOP) qui permet le transfert et l'adressage de donnes distantes.

Le fait de pouvoir rfrencer le mme objet de plusieurs points du rseau simule une mmoire partage distribue, ce qui n'est pas sans poser de problmes pour les rcuprateurs automatiques de mmoire.

Le fait de rfrencer un objet distant ne provoque pas le transfert de code. On ne peut recevoir une copie d'une instance d'une classe que si la classe existe dans le serveur. Or pour certaines applications client-serveur, il peut tre ncessaire d'effectuer un chargement dynamique de code (applets en Java) et mme de faire migrer des processus avec leur code. Un exemple intressant de chargement dynamique de code distant est le navigateur MMM ralis en Objective CAML par Franois Rouaix :

Lien


http://caml.inria.fr/~rouaix/mmm/
Ce navigateur permet de consulter le Web, mais aussi de tlcharger des applets Objective CAML d'un serveur pour les excuter dans sa fentre graphique.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora185.html0000644000000000000000000000374607421273602014510 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

Ce chapitre prsente les lments de base du rseau Internet que sont les prises de communication, pour la construction d'applications rparties, en particulier les applications client-serveur, en dtaillant les difficults qu'il y a raliser des protocoles de communication.

La premire section explique brivement le rseau Internet, son adressage et ses principaux services.

La deuxime section montre la communication, travers des sockets, de diffrents processus Objective CAML, locaux ou distants.

La troisime section dcrit le modle client-serveur en commentant les programmes de serveurs et de clients universels.

La quatrime section montre l'importance des protocoles de communication pour la construction de services rseau.

Ce chapitre gagne tre lu aprs les chapitres sur la programmation systme (chapitre 18) et sur la programmation concurrente (chapitre 19).


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora001.html0000644000000000000000000000546507421273601014472 0ustar Prambule Index Suivant

Prambule

La volont d'crire un livre sur Objective CAML est venue de l'exprience pdagogique des auteurs dans l'apprentissage des concepts de la programmation travers le langage Objective CAML. Les tudiants des diffrents cycles et les ingnieurs en formation permanente de l'Universit Pierre et Marie Curie ont par leur dynamisme et leurs critiques grandement fait voluer notre prsentation du langage Objective CAML. Plusieurs exemples de ce livre s'inspirent directement de leurs ralisations.

L'implantation du langage Caml se poursuit depuis quinze ans dj. Son dveloppement provient des projets Formel puis Cristal de l'INRIA en collaboration avec l'Universit Denis Diderot et l'cole Normale Suprieure. Les efforts continus, tant thoriques que d'implantation, des chercheurs de ces quipes ont produit au fil des ans un langage de grande qualit. Ils ont su tenir compte de l'volution constante du domaine en intgrant dans un cadre formel les nouveaux modles de programmation. Nous esprons contribuer par cet ouvrage la diffusion que mrite ce travail.

La forme et le fond de ce livre ne seraient pas ce qu'ils sont sans l'aide de nombreux collgues. Ils n'ont pas t rebuts par la relecture de nos premiers manuscrits. Leurs remarques et leur commentaires ont permis d'amliorer cet ouvrage tout au long de sa ralisation. Nous tenons particulirement remercier Mara-Virginia Aponte, Sylvain Baro, Christian Codognet, Hlne Cottier, Guy Cousineau, Pierre Crgut, Titou Durand, Christophe Gonzales, Michelle Morcrette, Christian Queinnec, Attila Raksany et Didier Rmy.

La version HTML de ce livre n'aurait pas vu le jour sans les outils hevea et VideoC. Un grand merci leurs auteurs respectifs, Luc Maranget et Christian Queinnec, qui ont toujours rpondu dans les plus brefs dlais nos questions et nos demandes d'volution.








Index Suivant ocaml-book-1.0/fr/html/book-ora064.gif0000644000000000000000000000343607073350152014300 0ustar GIF89a UUU!, H0I8ͻ@(dihl~+tm |ooH,rƤ2#4hZUMMR3ȳS߰/|F4~hW|wroxSs]Q a}UvlRjgDz>`G>YԠ"B9!1 !=m"Z*\+r: 0; xx4:* }KMG:!$ut#,&M pq#v2mCzHL+*A(fS:J&_IiMq&'vQuBq+#b;Ȃ\ndPĕO@7:Nzb|e SeO U@q/?u8I-bFrx@m+lgGNpU 7ʨd;2eK3æ:Q.nxSBRc<$-H3NnJ BI%\iFGƸEZebIf-^i j҈Bi e≦ *f" V2jABZ'`2Z")کf|ꡪxt*pʠjjd Z{T˝N"H2I m:iHi GkCyvkhr"())zm+(m+-+* '.Rh)LrYP(\%/aUp0=0(%2k1Hi1l K4jC$'Dt Ӌ"C7lB(A-499'}CcE M BQ M2k3d:н^}_7`w}ufww%x^ff2'KE܃]X^L5ާ꽶v%6n؋w^؅CcCŷ4KbCG6dILUhi123[?Q|6_UpYGn-sש:?$9Y5 <1RƂ7~RI׫fv`<( )&XXCJHDHL-1[:lZEn)`bH&iOU ax96+od̢4I1Dc-ɍ}H=0%H@"Eq_d!#ɱIRr?$J,(+%(+UU+cYԑ .95]Ȗe/I* S< =/}hg>A n ?WU XVt6mb:  }},/5w8MAiٽ&i@ 1)Yz:~tV6wtYD@& ~FZSyj$BiE] Ɋ| :~$!s|܌ï{=luMRRV.XOQlP䡌 BTU g-Yׁ65&zЪ~:g["t ;ocaml-book-1.0/fr/html/book-ora171.html0000644000000000000000000000311207421273602014466 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a prsent les principales fonctions d'interface avec le systme fournies par le module Unix. Malgr son nom, ce module offre un grand nombre de fonctions utilisables sous Windows (voir figure 18.1).
En marge de la cration de processus, nous nous sommes surtout concentrs sur les possibilits offertes pour communiquer entre plusieurs programmes Objective CAML s'excutant en mme temps sur la mme machine. En particulier, les oprations de bas niveau sur les fichiers, les signaux et les tubes de communication ont t dtailles.


Prcdent Index Suivant ocaml-book-1.0/fr/html/contents_motif.gif0000644000000000000000000000047407073350152015367 0ustar GIF89ap!" Imported from XPM image: toc.xpm!,@6313c B0 0 A0 0 0 0 `0@`0 `  `0@`0 `0@`0000000000 0000000000 00000000 000000 0000 000000000 00000000000 00000000000000` ;ocaml-book-1.0/fr/html/book-ora156.html0000644000000000000000000000433607421273602014502 0ustar Organisations mixtes Prcdent Index Suivant

Organisations mixtes

Le dernier exemple de la section prcdente a montr l'intrt qu'il y avait mixer les deux modles pour le problme de l'extensibilit des composants. On propose maintenant de mixer modules paramtrs et liaison retarde pour profiter de la puissance de ces deux traits. L'application du foncteur produira de nouveaux modules contenant des classes utilisant le type et les fonctions du module paramtre. Si, de surcrot, la signature obtenue est compatible avec la signature du module paramtre, il est alors possible de rappliquer le module paramtr sur le module rsultat, permettant ainsi de construire automatiquement de nouvelles classes.

Un exemple complet est donn dans la dernire partie de cet ouvrage qui est consacre aux programmes concurrents et/ou distribus (page ??). Nous utilisons un foncteur pour engendrer un protocole de communication partir d'un type donn; un second foncteur permet ensuite de dduire de ce protocole une classe implantant un serveur gnrique traitant des requtes exprimes dans ce protocole. L'hritage peut ensuite tre utilis pour spcialiser le serveur vers ce qui est rellement le service attendu.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora133.html0000644000000000000000000004654207421273602014502 0ustar Modules paramtrs Prcdent Index Suivant

Modules paramtrs

Les modules paramtrs sont aux modules ce que les fonctions sont aux valeurs. Tout comme une fonction construit une nouvelle valeur partir de valeurs paramtres, un module paramtr permet de construire un nouveau module partir de modules dj construits. On donne galement le nom de foncteurs aux modules paramtrs.

L'introduction des foncteurs dans le langage des modules permet d'accrotre les possibilits de rutilisation du code dfini dans les structures.

Un foncteur se dfinit avec une syntaxe fonctionnelle :

Syntaxe


functor ( Nom : signature ) -> structure


# module Couple = functor ( Q : sig type t end ) -> 
struct type couple = Q.t * Q.t end ;;
module Couple :
functor(Q : sig type t end) -> sig type couple = Q.t * Q.t end


Comme pour les fonctions, il existe un raccourci syntaxique la dclaration de foncteurs :

Syntaxe


module Nom1 ( Nom2 : signature ) = structure

# module Couple ( Q : sig type t end ) = struct type couple = Q.t * Q.t end ;;
module Couple :
functor(Q : sig type t end) -> sig type couple = Q.t * Q.t end


Un foncteur peut admettre plusieurs paramtres.

Syntaxe


functor ( Nom1 : signature1 ) ->   :
functor ( Nomn : signaturen ) ->   structure

On peut, ici galement, utiliser le raccourci syntaxique :

Syntaxe


module Nom (Nom1 : signature1 ) ...( Nomn : signaturen ) =
  structure



L'application d'un foncteur ses paramtres se fait selon le schma syntaxique suivant :

Syntaxe


module Nom = foncteur ( structure1 ) ...( structuren )
Notez que chaque paramtre est inscrit entre parenthses. Le rsultat de l'application peut tre un module simple ou, nouveau un foncteur, selon le nombre de paramtres rclams par le foncteur.

Warning


Il n'est pas possible de crer une signature par application d'une signature << fonctorielle >> d'autres signatures.


Un foncteur qui explicite compltement sa communication avec d'autres modules, c'est--dire qui ne fait pas rfrence directement un lment d'un autre module hormis ses paramtres, est facilement rutilisable. On peut effectivement l'appliquer diffrents arguments selon le cadre de son emploi. Il y a un parallle entre une fonction compltement paramtre (sans variable libre) et un foncteur compltement paramtr.

Foncteurs et rutilisabilit

La distribution d'Objective CAML fournit trois modules dfinissant des foncteurs. Deux d'entre eux prennent comme argument un module fournissant un type de donnes totalement ordonn; c'est--dire respectant la signature suivante :

# module type OrderedType =
sig
type t
val compare: t -> t -> int
end ;;
module type OrderedType = sig type t val compare : t -> t -> int end


La fonction compare prend deux arguments et rend une valeur ngative si le premier est infrieur au second, une valeur nulle s'ils sont gaux, et une valeur positive si le premier est suprieur au second. Voici un exemple de type ordonn.

# module OrderedIntPair = 
struct
type t = int * int
let compare (x1,x2) (y1,y2) = let q = x1-y1 in if q=0 then x2-y2 else q
end ;;
module OrderedIntPair :
sig type t = int * int val compare : int * int -> int * int -> int end


Le foncteur Make du module Map construit un module qui implante des tables d'association ayant pour cls des valeurs du type ordonn pass en argument. Ce module fournit des services comparables ceux des listes d'associations du module List mais en utilisant une structure de donnes lgrement plus complexe (des arbres binaires quilibrs).

# module AssocIntPair = Map.Make (OrderedIntPair) ;;
module AssocIntPair :
sig
type key = OrderedIntPair.t
and 'a t = 'a Map.Make(OrderedIntPair).t
val empty : 'a t
val add : key -> 'a -> 'a t -> 'a t
val find : key -> 'a t -> 'a
val remove : key -> 'a t -> 'a t
val mem : key -> 'a t -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val map : ('a -> 'b) -> 'a t -> 'b t
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
end


Le foncteur Make permet de construire des tables d'associations ayant pour cl n'importe quel type de donnes dont on saura crire une fonction compare.

Le module Set fournit lui aussi un foncteur Make prenant en argument un type ordonn et construisant un module implantant les ensembles de valeurs de ce type.

# module SetIntPair = Set.Make (OrderedIntPair) ;;
module SetIntPair :
sig
type elt = OrderedIntPair.t
and t = Set.Make(OrderedIntPair).t
val empty : t
val is_empty : t -> bool
val mem : elt -> t -> bool
val add : elt -> t -> t
val singleton : elt -> t
val remove : elt -> t -> t
val union : t -> t -> t
val inter : t -> t -> t
val diff : t -> t -> t
val compare : t -> t -> int
val equal : t -> t -> bool
val subset : t -> t -> bool
val iter : (elt -> unit) -> t -> unit
val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a
val cardinal : t -> int
val elements : t -> elt list
val min_elt : t -> elt
val max_elt : t -> elt
val choose : t -> elt
end


Nous avons avec SetIntPair.t un type pour les ensembles de couples d'entiers avec toutes les fonctions classiques sur les ensembles. L'illustration de la rutilisabilit nous est donn en construisant les ensembles d'ensembles de paires d'entiers.

# module SetofSet = Set.Make (SetIntPair) ;;

# let x = SetIntPair.singleton (1,2) ;; (* x = { (1,2) } *)
val x : SetIntPair.t = <abstr>
# let y = SetofSet.singleton SetIntPair.empty ;; (* y = { {} } *)
val y : SetofSet.t = <abstr>
# let z = SetofSet.add x y ;; (* z = { {(1,2)} ; {} } *)
val z : SetofSet.t = <abstr>


Le foncteur Make du module Hashtbl fonctionne de manire similaire celui du module Map mais en construisant des tables de hachage la place d'arbres quilibrs. L'argument de ce foncteur est un peu diffrent; outre le type des cls de la table de hachage, il doit fournir simplement une fonction testant l'galit entre deux cls la place d'une fonction de comparaison. De plus, il est ncessaire de donner la fonction de hachage, c'est--dire une fonction associant un entier une cl.

# module type HashedType =
sig
type t
val equal: t -> t -> bool
val hash: t -> int
end ;;
module type HashedType =
sig type t val equal : t -> t -> bool val hash : t -> int end
# module IntMod13 =
struct
type t = int
let equal = (=)
let hash x = x mod 13
end ;;
module IntMod13 :
sig type t = int val equal : 'a -> 'a -> bool val hash : int -> int end
# module TblInt = Hashtbl.Make (IntMod13) ;;
module TblInt :
sig
type key = IntMod13.t
and 'a t = 'a Hashtbl.Make(IntMod13).t
val create : int -> 'a t
val clear : 'a t -> unit
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_all : 'a t -> key -> 'a list
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
end


Dclarations locales de modules

Parmi les extensions du langage, il existe la possibilit de dclarer localement un module.

Syntaxe


let module Nom = structure
  in expr



Par exemple, nous souhaitons utiliser le module Set pour crire une fonction de tri sur les listes d'entiers en insrant chaque lment de la liste dans un ensemble et en rcuprant la fin la liste des lments de l'ensemble obtenu.

# let sort l =
let module M =
struct
type t = int
let compare = (-)
end
in
let module MSet = Set.Make(M)
in MSet.elements (List.fold_right MSet.add l MSet.empty) ;;
val sort : int list -> int list = <fun>

# sort [ 5 ; 3 ; 8 ; 7 ; 2 ; 6 ; 1 ; 4 ] ;;
- : int list = [1; 2; 3; 4; 5; 6; 7; 8]


Objective CAML ne permet pas de faire sortir d'un module local une valeur ayant un type qui n'est pas connu l'extrieur de l'expression.

# let test =
let module Foo =
struct
type t
let id x = (x:t)
end
in Foo.id ;;
Characters 15-101:
This `let module' expression has type Foo.t -> Foo.t
In this type, the locally bound module name Foo escapes its scope



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora158.html0000644000000000000000000000314307421273602014477 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a compar les mrites respectifs des modles d'organisation fonctionnel/modulaire et objet. Chacun essaie de rpondre sa manire aux difficults de rutilisabilit et de modifiabilit du logiciel. Les diffrences principales proviennent de leur systme de types, galit de types entre paramtres de foncteurs et sous-typage en objet, et de l'valuation avec la liaison tardive des objets. Les deux modles n'arrivent pas rsoudre seuls le problme de l'extensibilit des composants, d'o la proposition d'une organisation mixte. Ce mlange d'organisation permet d'autre part de nouvelles structurations.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora057.gif0000644000000000000000000000505307073350152014277 0ustar GIF89a UUU!, ڋ޼"扦ʶnL{@e@L*#{Ҫb-Iwȷ85 /yF>(Xh@ȖxxH6iP)vYuى9z%jiJ5T%Z "` p˵Ckh|$̭< [{<[rM~m(L}{$N4/%*ͫ`Z N]/a\О js]qČ,$[Gh cy@%(7A m渥J|$&>t1"IdU|&E*sX=թSb >]h 16i ]ׯ]G$f!r(k!T.gYHC?-}!Ԧަt lؾܼ{ kN>m<9ប;{!CIٻ3$&<)ۧN>~-˿?8" 0 .`@AaSn(q~(HOb=1b.jD/37(ڎ>zޏB(אFȒJ6ߒNz⊎OhSRفWbۖ^Zbf2?hykTr"VoΉgx渳b~`BB}IhٰYh.h> iFi^hJF"ijK X[*jk"٭+`kL2`Lm(|geɺ,9``m%PTYgũFߒ-M(TRdfwXKpT7{8t8y:ǧX}v-νve`խQ{)GOr3ởxTsM!yb#P]Ѕo/,-Ս/\]~͙6r?^XB R.oߏ臄W1diM<٧׶u%Kۭ%[ez:ܒ].򤊺zO,<+=io/v Exercices Prcdent Index Suivant

Exercices

Coordonnes polaires

Les coordonnes de la bibliothque Graphics sont cartsiennes. Un segment y est reprsent par son point de dpart (x0,y0) et son point d'arrive (x1,y2) Il peut tre utile d'utiliser des coordonnes polaires. Un segment est alors dcrit par son point de dpart (x0,y0) la taille du segment (r) et son angle (a) Le rapport entre coordonnes cartsiennes et coordonnes polaires est dfinie par les quations :


x1 = x0 + r * cos(a)
y1 = y0 + r * sin(a)
Le type suivant dfinit les coordonnes polaires d'un segment :

# type seg_pol = {x:float; y:float; r:float; a:float};;
type seg_pol = { x: float; y: float; r: float; a: float }


  1. crire la fonction to_cart de transformation de coordonnes polaires en coordonnes cartsiennes.

    # let to_cart p =
    (p.x,p.y),(p.x +. p.r *. (cos p.a), p.y +. p.r *. (sin p.a)) ;;
    val to_cart : seg_pol -> (float * float) * (float * float) = <fun>


  2. crire la fonction draw_seg qui affiche un segment dfini en coordonnes polaires dans le repre de Graphics.

    # let draw_seg p =
    let (x1,y1),(x2,y2) = to_cart p in
    Graphics.moveto (int_of_float x1) (int_of_float y1);
    Graphics.lineto (int_of_float x2) (int_of_float y2) ;;
    val draw_seg : seg_pol -> unit = <fun>


  3. Un des intrts des coordonnes polaires est de pouvoir facilement appliquer des transformations sur un segment. Une translation ne modifiera que le point de dpart, une rotation sera uniquement effectue sur le champ angle, de mme pour un changement d'chelle sur le champ longueur. De faon gnrale, on peut reprsenter une transformation comme un triplet de flottants : le premier reprsente la translation (on ne considre, ici, que des translations sur la droite du segment), le second la rotation et le troisime le facteur d'chelle. Dfinir la fonction app_trans qui prend un segment en coordonnes polaires et un triplet de transformations et retourne le nouveau segment.

    # let app_trans seg ( da, dr, dxy ) =
    let _,(x1,y1) = to_cart {seg with r = seg.r *. dxy} in
    {x=x1; y=y1; a=seg.a +. da; r=seg.r *. dr} ;;
    val app_trans : seg_pol -> float * float * float -> seg_pol = <fun>


  4. On peut construire des dessins rcursifs par itration de transformations. crire la fonction dessin_r qui prend en arguments un segment s, un nombre d'itrations n, une liste de transformations l et affiche tous les segments rsultant des transformations sur s itres jusqu' n.

    # let rec dessin_r s n l =
    if n = 0 then ()
    else
    begin
    draw_seg s;
    List.iter (fun t -> dessin_r (app_trans s t) (n-1) l) l
    end ;;
    val dessin_r : seg_pol -> int -> (float * float * float) list -> unit = <fun>


  5. Vrifier que le programme suivant produit bien les images de la figure 5.10.

    let pi = 3.1415927 ;;
    let s = {x=100.; y= 0.; a= pi /. 2.; r = 100.} ;;
    dessin_r s 6 [ (-.pi/.2.),0.6,1.; (pi/.2.), 0.6,1.0] ;;
    Graphics.clear_graph();;
    dessin_r s 6 [(-.pi /. 6.), 0.6, 0.766;
    (-.pi /. 4.), 0.55, 0.333;
    (pi /. 3.), 0.4, 0.5 ] ;;

    # Graphics.close_graph();;
    - : unit = ()
    # Graphics.open_graph ":0 200x200";;
    - : unit = ()
    # let pi = 3.1415927 ;;
    val pi : float = 3.1415927
    # let s = {x=100.; y= 0.; a= pi /. 2.; r = 100.} ;;
    val s : seg_pol = {x=100; y=0; r=100; a=1.57079635}
    # dessin_r s 6 [ (-.pi/.2.),0.6,1.; (pi/.2.), 0.6,1.0] ;;
    - : unit = ()
    # Graphics.clear_graph();;
    - : unit = ()
    # dessin_r s 6 [(-.pi /. 6.), 0.6, 0.766;
    (-.pi /. 4.), 0.55, 0.333;
    (pi /. 3.), 0.4, 0.5 ] ;;
    - : unit = ()


Figure 5.10 : dessins rcursifs


diteur de bitmaps

On cherche un crire un petit diteur de bitmap ( la manire de la commande bitmap de X-window). Pour cela on reprsente un bitmap par ses dimensions (largeur et hauteur), la taille des pixels, et un tableau 2 dimensions de boolens.

  1. Dfinir un type etat_bitmap dcrivant l'information ncessaire pour conserver les valeurs des pixels, la taille du bitmap et les couleurs d'affichage et d'effacement.


    # type etat_bitmap =
    {w : int; h : int; fg : Graphics.color; bg : Graphics.color;
    pix : bool array array; s : int} ;;
    type etat_bitmap =
    { w: int;
    h: int;
    fg: Graphics.color;
    bg: Graphics.color;
    pix: bool array array;
    s: int }


  2. crire les fonctions de cration d'un bitmap (create_bitmap) et d'affichage d'un bitmap (draw_bitmap) .

    # let create_bitmap x y f g t =
    let r = Array.make_matrix x y false in
    { w = x; h = y; fg = f; bg = g; pix = r; s = t} ;;
    val create_bitmap :
    int -> int -> Graphics.color -> Graphics.color -> int -> etat_bitmap =
    <fun>

    # let draw_pix i j s c =
    Graphics.set_color c;
    Graphics.fill_rect (i*s+1) (j*s+1) (s-1) (s-1) ;;
    val draw_pix : int -> int -> int -> Graphics.color -> unit = <fun>

    # let draw_bitmap b =
    for i=0 to b.w-1 do
    for j=0 to b.h-1 do
    draw_pix i j b.s (if b.pix.(i).(j) then b.fg else b.bg)
    done
    done ;;
    val draw_bitmap : etat_bitmap -> unit = <fun>


  3. crire les fonctions read_bitmap et write_bitmap qui respectivement lit et crit un bitmap dans un fichier pass en paramtre en suivant le format ASCII de X-window. Si le fichier n'existe pas, alors la fonction de lecture cre un nouveau bitmap en utilisant la fonction create_bitmap.

    # let read_file filename =
    let ic = open_in filename in
    let rec aux () =
    try
    let line = (input_line ic) in
    line :: (aux ())
    with End_of_file -> close_in ic ; []
    in aux ();;
    val read_file : string -> string list = <fun>

    # let read_bitmap filename =
    let r = Array.of_list (read_file filename) in
    let h = Array.length r in
    let w = String.length r.(0) in
    let b = create_bitmap w h Graphics.black Graphics.white 10 in
    for j = 0 to h - 1 do
    for i = 0 to w - 1 do
    b.pix.(i).(j) <- ( r.(j).[i] = '#')
    done
    done;
    b ;;
    val read_bitmap : string -> etat_bitmap = <fun>

    # let save_bitmap filename b =
    let oc = open_out filename in
    let f x = output_char oc (if x then '#' else '-') in
    Array.iter (fun x -> (Array.iter f x); output_char oc '\n') b.pix ;
    close_out oc ;;
    val save_bitmap : string -> etat_bitmap -> unit = <fun>
    Un pixel allum est reprsent par le caractre #, l'absence d'un pixel par le caractre -. Chaque ligne de caractres reprsente une ligne du bitmap. On pourra tester le programme en utilisant les fonctions atobm et bmtoa de X-window qui effectueront les conversions entre ce format ASCII et le format des bitmaps crs par la commande bitmap. Voici un exemple.

    
     ###################-------------#######---------######
     ###################---------------###-------------##--
     ###-----###-----###---------------###-------------#---
     ##------###------##----------------###-----------##---
     #-------###-------#-----------------###---------##----
     #-------###-------#-----------------###--------##-----
     --------###--------------------------###-------#------
     --------###-------###############-----###----##-------
     --------###-------###---------###------###--##--------
     --------###-------###----------##-------###-#---------
     --------###-------###-----------#-------#####---------
     --------###-------###-----------#--------###----------
     --------###-------###--------------------####---------
     --------###-------###--------------------####---------
     --------###-------###------#-----------##---###-------
     --------###-------###------#----------##----###-------
     --------###-------##########----------#------###------
     --------###-------##########---------##-------###-----
     --------###-------###------#--------##--------###-----
     --------###-------###------#-------##----------###----
     --------###-------###--------------#------------###---
     ------#######-----###-----------#######--------#######
     ------------------###---------------------------------
     ------------------###-----------#---------------------
     ------------------###-----------#---------------------
     ------------------###----------##---------------------
     ------------------###---------###---------------------
     ------------------###############---------------------
    
  4. On reprend le squelette de boucle d'interaction de la page ?? pour construire l'interface graphique de l'diteur. L'interface homme-machine est fort simple. Le bitmap est affich en permanence dans la fentre graphique. Un clic souris sur une des cases du bitmap inverse sa valeur. Ce changement est rpercut l'cran. L'appui sur la touche 'S' sauve le bitmap dans le fichier. L'appui sur la touche 'Q' fait sortir du programme.
    • crire la fonction start de type etat_bitmap -> unit -> unit qui ouvre la fentre graphique et affiche le bitmap pass en paramtre.

      # exception Fin ;;
      exception Fin
      # let squel f_init f_end f_key f_mouse f_except =
      f_init ();
      try
      while true do
      try
      let s = Graphics.wait_next_event
      [Graphics.Button_down; Graphics.Key_pressed] in
      if s.Graphics.keypressed
      then f_key s.Graphics.key
      else if s.Graphics.button
      then f_mouse s.Graphics.mouse_x s.Graphics.mouse_y
      with
      Fin -> raise Fin
      | e -> f_except e
      done
      with
      Fin -> f_end () ;;
      val squel :
      (unit -> 'a) ->
      (unit -> unit) ->
      (char -> unit) -> (int -> int -> unit) -> (exn -> unit) -> unit = <fun>

      # let start b () =
      let ow = 1+b.w*b.s and oh = 1+b.h*b.s in
      Graphics.open_graph (":0 " ^ (string_of_int ow) ^ "x" ^ (string_of_int oh)) ;
      Graphics.set_color (Graphics.rgb 150 150 150) ;
      Graphics.fill_rect 0 0 ow oh ;
      draw_bitmap b ;;
      val start : etat_bitmap -> unit -> unit = <fun>
    • crire la fonction stop qui ferme la fentre graphique et sort du programme.

      # let stop () = Graphics.close_graph() ; exit 0 ;;
      val stop : unit -> 'a = <fun>
    • crire la fonction mouse de type etat_bitmap -> int -> int -> unit qui modifie l'tat du pixel correspondant au clic souris et affiche ce changement.

      # let mouse b x y =
      let i,j = (x / b.s),(y/b.s) in
      if ( i < b.w ) && ( j < b.h) then
      begin
      b.pix.(i).(j) <- not b.pix.(i).(j) ;
      draw_pix i j b.s (if b.pix.(i).(j) then b.fg else b.bg)
      end ;;
      val mouse : etat_bitmap -> int -> int -> unit = <fun>
    • crire la fonction key de type string -> etat_bitmap -> char -> unit qui prend en paramtre un nom de fichier, un bitmap et le caractre de la touche appuye et effectue les actions associes : sauvegarde dans un fichier pour la touche 'S' et dclenchement de l'exception Fin pour la touche 'Q'.

      # let key filename b c =
      match c with
      'q' | 'Q' -> raise Fin
      | 's' | 'S' -> save_bitmap filename b
      | _ -> () ;;
      val key : string -> etat_bitmap -> char -> unit = <fun>

  5. crire une fonction go qui prend en paramtre un nom de fichier, charge le bitmap puis l'affiche et lance la boucle d'interaction.

    # let go name =
    let b = try
    read_bitmap name
    with
    _ -> create_bitmap 10 10 Graphics.black Graphics.white 10
    in squel (start b) stop (key name b) (mouse b) (fun e -> ()) ;;
    val go : string -> unit = <fun>

Ver de Terre

Le ver de terre est un petit organisme, longiforme, d'une certaine taille qui va crotre soit avec le temps soit en ramassant des objets dans un monde. Le ver de terre est toujours en mouvement selon une direction. Ses actions propres ou diriges par un joueur permettent uniquement un changement de direction. Le ver de terre disparat s'il touche un bord du monde ou s'il passe sur une partie de son corps. On le reprsente le plus souvent par un vecteur de coordonnes avec deux indices principaux : sa tte et sa queue. Un mouvement sera donc le calcul des nouvelles coordonnes de sa tte, et son affichage, et l'effacement de sa queue. Une croissance ne modifiera que sa tte sans toucher la queue du ver de terre.
  1. crire le ou les types Objective CAML pour reprsenter un ver de terre et le monde o il volue. On peut reprsenter un ver de terre par une file d'attente de ses coordonnes.

    # type cell = Vide | Pleine ;;
    type cell = | Vide | Pleine

    # type monde = { l : int; h : int; cases : cell array array } ;;
    type monde = { l: int; h: int; cases: cell array array }

    # type vdt = { mutable tete : int; mutable queue : int; mutable taille : int;
    mutable vx : int; mutable vy : int;
    pos : (int * int) array } ;;
    type vdt =
    { mutable tete: int;
    mutable queue: int;
    mutable taille: int;
    mutable vx: int;
    mutable vy: int;
    pos: (int * int) array }

    # type jeu = { m : monde; v : vdt; t_cell : int;
    fg : Graphics.color; bg : Graphics.color } ;;
    type jeu =
    { m: monde;
    v: vdt;
    t_cell: int;
    fg: Graphics.color;
    bg: Graphics.color }


  2. crire les fonctions d'initialisation et d'affichage d'un ver de terre dans un monde.

    # let init_monde larg haut =
    { l = larg; h = haut; cases = Array.create_matrix larg haut Vide} ;;
    val init_monde : int -> int -> monde = <fun>

    # let init_vdt t t_max larg =
    if t > larg then failwith "init_vdt"
    else
    begin
    let v = { tete = t-1; queue = 0; taille = t; vx = 1; vy = 0;
    pos = Array.create t_max (0,0) } in
    let y = larg / 2
    and rx = ref (larg/2 - t/2) in
    for i=0 to t-1 do v.pos.(i) <- (!rx,y) ; incr rx done ;
    v
    end ;;
    val init_vdt : int -> int -> int -> vdt = <fun>

    # let init_jeu t t_max larg haut tc c1 c2 =
    let j = { m = init_monde larg haut; v = init_vdt t t_max larg;
    t_cell = tc; fg = c1; bg = c2 } in
    for i=j.v.tete to j.v.queue do
    let (x,y) = j.v.pos.(i) in
    j.m.cases.(x).(y) <- Pleine
    done ;
    j ;;
    val init_jeu :
    int -> int -> int -> int -> int -> Graphics.color -> Graphics.color -> jeu =
    <fun>

    # let affiche_case x y t c =
    Graphics.set_color c;
    Graphics.fill_rect (x*t) (y*t) t t ;;
    val affiche_case : int -> int -> int -> Graphics.color -> unit = <fun>

    # let affiche_monde jeu =
    let m = jeu.m in
    for i=0 to m.l-1 do
    for j=0 to m.h-1 do
    let col = if m.cases.(i).(j) = Pleine then jeu.fg else jeu.bg in
    affiche_case i j jeu.t_cell col
    done
    done ;;
    val affiche_monde : jeu -> unit = <fun>


  3. Modifier la fonction squel de squelette du programme qui chaque tour dans la boucle d'interaction effectue une action, paramtre par une fonction. La gestion de l'vnement clavier ne doit pas tre bloquante.

    (************** interaction **********)

    # let tempo ti =
    for i = 0 to ti do ignore (i * ti * ti ) done ;;
    val tempo : int -> unit = <fun>

    # exception Fin;;
    exception Fin
    # let squel f_init f_end f_key f_mouse f_except f_run =
    f_init ();
    try
    while true do
    try
    tempo 200000 ;
    if Graphics.key_pressed() then f_key (Graphics.read_key()) ;
    f_run ()
    with
    Fin -> raise Fin
    | e -> f_except e
    done
    with
    Fin -> f_end () ;;
    val squel :
    (unit -> 'a) ->
    (unit -> unit) ->
    (char -> unit) -> 'b -> (exn -> 'c) -> (unit -> 'c) -> unit = <fun>


  4. crire la fonction run qui fait avancer le ver de terre dans le jeu. Cette fonction dclenche les exceptions Gagne (si le ver a atteint une certaine taille) et Perdu s'il rencontre une case pleine ou un bord du monde.

    # exception Perdu;;
    exception Perdu
    # exception Gagne;;
    exception Gagne

    # let run jeu temps itemps () =
    incr temps;
    let v = jeu.v in
    let t = Array.length v.pos in
    let ox,oy = v.pos.(v.tete) in
    let nx,ny = ox+v.vx,oy+v.vy in
    if (nx < 0 ) || (nx >= jeu.m.l) || (ny < 0) || (ny >= jeu.m.h)
    then raise Perdu
    else if jeu.m.cases.(nx).(ny) = Pleine then raise Perdu
    else if v.tete = v.queue then raise Gagne
    else
    begin
    let ntete = (v.tete + 1) mod t in
    v.tete <- ntete ;
    v.pos.(v.tete) <- (nx,ny);
    jeu.m.cases.(nx).(ny) <- Pleine ;
    affiche_case nx ny jeu.t_cell jeu.fg ;
    if (!temps mod !itemps < (!itemps - 2)) then
    begin
    let qx,qy = v.pos.(v.queue) in
    jeu.m.cases.(qx).(qy) <- Vide ;
    affiche_case qx qy jeu.t_cell jeu.bg ;
    v.queue <- (v.queue + 1) mod t
    end
    end ;;
    val run : jeu -> int ref -> int ref -> unit -> unit = <fun>


  5. crire la fonction d'interaction clavier qui modifie la direction du ver de terre.

    # let key lact jeu c =
    match c with
    'q' | 'Q' -> raise Fin
    | 'p' | 'P' -> ignore (Graphics.read_key ())
    | '2' | '4' | '6' | '8' ->
    let dx,dy = List.assoc c lact in
    jeu.v.vx <- dx ;
    jeu.v.vy <- dy
    | _ -> () ;;
    val key : (char * (int * int)) list -> jeu -> char -> unit = <fun>


  6. crire les autres fonctions utilitaires de gestion de l'interaction et les passer au nouveau squelette de programme.

    # let start jeu () =
    let ow = jeu.t_cell * jeu.m.l and oh = jeu.t_cell * jeu.m.h in
    let size = (string_of_int ow) ^ "x" ^ (string_of_int oh) in
    Graphics.open_graph (":0 " ^ size) ;
    Graphics.set_color (Graphics.rgb 150 150 150);
    Graphics.fill_rect 0 0 ow oh;
    affiche_monde jeu;
    ignore (Graphics.read_key()) ;;
    val start : jeu -> unit -> unit = <fun>

    # let stop jeu () =
    ignore (Graphics.read_key());
    Graphics.close_graph() ;;
    val stop : 'a -> unit -> unit = <fun>

    # let mouse x y = () ;;
    val mouse : 'a -> 'b -> unit = <fun>

    # let except e = match e with
    Perdu -> print_endline "PERDU"; raise Fin
    | Gagne -> print_endline "GAGNE"; raise Fin
    | e -> raise e ;;
    val except : exn -> 'a = <fun>


  7. crire la fonction principale de lancement de l'application.

    # let la = [ ('2',(0,-1)) ; ('4',(-1,0)) ; ('6',(1,0)) ; ('8',(0,1)) ] ;;
    val la : (char * (int * int)) list =
    ['2', (0, -1); '4', (-1, 0); '6', (1, 0); '8', (0, ...)]

    # let go larg haut tc =
    let col = Graphics.rgb 150 150 150 in
    let jeu = init_jeu 5 (larg*haut /2) larg haut tc Graphics.black col in
    squel (start jeu) (stop jeu) (key la jeu) mouse except (run jeu (ref 0) (ref 100));;
    val go : int -> int -> int -> unit = <fun>

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora054.gif0000644000000000000000000000223407073350152014272 0ustar GIF89a?tyyy!,?tڋH扦ʶ L׮a p¢L*%" JK'j"+ wޱN e:y`09HX6huxLjx(Y)F9uY Iyj :ښe {0{UkK% a{{0,%3`|ʬ  ]m[w1NRkZ1/l5/S@y7auı׾v5{B Mf<-AG 0z㣵8twK Mw'&8[ַ FƒMHfXW*4am(b#"'$Ȣ-܊04؄8<7)DH(6dPFTr)Z\v9`'i{hIޚln)grtyϝg~ h YnCh.'Z"NڨARDV`ˣE|J' Rjᩪj"`k|ʞ /!(+뱶N !+D~^ QעJmO,>8 mʻsN/"jjξ9;p p+py gZ0]{qi +>$SkdL.*c"z$OӺ̳ʬl/9w+Ͳ:t{0M+s'\1S ]ŔxSstٙ 0w7)˭c=u ΀N0{8#$q9fWxەKqEyzJ' ۢZ^ Oz)E{2"o*'O{D/$Wt= yk;ocaml-book-1.0/fr/html/book-ora070.gif0000644000000000000000000001006107073350152014265 0ustar GIF89aywvJHF!,ڋ޼nHy Dz\qSdL$g$fsɀ2#b\Am 墶Zyy={8-w7(u Gg(gxٸW5vwxfFhIDV幪*K; ee6,;V[z57X.>.!}Dn.?noG?eS|/@+ d :Đ0ĉ+Z1;zhȑ$K<2ʕ,[| 3̙4kڼ2$Ν<{ 4Р:=4ҥ@2} 5ԩ+*EHQۭ3Vخ ǚr[.A‹!)d Mnt>n:D*lS`լ~RxqCohk?ъBh 3JA R XXA* 8A>j_j!`(.a. !æ gpWJ>HZbQ!@4 X-k)c"$E]h]/1NA=юHcCBrDh}cBC&2<%G6^Rb!MI:~R',UJNq|c>E\)he(HFd|KP2%1L'rEl-IY|0OgvӛTf8M 3}5rQ0's(Nv1g5׷zSg42~3ټC)͵3ˣD y4P/>PhJt!)L!il*ST^7@%w-^de4+Q=*p  ~2.q% X8[[[ߊVu士]j?u۫_K,t[7Ɓ{ONɱE\\fYi#$p6u6BՊϨZk]ӛImHZ^ymoG*ۥg#-Mj6x kJyK,ͩW>6]w0.A܆涃wE&+b=vA:}*7e[zZnӽhIyNW/H"Tes~+\`)a wSB#"w8uU1`\L*r~Ӝ f= ܞO8Ao5cJ)f|!J>"\_.8p"}e*2LKV\r|g:ޤٌ]<ǸL|M@U*ڋ3w5o xem6!Z8G.CzQ&_~a}T7ǽwl[)Hsuwcc<5kfz-_7.xYI#7-?ho~ L4 zef2{tHuK|GGTLPb}hN c X{h_G5zfVog 8fGT8z(hDM'GhDM3Q{dSF"7z,xlVa|ewOtizg|aMcOXzv0NGOGr~G owr'GxgxXXNxU(oR8{_xWY.c1vxXz@aAx u{KxC(lSfWpc8W_özhy'V^7i&b8x 5#szyN7uWrR'VuYv1WUd>еT8GŘt(=gȌL898(t戎}Zgt5Xp1wZWre8xr؋9 '{erHx8AuWvuy]gHmwƑW QɃjXX|ىo*)ji)I TXF7-4yPIuIjRq0I)؉^X}YI|X98ؒ\X'|KZaĖ}-}^hf {y鑏>i;e(hG5R+~Ofh.\9Sh7%X2٧u)^EK)cb )9nI};ƙuvQEygIB|wٖHhv {}b)TWNdW kٞ&|{_9﹕Oy5Y J9k[uX㇦h: A:xwsAwJ@m垝vDIBIv`)9aW1J·S6i'82)C)j/9gt(H`[jcXdjezi*VjʦئozYp*b:u:pu8BzD Jzx:*:٨mFfaz~z$oM*PJNڐ53zd bȨt磀J<Ɉ=٩FړVZ 'ڟ"*eY+x {Ѻ6ʞGjڪzwV7)kys|z|ziiFiƔi!*ǘϚJN8~{KJu3F i KZ7bٜǯ [ $[(,KXQ!kʱe13[ q7c9+˳= aZ#+~*ʞʟېچ>ڱ*Kz(jxJd ?k!:;ZNڒʪЪhvGJ9Jtk{[CJǪ\ڟUgZj+Gz}ڻG: { Uʋ6+q̺tkڑvZȽߛ+H;t狾?s7KP;ocaml-book-1.0/fr/html/book-ora157.html0000644000000000000000000010204607421273602014500 0ustar Exercices Prcdent Index Suivant

Exercices

Classes et modules pour structures de donnes

On dsire construire des hirarchies de classes partir de l'application de foncteurs pour des structures de donnes classiques.

On dfinit les signatures suivantes :

# module type ELEMENT = 
sig
class element :
string ->
object
method to_string : unit -> string
method of_string : string -> unit
end
end ;;

# module type STRUCTURE =
sig
class ['a] structure :
object
method add : 'a -> unit
method del : 'a -> unit
method mem : 'a -> bool
method get : unit -> 'a
method all : unit -> 'a list
method iter : ('a -> unit) -> unit
end
end ;;
  1. crire un module 2 paramtres M1 et M2 de types ELEMENT et STRUCTURE, construisant une sous-classe de ['a] structure dont le 'a est contraint M1.element.

    # module Struct_Elmt (E:ELEMENT) (S:STRUCTURE) =
    struct
    class e_structure =
    object
    inherit [E.element] S.structure
    end
    end ;;
    module Struct_Elmt :
    functor(E : ELEMENT) ->
    functor(S : STRUCTURE) ->
    sig
    class e_structure :
    object
    method add : E.element -> unit
    method all : unit -> E.element list
    method del : E.element -> unit
    method get : unit -> E.element
    method iter : (E.element -> unit) -> unit
    method mem : E.element -> bool
    end
    end


  2. crire un module simple Entier respectant la signature ELEMENT.

    # module Entier : ELEMENT =
    struct
    class element v =
    object
    val mutable n = int_of_string v
    method to_string () = string_of_int n
    method of_string x = n <- (int_of_string x)
    end
    end ;;
    module Entier : ELEMENT


  3. crire un module simple Pile respectant la signature de STRUCTURE.

    # module Pile : STRUCTURE =
    struct
    class ['a] structure =
    object
    val mutable p = ( [] : 'a list )
    method add x = p <- x::p
    method del x = p <- List.filter ((<>) x) p
    method mem x = List.mem x p
    method get () = List.hd p
    method all () = p
    method iter f = List.iter f p
    end
    end ;;
    module Pile : STRUCTURE


  4. Appliquer le foncteur ces deux paramtres.

    # module Pile_Entier = Struct_Elmt(Entier)(Pile) ;;
    module Pile_Entier :
    sig
    class e_structure :
    object
    method add : Entier.element -> unit
    method all : unit -> Entier.element list
    method del : Entier.element -> unit
    method get : unit -> Entier.element
    method iter : (Entier.element -> unit) -> unit
    method mem : Entier.element -> bool
    end
    end


  5. Modifier le foncteur initial en ajoutant les mthodes to_string et of_string.

    # let split c s =
    let suffix s i =
    try String.sub s i ((String.length s)-i)
    with Invalid_argument("String.sub") -> ""
    in
    let rec split_from n =
    try let p = String.index_from s n c
    in (String.sub s n (p-n)) :: (split_from (p+1))
    with Not_found -> [ suffix s n ]
    in
    if s="" then [] else split_from 0 ;;
    val split : char -> string -> string list = <fun>

    # module Elmt_Struct (E:ELEMENT) (S:STRUCTURE) =
    struct
    class element v =
    object (self)
    val n = new S.structure
    method to_string () =
    let res = ref "" in
    let f x = res := !res ^ ((x#to_string ()) ^ " ") in
    n#iter f ;
    !res
    method of_string x =
    let l = split ' ' x in
    List.iter (fun x -> n#add (new E.element x)) l
    initializer self#of_string v
    end
    end ;;
    module Elmt_Struct :
    functor(E : ELEMENT) ->
    functor(S : STRUCTURE) ->
    sig
    class element :
    string ->
    object
    val n : E.element S.structure
    method of_string : string -> unit
    method to_string : unit -> string
    end
    end


  6. Appliquer le foncteur de nouveau, puis l'appliquer au rsultat.

# module Entier_Pile = Elmt_Struct (Entier) (Pile) ;;
module Entier_Pile :
sig
class element :
string ->
object
val n : Entier.element Pile.structure
method of_string : string -> unit
method to_string : unit -> string
end
end

# module Entier_Pile_Pile = Elmt_Struct (Entier_Pile) (Pile) ;;
module Entier_Pile_Pile :
sig
class element :
string ->
object
val n : Entier_Pile.element Pile.structure
method of_string : string -> unit
method to_string : unit -> string
end
end


Types abstraits

En reprenant l'exercice prcdent, on cherche implanter un module de signature ELEMENT dont la classe element utilise une variable d'instance d'un type abstrait.

On dfinit le type paramtr suivant :

# type 'a t = {mutable x : 'a t; f : 'a t -> unit};;


  1. crire les fonctions apply, from_string et to_string. Ces deux dernires fonctions utilisent le module Marshal.

    # let apply val_abs = val_abs.f val_abs.x ;;
    val apply : 'a t -> unit = <fun>
    # let from_string s = Marshal.from_string s 0 ;;
    val from_string : string -> 'a = <fun>
    # let to_string v = Marshal.to_string v [Marshal.Closures] ;;
    val to_string : 'a -> string = <fun>


  2. crire une signature S correspondant la signature infre prcdemment en abstrayant le type t.

    # module type S =
    sig
    type t
    val apply : t -> unit
    val from_string : string -> t
    val to_string : t -> string
    end ;;
    module type S =
    sig
    type t
    val apply : t -> unit
    val from_string : string -> t
    val to_string : t -> string
    end


  3. crire un foncteur qui prend un paramtre de signature S et retourne un module dont la signature est compatible avec ELEMENT.

    # module Element_of (M:S) =
    struct
    class element v =
    object
    val mutable n = M.from_string v
    method to_string () = M.to_string n
    method of_string x = n <- M.from_string x
    end
    end ;;
    module Element_of :
    functor(M : S) ->
    sig
    class element :
    string ->
    object
    val mutable n : M.t
    method of_string : string -> unit
    method to_string : unit -> string
    end
    end


  4. Utiliser le module rsultat comme paramtre du module de l'exercice prcdent.

    # module Abstrait_Pile (M:S) = Elmt_Struct (Element_of(M)) ;;
    module Abstrait_Pile :
    functor(M : S) ->
    functor(S : STRUCTURE) ->
    sig
    class element :
    string ->
    object
    val n : Element_of(M).element S.structure
    method of_string : string -> unit
    method to_string : unit -> string
    end
    end

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora116.html0000644000000000000000000010754007421273602014477 0ustar Exploration des valeurs Objective CAML en C Prcdent Index Suivant

Exploration des valeurs Objective CAML en C

Le langage Objective CAML ne construit pas les valeurs de la mme faon que le langage C, y compris pour les valeurs simples comme les entiers. Cela provient de la ncessit pour le GC d'Objective CAML de conserver des informations supplmentaires pour effectuer la rcupration mmoire. Comme la reprsentation des valeurs en Objective CAML est uniforme, elles seront vues en C comme des valeurs d'un type unique, nomm value.

Quand Objective CAML appelle une fonction C et lui passe des arguments, ceux-ci doivent tre dcods pour tre utiliss en C. Il en est de mme pour le rsultat d'une fonction C appele depuis Objective CAML. Celui-ci sera encod avant d'tre transmis Objective CAML.

Un certain nombre de macros et de fonctions C ralisent ces oprations de conversion de valeurs. Elles sont regroupes dans les fichiers dcrits la figure 12.3 qui sont fournis dans la distribution d'Objective CAML. Ces fichiers se trouvent dans le rpertoire LIBOCAML/caml o LIBOCAML est le rpertoire o les bibliothques d'Objective CAML sont installes3.


   
mlvalues.h dfinition du type value et macros de manipulations de ces valeurs.
alloc.h fonctions d'allocations de valeurs Objective CAML.
memory.h macros de plus haut niveau pour la manipulation de valeurs.

Figure 12.3 : Fichiers utiles pour l'interfaage avec C


Classification des valeurs de type value

Une valeur de type value peut tre soit :
  • un entier ;
  • un pointeur vers le tas d'Objective CAML ;
  • un pointeur hors du tas d'Objective CAML.
Le tas d'Objective CAML est la zone d'allocation gre par le GC d'un programme Objective CAML. Un programme C, peut pour sa part crer et manipuler des valeurs dans sa propre zone d'allocation et communiquer une rfrence de cette valeur Objective CAML.

On dispose des macros de vrification et de conversion de types dcrites la figure 12.4.

   
Is_long(v) v est un entier Objective CAML?
Is_block(v) v est un pointeur Objective CAML?
   
Long_val(v) retourne un entier C long
Int_val(v) retourne un entier C
Bool_val(v) retourne un ``boolen'' C (0 pour false)

Figure 12.4 : Conversion des valeurs immdiates


Il est noter que C possde plusieurs types pour les entier : short, int et long alors qu'Objective CAML n'a qu'une seule reprsentation des entiers : le type int.

Accs aux valeurs immdiates

Les entiers servent encoder toutes les valeurs immdiates d'Objective CAML :
  • les entiers sont cods par leur valeur,
  • les caractres sont cods par leur code ASCII,
  • les constructeurs constants sont cods par un entier dnotant leur place dans la dclaration du type : le n me constructeur constant d'un type numr sera cod par n-1.
Le programme suivant dfinit une fonction C inspecte d'inspection une valeur de type value :
#include <stdio.h>
#include <caml/mlvalues.h>
value inspecte (value v)
{
if (Is_long(v))
printf ("v est un entier (%d) : %d",(int) v,Long_val(v));
else if (Is_block(v)) printf ("v est un pointeur") ;
else printf ("v n'est ni un entier ni un pointeur ???") ;
printf(" ");
fflush(stdout) ;
return v ;
}
La fonction inspecte teste si son argument est un entier Objective CAML. Si oui elle en affiche la valeur vue comme un int C puis cette mme valeur convertie par la macro Long_val en entier C (long).

La reprsentation des entiers d'Objective CAML diffre de celle de C comme le montre l'utilisation suivante de la fonction inspecte.

# external inspecte : 'a -> 'a = "inspecte" ;;
external inspecte : 'a -> 'a = "inspecte"
# inspecte 123 ;;
v est un entier (247) : 123 - : int = 123
# inspecte max_int;;
v est un entier (2147483647) : 1073741823 - : int = 1073741823
D'autres valeurs de type prdfinis, comme char et bool, sont reprsentes par des valeurs immdiates.

# inspecte 'A' ;;
v est un entier (131) : 65 - : char = 'A'
# inspecte true ;;
v est un entier (3) : 1 - : bool = true
# inspecte false ;;
v est un entier (1) : 0 - : bool = false
# inspecte [] ;;
v est un entier (1) : 0 - : '_a list = []


Soit le type foo dfini en Objective CAML :

# type foo = C1 | C2 of int | C3 | C4 ;;


La fonction inspecte affichera des rsultats de nature diffrente en prsence d'un constructeur constant ou d'un constructeur fonctionnel.

# inspecte C1 ;;
v est un entier (1) : 0 - : foo = C1
# inspecte C4 ;;
v est un entier (5) : 2 - : foo = C4
# inspecte (C2 1) ;;
v est un pointeur - : foo = C2 1


Lorsqu'elle reconnat une valeur immdiate, la fonction inspecte affiche entre parenthses le contenu << physique >> de cette valeur (i.e. sa valeur vue comme un entier sign sur un mot -- type int de C), puis elle affiche son contenu << logique >> (la valeur obtenue par les macros de dcodage). Nos exemples manifestent la diffrence entre ces deux contenus. Celle-ci provient du bit de tag4 qu'utilise le GC pour distinguer une valeur immdiate d'un pointeur (voir chapitre 9, page ??).

Discrimination des valeurs structures

Les valeurs structures d'Objective CAML correspondent toutes les valeurs non immdiates : n-uplets, listes non vides, constructeurs avec au moins un argument, vecteurs, enregistrements, fermetures et valeurs de type abstrait. Elles sont alloues dans le tas Objective CAML sous forme de blocs mmoire. Un bloc possde un en-tte contenant une information sur la nature du bloc ainsi que sa taille en mots machine. La figure 12.5 dcrit la structure d'un bloc pour une machine mots de 32 bits.


Figure 12.5 : communication de valeurs entre Objective CAML et C


Les deux bits << couleur >> sont utiliss par le GC lors de son exploration du graphe des valeurs du tas (voir chapitre 9, page ??). Le champ << tag >> (que nous appellerons tag, pour faire court) indique le << type >> de valeurs contenues dans le suite du bloc. Les fonctions de la figure 12.6 retournent ces informations.

   
Wosize_val(v) retourne la taille du bloc (sans l'en-tte)
Tag_val(v) retourne le tag du bloc

Figure 12.6 : Informations sur les blocs mmoire


Les diffrentes valeur de tag sont donnes la figure 12.7.

de 0 No_scan_tag-1 un tableau Objective CAML: un tableau de value
Closure_tag une fermeture
String_tag une chane de caractres
Double_tag un flottant en double-prcision
Double_array_tag un tableau de flottants
Abstract_tag un type de donnes abstrait
Final_tag idem avec une fonction de finalisation

Figure 12.7 : Description des marques des blocs mmoire


Suivant la valeur retourne par Tag_val, on utilise diffrentes macros pour accder aux contenus des blocs mmoire. Lorsque cette valeur est infrieure No_scan_tag, le bloc mmoire est structur comme un tableau de value. Les macros d'accs sont dcrites la figure 12.8. En accord avec les conventions de C et d'Objective CAML le premier lment d'un tableau se situe l'indice 0.

   
Field(v,n) retourne la n me value d'un tableau.
Code_val(v) retourne le pointeur de code d'une fermeture.
string_length(v) retourne la longueur d'une chane.
Byte(v,n) retourne le n me caractre d'une chane comme char.
Byte_u(v,n) idem mais retourne un unsigned char.
String_val(v) retourne la chane de caractre comme (char *).
Double_val(v) retourne le flottant cod dans v.
Double_field(v,n) retourne le n me lment d'un tableau de flottants.

Figure 12.8 : Accs aux lments d'un bloc


Comme nous l'avons fait pour les valeurs immdiates, nous allons dfinir une fonction d'inspection des blocs mmoires. La fonction C print_bloc teste si son argument (de type value) est une valeur immdiate ou un bloc mmoire. Dans ce dernier cas elle affiche le type du bloc et sa valeur vue de C. Elle est appele depuis inspecte_bloc qui sera utilise partir d'un programme Objective CAML.

#include <stdio.h>
#include <caml/mlvalues.h>

void marge (int n)
{ for (;n>0;n--) printf("."); return; }

void print_bloc (value v,int m)
{
int taille,i ;
marge(m);
if (Is_long(v))
{ printf("valeur immediate (%d)\n", Long_val(v)); return; };
printf ("bloc : taille=%d - ", taille=Wosize_val(v));
switch (Tag_val(v))
{
case Closure_tag :
printf("fermeture - Arguments (%d) :\n",taille==1?0:taille-2);
marge(m+4); printf("pointeur de code : %d\n",Code_val(v)) ;
for (i=1;i<taille;i++) print_bloc(Field(v,i), m+4);
break;
case String_tag :
printf("string : %s (%s)\n", String_val(v),(char *) v);
break;
case Double_tag:
printf("flottant : %g\n", Double_val(v));
break;
case Double_array_tag :
printf ("tableau de flottants : ");
for (i=0;i<(taille/2);i++) printf(" %g", Double_field(v,i));
printf("\n");
break;
case Abstract_tag : printf("type abstrait\n"); break;
case Final_tag : printf("type abstrait + final\n"); break;
default:
if (Tag_val(v)>=No_scan_tag) { printf("inconnu"); break; };
printf("tableau de valeurs (Tag=%d) :\n",Tag_val(v));
for (i=0;i<taille;i++) print_bloc(Field(v,i),m+4);
}
return ;
}

value inspecte_bloc (value v)
{ print_bloc(v,4); fflush(stdout); return v; }

Les diffrentes marques de blocs se retrouvent dans les cas du switch. On redfinit ensuite une fonction inspecte :

# external inspecte : 'a -> 'a = "inspecte_bloc" ;;
external inspecte : 'a -> 'a = "inspecte_bloc"
Celle-ci est utilise pour dcrire exprimentalement les reprsentations des valeurs structures Objective CAML. On vitera de lui passer une structure circulaire car son parcours bouclerait.

Tableaux, n-uplets et enregistrements

Les tableaux (array) et les n-uplets sont cods par des tableaux de value.

# inspecte [| 1; 2; 3 |] ;;
....bloc : taille=3 - tableau de valeurs (Tag=0) :
........valeur immediate (1)
........valeur immediate (2)
........valeur immediate (3)
- : int array = [|1; 2; 3|]
# inspecte ( 10 , true , () ) ;;
....bloc : taille=3 - tableau de valeurs (Tag=0) :
........valeur immediate (10)
........valeur immediate (1)
........valeur immediate (0)
- : int * bool * unit = 10, true, ()


Les enregistrements sont eux aussi cods par des tableaux de value dans le mme ordre que celui de la dclaration du type. Le fait qu'un champ soit modifiable ne change pas sa reprsentation physique.

# type foo = { ch1 : int ; mutable ch2:int } ;;
type foo = { ch1: int; mutable ch2: int }
# inspecte { ch1=10 ; ch2=20 } ;;
....bloc : taille=2 - tableau de valeurs (Tag=0) :
........valeur immediate (10)
........valeur immediate (20)
- : foo = {ch1=10; ch2=20}


Warning


Rien n'empche de modifier physiquement un champ non modifiable d'un enregistrement Objective CAML partir de fonctions C. C'est au programmeur de prendre garde ne pas introduire d'incohrence en utilisant une fonction C.


Types somme

Nous avons vu prcdemment que les constructeurs constants sont reprsents par des entiers. Les autres constructeurs sont cods par le tableau de leurs arguments. Le constructeur est identifi par la valeur de tag. Il reprsente le numro d'apparition du constructeur non constant dans la dclaration du type : 0 correspond au premier constructeur non constant, 1 au deuxime et ainsi de suite.

# type foo = C1 of int * int * int | C2 of int | C3 | C4 of int * int ;;
type foo = | C1 of int * int * int | C2 of int | C3 | C4 of int * int
# inspecte (C1 (1,2,3)) ;;
....bloc : taille=3 - tableau de valeurs (Tag=0) :
........valeur immediate (1)
........valeur immediate (2)
........valeur immediate (3)
- : foo = C1 (1, 2, 3)
# inspecte (C4 (1,2)) ;;
....bloc : taille=2 - tableau de valeurs (Tag=2) :
........valeur immediate (1)
........valeur immediate (2)
- : foo = C4 (1, 2)


Remarque


Le type list est un type somme dont la dclaration est :
type 'a list = [] | :: of 'a * 'a list. Il possde un seul constructeur non constant infixe (::) qui a un tag gal 0.


Chanes de caractres

Comme un caractre est cod sur un octet, la reprsentation des chanes de caractres utilisera un mot pour quatre caractres.

Warning


Les chanes de caractres Objective CAML peuvent contenir le caractre de code ASCII 0 qui est le caractre de fin de chane en C.


#include <stdio.h>
#include <caml/mlvalues.h>

value explore_string (value v)
{
char *s;
int i,taille;
s = (char *) v;
taille = Wosize_val(v) * sizeof(value);
for (i=0;i<taille;i++)
{
int p = (unsigned int) s[i] ;
if ((p>31) && (p<128)) printf("%c",s[i]) ;
else printf("(#%u)",p) ;
}
printf("\n");
fflush(stdout);
return v;
}

La fin d'une chane Objective CAML est dtermine par la taille du bloc la constituant, ainsi que par le dernier octet du dernier mot du bloc qui indique le nombre d'octets non utiliss dans le dernier mot. Les exemples ci-dessous permettent de comprendre le rle exact que joue ce dernier octet.

# external explore : string -> string = "explore_string" ;;
external explore : string -> string = "explore_string"
# ignore(explore "");
ignore(explore "a");
ignore(explore "ab");
ignore(explore "abc");
ignore(explore "abcd");
ignore(explore "abcd\000") ;;
(#0)(#0)(#0)(#3)
a(#0)(#0)(#2)
ab(#0)(#1)
abc(#0)
abcd(#0)(#0)(#0)(#3)
abcd(#0)(#0)(#0)(#2)
- : unit = ()
Dans les deux derniers cas ("abcd" et "abcd\000") les chanes explores sont respectivement de longueur 4 et 5. C'est pour cela que le dernier octet change de valeur pour ces deux valeurs.

Flottants et tableaux de flottants

Objective CAML ne possde qu'un seul type (float) pour les nombres virgule flottante. Les valeurs de type float sont alloues, elles sont reprsentes par un bloc de taille 2.

# inspecte 1.5 ;;
....bloc : taille=2 - flottant : 1.5
- : float = 1.5
# inspecte 0.0;;
....bloc : taille=2 - flottant : 0
- : float = 0


Les tableaux de flottants possdent une structure spciale qui permet d'optimiser leur occupation mmoire. C'est pourquoi ils ont un tag distinct de celui des autres tableaux ainsi qu'une macro d'accs particulire.

# inspecte [| 1.5 ; 2.5 ; 3.5 |] ;;
....bloc : taille=6 - tableau de flottants : 1.5 2.5 3.5
- : float array = [|1.5; 2.5; 3.5|]
Cette optimisation permet d'utiliser Objective CAML pour effectuer des calculs numriques qui manipulent ce genre de donnes beaucoup plus rapidement que s'il fallait passer par un pointeur dans le tas.

Warning


Si l'on alloue un bloc en C pour un tableau de flottants Objective CAML, il sera ncessaire d'allouer un bloc de taille double du nombre d'lments voulus.


En dehors des float array, les flottants contenus dans d'autres structures conservent leur reprsentation habituelle, c'est--dire qu'ils sont vus comme une valeur structure alloue dans le tas. L'exemple suivant inspecte une liste de flottants.

# inspecte [ 3.14; 1.2; 7.6];;
....bloc : taille=2 - tableau de valeurs (Tag=0) :
........bloc : taille=2 - flottant : 3.14
........bloc : taille=2 - tableau de valeurs (Tag=0) :
............bloc : taille=2 - flottant : 1.2
............bloc : taille=2 - tableau de valeurs (Tag=0) :
................bloc : taille=2 - flottant : 7.6
................valeur immediate (0)
- : float list = [3.14; 1.2; 7.6]
La liste est vue comme un bloc de taille 2 : sa tte et sa queue. La tte est un flottant, bloc de taille 2 aussi.

Fermetures

Une valeur fonctionnelle est reprsente d'une part par le code qui doit tre excut son application, et d'autre part par son environnement (voir chapitre 2, page??). Il y a deux faons d'obtenir une valeur fonctionnelle : en utilisation explicitement une abstraction (comme dans fun x -> x+1 ou en appliquant partiellement une fonction (comme dans (fun x -> fun y -> x+y) 1).

L'environnement d'une fermeture peut contenir trois catgories de variables : celles dclares globalement, celles dclares localement et les paramtres instancies par une application partielle. Du point de vue implantation, ces trois catgories de variables ont un traitement diffrent. Les variables globales font partie d'un environnement global et ne figurent pas explicitement dans les fermetures. Les variable locales et les paramtres instancis par application partielle peuvent apparatre dans la fermeture comme nous allons l'illustrer. Ainsi, du point de vue implantation, l'environnement d'une fermeture de concerne que des variables locales ou des paramtres abstraits.

Si la fermeture a un environnement vide, elle est uniquement constitue du pointeur vers le code :

# let f = fun x y z -> x+y+z ;;
val f : int -> int -> int -> int = <fun>
# inspecte f ;;
....bloc : taille=1 - fermeture - Arguments (0) :
........pointeur de code : 134893716
- : int -> int -> int -> int = <fun>
Si elle est obtenue par application partielle d'une abstraction sans dclaration locale, elle contient une valeur pour chacun des paramtres et une fermeture sans environnement.

# let a1 = f 1 ;;
val a1 : int -> int -> int = <fun>
# inspecte (a1) ;;
....bloc : taille=3 - fermeture - Arguments (1) :
........pointeur de code : 134893712
........bloc : taille=1 - fermeture - Arguments (0) :
............pointeur de code : 134893716
........valeur immediate (1)
- : int -> int -> int = <fun>
# let a2 = a1 2 ;;
val a2 : int -> int = <fun>
# inspecte (a2) ;;
....bloc : taille=4 - fermeture - Arguments (2) :
........pointeur de code : 134893712
........bloc : taille=1 - fermeture - Arguments (0) :
............pointeur de code : 134893716
........valeur immediate (1)
........valeur immediate (2)
- : int -> int = <fun>
La figure 12.9 illustre graphiquement l'affichage prcdent.


Figure 12.9 : reprsentation des fermetures


La fonction f ne contient pas de variable libre. Le pointeur de code d'une fermeture sans environnement pointe vers le code appliquer lorsque tous ses arguments sont passs, c'est--dire ici, le code correspondant x+y+z. Une fermeture avec environnement pointe vers un code unique (c'est le mme pour a1 et a2). Ce code vrifie quand on lui passe un nouvel argument si tous les arguments sont l. Si oui il empile les arguments et dclenche le code initial, sinon il cre une nouvelle fermeture. C'est ce qui se passe quand a1 est applique 2. On peut remarquer qu'une fermeture avec environnement contient toujours, comme premier lment de l'environnement, le pointeur vers la fermeture sans environnement dont l'application partielle est issue. Cela permettra, d'une part, de dclencher le code effectif et d'autre part, d'avoir une rfrence sur soi mme pour les fonctions rcursives.

En prsence d'une dclaration locale, l'application partielle donne la reprsentation suivante :

# let g x = let y=2 in fun z -> x+y+z ;;
val g : int -> int -> int = <fun>
# let a1 = g 1 ;;
val a1 : int -> int = <fun>
# inspecte a1 ;;
....bloc : taille=3 - fermeture - Arguments (1) :
........pointeur de code : 134893792
........valeur immediate (1)
........valeur immediate (2)
- : int -> int = <fun>


L'appel d'une fermeture C partir d'Objective CAML est dtaill la section 12.

Types abstraits

Une valeur d'un type abstrait est aussi reprsente par un tableau de value. En fait l'information sur le type n'est utile que pour le synthtiseur de types. aucun moment de l'excution d'un programme l'information de type n'est utile, seule la reprsentation mmoire doit tre connue du GC ainsi que la taille de la valeur.

Si on cre une valeur d'un type abstrait, comme 'a Stack.t, en fait sa reprsentation mmoire correspond celle de son implantation, c'est--dire ici comme une rfrence sur une liste.

# let p = Stack.create();;
val p : '_a Stack.t = <abstr>
# Stack.push 3 p;;
- : unit = ()
# inspecte p;;
....bloc : taille=1 - tableau de valeurs (Tag=0) :
........bloc : taille=2 - tableau de valeurs (Tag=0) :
............valeur immediate (3)
............valeur immediate (0)
- : int Stack.t = <abstr>
Par contre certains types abstraits sont implants en utilisant une reprsentation non exprimable en Objective CAML. C'est par exemple le cas pour les vecteurs de pointeurs faibles ou les canaux d'entres-sorties. Pour ceux-l la marque Abstract_tag sera utilise.

# let w = Weak.create 10;;
val w : '_a Weak.t = <abstr>
# Weak.set w 0 (Some p);;
- : unit = ()
# inspecte w;;
....bloc : taille=11 - type abstrait
- : int Stack.t Weak.t = <abstr>
Certains de ces types possdent aussi une fonction de finalisation, permettant une dernire action avant de disparatre (en particulier la libration de la mmoire qu'ils occupent). C'est particulirement utile pour rendre une ressource externe, comme un tampon d'entres-sorties. C'est pour cela que l'inspection de la sortie standard indiquera que le type out_channel possde une fonction de finalisation :

# inspecte (stdout) ;;
....bloc : taille=2 - type abstrait + final
- : out_channel = <abstr>



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora046.gif0000644000000000000000000000063107073350152014272 0ustar GIF89a$yyy!,$Z޼H扢Xʶ *Ģ4<L9Z5SjrTvjݦ8>Cfa3tYϟ\T%8xWw qfH"Yhٳ)@ٶXi +Zi[; L!J|[jL,m}=^; ^ޡ~. /O O^:nKKWB jL>kY+z3-FGdB!3eJ1rF3'Y2T0/ eYCl1]$q#PjJ%Bի@jiW"8f5 HZ{ܹtڽk;ocaml-book-1.0/fr/html/book-ora106.html0000644000000000000000000007714607421273602014506 0ustar Lexique Prcdent Index Suivant

Lexique

L'analyse lexicale est le pralable indispensable tout traitement des suites de caractres : elle dcoupe cette suite en une suite de mots dits aussi d'units lexicales, ou lexmes.

Le module Genlex

Ce module fournit une primitive simple permettant d'analyser une suite de caractres selon plusieurs catgories d'units lexicales prdfinies. Ces catgories sont distingues par le type :

# type token =
Kwd of string
| Ident of string
| Int of int
| Float of float
| String of string
| Char of char ;;
On pourra ainsi reconnatre dans une suite de caractres un entier (constructeur Int) et rcuprer la valeur reprsente (argument du constructeur de type int) Les chanes et les caractres reconnus obissent aux conventions usuelles : une chane est dlimite par deux (") et un caractre par deux ('). Un flottant est reprsent soit en utilisant la notation pointe (par exemple 0.01) soit la notation en mantisse et exposant (par exemple 1E-2). Restent les deux constructeurs Kwd et Ident.

Le constructeur Ident dsigne la catgorie des identificateurs. Ce sont les noms de variables ou de fonctions des langages de programmation, par exemple. Ils correspondent n'importe quelle suite de lettres et de chiffres pouvant inclure le caractre soulign (_) ou l'apostrophe ('). Cette suite ne doit pas commencer par un chiffre. On considre galement comme identificateur (pour ce module, du moins) toute suite de symboles d'oprations ou de relations tels +, *, > ou =. Enfin, le constructeur Kwd dfinit la catgorie des mots cls qui contient des identificateurs distingus ou des caractres spciaux.

La seule catgorie paramtrable de cet ensemble d'units prdfinies est celle des mots cls. La primitive suivante permet de crer un analyseur lexical en prenant comme ensemble de mots cls la liste passe en premier argument.

# Genlex.make_lexer ;;
- : string list -> char Stream.t -> Genlex.token Stream.t = <fun>
On obtient alors une fonction qui prend en entre un flot de caractres et retourne un flot d'units lexicales (type token).

On peut ainsi obtenir facilement un analyseur lexical pour notre BASIC. On dclare l'ensemble des mots cls :

# let keywords =
[ "REM"; "GOTO"; "LET"; "PRINT"; "INPUT"; "IF"; "THEN";
"-"; "!"; "+"; "-"; "*"; "/"; "%";
"="; "<"; ">"; "<="; ">="; "<>";
"&"; "|" ] ;;


On fabrique, avec cet ensemble, la fonction d'analyse lexicale :

# let line_lexer l = Genlex.make_lexer keywords (Stream.of_string l) ;;
val line_lexer : string -> Genlex.token Stream.t = <fun>
# line_lexer "LET x = x + y * 3" ;;
- : Genlex.token Stream.t = <abstr>
Le fonction line_lexer prend en entre une chane de caractres et produit le flot de lexmes correspondants.

Utilisation des flots

On peut procder <<  la main >> l'analyse lexicale en manipulant directement des flots.

L'exemple suivant est un analyseur lexical pour les expressions arithmtiques. La fonction lexer prend un flot de caractres et construit un flot d'units lexicales de type lexeme Stream.t1. Les espaces, tabulations et sauts de lignes sont limins. Pour simplifier, nous ne traitons pas les variables ni les entiers ngatifs.

# let rec spaces s =
match s with parser
[<'' ' ; rest >] -> spaces rest
| [<''\t' ; rest >] -> spaces rest
| [<''\n' ; rest >] -> spaces rest
| [<>] -> ();;
val spaces : char Stream.t -> unit = <fun>
# let rec lexer s =
spaces s;
match s with parser
[< ''(' >] -> [< 'Lsymbol "(" ; lexer s >]
| [< '')' >] -> [< 'Lsymbol ")" ; lexer s >]
| [< ''+' >] -> [< 'Lsymbol "+" ; lexer s >]
| [< ''-' >] -> [< 'Lsymbol "-" ; lexer s >]
| [< ''*' >] -> [< 'Lsymbol "*" ; lexer s >]
| [< ''/' >] -> [< 'Lsymbol "/" ; lexer s >]
| [< ''0'..'9' as c;
i,v = lexint (Char.code c - Char.code('0')) >]
-> [<'Lint i ; lexer v>]
and lexint r s =
match s with parser
[< ''0'..'9' as c >]
-> let u = (Char.code c) - (Char.code '0') in lexint (10*r + u) s
| [<>] -> r,s
;;
val lexer : char Stream.t -> lexeme Stream.t = <fun>
val lexint : int -> char Stream.t -> int * char Stream.t = <fun>
La fonction lexint est ddie l'analyse du morceau de flot de caractres correspondant une constante entire. Elle est appele par la fonction lexer lorsque celle-ci rencontre un caractre codant un chiffre. La fonction lexint consomme alors tous les chiffres conscutifs pour donner la valeur de l'entier correspondant.

Expressions rationnelles

Prenons un peu de hauteur et considrons le problme des units lexicales d'un point de vue plus thorique.

De ce point de vue, une unit lexicale est un mot. Un mot est form par la concatnation d'lments d'un alphabet. Pour ce qui nous concerne, l'alphabet considr est pris dans l'ensemble des caractres ASCII. Thoriquement, un mot peut ne contenir aucun caractre (le mot vide2) ou ne contenir qu'un seul caractre. L'tude thorique de l'assemblage d'lments de l'alphabet pour former des lments du lexique (les lexmes) a abouti un formalisme simple connu sous le nom d'expressions rationnelles.

Dfinition
Une expression rationnelle permet de dfinir des ensembles de mots. Par exemple : l'ensemble des identificateurs. Son mode de dfinition est bas sur un certain nombre d'oprations ensemblistes. Soient M et N deux ensembles de mots, on peut former :
  1. l'union de M et N que l'on note M | N.
  2. le complment de M que l'on note ^M. C'est l'ensemble de tous les mots sauf ceux de M.
  3. la concatnation de M et N qui est l'ensemble des mots construits en mettant bout bout un mot de M et un mot de N. On note simplement MN cet ensemble.
  4. on peut itrer l'opration de concatnation sur les mots de l'ensemble M pour obtenir l'ensemble des mots forms d'une suite finie, mais de longueur quelconque, des mots de M. On note M+ cet ensemble. Il contient tous les mots de M, puis tous les mots forms par la concatnation de deux mots de M, puis de trois mots, etc... Si l'on dsire en plus que cet ensemble contienne le mot vide, on note M*.
    Comme cela s'avre utile, on se donne la construction M? qui dsigne l'ensemble des mots de M plus le mot vide.
On assimile un caractre au singleton ne contenant que ce seul caractre. L'expression a | b | c dsigne alors l'ensemble contenant les trois mots a, b et c. On utilisera la syntaxe plus compacte : [abc] pour dfinir cet ensemble. Comme notre alphabet est ordonn (par l'ordre des codes ASCII) on peut galement dfinir un intervalle. Par exemple, l'ensemble des chiffres s'crit : [0-9]. On peut se servir des parenthses pour grouper des expressions.

Si l'on veut utiliser, pour lui mme, un des caractres reprsentant un oprateur, on le fait prcder du caractre d'chappement . Par exemple (*)* dsigne l'ensemble des suites d'toiles.

Exemple
Considrons l'alphabet comprenant les chiffres (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) les symboles plus (+) et moins (-), le point (.) et la lettre E. On peut dfinir l'ensemble num des mots reprsentant les nombres. Appelons entiers l'ensemble dfini par [0-9]+. On dfinit unum, l'ensemble des nombres non signs, par :
entiers?(.entier)?(E(+|-)?entiers)?

L'ensemble des nombres signs num est alors dfini par :
unum | -unum ou par -?unum

Reconnaissance
Une fois dfini un ensemble d'expressions, reste le problme de savoir si une chane de caractres ou une de ses sous-chanes appartient ou non cet ensemble. Pour cela, il faut traduire la dfinition formelle de l'ensemble en un programme de reconnaissance et de traitement des expressions. Dans le cas des expressions rationnelles, cette traduction peut tre automatise. De telles techniques de traduction sont mises en oeuvre par le module Genlex, dans la bibliothque Str et dans l'outil ocamllex que nous prsentons dans les deux prochains paragraphes.

La bibliothque Str

Ce module contient un type abstrait regexp reprsentant les expressions rationnelles ainsi qu'une fonction regexp qui prend une chane de caractres dcrivant une expression rationnelle, plus ou moins selon la syntaxe dcrite ci-dessus, et qui la transforme en sa reprsentation abstraite.

Ce module contient galement un certain nombre de fonctions exploitant les expressions rationnelles et manipulant des chanes de caractres. La syntaxe des expressions rationnelles pour la bibliothque Str est donne figure 11.1.

. n'importe quel caractre sauf \n
* zro ou plusieurs occurrences de l'expression prcdente
+ au moins une occurrence de l'expression prcdente
? zro ou une occurrence de l'expression prcdente
[..] ensemble de caractres
  l'intervalle est not - (exemple [0-9])
  le complmentaire est not ^ (exemple [^A-Z])
^ dbut de ligne
  ne pas confondre avec l'utilisation de ^ comme complmentaire d'un ensemble
$ fin de ligne
| alternative
(..) regroupement en une expression compose
  on pourra alors nommer cette expression compose
i avec i constante entire, texte filtr par la i-ime expression compose
\ caractre d'chappement
  on l'utilise lorsque l'on veut filtrer l'un des caractres rservs des expressions rationnelles.

Figure 11.1 : Expressions rationnelles


Exemple
Nous voulons crire une fonction traduisant des dates au format anglo-saxon en des dates franaises dans un fichier de donnes. On suppose que le fichier est organis en lignes de champs et que les composants d'une date anglo-saxonne sont spars par un point. Nous dfinissons une fonction qui prend en argument une chane (i.e. une ligne du fichier), qui isole la date, la dcompose, la traduit et la remplace par cette traduction.

# let french_date_of d =
match d with
[mm; jj; yy] -> jj^"/"^mm^"/"^yy
| _ -> failwith "Bad date format" ;;
val french_date_of : string list -> string = <fun>

# let english_date_format = Str.regexp "[0-9]+\.[0-9]+\.[0-9]+" ;;
val english_date_format : Str.regexp = <abstr>

# let trans_date l =
try
let i=Str.search_forward english_date_format l 0 in
let d1 = Str.matched_string l in
let d2 = french_date_of (Str.split (Str.regexp "\.") d1) in
Str.global_replace english_date_format d2 l
with Not_found -> l ;;
val trans_date : string -> string = <fun>

# trans_date "..............06.13.99............" ;;
- : string = "..............13/06/99............"


L'outil ocamllex

L'outil ocamllex est un gnrateur d'analyseur lexical construit pour Objective CAML sur le modle de l'outil lex ddi au langage C. Il produit un fichier source Objective CAML partir d'un fichier contenant la description des lments du lexique reconnatre sous forme d'ensembles d'expressions rationnelles. la description de chacun des lments lexicaux, le programmeur peut attacher une action de traitement dite action smantique. Le code engendr manipule un type abstrait lexbuf dfini dans le module Lexing. Ce module contient quelques fonctions de manipulation des buffers lexicaux que le programmeur peut utiliser pour dfinir ses traitements.

L'usage est de donner aux fichiers de description lexicale l'extension .mll. Pour obtenir un source Objective CAML partir d'un fichier lex_file.mll on tape la commande
ocamllex lex_file.mll
On obtient un fichier lex_file.ml contenant le code de l'analyseur correspondant. Ce fichier peut alors tre intgr dans une application Objective CAML. chaque ensemble de rgles d'analyse lexicale correspond une fonction prenant en entre un buffer lexical (type Lexing.lexbuf) et retournant la valeur dfinie par les actions smantiques. Par consquent, toutes les actions d'une mme rgle doivent produire une valeur du mme type.

Le format gnral d'un fichier pour ocamllex est

{
en-tte
}
let ident = regexp
...
rule ruleset1 = parse
regexp { action }
| ...
| regexp { action }
and ruleset2 = parse
...
and ...
{
suite-et-fin
}

Les deux sections << en-tte >> et << suite-et-fin >> sont facultatives. Elles contiennent du code Objective CAML dfinissant types, fonctions, etc. ncessaires au traitement. La dernire section peut dfinir des fonctions utilisant les rgles d'analyse d'ensemble lexicaux donnes dans la section mdiane. La srie de dclarations prcdant la dfinition des rgles permet de nommer certaines expressions rationnelles. On utilisera alors leur nom dans la dfinition des rgles.

Exemple
Reprenons notre exemple du BASIC. Nous allons pouvoir y affiner le type des units lexicales retournes. De surcrot, nous pourrons retrouver la fonction lexer dfinie page ?? avec le mme type de sortie (lexeme), mais qui prendra en entre un buffer de type Lexing.lexbuf.

{
let string_chars s =
String.sub s 1 ((String.length s)-2) ;;
}

let op_ar = ['-' '+' '*' '%' '/']
let op_bool = ['!' '&' '|']
let rel = ['=' '<' '>']

rule lexer = parse
[' '] { lexer lexbuf }

| op_ar { Lsymbol (Lexing.lexeme lexbuf) }
| op_bool { Lsymbol (Lexing.lexeme lexbuf) }

| "<=" { Lsymbol (Lexing.lexeme lexbuf) }
| ">=" { Lsymbol (Lexing.lexeme lexbuf) }
| "<>" { Lsymbol (Lexing.lexeme lexbuf) }
| rel { Lsymbol (Lexing.lexeme lexbuf) }

| "REM" { Lsymbol (Lexing.lexeme lexbuf) }
| "LET" { Lsymbol (Lexing.lexeme lexbuf) }
| "PRINT" { Lsymbol (Lexing.lexeme lexbuf) }
| "INPUT" { Lsymbol (Lexing.lexeme lexbuf) }
| "IF" { Lsymbol (Lexing.lexeme lexbuf) }
| "THEN" { Lsymbol (Lexing.lexeme lexbuf) }

| '-'? ['0'-'9']+ { Lint (int_of_string (Lexing.lexeme lexbuf)) }
| ['A'-'z']+ { Lident (Lexing.lexeme lexbuf) }
| '"' [^ '"']* '"' { Lstring (string_chars (Lexing.lexeme lexbuf)) }
La traduction de ce fichier par ocamllex fournit la fonction lexer de type Lexing.lexbuf -> lexeme. Nous verrons plus loin comment une telle fonction s'utilise en corrlation avec l'analyse syntaxique (voir page ??).


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora020.html0000644000000000000000000011647107421273601014473 0ustar Exercices Prcdent Index Suivant

Exercices

Fusion de deux listes

  1. crire une fonction fusion_i qui prend en entre deux listes d'entiers tries en ordre croissant et retourne une nouvelle liste trie contenant les lments des deux premires.

    # let rec fusion_i l1 l2 = match (l1,l2) with
    [],_ -> l2
    | _,[] -> l1
    | t1::q1,t2::q2 ->
    if t1 < t2 then t1::(fusion_i q1 l2)
    else t2::(fusion_i l1 q2);;
    val fusion_i : 'a list -> 'a list -> 'a list = <fun>


  2. crire une fonction fusion gnrale, qui prend en argument une fonction de comparaison et deux listes tries selon cet ordre et retourne la liste fusionne selon le mme ordre. La fonction de comparaison sera du type 'a -> 'a -> bool.

    # let rec fusion f_ord l1 l2 = match (l1,l2) with
    [],_ -> l2
    | _,[] -> l1
    | t1::q1,t2::q2 ->
    if f_ord t1 t2 then t1::(fusion f_ord q1 l2)
    else t2::(fusion f_ord l1 q2);;
    val fusion : ('a -> 'a -> bool) -> 'a list -> 'a list -> 'a list = <fun>


  3. Appliquer cette fonction 2 listes d'entiers tries en ordre dcroissant, puis deux listes de chanes de caractres tries en ordre dcroissant.

    # fusion (>) [44;33;22;11] [55;30;10];;
    - : int list = [55; 44; 33; 30; 22; 11; 10]
    # fusion (<) ["mon"; "premier"; "programme"]
    ["bon";"essai";"numero";"un"];;
    - : string list =
    ["bon"; "essai"; "mon"; "numero"; "premier"; "programme"; "un"]


  4. Que se passe-t-il si une des listes ne respecte pas l'ordre donn? Si au moins une des deux listes ne respecte pas l'ordre, alors le rsultat ne le respectera probablement pas.

  5. crire un nouveau type liste sous la forme d'un enregistrement contenant trois champs : la liste classique, une fonction d'ordre et un boolen indiquant si la liste respecte l'ordre.

    # type 'a liste = {l: 'a list; f_ord : 'a -> 'a -> bool; r : bool};;
    type 'a liste = { l: 'a list; f_ord: 'a -> 'a -> bool; r: bool }


  6. crire la fonction ajoute qui ajoute un lment une liste de ce type.

  7. crire une fonction tri qui trie par insertion les lments d'une liste.

    # let ajoute e lt =
    let rec ajoute_rec e l = match l with
    [] -> [e]
    | t::q -> if lt.f_ord e t then e::l
    else t::(ajoute_rec e q)
    in
    if lt.r then {lt with l=ajoute_rec e lt.l}
    else {lt with l = e::lt.l};;
    val ajoute : 'a -> 'a liste -> 'a liste = <fun>

    # let tri lt =
    if lt.r then lt
    else List.fold_right ajoute lt.l {l=[]; f_ord=lt.f_ord; r=true};;
    val tri : 'a liste -> 'a liste = <fun>


  8. crire une nouvelle fonction fusion pour ces listes.

    # let rec fusion_lt l1 l2 =
    if l1.f_ord <> l2.f_ord then failwith "ordre incompatible"
    else
    if l1.r then
    if l2.r then {l = fusion l1.f_ord l1.l l2.l; f_ord = l1.f_ord; r = true}
    else List.fold_right ajoute l2.l l1
    else
    if l2.r then fusion_lt l2 l1
    else fusion_lt (tri l1) l2;;
    val fusion_lt : 'a liste -> 'a liste -> 'a liste = <fun>

Arbres lexicaux

Pour la reprsentation de dictionnaires, on utilisera des arbres lexicaux (ou tries).

# type noeud_lex = Lettre of char * bool * arbre_lex
and arbre_lex = noeud_lex list;;
# type mot = string;;
La valeur boolenne du noeud_lex marque la fin d'un mot lorsqu'elle vaut true. Dans une telle structure, la suite de mots <<  fa, far, faux, frise, frit, frite >> est stocke de la faon suivante :
L'toile (*) marque la fin d'un mot.
  1. crire la fonction existe qui teste si un mot appartient un dictionnaire de type arbre_lex.

    # let rec existe m d =
    let aux sm i n = match d with
    [] -> false
    | (Lettre (c,b,l))::q when c=sm.[i] ->
    if n = 1 then b else existe (String.sub sm (i+1) (n-1)) l
    | (Lettre (c,b,l))::q -> existe sm q
    in aux m 0 (String.length m) ;;
    val existe : string -> arbre_lex -> bool = <fun>


  2. crire une fonction ajoute qui prend un mot et un dictionnaire et retourne un nouveau dictionnaire qui contient ce mot en plus. Si le mot existe dj, il n'est pas ncessaire de l'ajouter.

    # let rec ajoute m d =
    let aux sm i n =
    if n = 0 then d
    else match d with
    [] -> [Lettre (sm.[i], n = 1, ajoute (String.sub sm (i+1) (n-1)) [])]
    | (Lettre(c,b,l))::q when c=sm.[i]->
    if n = 1 then (Lettre(c,true,l))::q
    else Lettre(c,b,ajoute (String.sub sm (i+1) (n-1)) l)::q
    | (Lettre(c,b,l))::q -> (Lettre(c,b,l))::(ajoute sm q)
    in aux m 0 (String.length m) ;;
    val ajoute : string -> arbre_lex -> arbre_lex = <fun>


  3. crire une fonction construit qui prend une liste de mots et construit le dictionnaire correspondant.

    # let construit l =
    let rec aux l d = match l with
    [] -> d
    | t::q -> aux q (ajoute t d)
    in aux l [] ;;
    val construit : string list -> arbre_lex = <fun>


  4. crire une fonction verifie qui prend une liste de mots et un dictionnaire et retourne la liste des mots n'appartenant pas ce dictionnaire.

    # let rec filter p = function
    [] -> []
    | t::q -> if p t then t::(filter p q) else filter p q ;;
    val filter : ('a -> bool) -> 'a list -> 'a list = <fun>

    # let verifie l d = filter (function x -> not (existe x d)) l ;;
    val verifie : string list -> arbre_lex -> string list = <fun>


  5. crire une fonction selecte qui prend un dictionnaire et une longueur et retourne l'ensemble des mots de cette longueur.

    # let string_of_char c = String.make 1 c ;;
    val string_of_char : char -> string = <fun>
    # let rec selecte n d = match d with
    [] -> []
    | (Lettre(c,b,l))::q when n=1 ->
    let f (Lettre (c,b,_)) = if b then string_of_char c else "!" in
    filter (function x -> x <> "!") (List.map f d)
    | (Lettre(c,b,l))::q ->
    let r1 = selecte (n-1) l
    and r2 = selecte n q in
    let pr = List.map (function s -> (string_of_char c)^s) r1 in
    pr@r2 ;;
    val selecte : int -> arbre_lex -> string list = <fun>

Parcours de graphes

On dfinit un type 'a graphe reprsentant les graphes orients par listes d'adjacence contenant pour chaque sommet la liste de ses successeurs :

# type 'a graphe = ( 'a * 'a list) list ;;


  1. crire une fonction ajoute_som qui ajoute un sommet un graphe et retourne le nouveau graphe.

    # let rec ajoute_som s g =
    match g with
    [] -> [(s,[])]
    | (t,_)::_ when t=s -> failwith "sommet existant"
    | sl::q -> sl::(ajoute_som s q) ;;
    val ajoute_som : 'a -> ('a * 'b list) list -> ('a * 'b list) list = <fun>

    # let ajoute_som = (ajoute_som : 'a -> 'a graphe -> 'a graphe) ;;
    val ajoute_som : 'a -> 'a graphe -> 'a graphe = <fun>


  2. crire une fonction ajoute_arc qui ajoute un arc un graphe possdant dj ces deux sommets.

    # let rec ajoute_arc s1 s2 g =
    match g with
    [] -> failwith "sommet inconnu"
    | (t,la)::q when t=s1 ->
    if List.mem s2 la then failwith "arc existant"
    else (s1,s2::la)::q
    | sl::q -> sl::(ajoute_arc s1 s2 q) ;;
    val ajoute_arc : 'a -> 'b -> ('a * 'b list) list -> ('a * 'b list) list =
    <fun>

    # let ajoute_arc = (ajoute_arc : 'a -> 'a -> 'a graphe -> 'a graphe ) ;;
    val ajoute_arc : 'a -> 'a -> 'a graphe -> 'a graphe = <fun>


  3. crire une fonction arcs_dir qui retourne tous les sommets accessibles partir d'un sommet donn.

    # let rec arcs_dir s g = match g with
    [] -> []
    | (t,la)::_ when t=s -> la
    | _::q -> arcs_dir s q ;;
    val arcs_dir : 'a -> ('a * 'b list) list -> 'b list = <fun>

    # let arcs_dir = (arcs_dir : 'a -> 'a graphe -> 'a list) ;;
    val arcs_dir : 'a -> 'a graphe -> 'a list = <fun>


  4. crire une fonction arcs_vers qui retourne la liste de tous les sommets atteignant un sommet donn.

    # let rec arcs_vers s g = match g with
    [] -> []
    | (t,la)::q -> if List.mem s la then t::(arcs_vers s q)
    else (arcs_vers s q) ;;
    val arcs_vers : 'a -> ('b * 'a list) list -> 'b list = <fun>

    # let arcs_vers = (arcs_vers : 'a -> 'a graphe -> 'a list) ;;
    val arcs_vers : 'a -> 'a graphe -> 'a list = <fun>



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora184.html0000644000000000000000000000521207421273602014475 0ustar Introduction Prcdent Index Suivant

Introduction

La programmation rpartie permet de construire des applications fonctionnant sur plusieurs machines relies ensemble sur un rseau et communiquant entre elles pour accomplir une tche. Le modle sous-jacent est celui de la programmation parallle mmoire rpartie. Les programmes locaux ou distants communiquent via un mdia qui correspond un protocole rseau. Le plus connu et le plus utilis d'entre eux est IP (Internet protocol) et ses surcouches TCP et UDP. partir de ces communications de bas niveau, de nombreux services sont btis sur le modle client-serveur o un serveur attend des requtes de diffrents clients, les traite et envoie les rponses ces demandes. On peut citer le protocole HTTP permettant la communication entre navigateur et serveur Web. La rpartition des tches entre clients et serveurs correspond diffrentes architectures logicielles.

Le langage Objective CAML offre, via sa bibliothque Unix, diffrentes possibilits de communication entre programmes. Les prises de communication (sockets en anglais) permettent la communication selon le protocole TCP/IP et UDP/IP. Cette partie de la bibliothque Unix est porte sous Windows. La possibilit de dupliquer des processus <<lourds>> (Unix.fork) ou de crer des processus lgers (Thread.create) permet de raliser des serveurs acceptant plusieurs requtes en mme temps. partir de ces couches de bas niveau, il est ais de construire des client-serveur universels paramtrs par une fonction de traitement. Enfin, un point important pour la cration de nouveaux services est la dfinition d'un protocole propre l'application.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora209.html0000644000000000000000000002634607421273603014507 0ustar Types cycliques Prcdent Index Suivant

Types cycliques

Objective CAML permet de dclarer des structures de donnes rcursives, c'est dire des structures pouvant comporter une valeur ayant la mme structure.

 
# type somme_ex1 = Ctor of somme_ex1 ;;
type somme_ex1 = | Ctor of somme_ex1

# type record_ex1 = { champ : record_ex1 } ;;
type record_ex1 = { champ: record_ex1 }


La construction de valeurs de ces types n'est pas immdiate car il est ncessaire de possder une valeur pour en construire une. La dclaration rcursive de valeurs permet de se tirer de cercle vicieux.

# let rec somme_val = Ctor somme_val ;;
val somme_val : somme_ex1 = Ctor (Ctor (Ctor (Ctor (Ctor ...))))

# let rec val_record_1 = { champ = val_record_2 }
and val_record_2 = { champ = val_record_1 } ;;
val val_record_1 : record_ex1 = {champ={champ={champ={champ={champ=...}}}}}
val val_record_2 : record_ex1 = {champ={champ={champ={champ={champ=...}}}}}


Les arbres planaires gnraux peuvent se reprsenter par une structure de donnes de cette forme.

# type 'a arbre = Sommet of 'a * 'a arbre list ;; 
type 'a arbre = | Sommet of 'a * 'a arbre list
# let hauteur_1 = Sommet (0,[]) ;;
val hauteur_1 : int arbre = Sommet (0, [])
# let hauteur_2 = Sommet (0,[ Sommet (1,[]); Sommet (2,[]); Sommet (3,[]) ]) ;;
val hauteur_2 : int arbre =
Sommet (0, [Sommet (1, []); Sommet (2, []); Sommet (3, [])])
# let hauteur_3 = Sommet (0,[ hauteur_2; hauteur_1 ]) ;;
val hauteur_3 : int arbre =
Sommet
(0,
[Sommet (0, [Sommet (...); Sommet (...); Sommet (...)]); Sommet (0, [])])

(* idem avec un record *)
# type 'a arbre_rec = { etiquette:'a ; fils:'a arbre_rec list } ;;
type 'a arbre_rec = { etiquette: 'a; fils: 'a arbre_rec list }
# let haut_rec_1 = { etiquette=0; fils=[] } ;;
val haut_rec_1 : int arbre_rec = {etiquette=0; fils=[]}
# let haut_rec_2 = { etiquette=0; fils=[haut_rec_1] } ;;
val haut_rec_2 : int arbre_rec = {etiquette=0; fils=[{etiquette=0; fils=[]}]}


On pourrait se dire qu'il n'est pas utile d'avoir un type numr avec un seul constructeur mais, par dfaut, Objective CAML n'accepte pas les abrviations de types rcursives.
 
# type 'a arbre = 'a * 'a arbre list ;;
Characters 7-36:
The type abbreviation arbre is cyclic


On peut dfinir des valeurs ayant cette structure, mais elles n'ont pas le mme type.

# let arbre_1 = (0,[]) ;;
val arbre_1 : int * 'a list = 0, []
# let arbre_2 = (0,[ (1,[]); (2,[]); (3,[]) ]) ;;
val arbre_2 : int * (int * 'a list) list = 0, [1, []; 2, []; 3, []]
# let arbre_3 = (0,[ arbre_2; arbre_1 ]) ;;
val arbre_3 : int * (int * (int * 'a list) list) list =
0, [0, [...; ...; ...]; 0, []]


De la mme manire, Objective CAML n'est pas capable d'infrer un type une fonction prenant en argument une valeur de cette forme.

# let max_list = List.fold_left max 0 ;;
val max_list : int list -> int = <fun>

# let rec hauteur = function
Sommet (_,[]) -> 1
| Sommet (_,fils) -> 1 + (max_list (List.map hauteur fils)) ;;
val hauteur : 'a arbre -> int = <fun>


# let rec hauteur2 = function
(_,[]) -> 1
| (_,fils) -> 1 + (max_list (List.map hauteur2 fils)) ;;
Characters 97-101:
This expression has type 'a list but is here used with type
('b * 'a list) list


Le message d'erreur nous montre que la fonction hauteur2 serait typable si nous avions l'galit entre les types 'a et 'b * 'a list. C'est justement cette galit qui nous a t refuse dans la dclaration de l'abrviation de type arbre.

Pourtant, le typage des objets permet de construire des valeurs dont le type est cyclique. Examinons la fonction suivante et tentons de deviner son type.

# let f x = x#copy = x ;;
Le type de x est une classe qui possde la mthode copy. Le type de cette mthode est le mme que celui de x puisque l'on teste leur galit. En consquence, si foo est le type de x il est de la forme < copy : foo ; .. >. D'aprs ce que nous avons vu plus haut, son type tant cyclique, cette fonction devrait tre rejete; et pourtant il n'en est rien.

# let f x = x#copy = x ;;
val f : (< copy : 'a; .. > as 'a) -> bool = <fun>
Objective CAML accepte cette fonction et dnote la cyclicit du type par le as qui identifie 'a avec un type contenant 'a.

En ralit, les deux problmes sont exactement les mmes, mais par dfaut, Objective CAML n'accepte ce genre de types que lorsqu'ils concernent des objets. La fonction hauteur est typable si elle produit une cyclicit sur le type d'un objet.

# let rec hauteur a = match a#fils with 
[] -> 1
| l -> 1 + (max_list (List.map hauteur l)) ;;
val hauteur : (< fils : 'a list; .. > as 'a) -> int = <fun>



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora067.gif0000644000000000000000000000705507073350152014304 0ustar GIF89aUUU!,ڋ޼Hp L/˨ P*̦syTDϪF :춆{<x0hxXSВ)9IYiyd * YjyAzʣ*;KC[b 0l|\wlbИLU-U-M>>[>κNɕ$m\o' >s Z7m*dQÈMϨ2zLtHHF"ɕvTa ˄b3f͛<9P rv<*:MEPԧTEěVWf 깠GÚ@*lr\eҝ^Xx o^a ~j+1N/~̸'F rd!:W.QH!ћ3x8GKozR$ YޓbϾO_wю0~F{f-]6=qޣ=uOۏG]oю|Qǯ:9pDkhLڧ}rWkrx7tB'`"+f8D݋أ+s ]w%X ) w߽8%w}a^Yc)IdZ~HH)pfAXzVޝ<~VT͡1=ʁiNJlm,Z jG*WjjHzn agZ+I!HʪzHK'Z2ʞl]<{mfaޅ@,``xێ[z^Y\pEq ˮc/oEہVDGXh /-xdQNL 0#kK2'KXQ (glƔI7`A򽫀rr- {{Rl>l瓡<<2g҅t;|8 *1K皈D'JqQbžx+fZ"/6ls`d0Bmd[7lrMS5 $: Tчop DBACY$q9hiu*O*1$A>T#. 0x/r!(?Yq_fQ ם&[m+%ΫԴ 0y \W?۩Gi.Yꥸ!4(.> dICh)H^[fWx$ɄfwRfOz#%Ap2_zIl%*N=qXL ca3_% ֱjZIkǀMy)r ik$4)%4]C%s%KYr\pNgaV*d'־6n nyKrW%ϋƊ07LK ^ z8F|I xg+g2^/[/N0#`-l`ҒQ 'y,[~l^n8c968- k,F?|".Kf2K!$IgAd CQhsl+cnr? _F 9wH'*teּ )QhBS$OD!4chB[Z-͌j!7ί&;i\yϷ\:յ  C[վ4jN[pkώn3bm0чu"{J6VN E'%ySʝSZFw񔥪)J%מ5f&UKz (Sw+=kUUq<Z-}hsd4 + ٘>EZ٥قwn|PvԿpal]O"b]nN43\l'䥴< on9-pϏ^wi=VG]l/ήt0`%SüN|X˟1&𘾾f?p+Kρ;H7bDSSk xdk8o\_%8 Gk܆[~fc{7l8} Ȁ(4d!a0dQ8XTԶ-pn>xזZƷ/OA3dO3M(cOzIȅCx;XbyT-"] j8Iŗ1IV?PWqL1m=؆ }v#7W2hw4sg,!au tqs9xrriXoirts"2Y\X7:7@{O 1YHo*t2QvI5 aT"d.7FuJNjPzbhu8}h'w2&[VvuXh{RḆh }rG#w9/o#/ `$c;J3{Xs:4'UH&YŅ/y3&H7iP#Ó:pdѓCd_b5@y.E)f2є',ITWقYI /_W&eyvgWY}.8+8Uk<؀9h7ⷌ{ Qih>h8tIyf{Z@`Oqفg~~ G9W9JIiVDHWzq'mn9&IL& Ԛ"҇JeϹ L։KhhpK&Թ!)$\U .upRiY(t#О($2qɞŸu4qz TPHku(kr lbxg$GWN{9I\dC[OPxj.c+Z_~* 2P&N*NaQ7hziE5Jt<'"$: t:QŸPxB| 4[ꥥ'`±Rv8.S9C Ĝf@$`Ɖp}V|g9V|I*y਄pJ :7;ocaml-book-1.0/fr/html/book-ora032.html0000644000000000000000000000334607421273601014472 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a montr l'intgration des principaux traits de la programmation imprative (valeurs physiquement modifiables, entres-sorties, structures de contrle itratives) dans un langage fonctionnel. Seules les valeurs mutables, comme les chanes, les vecteurs et les enregistrements champs mutables, peuvent tre physiquement modifies. Les autres valeurs, une fois cres, sont immuables. On obtient ainsi des valeurs en lecture seule (RO) pour la partie fonctionnelle et des valeurs en lecture-criture (RW) pour la partie imprative.

Il est noter que, si l'on ne se sert pas des traits impratifs du langage, cette extension au noyau fonctionnel ne change pas la partie fonctionnelle, en dehors de considrations de typage contournables.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora082.html0000644000000000000000000000177207421273603014502 0ustar Notes
1
JAVA utilise le terme srialisation
2
Les tableaux de caractres, par exemple.
3
Le mode interactif possde un rcuprateur d'exception gnral qui affiche un message signalant qu'une exception n'a pas t rcupre.
ocaml-book-1.0/fr/html/book-ora007.gif0000644000000000000000000000232107073350152014265 0ustar GIF89a!,ڋޜ~ݨ!:^p2K 0:ȦSyjGd-z*-76v '<_u>XGxH2`h8ɡx`'Ii)9)Wh:z(f&u [{akǫ;l\3L֬*Eel'Xnޡ~ _;l?Ԯ\  :'Po &L!+#FEQ fqEs#bX2cJ@Ѹ^z!N6l+Cb6Aؓ轓E'EX3Re,AX*)hWIF4U+'[pkxo2_v nUjE61&R#K^e|sTns-:i63N5uҦVmpopTOBmxǗ3W9}y~3髷s=;2є^)g#`Cg`7X '܄VhmdhۆՇT#b$.fv&@Za%Z 6ַ8;t'Bc)_J%=N'uQ &WbeqYfb[!g%dk9eo9,M2Iggq)WUg9理*hB>Z 1U4"cbJ ""** +Xj`PZi(릴jgꮇv믞K֖ߧޭv eiRRfjU+5ܷxlOԹ;-Zֻ["+/j/| vdvb,\F]E"uZ]*mF1 <&Ιa'#ܲ3  %_7/?;4KkGC].2JǕzr[8a=v)(}1xФtܦ'73ްvmw h?Dx'N:qZBsrr9t2u{kޚHNʓi~wL2]8ب+5 jWni8`Ӽcx "$ovÎY܏O:5ƶ~5L|tF_|ꇿZwv@Jlb[ԽՙI òU;ocaml-book-1.0/fr/html/book-ora155.html0000644000000000000000000006527607421273602014513 0ustar tendre les composants Prcdent Index Suivant

tendre les composants

On appelle composant un regroupement de donnes et de traitements sur ces donnes. Dans le modle fonctionnel/modulaire, un composant comprend la dfinition d'un type et des fonctions manipulant ce type. De mme un composant dans le modle objet correspond une hirarchie de classes, hritant d'une mme classe et donc possdant tous les comportements de celle-ci. Le problme de l'extensibilit des composants consiste vouloir d'une part augmenter les traitements/comportements sur ceux-ci et d'autre part d'augmenter les donnes traites et cela sans modifier les programmes sources initiaux. Par exemple un composant image peut tre soit un rectangle, soit un cercle que l'on peut afficher ou dplacer.

  rectangle cercle groupe
affiche X X  
dplace X X  
agrandit      

On peut vouloir tendre le composant image par le traitement agrandir et crer des groupes d'images. Le comportement des deux modles diffre selon le sens de l'extension : donnes ou traitements. On dfinit tout d'abord, dans chaque modle, la partie commune du composant image, puis on cherche l'tendre.

En fonctionnel

On dfinit le type image comme un type somme contenant deux cas. Les traitements prennent en entre un paramtre de type image et effectuent le travail adquat.

# type image = Rect of float | Cercle of float ;;
# let affiche = function Rect r -> ... | Cercle c -> ... ;;
# let dplace = ... ;;


On encapsule ensuite ces dclarations globales dans un module simple.

Extension des traitements

L'extension des traitements dpend de la reprsentation du type image dans le module. Si celui-ci est abstrait, il n'y a plus de possibilit d'extension des traitements. Dans le cas o le type est rest concret, il est facile d'ajouter une fonction agrandir qui effectue le changement d'chelle d'une image en slectionnant un rectangle ou un cercle par filtrage de motifs.

Extension des donnes

L'extension des donnes ne peut pas s'effectuer avec le type image. En effet les types Objective CAML ne sont pas extensibles, sauf pour le type exn reprsentant des exceptions. Il n'est pas possible d'tendre les donnes en conservant le mme type, il est donc ncessaire de dfinir un nouveau type n_image de la manire suivante :
type n_image = I of image | G of n_image * n_image;;
On doit redfinir alors les traitements pour ce nouveau type en simulant une sorte d'hritage. Cela devient complexe ds qu'il y a plusieurs extensions.

En objet

On dfinit les classes rectangle et cercle, sous-classes de la classe abstraite image qui a deux mthodes abstraites affiche et deplace.

# class virtual image () =
object(self:'a)
method virtual affiche : unit -> unit
method virtual deplace : float * float -> unit
end;;
# class rectangle x y w h =
object
inherit image ()
val mutable x = x
val mutable y = y
val mutable w = w
val mutable h = h
method affiche () = Printf.printf "R: (%f,%f) [%f,%f]" x y w h
method deplace (dx,dy) = x <- x +. dx; y <- y +. dy
end;;
# class cercle x y r =
object
val mutable x = x
val mutable y = y
val mutable r = r
method affiche () = Printf.printf "C: (%f,%f) [%f]" x y r
method deplace (dx, dy) = x <- x +. dx; y <- y +. dy
end;;


Le programme suivant construit une liste d'images et l'affiche.

# let r = new rectangle 1. 1. 3. 4.;;
val r : rectangle = <obj>
# let c = new cercle 1. 1. 4.;;
val c : cercle = <obj>
# let l = [ (r :> image); (c :> image)];;
val l : image list = [<obj>; <obj>]
# List.iter (fun x -> x#affiche(); print_newline()) l;;
R: (1.000000,1.000000) [3.000000,4.000000]
C: (1.000000,1.000000) [4.000000]
- : unit = ()


Extension des donnes

Les donnes sont facilement extensibles en ajoutant une sous-classe la classe image de la manire suivante.

# class groupe i1 i2 =
object
val i1 = (i1:#image)
val i2 = (i2:#image)
method affiche () = i1#affiche(); print_newline (); i2#affiche()
method deplace p = i1#deplace p; i2#deplace p
end;;


On s'aperoit alors que le <<type>> image devient rcursif car la classe groupe dpend hors hritage de la classe image.

# let g = new groupe (r:>image) (c:>image);;
val g : groupe = <obj>
# g#affiche();;
R: (1.000000,1.000000) [3.000000,4.000000]
C: (1.000000,1.000000) [4.000000]- : unit = ()


Extension des traitements

On dfinit une sous-classe abstraite d'image contenant une nouvelle mthode.

# class virtual e_image () =
object
inherit image ()
method virtual surface : unit -> float
end;;


On peut dfinir les classes e_rectangle et e_cercle qui hritent de e_image et de rectangle et cercle respectivement. On peut alors travailler sur ces images tendues pour utiliser cette nouvelle mthode. Il subsiste une difficult pour la classe groupe. Celle-ci possde deux champs de type image, donc mme en hritant de la classe e_image il ne sera pas possible d'envoyer le message agrandit aux champs images. Il est donc possible d'tendre les traitements sauf pour les cas de sous-classes correspondant des types rcursifs.

Extension des donnes et des traitements

Pour raliser l'extension dans les deux sens, il est ncessaire de dfinir les types rcursifs sous forme de classe paramtre. On redfinit la classe groupe.

# class ['a] groupe i1 i2 =
object
val i1 = (i1:'a)
val i2 = (i2:'a)
method affiche () = i1#affiche(); i2#affiche()
method deplace p = i1#deplace p; i2#deplace p
end;;


On reprend alors le mme principe que pour la classe e_image.

# class virtual ext_image () =
object
inherit image ()
method virtual surface : unit -> float
end;;
# class ext_rectangle x y w h =
object
inherit ext_image ()
inherit rectangle x y w h
method surface () = w *. h
end;;
# class ext_cercle x y r=
object
inherit ext_image ()
inherit cercle x y r
method surface () = 3.14 *. r *.r
end;;


L'extension de la classe groupe devient alors

# class ['a] ext_groupe ei1 ei2 =
object
inherit image()
inherit ['a] groupe ei1 ei2
method surface () = ei1#surface() +. ei2#surface ()
end;;


On obtient le programme suivant qui construit une liste le de type ext_image.

# let er = new ext_rectangle 1. 1. 2. 4. ;;
val er : ext_rectangle = <obj>
# let ec = new ext_cercle 1. 1. 8.;;
val ec : ext_cercle = <obj>
# let eg = new ext_groupe er ec;;
val eg : ext_rectangle ext_groupe = <obj>
# let le = [ (er:>ext_image); (ec :> ext_image); (eg :> ext_image)];;
val le : ext_image list = [<obj>; <obj>; <obj>]
# List.map (fun x -> x#surface()) le;;
- : float list = [8; 200.96; 208.96]


Gnralisation

Pour gnraliser l'extension des traitements il est prfrable d'intgrer des fonctions dans une mthode traitement et de construire une classe paramtre par le type du rsultat du traitement. Pour cela on dfinit la classe suivante :

# class virtual ['a] get_image (f: 'b -> unit -> 'a) =
object(self:'b)
inherit image ()
method traitement () = f(self) ()
end;;


Les classes suivantes possdent alors un paramtre fonctionnel supplmentaire pour la construction de leurs instances.

# class ['a] get_rectangle f x y w h =
object(self:'b)
inherit ['a] get_image f
inherit rectangle x y w h
method get = (x,y,w,h)
end;;
# class ['a] get_cercle f x y r=
object(self:'b)
inherit ['a] get_image f
inherit cercle x y r
method get = (x,y,r)
end;;


L'extension de la classe groupe prend alors deux paramtres de type :

# class ['a,'c] get_groupe f eti1 eti2 =
object
inherit ['a] get_image f
inherit ['c] groupe eti1 eti2
method get = (i1,i2)
end;;


On obtient le programme qui tend le traitement des instances de get_image.

# let etr = new get_rectangle
(fun r () -> let (x,y,w,h) = r#get in w *. h) 1. 1. 2. 4. ;;
val etr : float get_rectangle = <obj>
# let etc = new get_cercle
(fun c () -> let (x,y,r) = c#get in 3.14 *. r *. r) 1. 1. 8.;;
val etc : float get_cercle = <obj>
# let etg = new get_groupe
(fun g () -> let (i1,i2) = g#get in i1#traitement() +. i2#traitement())
(etr :> float get_image) (etc :> float get_image);;
val etg : (float, float get_image) get_groupe = <obj>
# let gel = [ (etr :> float get_image) ; (etc :> float get_image) ;
(etg :> float get_image) ];;
val gel : float get_image list = [<obj>; <obj>; <obj>]
# List.map (fun x -> x#traitement()) gel;;
- : float list = [8; 200.96; 208.96]


L'extension des donnes et des traitements est plus facile dans le modle objet en s'appuyant sur le modle fonctionnel.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora063.html0000644000000000000000000000124107421273603014470 0ustar Notes
2
Abstract Windowing Toolkit
ocaml-book-1.0/fr/html/book-ora009.gif0000644000000000000000000000167607073350152014303 0ustar GIF89a  !,  ڋ޼H扦 '+t_bNhL*$$ Ԫj, yrilN1z _︝]=ZN^*?m_^';?^ ( :b C'h_c71aHz TI%ܔ'1gi Μx Р2:Ŗڔ"TԨ|R4Z3uZ4\(kVæiXmKeZ5JFc 6 1'3[yx9t"`В`4ͪ^nkخZm˻…OBmC3\vtӁ8u۹ Plan du chapitre Prcdent Index Suivant

Plan du chapitre

Ce chapitre dcrit l'ensemble des bibliothques de la distribution du langage Objective CAML. Certaines ont dj t rencontres dans les chapitres prcdents, comme la bibliothque Graphics (voir chapitre 5), ou abordes comme la bibliothque Array. La premire section montre l'organisation des diffrentes bibliothques. La deuxime section complte la description du module Pervasives prcharg. La troisime section classifie l'ensemble des modules regroups dans la bibliothque standard. La quatrime section dtaille les bibliothques sur l'arithmtique exacte et sur le chargement dynamique de code.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora079.gif0000644000000000000000000000360507073350152014304 0ustar GIF89a!,ڋ\~HY* JN 1a<*5|J)$j͢k pEgj4mç9n$},uΫ懲Acz+hn{k_[fTrWTd,ʛ{2ҤETDBnļz3GZ>5)c_Nz UȽfʶuq+`;鶋so\Y7zM5ދ7~8Ֆ,a{Skwrkே._m;HN,NN7.ڋn ʘ O{KZGJ=`-mӷ8f0)twow-ɒ$|g&Z$8\!1ih{ʛXV= -У&R?x$\sGIsilQ93t>R T]W0//b%$ؐ宆UKeC.P Kw3)jo1]x&}U!#U­qeJ8?:,Sˑ8A&:|"G!Oq,$II5Zr@#7imQԤRolR&J|e\(XrF4ȌS#Ւs  27!3dr(ӌ64f%TfY6M_VӓҌf8ljfT6Nl}\;K5f ONU 5)N~ E1 E*)+P23vuUiAYLjJg!;KTO"T{5*TӦI#GSz҃ZsԌ"O*LJԙKUfL9|cE6ٺdmQZ*P?RWי>e~ը\oyH P1&"R?̑A ̻~@E ٷ媨(ٜ{5<ڿ$lU[bBoʘ*sc{[Pv]JisK]Ht{ݽ$y&wH]3ћ6IohKAז~n LD* e;ocaml-book-1.0/fr/html/book-ora104.html0000644000000000000000000000677607421273602014505 0ustar Introduction Prcdent Index Suivant

Introduction

La dfinition et la mise au point d'outils d'analyse lexicale et syntaxique ont t un important domaine de la recherche en informatique. Ce travail a abouti la ralisation des gnrateurs d'analyseurs lexicaux et syntaxiques lex et yacc dont camllex et camlyacc, que nous prsentons dans ce chapitre, sont les dignes hritiers. Ces deux outils sont les outils rois du domaine, cependant, d'autres, plus simples, comme les flots ou la bibliothque d'analyse des expressions rationnelles (ou expressions rgulires) Str sont suffisants pour des application ne ncessitant pas une analyse puissante.

La ncessit de tels outils s'est essentiellement fait sentir dans le domaine de la compilation des langages de programmation volus, mais d'autres applications peuvent mettre en oeuvre avec profit de tels outils : les systmes de bases de donnes qui offrent la possibilit de formuler des requtes, les tableurs qui dfinissent le contenu de certaines cases comme rsultat de l'valuation d'une formule. En un mot, toute application dont l'interaction avec l'utilisateur se fait au moyen d'un langage met en oeuvre les analyses lexicale et syntaxique.

un niveau plus modeste, il est frquent d'utiliser un format ASCII pour stocker des donnes : fichiers de configurations systmes, donnes d'une feuille de calcul. L encore, l'exploitation de ces donnes ncessite une phase d'analyse lexicale et une phase d'analyse syntaxique.

Dans les cas simples comme dans les cas compliqus, le problme que doit rsoudre l'analyse lexicale et syntaxique est la transformation d'un flot linaire de caractres en une donne la structure plus riche : une suite de mots, une structure d'enregistrements, l'arbre de syntaxe abstraite d'un programme, etc.

Tout langage possde un vocabulaire (le lexique) et une grammaire permettant d'assembler correctement les lments du vocabulaire (la syntaxe). Pour qu'une machine, ou un programme, soit capable de traiter correctement un langage, celui-ci doit obir des rgles lexicales et syntaxiques trs prcises. Une machine n'a pas << l'esprit de finesse >> requis pour prendre une dcision face l'ambigut des langues naturelles. Il convient donc de s'adresser elle selon des rgles clairement fixes ne souffrant pas d'exception. Aussi, les notions de lexique et de syntaxe ont-elles reues des dfinitions formelles que nous prsentons brivement dans ce chapitre avant d'en montrer les applications.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora027.html0000644000000000000000000002575007421273601014501 0ustar Entres-sorties Prcdent Index Suivant

Entres-sorties

Les fonctions d'entres-sorties calculent une valeur (souvent de type unit), mais durant ce calcul elles effectuent une modification de l'tat des priphriques d'entres-sorties : modification du buffer du clavier, affichage l'cran, criture dans un fichier ou modification du pointeur de lecture. Les deux types suivants sont prdfinis : in_channel et out_channel pour respectivement les canaux d'entres et de sorties. Quand une fin de fichier est rencontre l'exception End_of_file est dclenche. Enfin, les trois constantes suivantes correspondent aux canaux standard d'entre, de sortie et d'erreur la manire d'Unix : stdin, stdout et stderr.

Canaux

Les fonctions d'entres-sorties de la bibliothque standard d'Objective CAML manipulent des canaux de communication : valeurs de types in_channel ou out_channel. En dehors des trois valeurs standard prdfinies, la cration d'un canal utilise une des fonctions suivantes :

# open_in;;
- : string -> in_channel = <fun>
# open_out;;
- : string -> out_channel = <fun>
open_in ouvre le fichier s'il existe2, dclenche l'exception Sys_error sinon.
open_out cre le fichier indiqu s'il n'existe pas ou l'crase s'il existe.

# let ic = open_in "koala";;
val ic : in_channel = <abstr>
# let oc = open_out "koala";;
val oc : out_channel = <abstr>
Les fonctions de fermeture des canaux sont :

# close_in ;;
- : in_channel -> unit = <fun>
# close_out ;;
- : out_channel -> unit = <fun>


Lecture et criture

Les fonctions les plus gnrales de lecture et d'criture sont les suivantes :

# input_line ;;
- : in_channel -> string = <fun>
# input ;;
- : in_channel -> string -> int -> int -> int = <fun>
# output ;;
- : out_channel -> string -> int -> int -> unit = <fun>


  • input_line ic : lit sur un canal d'entre ic tous les caractres jusqu'au premier retour chariot ou la fin de fichier, et les rend sous la forme d'une chane de caractres (le retour chariot exclu).
  • input ic s p l : tente de lire l caractres sur un canal d'entre ic, et les stocke dans la chane s partir du pieme caractre. Le nombre de caractres effectivement lus est retourn.
  • output oc s p l : crit sur un canal de sortie oc une partie de la chane s commenant en p-ime caractre de longueur l.
Les fonctions suivantes lisent sur l'entre standard ou crivent sur la sortie standard :

# read_line ;;
- : unit -> string = <fun>
# print_string ;;
- : string -> unit = <fun>
# print_newline ;;
- : unit -> unit = <fun>


D'autres valeurs de type simple peuvent tre aussi directement lues ou affiches. Ce sont les valeurs des types convertibles en chanes de caractres.

Dclarations locales et ordre d'valuation
Nous pouvons simuler une suite d'affichages des expressions de la forme let x = e1 in e2. Sachant que, en gnral, x est une variable locale susceptible d'tre utilise dans e2, nous savons que e1 est value en premier et qu'ensuite vient le tour d'e2. Si les deux expressions sont des fonctions impratives dont le rsultat est () mais qu'elles ont eu des effets de bord, nous les avons alors excutes dans le bon ordre. En particulier puisque nous connaissons la valeur de retour de e1  : la constante () de type unit, on obtient une squence d'affichage en crivant la suite de dclarations par filtrage du motif ().

# let () = print_string "et un," in
let () = print_string " et deux," in
let () = print_string " et trois" in
print_string " zro";;
et un, et deux, et trois zro- : unit = ()


Exemple : C+/C-

L'exemple suivant implante le jeu << C'est plus/C'est moins >> qui consiste cacher un nombre qu'un utilisateur devra trouver. Le programme indique chaque rponse si le nombre cach est plus petit ou plus grand que le nombre propos.

#  let rec cpcm n =
let () = print_string "taper un nombre : " in
let i = read_int ()
in
if i = n then
let () = print_string "BRAVO" in
let () = print_newline ()
in print_newline ()
else
let () =
if i < n then
let () = print_string "C+"
in print_newline ()
else
let () = print_string "C-"
in print_newline ()
in cpcm n ;;
val cpcm : int -> unit = <fun>


Voici un exemple de session :
# cpcm 64;;
taper un nombre : 88
C-
taper un nombre : 44
C+
taper un nombre : 64
BRAVO

- : unit = ()

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora125.html0000644000000000000000000054254707421273602014511 0ustar Construction d'une interface graphique Prcdent Index Suivant

Construction d'une interface graphique

La ralisation de l'interface graphique d'un programme est un travail fastidieux si les outils pour le faire ne sont pas assez riches. C'est le cas avec la bibliothque Graphics. Le confort d'utilisation d'un programme provient en partie de son interface. Pour cela nous allons tout d'abord construire, au dessus de Graphics, une nouvelle bibliothque, appele Upi et l'utiliser en tant que module simple pour la construction de l'interface d'une application.

Cette interface graphique manipule des composants. Un composant est une zone de la fentre principale qui peut tre affiche, dans un certain contexte graphique, et traiter des vnements qui lui sont destins. Il y a principalement deux types de composants : les composants simples, comme un bouton de confirmation ou une zone de saisie d'un texte, et les conteneurs qui acceptent de recevoir dans leur zone d'autres composants. Un composant ne peut tre attach qu' un seul conteneur. L'interface d'une application devient alors un arbre dont la racine correspond au conteneur principal (la fentre graphique), les noeuds sont d'autres conteneurs et les feuilles des composants simples ou des conteneurs sans descendant. Cette arborescence facilite la propagation des vnements produits par l'interaction avec l'utilisateur. Si un composant reoit un vnement, il vrifie si l'un de ses descendants peut le traiter, si oui il le lui envoie et sinon il effectue l'action associe cet vnement.

Le composant est l'lment essentiel de cette bibliothque. Nous le dfinissons comme un enregistrement qui contient principalement des informations de taille, un contexte graphique, le composant pre et les composants fils, ainsi que les fonctions d'affichage et de traitement des vnements. Les conteneurs possdent une fonction de placement des composants. Pour pouvoir dfinir le type component, on construit les types pour le contexte graphique, les vnements et les options de cration. Un contexte graphique est utilis pour les informations de << styles graphiques >> comme les couleurs de fond et de trac, la largeur des traits, le point courant du composant et les polices de caractres choisies. On dfinit ensuite la nature des vnements envoys aux composants. Ceux-ci sont plus varis que ceux de la bibliothque Graphics sur lesquels ils reposent. On intgre un mcanisme simple d'options qui facilite la spcialisation de contextes graphiques ou de composants. Une difficult d'implantation provient du placement d'un composant dans un conteneur.

La boucle gnrale de gestion des vnements reoit des vnements physiques par la fonction d'coute de la bibliothque Graphics, dtermine si d'autres vnements sont consquence de ces vnements physiques puis les envoie au composant racine. On dcrit ensuite les composants suivants : textes afficher, boutons, listes de choix, zones de saisie et composants enrichis.

On montre l'assemblage de composants pour la construction d'interfaces graphiques, en l'illustrant par un convertisseur Francs/Euros. Les divers composants de cette application communiquent entre eux en partageant un tat.

Contexte graphique, vnements et options

On dfinit les types de base, et les fonctions de cration et de modification, des contextes graphiques, des vnements et des options. Ce dernier type facilite la paramtrisation des fonctions de cration d'objets graphiques.

Contexte graphique

Le contexte graphique permet de tenir compte des couleurs de fond et d'encre, de la police de caractre, de la largeur des traits et du point courant. Le type suivant dcoule de cette dfinition.

# type g_context = {
mutable bcol : Graphics.color;
mutable fcol : Graphics.color;
mutable font : string;
mutable font_size : int;
mutable lw : int;
mutable x : int;
mutable y : int };;


La fonction make_default_context cre un nouveau contexte graphique contenant des valeurs par dfaut1.

# let default_font = "fixed"
let default_font_size = 12
let make_default_context () =
{ bcol = Graphics.white; fcol = Graphics.black;
font = default_font;
font_size = default_font_size;
lw = 1;
x = 0; y = 0;};;
val default_font : string = "fixed"
val default_font_size : int = 12
val make_default_context : unit -> g_context = <fun>


Les fonctions d'accs aux champs permettent de rcuprer les diffrentes valeurs d'un contexte graphique sans connatre la reprsentation du type.

# let get_gc_bcol gc = gc.bcol
let get_gc_fcol gc = gc.fcol
let get_gc_font gc = gc.font
let get_gc_font_size gc = gc.font_size
let get_gc_lw gc = gc.lw
let get_gc_cur gc = (gc.x,gc.y);;
val get_gc_bcol : g_context -> Graphics.color = <fun>
val get_gc_fcol : g_context -> Graphics.color = <fun>
val get_gc_font : g_context -> string = <fun>
val get_gc_font_size : g_context -> int = <fun>
val get_gc_lw : g_context -> int = <fun>
val get_gc_cur : g_context -> int * int = <fun>


Les fonctions de modification des champs reposent sur le mme modle.

# let set_gc_bcol gc c = gc.bcol <- c
let set_gc_fcol gc c = gc.fcol <- c
let set_gc_font gc f = gc.font <- f
let set_gc_font_size gc s = gc.font_size <- s
let set_gc_lw gc i = gc.lw <- i
let set_gc_cur gc (a,b) = gc.x<- a; gc.y<-b;;
val set_gc_bcol : g_context -> Graphics.color -> unit = <fun>
val set_gc_fcol : g_context -> Graphics.color -> unit = <fun>
val set_gc_font : g_context -> string -> unit = <fun>
val set_gc_font_size : g_context -> int -> unit = <fun>
val set_gc_lw : g_context -> int -> unit = <fun>
val set_gc_cur : g_context -> int * int -> unit = <fun>
On peut ainsi crer de nouveaux contextes, avoir accs aux diffrents champs d'un contexte et modifier un des champs d'une valeur de type g_context.

La fonction use_gc applique la fentre graphique les informations du contexte graphique.

# let use_gc gc =
Graphics.set_color (get_gc_fcol gc);
Graphics.set_font (get_gc_font gc);
Graphics.set_text_size (get_gc_font_size gc);
Graphics.set_line_width (get_gc_lw gc);
let (a,b) = get_gc_cur gc in Graphics.moveto a b;;
val use_gc : g_context -> unit = <fun>


Certaines informations, comme la couleur du fond, ne sont pas directement exploitables par la bibliothque Graphics et n'apparaissent pas dans la fonction use_gc.

vnements

La bibliothque Graphics ne contient qu'un nombre limit d'vnements d'interaction : clic souris, dplacement de souris et appui de touche. On dsire enrichir la nature des vnements survenant dans l'interaction en intgrant des vnements propres aux composants. Pour cela on dfinit le type rich_event :

# type rich_event =
MouseDown | MouseUp | MouseDrag | MouseMove
| MouseEnter | MouseExit | Exposure
| GotFocus | LostFocus | KeyPress | KeyRelease;;


Pour pouvoir engendrer de tels vnements, il est ncessaire de conserver un historique des derniers vnements produits. Les vnements MouseDown et MouseMove correspondent respectivement aux vnements de la souris (clic et dplacement) grs par Graphics. Les autres vnements souris sont crs en fonction soit de l'avant dernier vnement (MouseUp) ou du dernier composant ayant trait un vnement physique (MouseExit). L'vnement Exposure correspond une demande de raffichage du composant. La notion de focus exprime l'attention particulire d'un composant un type d'vnement. Typiquement, la saisie d'un texte dans un composant ayant accapar le focus du clavier indique qu'il est le seul traiter les vnements KeyPress et KeyRelease. Un vnement MouseDown sur un composant <<saisie de texte>> lui donnera le focus clavier en le retirant du composant qui le possdait ventuellement.

La cration de ces nouveaux vnements est contrle par la boucle de gestion des vnements, dcrite page ??.

Options

Une interface graphique a besoin d'une rgle pour le passage et le dcodage des options de cration des objets graphiques (composants, contextes graphiques). Si l'on veut crer un contexte graphique avec des couleurs spcifiques, il est actuellement ncessaire de construire un contexte graphique avec les valeurs par dfaut puis d'appeler les deux fonctions de modification des champs de couleur sur ce contexte. Dans le cas d'objets graphiques plus complexes, cela peut devenir trs vite fastidieux. Ces options doivent pouvoir tre tendues ce qui permet d'augmenter les composants de la bibliothque. Il y a besoin d'un type somme << extensible >>. Le seul fourni par Objective CAML est le type exn pour les exceptions. Comme il est prfrable pour la clart des programmes de n'utiliser ce type que pour de vraies exceptions, on simule un type somme extensible par des faux constructeurs reprsents par des chanes de caractres. On dfinit le type opt_val pour les valeurs des options. Une option devient un couple dont le premier lment est le nom de l'option et le deuxime sa valeur. Le type lopt regroupe une liste d'options.

# type opt_val = Copt of Graphics.color | Sopt of string
| Iopt of int | Bopt of bool;;
# type lopt = (string * opt_val) list ;;


Les fonctions de dcodage prennent en entre une liste d'options, un nom d'option et une valeur par dfaut. Si le nom appartient la liste alors la valeur associe est retourne, sinon c'est la valeur par dfaut. On ne dcrit que les fonctions de dcodage des options entires et boolennes, les autres reposent sur le mme modle.

# exception OptErr;;
exception OptErr
# let theInt lo name default =
try
match List.assoc name lo with
Iopt i -> i
| _ -> raise OptErr
with Not_found -> default;;
val theInt : ('a * opt_val) list -> 'a -> int -> int = <fun>
# let theBool lo name default =
try
match List.assoc name lo with
Bopt b -> b
| _ -> raise OptErr
with Not_found -> default;;
val theBool : ('a * opt_val) list -> 'a -> bool -> bool = <fun>


On peut donc crire une fonction de mise jour d'un contexte graphique en passant une liste d'options de la manire suivante :

# let set_gc gc lopt =
set_gc_bcol gc (theColor lopt "Background" (get_gc_bcol gc));
set_gc_fcol gc (theColor lopt "Foreground" (get_gc_fcol gc));
set_gc_font gc (theString lopt "Font" (get_gc_font gc));
set_gc_font_size gc (theInt lopt "FontSize" (get_gc_font_size gc));
set_gc_lw gc (theInt lopt "LineWidth" (get_gc_lw gc));;
val set_gc : g_context -> (string * opt_val) list -> unit = <fun>


Cela permet de ne pas tenir compte de l'ordre des options passes.

# let dc = make_default_context () in
set_gc dc [ "Foreground", Copt Graphics.blue;
"Background", Copt Graphics.yellow];
dc;;
- : g_context =
{bcol=16776960; fcol=255; font="fixed"; font_size=12; lw=1; x=0; y=0}


On obtient un systme assez souple qui malheureusement chappe en partie au systme de types. Le nom d'une option est du type string et rien n'empche de construire un nom inexistant. La consquence est alors de ne pas tenir compte de sa valeur.

Composants et conteneurs

Le composant est la brique essentielle de cette bibliothque. On dsire avoir la possibilit de crer des composants et de facilement les assembler pour construire des interfaces. Ils doivent pouvoir s'afficher, savoir qu'un vnement leur est destin et traiter cet vnement. Quand ils sont conteneurs, ils doivent pouvoir recevoir d'autres composants ou en retirer. On suppose qu'un composant simple ne peut tre ajout qu' un seul conteneur.

Cration de composants

Une valeur de type component possde une taille (w et h), une position absolue dans la fentre principale (x et y), un contexte graphique pour l'affichage (gc), un indicateur pour savoir s'il est un conteneur (container), un pre s'il est attach un conteneur (father), une liste de ses fils (sons) et quatre fonctions permettant le placement d'autres composants (layout), son affichage (display), l'appartenance d'un point sa zone (mem) et une fonction de traitement d'un vnement (listener) qui retourne true si l'vnement est trait et false sinon. Le paramtre de listener prend un tat enrichi (type rich_status) qui contient le nom de l'vnement, l'tat de l'vnement physique provenant de Graphics, les informations sur le focus clavier et le focus gnral, ainsi que le dernier composant ayant trait un vnement. On obtient les dclarations mutuellement rcursives suivantes :

# type component =
{ mutable info : string;
mutable x : int; mutable y : int;
mutable w :int ; mutable h : int;
mutable gc : g_context;
mutable container : bool;
mutable father : component list;
mutable sons : component list;
mutable layout_options : lopt;
mutable layout : component -> lopt -> unit;
mutable display : unit -> unit;
mutable mem : int * int -> bool;
mutable listener : rich_status -> bool }
and rich_status =
{ re : rich_event;
stat : Graphics.status;
mutable key_focus : component;
mutable gen_focus : component;
mutable last : component};;


On accde aux champs informatifs d'un composant par les fonctions suivantes.

# let get_gc c = c.gc;;
val get_gc : component -> g_context = <fun>
# let is_container c = c.container;;
val is_container : component -> bool = <fun>


Les trois fonctions suivantes vont dfinir le comportement par dfaut d'un composant. La fonction d'appartenance (in_rect) vrifie que la coordonne passe est bien dans le rectangle dfini par les coordonnes du composant. La fonction d'affichage par dfaut (display_rect) remplit le rectangle du composant par la couleur de fond de son contexte graphique. Le placement par dfaut (direct_layout) est celui du positionnement relatif du composant dans le conteneur. Les options acceptes sont "PosX" et "PosY" correspondant aux positions relatives du composant dans le conteneur.

# let in_rect c (xp,yp) =
(xp >= c.x) && (xp < c.x + c.w) && (yp >= c.y) && (yp < c.y + c.h) ;;
val in_rect : component -> int * int -> bool = <fun>
# let display_rect c () =
let gc = get_gc c in
Graphics.set_color (get_gc_bcol gc);
Graphics.fill_rect c.x c.y c.w c.h ;;
val display_rect : component -> unit -> unit = <fun>
# let direct_layout c c1 lopt =
let px = theInt lopt "PosX" 0
and py = theInt lopt "PosY" 0 in
c1.x <- c.x + px; c1.y <- c.y + py ;;
val direct_layout :
component -> component -> (string * opt_val) list -> unit = <fun>


Il est maintenant possible de dfinir un composant grce la fonction create_component qui prend une largeur et une hauteur et qui utilise les trois fonctions prcdentes.

# let create_component iw ih =
let dc =
{info="Anonymous";
x=0; y=0; w=iw; h=ih;
gc = make_default_context() ;
container = false;
father = []; sons = [];
layout_options = [];
layout = (fun a b -> ());
display = (fun () -> ());
mem = (fun s -> false);
listener = (fun s -> false);}
in
dc.layout <- direct_layout dc;
dc.mem <- in_rect dc;
dc.display <- display_rect dc;
dc ;;
val create_component : int -> int -> component = <fun>


On dfinit alors le composant vide suivant :

# let empty_component = create_component 0 0 ;;
Celui-ci est utilis comme valeur par dfaut pour la construction de valeurs contenant au moins un composant comme, par exemple, une valeur de type rich_status.

Ajout de composants

La difficult de l'ajout d'un composant un conteneur est le positionnement du composant dans le conteneur. Le champ layout contient la fonction de placement. Elle prend un composant (un fils) et une liste d'options et calcule les nouvelles coordonnes du composant fils dans le conteneur. Diffrentes options peuvent tre utilises selon chaque fonction de placement. On dtaille plusieurs fonctions de placement lors de la dfinition des conteneurs panel (voir infra, page ??). On dcrit ici uniquement la mcanique de propagation de l'affichage, du changement de coordonnes et du traitement des vnements. Cette propagation d'actions utilise intensivement la fonction List.iter qui applique une fonction tous les lments d'une liste.

La fonction change_coord applique un changement relatif de coordonnes un composant et tous ses fils.

# let rec change_coord c (dx,dy) =
c.x <- c.x + dx; c.y <- c.y + dy;
List.iter (fun s -> change_coord s (dx,dy) ) c.sons;;
val change_coord : component -> int * int -> unit = <fun>


La fonction add_component vrifie que les conditions d'ajout sont bien remplies, puis raccroche pre (c) et fils (c1). La liste des options de placement est conserve dans le composant fils ce qui permet de la rutiliser en cas de modification de la rgle de placement du pre. La liste d'options passe cette fonction concerne les options de la fonction de placement. Il y a trois cas d'chec : le composant fils a dj un pre, le pre n'est pas un conteneur et la taille du fils est trop grande.

# let add_component c c1 lopt =
if c1.father <> [] then failwith "add_component : yet a father"
else
if not (is_container c ) then
failwith "add_component : not a container"
else
if (c1.x + c1.w > c.w) || (c1.y + c1.h > c.h)
then failwith "add_component : bad position"
else
c.layout c1 lopt;
c1.layout_options <- lopt;
List.iter (fun s -> change_coord s (c1.x,c1.y)) c1.sons;
c.sons <- c1::c.sons;
c1.father <- [c] ;;
val add_component : component -> component -> lopt -> unit = <fun>


Le retrait d'un composant un niveau de l'arbre, ralis par la fonction suivante, entrane une modification des liens entre pre et fils, mais aussi une modification des coordonnes du fils et de ses descendants :

# let remove_component c c1 =
c.sons <- List.filter ((!=) c1) c.sons;
c1.father <- List.filter ((!=) c) c1.father;
List.iter (fun s -> change_coord s (- c1.x, - c1.y)) c1.sons;
c1.x <- 0; c1.y <- 0;;
val remove_component : component -> component -> unit = <fun>


Le changement de la fonction de placement d'un conteneur dpend de l'existence de fils du conteneur. Dans le cas sans descendance, la modification est immdiate. Dans l'autre cas il faut d'abord enlever les fils du conteneur, modifier sa fonction de placement, puis rajouter les composants enlevs avec les mmes options qu'au premier placement.

# let set_layout f c =
if c.sons = [] then c.layout <- f
else
let ls = c.sons in
List.iter (remove_component c) ls;
c.layout <- f;
List.iter (fun s -> add_component c s s.layout_options) ls;;
val set_layout : (component -> lopt -> unit) -> component -> unit = <fun>
C'est pour cela que la liste d'options de placement d'un composant est conserve. Si la liste des options n'est pas reconnue par la nouvelle fonction de placement, celle-ci utilise les valeurs par dfaut de ses options.

L'affichage d'un composant doit tre propag ses fils pour les conteneurs. Un conteneur est affich en arrire plan par rapport ses fils. L'ordre d'affichage des fils importe peu car leur intersection est toujours vide.

# let rec display c =
c.display ();
List.iter (fun cx -> display cx ) c.sons;;
val display : component -> unit = <fun>
Cette fonction peut tre directement applique un composant pour le visualiser.

Gestion des vnements

La gestion des vnements physiques (clic souris, appui d'une touche, dplacement de la souris) utilise la fonction Graphics.wait_next_event (voir page ??) qui retourne un tat physique (de type Graphics.status) suite une action de l'utilisateur. Cet tat physique est utilis pour calculer un tat enrichi (de type rich_status) contenant la nature de l'vnement (type rich_event), l'tat physique, les composants possdant le focus clavier et le focus gnral ainsi que le dernier composant ayant trait avec succs un tel tat. Le focus gnral correspond un composant prenant tous les vnements.

On dcrit les fonctions de manipulation des tats enrichis, la propagation de ces tats aux composants pour leur traitement, la cration de ces tats et la boucle principale de gestion des vnements.

Fonctions sur les tats

Les fonctions suivantes consultent les valeurs de positionnement souris et de focus. Les fonctions sur les focus ncessitent un paramtre supplmentaire : le composant gagnant ou perdant le focus.

# let get_event e = e.re;;
# let get_mouse_x e = e.stat.Graphics.mouse_x;;
# let get_mouse_y e = e.stat.Graphics.mouse_y;;
# let get_key e = e.stat.Graphics.key;;

# let has_key_focus e c = e.key_focus == c;;
# let take_key_focus e c = e.key_focus <- c;;
# let lose_key_focus e c = e.key_focus <- empty_component;;
# let has_gen_focus e c = e.gen_focus == c;;
# let take_gen_focus e c = e.gen_focus <- c;;
# let lose_gen_focus e c = e.gen_focus <- empty_component;;


Propagation des vnements

Un tat enrichi est envoy un composant pour son traitement. De la mme manire que pour l'affichage, les composants fils ont priorit sur leur pre pour le traitement d'un vnement souris simple. Ils sont devant leur pre et ils ne se recouvrent pas entre eux. Quand un composant reoit l'tat associ un vnement, il regarde s'il existe un descendant pour le traiter. Si c'est le cas, le descendant renvoie true et sinon, false. Dans ce dernier cas le composant anctre essaie de le traiter par la fonction de son champ listener.

Les tats provenant d'une action clavier sont propags diffremment. Le composant anctre regarde s'il possde le focus clavier, si oui il traite l'vnement et sinon. il le propage vers ses fils.

Certains tats sont produits par le traitement d'un vnement premier (par exemple : la prise de focus par un composant entrane sa perte par d'autres composants). De tels tats sont traits immdiatement par le composant cible. Il en est de mme pour les entres et sorties de la souris de la zone d'un composant.

La fonction send_event prend une valeur de type rich_status et un composant, elle retourne un boolen indiquant si l'vnement a t trait ou non.

# let rec send_event rs c =
match get_event rs with
MouseDown | MouseUp | MouseDrag | MouseMove ->
if c.mem(get_mouse_x rs, get_mouse_y rs) then
if List.exists (fun sun -> send_event rs sun) c.sons then true
else ( if c.listener rs then (rs.last <-c; true) else false )
else false
| KeyPress | KeyRelease ->
if has_key_focus rs c then
( if c.listener rs then (rs.last<-c; true)
else false )
else List.exists (fun sun -> send_event rs sun) c.sons
| _ -> c.listener rs;;
val send_event : rich_status -> component -> bool = <fun>


Il est noter que la hirarchie des composants est bien un arbre et non un graphe avec cycle. Ce qui garantit que la fonction rcursive send_event ne bouclera pas.

Cration d'vnements

On diffrencie deux types d'vnements : ceux produits par une action physique (comme un clic souris) et ceux qui dcoulent d'une action lie l'historique (comme la sortie du curseur de la souris de la zone graphique d'un composant). On dfinira, en consquence, deux fonctions de cration d'tats enrichis.

La fonction qui traite les premiers construit un tat enrichi partir de deux tats physiques :

# let compute_rich_event s0 s1 =
if s0.Graphics.button <> s1.Graphics.button then (* bouton *)
begin
if s0.Graphics.button then MouseDown else MouseUp
end
else if s1.Graphics.keypressed then KeyPress (* cl *)
else if (s0.Graphics.mouse_x <> s1.Graphics.mouse_x ) || (* dplace *)
(s0.Graphics.mouse_y <> s1.Graphics.mouse_y ) then
begin
if s1.Graphics.button then MouseDrag else MouseMove
end
else raise Not_found;;
val compute_rich_event : Graphics.status -> Graphics.status -> rich_event =
<fun>


La fonction qui cre d'autres vnements utilisent les deux derniers tats enrichis :

# let send_new_events res0 res1 =
if res0.key_focus <> res1.key_focus then
begin
ignore(send_event {res1 with re = LostFocus} res0.key_focus);
ignore(send_event {res1 with re = GotFocus} res1.key_focus)
end;
if (res0.last <> res1.last) &&
(( res1.re = MouseMove) || (res1.re = MouseDrag)) then
begin
ignore(send_event {res1 with re = MouseExit} res0.last);
ignore(send_event {res1 with re = MouseEnter} res1.last )
end;;
val send_new_events : rich_status -> rich_status -> unit = <fun>


On dfinit une valeur initiale pour le type rich_event. Elle est employe pour initialiser l'historique de la boucle d'vnements.

# let initial_re =
{ re = Exposure;
stat = { Graphics.mouse_x=0; Graphics.mouse_y=0;
Graphics.key = ' ';
Graphics.button = false;
Graphics.keypressed = false };
key_focus = empty_component;
gen_focus = empty_component;
last = empty_component } ;;


Boucle d'vnements

La boucle d'vnements gre l'ensemble des interactions sur un composant, en gnral le conteneur anctre de tous les composants de l'interface. Elle prend en entre deux boolens, indiquant le raffichage de l'interface aprs chaque traitement d'un vnement physique (b_disp) et la gestion du dplacement de la souris (b_motion). Le dernier argument (OCnamec), est le composant principal, racine de l'arbre des composants.

# let loop b_disp b_motion c =
let res0 = ref initial_re in
try
display c;
while true do
let lev = [Graphics.Button_down; Graphics.Button_up;
Graphics.Key_pressed] in
let flev = if b_motion then (Graphics.Mouse_motion) :: lev
else lev in
let s = Graphics.wait_next_event flev
in
let res1 = {!res0 with stat = s} in
try
let res2 = {res1 with
re = compute_rich_event !res0.stat res1.stat} in
ignore(send_event res2 c);
send_new_events !res0 res2;
res0 := res2;
if b_disp then display c
with Not_found -> ()
done
with e -> raise e;;
val loop : bool -> bool -> component -> unit = <fun>
La seule manire de sortir de cette boucle est qu'une des actions de traitement d'un vnement dclenche une exception.

Fonctions de test

On dfinit les deux fonctions suivantes, pour crer la main des tats correspondants aux vnements souris et clavier.

# let make_click e x y =
{re = e;
stat = {Graphics.mouse_x=x; Graphics.mouse_y=y;
Graphics.key = ' '; Graphics.button = false;
Graphics.keypressed = false};
key_focus = empty_component;
gen_focus = empty_component;
last = empty_component}

let make_key e ch c =
{re = e;
stat = {Graphics.mouse_x=0; Graphics.mouse_y=0;
Graphics.key = c; Graphics.button = false;
Graphics.keypressed = true};
key_focus = empty_component;
gen_focus = empty_component;
last = empty_component};;
val make_click : rich_event -> int -> int -> rich_status = <fun>
val make_key : rich_event -> 'a -> char -> rich_status = <fun>


On pourra donc simuler l'envoi d'un vnement souris pour les tests de l'interface en utilisant cette fonction.

Dfinition de composants

Les diffrents mcanismes d'affichage, de modification de coordonnes et de propagation d'vnements sont en place. Il reste maintenant dfinir des composants utiles et faciles manipuler. On peut classer les composants en trois catgories :
  • les composants simples ne traitant pas les vnements (comme les textes afficher);
  • les composants simples traitant des vnements, comme les champs de saisie;
  • les conteneurs et les variantes de placement.
La communication de valeurs entre deux composants ou entre un composant et l'application s'effectue par modification physique d'une donne partage. Le partage est ralis en crant des fermetures qui contiennent dans leur environnement les valeurs modifier. De plus comme le comportement d'un composant peut varier la suite du traitement d'un vnement, ils conservent aussi dans leur fermeture de traitement un tat interne. Par exemple la fonction de traitement d'une zone de saisie a accs au texte en cours d'criture. Pour cela on crit les composants de la manire suivante :
  • dfinition d'un type pour l'tat interne du composant;
  • dclaration des fonctions de manipulation de cet tat;
  • criture des fonctions d'affichage, d'appartenance d'un point au composant et de traitement des vnements;
  • ralisation de la fonction de cration du composant qui associe ces fermetures aux champs du composant;
  • test du composant en simulant le dclenchement d'vnements.
Les fonctions de cration prennent une liste d'options pour paramtrer le contexte graphique. Le calcul de la taille d'un composant sa cration a besoin d'utiliser le contexte graphique du composant sur le fentre graphique pour connatre la taille du texte afficher.

On dtaille l'implantation des composants suivants :
  • texte simple (label)
  • conteneur simple (panel)
  • bouton simple (button)
  • liste de choix (choice)
  • champ de saisie (textfield)
  • composant enrichi (border)

Composant label

Le composant le plus simple, que l'on appelle un label, affiche une chane de caractres l'cran. Il ne traite pas d'vnement. On dcrit tout d'abord sa fonction d'affichage puis sa fonction de cration.

L'affichage doit tenir compte des couleurs d'encre et de fond et des polices de caractres. C'est le rle de la fonction display_init qui efface la zone graphique du composant et slectionne la couleur de l'encre et la position du point courant. La fonction display_label affiche la chane passe en paramtre juste aprs l'appel display_init.

# let display_init c =
Graphics.set_color (get_gc_bcol (get_gc c)); display_rect c ();
let gc= get_gc c in
use_gc gc;
let (a,b) = get_gc_cur gc in
Graphics.moveto (c.x+a) (c.y+b)
let display_label s c () =
display_init c; Graphics.draw_string s;;
val display_init : component -> unit = <fun>
val display_label : string -> component -> unit -> unit = <fun>


Comme ce composant est fort simple, il n'est pas ncessaire de crer un tat interne. Seule la fonction display_label connat la chane afficher qui est passe la fonction de cration.

# let create_label s lopt =
let gc = make_default_context () in set_gc gc lopt; use_gc gc;
let (w,h) = Graphics.text_size s in
let u = create_component w h in
u.mem <- (fun x -> false); u.display <- display_label s u;
u.info <- "Label"; u.gc <- gc;
u;;
val create_label : string -> (string * opt_val) list -> component = <fun>


Si l'on dsire changer les couleurs de ce composant, il est ncessaire d'intervenir directement sur son contexte graphique.

L'affichage du label l1 suivant est reprsent la figure 13.1.

# let courier_bold_24 = Sopt "*courier-bold-r-normal-*24*"
and courier_bold_18 = Sopt "*courier-bold-r-normal-*18*";;
# let l1 = create_label "Login : " ["Font", courier_bold_24;
"Background", Copt gris1];;




Figure 13.1 : Affichage d'un label


Composant panel, conteneur et placement

On appelle panel un panneau graphique qui peut tre un conteneur. La fonction de cration par dfaut est trs simple, elle reprend principalement la fonction gnrale de cration de composants en acceptant de plus un boolen indiquant s'il est un conteneur ou non. Les fonctions d'appartenance la zone du panel et d'affichage sont celles par dfaut de la fonction create_component.

# let create_panel b w h lopt =
let u = create_component w h in
u.container <- b;
u.info <- if b then "Panel container" else "Panel";
let gc = make_default_context () in set_gc gc lopt; u.gc <- gc;
u;;
val create_panel :
bool -> int -> int -> (string * opt_val) list -> component = <fun>


Le point dlicat des conteneurs provient du placement des composants. On dfinit deux nouvelles fonctions de placement : center_layout et grid_layout. La premire, center_layout place un composant au centre d'un conteneur :

# let center_layout c c1 lopt =
c1.x <- c.x + ((c.w -c1.w) /2); c1.y <- c.y + ((c.h -c1.h) /2);;
val center_layout : component -> component -> 'a -> unit = <fun>


La seconde, grid_layout dcoupe un conteneur en grille o chaque case a la mme taille. Les options de placement sont "Col" pour le numro de la colonne et "Row" pour le numro de a ligne.

# let grid_layout (a, b) c c1 lopt =
let px = theInt lopt "Col" 0
and py = theInt lopt "Row" 0 in
if (px >= 0) && (px < a) && ( py >=0) && (py < b) then
let lw = c.w /a
and lh = c.h /b in
if (c1.w > lw) || (c1.h > lh) then
failwith "grid_placement : too big component"
else
c1.x <- c.x + px * lw + (lw - c1.w)/2;
c1.y <- c.y + py * lh + (lh - c1.h)/2;
else failwith "grid_placement : bad position";;
val grid_layout :
int * int -> component -> component -> (string * opt_val) list -> unit =
<fun>
Il est bien sr possible d'en dfinir d'autres. On peut donc particulariser un conteneur en modifiant sa fonction de placement (set_layout). La figure 13.2 montre un panel, dclar comme conteneur, dans lequel deux labels ont t ajouts et correspond au programme suivant :



Figure 13.2 : Affichage d'un panel


# let l2 = create_label "Passwd : " ["Font", courier_bold_24;
"Background", Copt gris1] ;;
# let p1 = create_panel true 150 80 ["Background", Copt gris2] ;;
# set_layout (grid_layout (1,2) p1) p1;;
# add_component p1 l1 ["Row", Iopt 1];;
# add_component p1 l2 ["Row", Iopt 0];;


Comme les composants ont besoin d'au moins un pre pour s'intgrer dans l'interface, et que la bibliothque Graphics ne possde qu'une seule fentre, on dfinit une fentre principale qui est un conteneur.

# let open_main_window w h =
Graphics.close_graph();
Graphics.open_graph (":0 "^(string_of_int w)^"x"^(string_of_int h));
let u = create_component w h in
u.container <- true;
u.info <- "Fentre principale";
u;;
val open_main_window : int -> int -> component = <fun>


Composant button

Un bouton simple ou button est un composant qui d'une part affiche un texte dans sa zone graphique et ragit au clic souris. Pour cela il conserve un tat, valeur de type button_state, qui contient son texte et sa fonction de traitement du clic souris.

# type button_state =
{ txt : string; mutable action : button_state -> unit } ;;


La cration d'un tat est effectue par la fonction create_bs. La fonction set_bs_action modifie la fonction d'action et la fonction get_bs_text rcupre le texte d'un bouton.

# let create_bs s = {txt = s; action = fun x -> ()}
let set_bs_action bs f = bs.action <- f
let get_bs_text bs = bs.txt;;
val create_bs : string -> button_state = <fun>
val set_bs_action : button_state -> (button_state -> unit) -> unit = <fun>
val get_bs_text : button_state -> string = <fun>


La fonction d'affichage est similaire celle des labels la diffrence prs que le texte provient de l'tat du bouton. La fonction d'coute par dfaut dclenche la fonction d'action par l'appui d'un bouton souris.

# let display_button c bs () =
display_init c; Graphics.draw_string (get_bs_text bs)
let listener_button c bs e = match get_event e with
MouseDown -> bs.action bs; c.display (); true
| _ -> false;;
val display_button : component -> button_state -> unit -> unit = <fun>
val listener_button : component -> button_state -> rich_status -> bool =
<fun>


Tout est prt pour dfinir la fonction de cration de boutons simples :

# let create_button s lopt =
let bs = create_bs s in
let gc = make_default_context () in
set_gc gc lopt; use_gc gc;
let w,h = Graphics.text_size (get_bs_text bs) in
let u = create_component w h in
u.display <- display_button u bs;
u.listener <- listener_button u bs;
u.info <- "Button";
u.gc <- gc;
u,bs;;
val create_button :
string -> (string * opt_val) list -> component * button_state = <fun>
Celle-ci retourne un couple dont le premier lment est le bouton cr et le deuxime l'tat interne de ce bouton. Cela est particulirement utile si l'on dsire modifier la fonction d'action de celui-ci.

La figure 13.3 cre un panel auquel est ajout un bouton. On associe une fonction d'action qui affiche la chane contenue dans le bouton sur la sortie standard.



Figure 13.3 : Affichage d'un bouton et raction au clic souris


# let b,bs = create_button "Validation" ["Font", courier_bold_24;
"Background", Copt gris1];;
# let p2 = create_panel true 150 60 ["Background", Copt gris2];;
# set_bs_action bs (fun bs -> print_string ( (get_bs_text bs)^ "...");
print_newline());;
# set_layout (center_layout p2) p2;;
# add_component p2 b [];;


la diffrence des labels, un composant bouton sait ragir un vnement souris. Pour le tester on envoie l'vnement << clic souris >> en position centrale du panel p2, qui est rpercut au bouton, ce qui dclenche l'action associe ce dernier :

# send_event (make_click MouseDown 75 30) p2;;
Validation...
- : bool = true
et retourne la valeur true indiquant que l'vnement a bien t trait.

Composant choice

Les listes de choix (ou choice) permet de slectionner un des choix proposs par un clic souris. Il existe toujours un choix courant. Un clic souris sur un autre choix fait changer le choix courant et dclenche une action. On utilise la mme technique que pour les boutons simples. On commence par dfinir l'tat d'une liste de choix :

# type choice_state =
{ mutable ind : int; values : string array; mutable sep : int;
mutable height : int; mutable action : choice_state -> unit } ;;
L'indice ind indique la chane marquer range dans values. Les champs sep et height exprims en pixels correspondent respectivement la sparation entre deux choix et la hauteur de chaque choix. La fonction d'action prend en entre un tel tat et peut s'aiguiller selon l'indice.

On dfinit maintenant la fonction de cration d'un tat et la fonction de modification du traitement.

# let create_cs sa = {ind = 0; values = sa; sep = 2;
height = 1; action = fun x -> ()}
let set_cs_action cs f = cs.action <- f
let get_cs_text cs = cs.values.(cs.ind);;
val create_cs : string array -> choice_state = <fun>
val set_cs_action : choice_state -> (choice_state -> unit) -> unit = <fun>
val get_cs_text : choice_state -> string = <fun>


L'affichage donne la liste de tous les choix et souligne le choix courant en inverse vido. La fonction de traitement des vnements dclenche la fonction de traitement de l'tat au relchement du bouton de souris.

# let display_choice c cs () =
display_init c;
let (x,y) = Graphics.current_point()
and nb = Array.length cs.values in
for i = 0 to nb-1 do
Graphics.moveto x (y + i*(cs.height+ cs.sep));
Graphics.draw_string cs.values.(i)
done;
Graphics.set_color (get_gc_fcol (get_gc c));
Graphics.fill_rect x (y+ cs.ind*(cs.height+ cs.sep)) c.w cs.height;
Graphics.set_color (get_gc_bcol (get_gc c));
Graphics.moveto x (y + cs.ind*(cs.height + cs.sep));
Graphics.draw_string cs.values.(cs.ind) ;;
val display_choice : component -> choice_state -> unit -> unit = <fun>

# let listener_choice c cs e = match e.re with
MouseUp ->
let x = e.stat.Graphics.mouse_x
and y = e.stat.Graphics.mouse_y in
let cy = c.y in
let i = (y - cy) / ( cs.height + cs.sep) in
cs.ind <- i; c.display ();
cs.action cs; true
| _ -> false ;;
val listener_choice : component -> choice_state -> rich_status -> bool =
<fun>


La cration d'une liste de choix prend une liste de chanes de caractres et une liste d'options pour retourner le composant ainsi que son tat.

# let create_choice lc lopt =
let sa = (Array.of_list (List.rev lc)) in
let cs = create_cs sa in
let gc = make_default_context () in
set_gc gc lopt; use_gc gc;
let awh = Array.map (Graphics.text_size) cs.values in
let w = Array.fold_right (fun (x,y) -> max x) awh 0
and h = Array.fold_right (fun (x,y) -> max y) awh 0 in
let h1 = (h+cs.sep) * (Array.length sa) + cs.sep in
cs.height <- h;
let u = create_component w h1 in
u.display <- display_choice u cs;
u.listener <- listener_choice u cs ;
u.info <- "Choice "^ (string_of_int (Array.length cs.values));
u.gc <- gc;
u,cs;;
val create_choice :
string list -> (string * opt_val) list -> component * choice_state = <fun>


Les trois images de la figure 13.4 montre un panel auquel a t ajout une liste de choix. On associe une fonction d'action qui affiche sur la sortie standard la chane correspondant au choix indiqu dans le button. Chaque image fait suite un clic souris simul par le programme suivant.


Figure 13.4 : Affichage d'une liste de choix et slection



# let c,cs = create_choice ["Helium"; "Gallium"; "Pentium"]
["Font", courier_bold_24;
"Background", Copt gris1];;
# let p3 = create_panel true 110 110 ["Background", Copt gris2];;
# set_cs_action cs (fun cs -> print_string ( (get_cs_text cs)^"...");
print_newline());;
# set_layout (center_layout p3) p3;;
# add_component p3 c [];;


L aussi nous pouvons le tester directement en envoyant plusieurs vnements. Cet envoi modifie la slection, comme dans l'image du centre de la figure 13.4.

# send_event (make_click MouseUp 60 55 ) p3;;
Gallium...
- : bool = true


De mme l'vnement suivant envoy slectionne le premier lment de la liste de choix

# send_event (make_click MouseUp 60 90 ) p3;;
Helium...
- : bool = true


Composant textfield

Un champ de saisie, ou textfield, est une zone permettant d'crire du texte sur une ligne. Le texte peut s'afficher de gauche droite ou de droite gauche comme pour une calculatrice. De plus un curseur indique la position du prochain caractre entrer. On dfinit un tat un peu plus complexe. Il comprend le texte en cours de saisie, la direction de la saisie, l'information sur le curseur, l'information sur l'affichage des caractres, et la fonction d'action.

# type textfield_state =
{ txt : string;
dir : bool; mutable ind1 : int; mutable ind2 : int; len : int;
mutable visible_cursor : bool; mutable cursor : char;
mutable visible_echo : bool; mutable echo : char;
mutable action : textfield_state -> unit } ;;


La cration d'un tel tat demande le texte initial, le nombre de caractres de la zone de saisie et la direction de saisie.

# let create_tfs txt size dir =
let l = String.length txt in
(if size < l then failwith "create_tfs");
let ind1 = if dir then 0 else size-1-l
and ind2 = if dir then l else size-1 in
let n_txt = (if dir then (txt^(String.make (size-l) ' '))
else ((String.make (size-l) ' ')^txt )) in
{txt = n_txt; dir=dir; ind1 = ind1; ind2 = ind2; len=size;
visible_cursor = false; cursor = ' '; visible_echo = true; echo = ' ';
action= fun x -> ()};;
val create_tfs : string -> int -> bool -> textfield_state = <fun>


Les fonctions suivantes permettent de modifier certains champs et de retourner le texte affich.

# let set_tfs_action tfs f = tfs.action <- f
let set_tfs_cursor b c tfs = tfs.visible_cursor <- b; tfs.cursor <- c
let set_tfs_echo b c tfs = tfs.visible_echo <- b; tfs.echo <- c
let get_tfs_text tfs =
if tfs.dir then String.sub tfs.txt tfs.ind1 (tfs.ind2 - tfs.ind1)
else String.sub tfs.txt (tfs.ind1+1) (tfs.ind2 - tfs.ind1);;


La fonction set_tfs_text modifie le text de l'tat tfs du composant tf par la chane txt.

# let set_tfs_text tf tfs txt =
let l = String.length txt in
if l > tfs.len then failwith "set_tfs_text";
String.blit (String.make tfs.len ' ') 0 tfs.txt 0 tfs.len;
if tfs.dir then (String.blit txt 0 tfs.txt 0 l;
tfs.ind2 <- l )
else ( String.blit txt 0 tfs.txt (tfs.len -l) l;
tfs.ind1 <- tfs.len-l-1 );
tf.display ();;
val set_tfs_text : component -> textfield_state -> string -> unit = <fun>


L'affichage tient compte des informations sur l'cho des caractres et sur la visibilit du curseur. La fonction display_textfield appelle la fonction display_cursor qui souligne la position du curseur.

# let display_cursor c tfs =
if tfs.visible_cursor then
( use_gc (get_gc c);
let (x,y) = Graphics.current_point() in
let (a,b) = Graphics.text_size " " in
let shift = a * (if tfs.dir then max (min (tfs.len-1) tfs.ind2) 0
else tfs.ind2) in
Graphics.moveto (c.x+x + shift) (c.y+y);
Graphics.draw_char tfs.cursor);;
val display_cursor : component -> textfield_state -> unit = <fun>
# let display_textfield c tfs () =
display_init c;
let s = String.make tfs.len ' '
and txt = get_tfs_text tfs in
let nl = String.length txt in
if (tfs.ind1 >= 0) && (not tfs.dir) then
Graphics.draw_string (String.sub s 0 (tfs.ind1+1) );
if tfs.visible_echo then (Graphics.draw_string (get_tfs_text tfs))
else Graphics.draw_string (String.make (String.length txt) tfs.echo);
if (nl > tfs.ind2) && (tfs.dir)
then Graphics.draw_string (String.sub s tfs.ind2 (nl-tfs.ind2));
display_cursor c tfs;;
val display_textfield : component -> textfield_state -> unit -> unit = <fun>


La fonction d'coute des vnements est plus complexe pour ce type de composant. En effet il est ncessaire de grer le dplacement de la chane saisie selon la direction indique la cration du composant. La prise de focus s'effectue par un clic souris dans la zone de saisie.

# let listener_text_field u tfs e =
match e.re with
MouseDown -> take_key_focus e u ; true
| KeyPress ->
( if Char.code (get_key e) >= 32 then
begin
( if tfs.dir then
( ( if tfs.ind2 >= tfs.len then (
String.blit tfs.txt 1 tfs.txt 0 (tfs.ind2-1);
tfs.ind2 <- tfs.ind2-1) );
tfs.txt.[tfs.ind2] <- get_key e;
tfs.ind2 <- tfs.ind2 +1 )
else
( String.blit tfs.txt 1 tfs.txt 0 (tfs.ind2);
tfs.txt.[tfs.ind2] <- get_key e;
if tfs.ind1 >= 0 then tfs.ind1 <- tfs.ind1 -1
);
)
end
else (
( match Char.code (get_key e) with
13 -> tfs.action tfs
| 9 -> lose_key_focus e u
| 8 -> if (tfs.dir && (tfs.ind2 > 0))
then tfs.ind2 <- tfs.ind2 -1
else if (not tfs.dir) && (tfs.ind1 < tfs.len -1)
then tfs.ind1 <- tfs.ind1+1
| _ -> ()
))); u.display(); true
| _ -> false;;
val listener_text_field :
component -> textfield_state -> rich_status -> bool = <fun>


La fonction de cration des champs de saisie reprend le mme schma de construction que pour les composants dj vus.

# let create_text_field txt size dir lopt =
let tfs = create_tfs txt size dir
and l = String.length txt in
let gc = make_default_context () in
set_gc gc lopt; use_gc gc;
let (w,h) = Graphics.text_size (tfs.txt) in
let u = create_component w h in
u.display <- display_textfield u tfs;
u.listener <- listener_text_field u tfs ;
u.info <- "TextField"; u.gc <- gc;
u,tfs;;
val create_text_field :
string ->
int -> bool -> (string * opt_val) list -> component * textfield_state =
<fun>


La cration retourne un composant et un tat pour les champs de saisie. On teste la cration des champs de saisie de la figure 13.5.

# let tf1,tfs1 = create_text_field "dupneu" 8 true ["Font", courier_bold_24];;
# let tf2,tfs2 = create_text_field "koala" 8 false ["Font", courier_bold_24];;
# set_tfs_cursor true '_' tfs1;;
# set_tfs_cursor true '_' tfs2;;
# set_tfs_echo false '*' tfs2;;
# let p4 = create_panel true 140 80 ["Background", Copt gris2];;
# set_layout (grid_layout (1,2) p4) p4;;
# add_component p4 tf1 ["Row", Iopt 1];;
# add_component p4 tf2 ["Row", Iopt 0];;




Figure 13.5 : Affichage de champs de saisie


Enrichissement des composants

partir des composants de base dcrits prcdemment, il est possible d'en construire de nouveaux comme par exemple des composants dont les bords sont mis en relief comme pour la calculatrice graphique de la page ??. La solution choisie pour ce rendu consiste crer un panel plus grand que le composant, de le remplir d'une certaine manire et d'ajouter le composant dsir en le plaant au centre. On dfinit tout d'abord le type de l'tat d'un bord.

# type border_state =
{mutable relief : string; mutable line : bool;
mutable bg2 : Graphics.color; mutable size : int};;


La fonction de cration prend une liste d'options et construit un tat.

# let create_border_state lopt =
{relief = theString lopt "Relief" "Flat";
line = theBool lopt "Outlined" false;
bg2 = theColor lopt "Background2" Graphics.black;
size = theInt lopt "Border_size" 2};;
val create_border_state : (string * opt_val) list -> border_state = <fun>


On reprend le trac de bord utilis pour les botes de la figure 5.6 (page ??) en dfinissant les options "Top", "Bot" et "Flat".

# let display_border bs c1 c () =
let x1 = c.x and y1 = c.y in
let x2 = x1+c.w-1 and y2 = y1+c.h-1 in
let ix1 = c1.x and iy1 = c1.y in
let ix2 = ix1+c1.w-1 and iy2 = iy1+c1.h-1 in
let border1 g = Graphics.set_color g;
Graphics.fill_poly [| (x1,y1);(ix1,iy1);(ix2,iy1);(x2,y1) |] ;
Graphics.fill_poly [| (x2,y1);(ix2,iy1);(ix2,iy2);(x2,y2) |]
in
let border2 g = Graphics.set_color g;
Graphics.fill_poly [| (x1,y2);(ix1,iy2);(ix2,iy2);(x2,y2) |] ;
Graphics.fill_poly [| (x1,y1);(ix1,iy1);(ix1,iy2);(x1,y2) |]
in
display_rect c ();
if bs.line then (Graphics.set_color (get_gc_fcol (get_gc c));
draw_rect x1 y1 c.w c.h);
let b1_col = get_gc_bcol ( get_gc c)
and b2_col = bs.bg2 in
match bs.relief with
"Top" -> (border1 b1_col; border2 b2_col)
| "Bot" -> (border1 b2_col; border2 b1_col)
| "Flat" -> (border1 b1_col; border2 b1_col)
| s -> failwith ("display_border : unknown relief : "^s)
;;
val display_border : border_state -> component -> component -> unit -> unit =
<fun>


La fonction de cration d'un bord prend un composant et une liste d'options et construit un panel contenant ce composant.

# let create_border c lopt =
let bs = create_border_state lopt in
let p = create_panel true (c.w + 2 * bs.size)
(c.h + 2 * bs.size) lopt in
set_layout (center_layout p) p;
p.display <- display_border bs c p;
add_component p c []; p;;
val create_border : component -> (string * opt_val) list -> component = <fun>


On teste alors la construction de composants avec bord sur le label et le champ de saisie tf1 dfinis dans les autres tests. Le rsultat est reprsent la figure 13.6.

# remove_component p1 l1;;
# remove_component p4 tf1;;
# let b1 = create_border l1 [];;
# let b2 = create_border tf1 ["Relief", Sopt "Top";
"Background", Copt Graphics.red;
"Border_size", Iopt 4];;
# let p5 = create_panel true 140 80 ["Background", Copt gris2];;
# set_layout (grid_layout (1,2) p5) p5;;
# add_component p5 b1 ["Row", Iopt 1];;
# add_component p5 b2 ["Row", Iopt 0];;




Figure 13.6 : Affichage de composants enrichis


Cration de la bibliothque Upi

Les lments essentiels de notre bibliothque sont maintenant crits. Toutes les dclarations2 de type et de valeur de cette section peuvent tre regroupes dans un mme fichier. Cette bibliothque ne contient alors qu'un seul module. Si le fichier est appel upi.ml on obtient un module de nom Upi. Le lien entre nom de fichier et nom de module est dcrit au chapitre 14.

La compilation de ce fichier produit le fichier d'interface compile upi.cmi et le fichier de byte-code upi.cmo ou de code natif upi.cmx selon le compilateur utilis. Dans le cas du compilateur de byte-code on crit la ligne suivante :
ocamlc -c upi.ml
Pour tre utilise au niveau du toplevel, il est ncessaire de charger le byte-code de notre nouvelle bibliothque par la directive #load "upi.cmo";; en ayant pris soin d'avoir pralablement charg la bibliothque Graphics. Le module Upi
# open Upi;;
# create_component;;
- : int -> int -> Upi.component = <fun>
Le type du rsultat de cette fonction est Upi.component, le chapitre 14 reviendra sur ce point.

Exemple : un convertisseur Francs/Euros

On se propose de construire un convertisseur Francs/Euros en utilisant cette nouvelle bibliothque. La conversion des monnaies est du niveau de difficult de la rgle de trois. La construction de l'interface illustrera la ncessaire communication entre composants. Pour la priode d'adaptation aux deux monnaies, on dsire pouvoir faire les conversions dans les deux sens. Voici les composants choisis :
  • une liste 2 choix pour le sens de la conversion ;
  • deux champs de saisie pour l'entre des valeurs et pour l'affichage de la conversion ;
  • un bouton simple pour la demande du calcul ;
  • deux labels pour indiquer la nature de chaque champ de saisie.
Ces diffrents composants sont affichs la figure 13.7.

La communication entre les composants est ralise le partage d'un tat. On dfinit pour cela le type etat_conv qui contient les champs pour les francs (a), les euros (b), le sens de la conversion (sens) et les coefficients de conversion (fa et fb).

# type etat_conv =
{ mutable a:float; mutable b:float; mutable sens : bool;
fa : float; fb : float } ;;


On dfinit l'tat initial :

# let e = 6.55957074
let fe = { a =0.0; b=0.0; sens = true; fa = e; fb = 1./. e};;


La fonction de conversion retourne un rsultat flottant selon le sens de la conversion.

# let calcule fe =
if fe.sens then fe.b <- fe.a /. fe.fa else fe.a <- fe.b /. fe.fb;;
val calcule : etat_conv -> unit = <fun>


Le clic souris sur la liste deux choix modifie le sens de la conversion. Les textes des choix sont "->" et "<-".

# let action_sens fe cs = match get_cs_text cs with
"->" -> fe.sens <- true
| "<-" -> fe.sens <- false
| _ -> failwith "action_sens";;
val action_sens : etat_conv -> choice_state -> unit = <fun>


L'action associe au bouton simple entrane le calcul et l'affichage du rsultat dans un des deux champs de saisie. Pour cela les deux champs de saisie sont aussi passs en paramtre.

# let action_go fe tf_fr tf_eu tfs_fr tfs_eu x =
if fe.sens then
let r = float_of_string (get_tfs_text tfs_fr) in
fe.a <- r; calcule fe;
let sr = Printf.sprintf "%.2f" fe.b in
set_tfs_text tf_eu tfs_eu sr
else
let r = float_of_string (get_tfs_text tfs_eu) in
fe.b <- r; calcule fe;
let sr = Printf.sprintf "%.2f" fe.a in
set_tfs_text tf_fr tfs_fr sr;;
val action_go :
etat_conv ->
component -> component -> textfield_state -> textfield_state -> 'a -> unit =
<fun>


Il reste construire l'interface. La fonction suivante prend une largeur, une hauteur et un tat d'un convertisseur et retourne le conteneur principal avec les trois composants actifs.

# let create_conv w h fe =
let gris1 = (Graphics.rgb 120 120 120) in
let m = open_main_window w h
and p = create_panel true (w-4) (h-4) []
and l1 = create_label "Francs" ["Font", courier_bold_24;
"Background", Copt gris1]
and l2 = create_label "Euros" ["Font", courier_bold_24;
"Background", Copt gris1]
and c,cs = create_choice ["->"; "<-"] ["Font", courier_bold_18]
and tf1,tfs1 = create_text_field "0" 10 false ["Font", courier_bold_18]
and tf2,tfs2 = create_text_field "0" 10 false ["Font", courier_bold_18]
and b,bs = create_button " Go " ["Font", courier_bold_24]
in
let gc = get_gc m in
set_gc_bcol gc gris1;
set_layout (grid_layout (3,2) m ) m;
let tb1 = create_border tf1 []
and tb2 = create_border tf2 []
and bc = create_border c []
and bb =
create_border b
["Border_size", Iopt 4; "Relief", Sopt "Bot";
"Background", Copt gris2; "Background2", Copt Graphics.black]
in
set_cs_action cs (action_sens fe);
set_bs_action bs (action_go fe tf1 tf2 tfs1 tfs2);
add_component m l1 ["Col",Iopt 0;"Row",Iopt 1];
add_component m l2 ["Col",Iopt 2;"Row",Iopt 1];
add_component m bc ["Col",Iopt 1;"Row",Iopt 1];
add_component m tb1 ["Col",Iopt 0;"Row",Iopt 0];
add_component m tb2 ["Col",Iopt 2;"Row",Iopt 0];
add_component m bb ["Col",Iopt 1;"Row",Iopt 0];
m,bs,tf1,tf2;;
val create_conv :
int -> int -> etat_conv -> component * button_state * component * component =
<fun>


La boucle de gestion d'vnements est lance sur le conteneur m construit ci-dessous. Son affichage est reprsent la figure 13.7.

# let (m,c,t1,t2) = create_conv 420 150 fe ;;
# display m ;;




Figure 13.7 : Affichage du convertisseur


Un clic sur la liste deux choix, modifie le texte affich et change le sens de la conversion car toutes les fermetures de traitement d'vnements partagent le mme tat.

Pour en faire plus

Les fermetures permettent d'enregistrer des traitements auprs des composants graphiques. Nanmoins il est impossible de << rouvrir >> ces fermetures pour tendre un traitement. Il est ncessaire de redfinir compltement un nouveau traitement. Nous discuterons des possibilits d'extension des traitements au chapitre 16 o nous comparons les modles fonctionnel et objet.

Dans notre application, plusieurs enregistrement possdent des champs de mme nom (par exemple txt). La dernire dclaration masque les prcdentes. Il devient ds lors difficile d'utiliser directement le nom des champs. C'est pourquoi, pour chaque type dcrit, nous avons dclar les fonctions de cration et de manipulation des valeurs de ce type. Une autre possibilit aurait t de dcouper notre bibliothque en plusieurs modules. ce moment l les champs d'enregistrements auraient pu tre discrimins par le nom du module. Nanmoins, grce aux fonctions de cration, on peut utiliser pleinement la bibliothque. Le chapitre 14 revient sur le masquage de types en introduisant les types abstraits de donnes. Ce masquage garantit par ailleurs une meilleure sret d'excution. Il vite une modification directe de certains champs sensibles, comme les relations de filiation entre composants ce qui empche de construire un graphe circulaire.

Il y a plusieurs points possibles d'amlioration de cette bibliothque.

Un intrt de la dfinition des composants est de pouvoir en crire bien d'autres. En particulier il est relativement facile de crer des composants de forme quelconque en utilisant des fonctions particulire pour l'appartenance et l'affichage. On pourra ainsi faire des boutons elliptiques ou en forme de gouttes d'eau.

Les quelques algorithmes de placement prsents ne permettent pas toujours un assemblage ais. On peut donc ajouter des grilles dont les cases sont de hauteur ou largeur variables. De mme il peut tre ncessaire d'effectuer un placement de composants les uns cot des autres tant qu'il y a la place. Enfin la possibilit de changer la taille d'un conteneur, en le rpercutant sur ses fils, doit tre prvue.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora164.html0000644000000000000000000000627407421273602014504 0ustar Introduction Prcdent Index Suivant

Introduction

Ce chapitre aborde deux grands traits de l'interfaage d'un langage de programmation avec le systme d'exploitation : la communication et les processus. Le module Sys, prsent au chapitre 8, a dj montr comment faire passer des valeurs un programme et comment lancer un programme partir d'un autre. Le but de ce chapitre est de prciser les notions de processus et de communication entre processus.

Le terme << processus >> est utilis pour un programme en cours d'excution. Les processus sont les composants premiers des applications parallles. Nous introduisons les processus sous la prsentation classique que l'on donne pour le systme Unix. Dans ce cadre, un processus est lanc par un autre processus, crant ainsi un lien de filiation. Il existe un processus premier, anctre de tous les autres. Cette relation de paternit autorise des attentes sur la fin de l'excution d'un processus, ainsi qu'une communication privilgie entre ascendant et descendant. Le modle de paralllisme sous-jacent est celui mmoire rpartie.

Le terme << communication >> recouvre trois ralits :
  • les entres-sorties au moyen de descripteurs de fichier. La notion de fichier sous Unix a une porte beaucoup plus large que la simple lecture ou criture de donnes sur un support de masse. On le verra en particulier au chapitre 20 o des programmes tournant sur diverses machines communiquent via de tels descripteurs ;

  • la cration et l'utilisation de tubes de communication entre processus qui permettent un change de donnes selon le principe des files d'attente;
  • la gnration et le traitement de signaux qui autorisent une interaction simple entre processus.
Les fonctions prsentes dans ce chapitre viennent, pour l'essentiel, du module Unix accompagnant la distribution d'Objective CAML. La terminologie et les notions utilises viennent du monde Unix, mais une grande partie des fonctions de ce module sont utilisables sous Windows. Nous prciserons, quand il y aura lieu, le champ d'application des fonctions prsentes.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora036.html0000644000000000000000000000346307421273601014476 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

Ce chapitre effectue une comparaison des modles fonctionnel et impratif du langage Objective CAML tant au niveau du contrle d'excution que de la reprsentation mmoire des valeurs. Le mlange de ces deux styles permet la construction de nouvelles structures de donnes. La premire section tudie cette comparaison par l'exemple. La deuxime section discute les lments de choix entre composition de fonctions et squence d'une part, partage ou copie de valeur d'autre part. La troisime section dgage l'intrt de mlanger ces deux styles pour la cration de donnes fonctionnelles modifiables permettant ainsi la construction de donnes non compltement values. La quatrime section dcrit les streams, flots de donnes potentiellement infinis, et leur intgration, via le filtrage, dans le langage.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora021.gif0000644000000000000000000001077207073350152014272 0ustar GIF89aUUU999!,H0I8ͻ`(di^*ULOxpH ?ɑtؚ W2\s\bٮK.M^dnURoGV|q3}sfuNwir~ICvQlALg8(wWZ%Qtejtø}ͦ>LЧ0db岐أ_4ފ]kn=X'^B0V(QѬA *ZP!J } "HbF j[)3n/CHJS]N O\PTP:HS6'fBUUZoK~b~dnݽ<R O0 $vciʿj/Bae|;MzU5[DK)p-\gR-Nud# p×Ѫ =n>z,r exbLGYzZGTWl=x6dUݧGI*WjW^ڈ%~b\lfPG#cUMH::3 bK8iH2q$:>\Zd 1`$vaKlj #C)eiԙh҉bʔhȹSyjO z(z:碅Hn9f2D)]ħ 褊93b2*륭{Bj駝J묬*Z kBk)x* .ۭ:u+.ؒ躢{n"{mӆ/-`d vWg֋/roR<C .jpVnJQ&N-ϯ [{sB RdIa!Fh3m lVov[p,)lـg'Mx1k `xqN@2t'=\w],DŽM6/}ب /nY.@?m>8nvAg';tT|dƭݍ(;m ͧ߷̩wO|ڿsxg3GRCPSo@f`x#<;Ptt[N^ơҍu;m/| >0AAN+>!C g  ¶P*n - 9 BX'1=XъӃ-h+:j/5\ ȽDЀD=2^HlG9.]8. i DÎ $ x01!!IfP 'Iʝ獂S1W,Nł#R/Ae aY^4/1eA/hr9ǔnԘ]@y-*̅wM\34DfљyTcwm~+g(7v^<=/fr|w# $]]p~vCx5x'V2[s}+95{z@Y9Kzq\:?at\&ә]teƵ^rw]Қ^vC]~Ks}Ʒɥ^ݢu_7ۉaDrp+ǩxRwtxoT|+muԍ .ݤYxӻRD6l?L&p*o\g0):+Ư34Di' ?{"31=S_Ǎ{i>/lQiu:٭%%YYse]LGy)lHwpf{}TGwE&lD&JeāTgE&Ԁ^(i*,xrkDjh!98|ggE(U>zk+vM2(.DŽ lOvH}FSXZ~uRgxW73WGz0q b Յs'hr(U g>gxQbYp.@2NM= ɗs eChb(QAe423g؇hy}V>565!$S545!#gw"~-Viˆ_9|(PX"tw,S7E5a19X8Vx픎N cQI8񏷓Zn'"N~~p8sJ#2z^oy%q#$??A>C2;e XiIiRNHH]Ǔ~LGvG4bԔDxg4 ǔ'`4@Ą[IWH3GyPR,4^'lXwf+GeԊ+e9aX$FV-W w%UY1iٗ!q0yA$y$),0hi(H?)4sv EȘ(gcrS64C902 ht~ɋMAW{궢h0ZU6j;&Y٣-㩚7? )IkF٤9NJ}S c@ o4:RZFJyX (yfYQ g%*I+C k3+4˴ޢA !{wjQȵ1^۲?D W[NFP]l; c[˱(iK׷LZq~zlz붌++sksZ<ظ* m[,{kZ0khQ[U*xۻcY۹[?ȻƛgKۼSkV)ZЛr:[{蛾껾۾ BI'뽤{׻ۿUKH[; #3e̫?kh⪸ )Nٽj;Sq2o$sKf#lXk+6|&#ç<[85Z3$ٌz9;i(*#7"#4ArTèY#Nl;xTH) !y) Vau3 X ۸7qlIlР/lCɄ3앝!092ʕ;<]ɛɟl A(ȲA!%l1T<< 4t=z z˨ *̍=L̠ۘѳ; "sJ !ƅ<$c#-’N^۞^ ?j=3wkO>#P2  b,5^OEdE9ѣ5T*V,\9ɗĦݼ)_. 3NnJdhFN]T Ti4FEU묦Ua z+b]v*٣pz}JcޱygŻW-@V&]"ފ\ߊhݠÈ j=meY.=Ŋiw;IC14fШՎY:iho׵Z$9瞫iV.8\g5{~=Ff:qEƌ}*?ok=+nϲ_,} xNdyt]l17n.p%΂f߀Z{!!9xzW]NVl:v]\_( c` )@BFdF:fT B?^5X VVBRH%\IdfV fnXnYfZMS~矂jN3&h09 i,*ixiCJ*駢@ɨj(ɪj꫖*$ȭ*뮆 ǰa6v$6 dzв!{jm(f;ƶx+.;Hn ;ocaml-book-1.0/fr/html/book-ora036.gif0000644000000000000000000000504707073350152014277 0ustar GIF89aUUU!,ڋ޼扖ʶ Lhu䄪L*ɌJ +Zܮ<U^c'E W"wGh'`'8 x Vę`yZىjzI:Ũi`:K,zS9_LuI1],]wDa}=?_o¾~IqKǑ} 8'ᇖHRa!!Q;0J_k).JZp)"Gͧf8$b[J.$FH%9A.zPmVFeJmQsure~iٓqh@(dڹ86*rp&tAY*:iG .gM+ y&ٽ֩#xii%9:ky)lNKm^^d%d&bj̚>'+Xu .Λo \i[ oB$в k{Bp§npS2,.qI*kL.l@{&қ M!l&8E!POa5'B,6i;U Yl"gu dɲܜAre=4gl( [g}[Ɔ zTz@ xB2I(M,ؘX;ϭ|>;ӏF=/SW$qm֤Y#ELUMkOo,;W\-iJᛘ*:S@ߞǽueE#=MiR`6̤q$ a`=.[@_DRJ R+EB}FDAtR;]]V#2Io"+_,D+ a3,# 3u`w:_+PAՐf;qS;H>6ҎadL(Bbs'Ik!$J~P;CT0-Ac-3n|sEozCLqmn–3 hJsZe5kff3pNH!'6y!tK̓:m~"Mę60̼>-B/WgE*cЁr3 PaYRD0!8ÎʨCHI'm:gI}edL[8&HKS{aD-T&RKiSiEt;Q%iUyծe[T#UUm;ǺT"Die* \j䕮k*چVǯ: [ۘS b|2l.O^d¬fqYp)*KZf6&Mm`Wֺ]lg2춸5n/[ުpKN T܋d.Yejww w-yϋwm{ w}w x.+x n Kx/ kx? x$.Obu,nsmg3!Aڮx61k<xA~{c` 2 F$/QvG@ILr*CbPpb157:y@)m[562'ג9~Le9o}^,kʂ~"5!LZ<-рs&sP{Ԅ9)&S}CNo v5ai[T k]n5s-l&[h҆v#6;6Ujsmk{^vD_ x;)|{ p6^%An,7CZ8sV8Pq0ߴF;N"9vjr5"ˉ򗣌2skh4y>Ws6S'f, SjKkN%a<6-Qm|nU $˼3dG ;wa 7ZH] 7tt"V&}Gmw~Y˭VmYE; 4U;kl?!;ocaml-book-1.0/fr/html/book-ora136.html0000644000000000000000000000371307421273602014476 0ustar Rsum Prcdent Index Suivant

Rsum

Nous avons fait le tour dans ce chapitre des possibilits qu'offre le langage de modules paramtrs d'Objective CAML.

Comme dans tout mcanisme de modules, on y retrouve la dualit interface-implantation que l'on a renomme dualit signature-structure. Elle permet le masquage de dfinitions de type, d'exception ou de valeur.

En masquant la reprsentation des types, on obtient la dfinition de types abstraits dont les valeurs sont manipulables uniquement avec les moyens fournis dans la signature associe au module. On a vu comment utiliser ce mcanisme pour faciliter le partage de valeurs caches entre fermetures ainsi que pour offrir plusieurs vues d'une mme structure. Dans ce dernier cas, il faut parfois avoir recours des indications explicites de partage de type.

Le mcanisme plus riche des modules paramtrs (ou foncteurs) nous a permis d'illustrer comment rutiliser le code par un mcanisme aussi simple que l'application d'une fonction.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora080.html0000644000000000000000000000306107421273602014470 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a donn une vue d'ensemble des diffrentes bibliothques d'Objective CAML prsentes comme des ensembles de modules simples (ou units de compilation). Les modules sur le formatage de sortie (Printf), sur la persistance de valeurs (Marshal), d'interface avec le systme (Sys) et de rcupration d'exceptions (module Printexc) ont t dtaills. Les modules concernant les analyses lexicale et syntaxique, la gestion mmoire, la programmation systme et rseau et les processus lgers seront prsents dans les chapitres suivants.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora035.html0000644000000000000000000001203107421273601014464 0ustar Introduction Prcdent Index Suivant

Introduction

Les langages de programmation fonctionnelle et de programmation imprative s'opposent principalement sur le contrle de l'excution des programmes et la gestion mmoire des donnes.
  • un programme fonctionnel calcule une expression. Ce calcul donne lieu la production d'une valeur. L'ordre dans lequel les oprations ncessaires ce calcul sont effectues et la reprsentation physique des donnes manipules sont indiffrents puisque le rsultat est identique dans tous les cas. Dans un tel cadre, la rcupration de la mmoire est gre implicitement par le langage : on fait appel un rcuprateur automatique de mmoire ou GC1.

  • un programme impratif est une suite d'instructions modifiant un tat mmoire. Chaque tape de l'excution est encadre par des structures de contrle rigides indiquant la prochaine instruction excuter. Les programmes impratifs manipulent plus souvent des pointeurs ou des rfrences sur des valeurs que les valeurs elles-mmes. Il faut alors allouer et rcuprer explicitement l'espace mmoire ncessaire au stockage des valeurs. Cela entrane parfois des erreurs d'accs mmoire. Rien n'empche nanmoins d'utiliser un GC.
Les langages impratifs fournissent un plus grand contrle de l'excution et de la reprsentation mmoire des donnes. tant plus proche de la machine relle, le code peut tre plus efficace mais perdre en sret d'excution. La programmation fonctionnelle, parce qu'elle offre un plus haut niveau d'abstraction, procure un meilleur niveau de sret d'excution : le typage (dynamique ou statique) peut y tre renforc, vitant ainsi des oprations sur des valeurs incohrentes ; la rcupration automatique de mmoire, quitte perdre en efficacit, assure l'existence effective des valeurs manipules.

Historiquement, ces deux paradigmes de programmation se sont vus consigns dans des univers distincts : les applications symboliques pour le premier, et numriques pour le second. Mais certaines choses ont volu, en particulier les techniques de compilation des langages fonctionnels et l'efficacit des GC. D'autre part, la sret d'excution est devenue un critre important, voire prpondrant, pour la qualit d'une application. En atteste << l'argument de vente >> du langage Java selon lequel l'efficacit ne doit pas prendre le pas sur la sret, tout en restant raisonnablement bonne. Et c'est une ide qui progresse dans le monde des producteurs de logiciel.

Objective CAML se situe dans cette ligne. Il marie les deux paradigmes de programmation permettant ainsi d'accrotre son domaine d'application en facilitant l'criture d'algorithmes dans un style ou dans l'autre. Il conserve nanmoins de bonnes proprits de sret d'excution grce son typage statique, son GC et son mcanisme d'exception. Les exceptions sont une premire structure de contrle d'excution explicite; elles permettent d'effectuer une rupture/reprise de calcul. Ce trait est la frontire de ces deux modles car bien qu'il ne change pas le rsultat d'un calcul, il peut modifier l'ordre d'excution. Le fait d'introduire des donnes physiquement modifiables peut changer le comportement de la partie purement fonctionnelle du langage. En effet l'ordre d'valuation des arguments d'une fonction devient dterminant si cette valuation effectue des effets de bord. Pour cette raison, de tels langages sont dits << langages fonctionnels impurs >>. On perd en effet en niveau d'abstraction car le programmeur doit tenir compte du modle mmoire ainsi que du droulement du programme. Cela n'est pas toujours ngatif, en particulier pour l'efficacit du code crit. Par contre, les traits impratifs modifient le systme de types du langage : certains programmes fonctionnels, correctement typs d'un point de vue thorique, ne le sont pas en pratique du fait de l'introduction des rfrences. Nanmoins, de tels programmes pourront se rcrire facilement.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora127.html0000644000000000000000000000160107421273603014471 0ustar Notes
1
Le nom de la police de caractre par dfaut peut varier selon les systmes d'exploitation.
2
On enlve uniquement les dclarations utilises pour les tests et les expressions.
ocaml-book-1.0/fr/html/book-ora005.gif0000644000000000000000000000171107073350152014265 0ustar GIF89aUUU!,ڋ޼7H扦ʶ xL+q}xS )"m˦Q4@Sg5\[d ˾CDL@5v1VhXȶ!q8)&iʳi sH&Sj xs{Hժ&[jlK*;M l<]:'1N>Ga^^~>)Yxnύ&Zg~D0C遘)@7Z2"G%IQ+]J2Kj| S'SD=Х|5c QơzȪ9Sm:rXdk[6FYպ=)۹@}Iݸykׯ`">bČ,>8r%3n% exoܙƠ2ihԗWeMڰFؠezLܹw?nགྷ&~Xm}d|Z͍?>XYѷ^{]ݯ{$o]Gm~g|_}x矞X~X \!71YOBQh %CX a:Œ8YbʐXb8"-(_T㌠Hč=ʨc>>~0db<IJ8d}eV^eZne^~)XRbIfPǤcմ\١73*FgkX!קLr\Z6(h'~kvmYi gW饟꤇wNu.My@r!5AP+6IYdYh KPYp=:!͵QM: Pl-K Ղ+Zm {kt։0 믁@pF|"` p|%*z1'/K*'+{3k tJ];ocaml-book-1.0/fr/html/book-ora003.gif0000644000000000000000000000231607073350152014265 0ustar GIF89aUUU!,ڋ!|-刦* Ǎ+򍳔~mDXL:: ͊6դ X"s2=`}W|7ww$&h9(Hyiv׹)` ÑZy ƺ*f*; U{{5K7Sq ؼ, MLt4}1,I%E|;y&~XHy5/S5 Mr*C&b(Cgx␱CS ǃu `JƔaQFPYmI#USL࣡ЕY2K#CMr[PKɠ9/vڶBY[c0a O~) Ću &]ȡ$6bf3+LmIn8pt鼬QWWWOQkd61mM ЫͦZ8;dÆs۴rkYNwcpU"|{FTU R8Nc}|yWBtTgphnHHgP~!-:ؠ.NXS HfOu!cYNDnj&Y#eZw1c~K}M|~G a-z &N*٦ExgJV:& jЌxqIff4xf7'/ g4%\Eq[n$t.j**izeɇaS%JkL.JC*PflEz! VQ7k SJUl.GJdqݺnơ^s;o.0TwrXG|q]hž+"阺z,b~1%UNUև5!"[%Ղ&YᔷA: v(%R9Yp]FNtd#uzlڏP*4|+u G]8 'Om}K;$8'4qwi >C)<+~Wz_Sf{#锗h4쳿`Xl%uS2n*3˃ao k=So$+?9en랻QQE7F;ocaml-book-1.0/fr/html/book-ora095.html0000644000000000000000000000502707421273602014502 0ustar Introduction Prcdent Index Suivant

Introduction

Les outils d'analyse de programmes apportent des informations complmentaires celles donnes par le compilateur ou l'dition de liens. Certains de ces outils pratiquent une analyse statique. C'est--dire qu'ils examinent le code (sous sa forme d'un texte ou d'un arbre de syntaxe) et en dterminent certaines proprits comme les dpendances entre modules ou les chappements d'exceptions. D'autres outils pratiquent une analyse dynamique, c'est--dire que leur champ d'examen est celui de l'excution. Ils sont utiles pour connatre par exemple le nombre d'appels aux diffrentes fonctions, avoir la trace du passage de leurs arguments ou savoir le temps pass dans certaines parties du programme. Ils peuvent aussi tre interactifs comme le sont les outils de mise au point d'un programme. Dans ce cas l'excution du programme est modifie pour tenir compte de l'interaction utilisateur. Celui-ci peut poser des points d'arrt dans le but d'explorer des valeurs ou de relancer une excution sur de nouveaux arguments.

La distribution d'Objective CAML inclut de tels outils. Certains d'entre eux ont des caractristiques peu communes, souvent lies au typage statique. En effet celui-ci garantit l'absence d'erreur de typage l'excution et est utilis par le compilateur pour produire un code efficace et des donnes petites. L'information de type des valeurs Objective CAML est en partie perdue dans les valeurs construites. Cela entrane certaines difficults comme par exemple l'impossibilit d'afficher les arguments de fonctions polymorphes.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora219.html0000644000000000000000000000365607421273603014507 0ustar Contacts Prcdent Index

Contacts

Liste de diffusion par messagerie lectronique

Si vous souhaitez recevoir priodiquement les annonces de nouveaux produits O'Reilly en franais, il vous suffit de souscrire un abonnement la liste d'annonces
parutions-oreilly
Merci d'expdier en ce cas un message lectronique contenant (dans le corps du message) :

subscribe parutions-oreilly votre_adresse_email

Exemple :
subscribe parutions-oreilly jean.dupond@ici.fr
Cette liste ne vhicule que des annonces et non des discussions, vous ne pourrez par consquent pas y poster. Son volume ne dpasse pas quatre messages par mois.

En cas de problme technique crire
parutions-oreilly-owner@ora.de

Site Web

Notre site Web http://www.editions-oreilly.fr/ diffuse diverses informations :
  • le catalogue des produits proposs par les ditions O'Reilly,
  • les errata de nos ouvrages,
  • des archives abritant les exemples,
  • la liste des revendeurs.

Prcdent Index ocaml-book-1.0/fr/html/book-ora081.html0000644000000000000000000000633607421273602014501 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

Le survol des bibliothques de la distribution du langage a montr la richesse de l'environnement de base. Pour le module Printf rien ne vaut la lecture d'un ouvrage sur le langage C, comme [HS90]. Dans [FW00] une solution est propose pour le typage des entres-sorties de valeurs (module Marshal). L'algorithme MD5 du module Digest est dcrit sur la page de son concepteur :

Lien


http://theory.lcs.mit.edu/~rivest/homepage.html
De mme on trouve de nombreux articles sur l'arithmtique exacte utilise par la bibliothque num sur la page de Valrie Mnissier-Morain :

Lien


http://www-calfor.lip6.fr/~vmm/


Il existe aussi d'autres bibliothques que celles de la distribution, dveloppes par l'ensemble de la communaut des programmeurs Objective CAML. La majorit d'entre elles sont listes sur le site de la << bosse du chameau >> :

Lien


http://caml.inria.fr/hump.html
Certaines d'entre elles seront prsentes et discutes dans le chapitre sur le dveloppement d'applications (22).

Pour connatre prcisment le contenu des diffrents modules, il ne faut pas hsiter lire la partie de description des bibliothques du manuel de rfrence [LRVD99] ou de consulter sa version en ligne au format HTML (1). Pour entrer dans le dtail des implantations de ces bibliothques, rien ne vaut la lecture des sources de ces bibliothques accessibles avec la distribution du langage (1).
Le chapitre 14 prsente le langage de modules d'Objective CAML. Celui-ci permet de construire des modules simples vus comme des units indpendantes de compilation qui correspondent aux modules prsents dans ce chapitre.




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora162.html0000644000000000000000000017654107421273602014507 0ustar Des robots en goguette Prcdent Index Suivant

Des robots en goguette

L'exemple de ce paragraphe illustre l'utilisation d'objets ainsi que la bibliothque graphique. Nous verrons comment Objective CAML  reprend les notions d'hritage simple, d'hritage multiple, de redfinition de mthode et de liaison dynamique. Nous verrons galement comment les classes paramtres peuvent tre mises profit.

L'application comprend deux catgories principales d'objets : un monde et des robots. Le monde est un ensemble de cases sur lesquelles voluent des robots. Nous aurons plusieures classes de robots. Chacune d'elles possdera sa propre stratgie de dplacement dans le monde. Le principe d'interaction du monde et des robots est ici extrmement simple. Le monde est entirement matre du jeu : il demande tour tour chacun des robots qu'il connat quelle est sa prochaine position. Chaque robot dtermine sa prochaine position l'aveugle : il ne connat ni la gomtrie du monde, ni les autres robots prsents. Si la position demande par un robot est lgale et libre alors le monde l'y dplace.

Le monde matrialisera l'volution des robots par une interface. La complexit (toute relative) de la conception et du dveloppement de cet exemple est dans la toujours ncessaire sparation entre un traitement (ici : l'volution des robots) et son interface (ici : la trace de cette volution).

Description gnrale.
L'application est dveloppe en deux temps.
  1. un ensemble de dfinitions donnant des classes de calcul pur pour le monde et pour divers robots envisags.
  2. un ensemble de dfinitions, utilisant les prcdentes et ajoutant ce qui est ncessaire la mise en place d'une interface. Nous donnerons deux exemples d'interfaces : une rudimentaire sous forme de texte ; une, plus labore, utilisant la bibliothque graphique.

Robots << thrs >>

Dans un premier temps, nous nous intressons aux robots hors de toute considration sur l'environnement qui les entourent, c'est dire de l'interface qui les affiche.

# class virtual robot (i0:int) (j0:int) = 
object
val mutable i = i0
val mutable j = j0
method get_pos = (i,j)
method set_pos (i', j') = i <- i'; j <- j'
method virtual next_pos : unit -> (int * int)
end ;;


En toute gnralit, un robot est une entit connaissant, ou croyant connatre, sa position (i et j), capable de la donner qui la lui demande (get_pos), susceptible de modifier cette connaissance si on la lui prcise (set_pos) et sachant dcider d'un ventuel mouvement vers une nouvelle position (next_pos).




Figure 17.9 : hirarchie de classes des robots purs


Pour amliorer la lisibilit du programme, nous dfinissons les mouvements relatifs une direction absolue :

# type dir = North | East | South | West | Nothing ;;
# let walk (x,y) = function
North -> (x,y+1) | South -> (x,y-1)
| West -> (x-1,y) | East -> (x+1,y)
| Nothing -> (x,y) ;;
val walk : int * int -> dir -> int * int = <fun>
# let turn_right = function
North -> East | East -> South | South -> West | West -> North | x -> x ;;
val turn_right : dir -> dir = <fun>


Du schma induit par la classe virtuelle des robots, nous dfinissons quatre espces de robots distinctes (voir la figure 17.9) en prcisant leur manire de se dplacer :
  • les robots fixes qui ne bougent jamais :

    # class fix_robot i0 j0 =
    object
    inherit robot i0 j0
    method next_pos() = (i,j)
    end ;;
  • les robots fous qui se dplacent au hasard :

    # class crazy_robot i0 j0 =
    object
    inherit robot i0 j0
    method next_pos () = ( i+(Random.int 3)-1 , j+(Random.int 3)-1 )
    end ;;
  • les robots obstins qui conservent la mme direction tant qu'ils peuvent avancer,

    # class obstinate_robot i0 j0 =
    object(self)
    inherit robot i0 j0
    val mutable wanted_pos = (i0,j0)
    val mutable dir = West

    method private set_wanted_pos d = wanted_pos <- walk (i,j) d
    method private change_dir = dir <- turn_right dir
    method next_pos () = if (i,j) = wanted_pos
    then let np = walk (i,j) dir in ( wanted_pos <- np ; np )
    else ( self#change_dir ; wanted_pos <- (i,j) ; (i,j) )
    end ;;
  • les robots tlguids qui obissent un oprateur extrieur :

    # class virtual interactive_robot i0 j0 =
    object(self)
    inherit robot i0 j0
    method virtual private get_move : unit -> dir
    method next_pos () = walk (i,j) (self#get_move ())
    end ;;
Le cas du robot interactif est diffrent des autres car son comportement est li l'interface qui permettra de lui communiquer des ordres. En attendant, nous nous appuyons sur une mthode qui, virtuellement, communique cet ordre et en consquence la classe interactive_robot demeure abstraite.

Remarquons que non seulement les quatre classes de robots spcialiss hritent de la classe robot mais que de surcrot elles en ont le type. En effet, les seules mthodes que nous ayons ajoutes sont des mthodes prives et donc n'apparaissent pas dans le type des instances de ces classes (voir page ??). Ce point nous est indispensable si nous souhaitons considrer tous les robots comme des objets de mme type.

Monde pur

Un monde pur est un monde indpendant de l'interface. Il y est connu l'ensemble des positions qu'un robot est susceptible d'occuper. Cela prend la forme d'une grille de taille lh, d'une mthode is_legal assurant qu'un couple d'entiers est bien une position dans le monde, et d'une mthode is_free indiquant si un robot occupe ou non une position donne.

En outre, un monde dispose de la liste des robots prsents sur sa surface robots ainsi que d'une mthode add permettant de faire rentrer de nouveaux robots.

Pour finir, un monde est pourvu de la mthode run lui permettant de prendre vie.

# class virtual ['robot_type] world (l0:int) (h0:int) =
object(self)
val l = l0
val h = h0
val mutable robots = ( [] : 'robot_type list )
method add r = robots <- r::robots
method is_free p = List.for_all (fun r -> r#get_pos <> p) robots
method virtual is_legal : (int * int) -> bool

method private run_robot r =
let p = r#next_pos ()
in if (self#is_legal p) & (self#is_free p) then r#set_pos p

method run () =
while true do List.iter (function r -> self#run_robot r) robots done
end ;;
class virtual ['a] world :
int ->
int ->
object
constraint 'a =
< get_pos : int * int; next_pos : unit -> int * int;
set_pos : int * int -> unit; .. >
val h : int
val l : int
val mutable robots : 'a list
method add : 'a -> unit
method is_free : int * int -> bool
method virtual is_legal : int * int -> bool
method run : unit -> unit
method private run_robot : 'a -> unit
end


Le systme de type d'Objective CAML ne permet pas de laisser le type des robots non dtermin (voir page ??). Pour rsoudre ce problme, nous avions la possibilit de restreindre ce type celui de la classe robot. Mais dans ce cas, nous nous interdisions de pouvoir peupler un monde d'autres objets que ceux ayant exactement le mme type que robot. Donc, nous avons choisi de paramtrer la classe world par le type des robots qui le peuplent. Nous pourrons ensuite instancier ce paramtre de type par des robots textuels ou par des robots graphiques.

Robots textuels

Des objets texte
Pour obtenir des robots grables par une interface texte, nous dfinissons la classe des objets textuels (txt_object).

# class txt_object (s0:string) =
object
val name = s0
method get_name = name
end ;;


Une classe de spcification : les robots textuels abstraits
Par hritage double de robots et txt_object, nous obtenons la classe abstraite txt_robot des robots textuels.

# class virtual txt_robot i0 j0 =
object
inherit robot i0 j0
inherit txt_object "Anonymous"
end ;;
class virtual txt_robot :
int ->
int ->
object
val mutable i : int
val mutable j : int
val name : string
method get_name : string
method get_pos : int * int
method virtual next_pos : unit -> int * int
method set_pos : int * int -> unit
end


Cette classe nous sert pour dfinir un monde interface texte (voir page ??). Les habitants de ce monde ne seront ni des objets de txt_robot (puisque cette classe est abstraite) ni des hritiers de cette classe. La classe txt_robots est en quelque sorte une classe de spcification permettant au compilateur d'identifier les types des mthodes (calcul et interface) des habitants du monde interface texte. L'utilisation d'une telle classe de spcification vient de la sparation que nous voulons maintenir entre les calculs et l'interface.

Les robots concrets en mode texte
Ils s'obtiennent simplement par double hritage; la figure 17.10 donne leur hirarchie de classes.




Figure 17.10 : hirarchie de classes des robots en mode texte


# class fix_txt_robot i0 j0 =
object
inherit fix_robot i0 j0
inherit txt_object "Fix robot"
end ;;

# class crazy_txt_robot i0 j0 =
object
inherit crazy_robot i0 j0
inherit txt_object "Crazy robot"
end ;;

# class obstinate_txt_robot i0 j0 =
object
inherit obstinate_robot i0 j0
inherit txt_object "Obstinate robot"
end ;;


Les robots interactifs doivent pour devenir concrets dfinir leur mthode d'interaction avec l'utilisateur.

# class interactive_txt_robot i0 j0 =
object
inherit interactive_robot i0 j0
inherit txt_object "Interactive robot"
method private get_move () =
print_string "Which dir : (n)orth (e)ast (s)outh (w)est ? ";
match read_line() with
"n" -> North | "s" -> South
| "e" -> East | "w" -> West
| _ -> Nothing
end ;;


Monde textuel

Le monde interface texte se drive du monde pur par
  1. hritage de la classe gnrique world en instanciant son paramtre de type par la classe de spcification txt_robot,
  2. redfinition de la mthode run pour y inclure les diffrents affichages textuels.

# class virtual txt_world (l0:int) (h0:int) =
object(self)
inherit [txt_robot] world l0 h0 as super

method private display_robot_pos r =
let (i,j) = r#get_pos in Printf.printf "(%d,%d)" i j

method private run_robot r =
let p = r#next_pos ()
in if (self#is_legal p) & (self#is_free p)
then
begin
Printf.printf "%s is moving from " r#get_name ;
self#display_robot_pos r ;
print_string " to " ;
r#set_pos p;
self#display_robot_pos r ;
end
else
begin
Printf.printf "%s is staying at " r#get_name ;
self#display_robot_pos r
end ;
print_newline () ;
print_string"next - ";
ignore (read_line())

method run () =
let print_robot r =
Printf.printf "%s is at " r#get_name ;
self#display_robot_pos r ;
print_newline ()
in
print_string "Initial state :\n";
List.iter print_robot robots;
print_string "Running :\n";
super#run() (* 1 *)
end ;;


Nous attirons l'attention du lecteur sur l'appel la mthode run de la classe anctre (marqu (* 1 *) dans le code) dans la redfinition de cette mme mthode. Nous avons l une illustration des deux types de liaison des mthodes possibles : statique ou dynamique (voir page ??). L'appel super#run est statique; c'est l'intrt de nommer la superclasse que de pouvoir appeler ses mthodes alors qu'elles ont t redfinies. Par contre, dans cette mthode super#run se trouve un appel self#run_robot. C'est ici une liaison dynamique qui a lieu; c'est la mthode dfinie dans la classe txt_world qui est excute et non celle de world, sans quoi nous n'obtiendrions aucun affichage.

Le monde plan rectangulaire textuel
s'obtient en implantant la dernire mthode encore abstraite : is_legal.

# class closed_txt_world l0 h0 =
object(self)
inherit txt_world l0 h0
method is_legal (i,j) = (0<=i) & (i<l) & (0<=j) & (j<h)
end ;;





Figure 17.11 : hirarchie de classes du monde plan rectangulaire en mode texte


On peut procder un petit essai en tapant :

let w = new closed_txt_world 5 5
and r1 = new fix_txt_robot 3 3
and r2 = new crazy_txt_robot 2 2
and r3 = new obstinate_txt_robot 1 1
and r4 = new interactive_txt_robot 0 0
in w#add r1; w#add r2; w#add r3; w#add r4; w#run () ;;


Nous allons passer prsent la ralisation de l'interface graphique pour notre monde de robots. En fin de course, nous obtiendrons une application ayant l'apparence de la figure 17.12.




Figure 17.12 : Le monde graphique des robots


Robots graphiques

Nous obtenons des robots en mode graphique en suivant le mme schma que le mode texte :
  1. dfinition d'un objet graphique gnrique,
  2. dfinition d'une classe abstraite de robots graphiques par double hritage des robots et des objets graphiques (analogue de la classe de spcification du paragraphe 17),
  3. dfinition par double hritage des robots possdant un comportement particulier.

Objets graphiques gnriques

Un objet graphique simple est un objet possdant une mthode display qui prend en argument les coordonnes d'un pixel et s'affiche.

# class virtual graph_object =
object
method virtual display : int -> int -> unit
end ;;


De cette spcification, il est possible de tirer des objets graphiques extrmement complexes. Nous allons nous contenter ici d'une classe graph_item affichant le bitmap qui sert la construire.

# class graph_item x y im = 
object (self)
val size_box_x = x
val size_box_y = y
val bitmap = im
val mutable last = None

method private erase = match last with
Some (x,y,img) -> Graphics.draw_image img x y
| None -> ()

method private draw i j = Graphics.draw_image bitmap i j
method private keep i j =
last <- Some (i,j,Graphics.get_image i j size_box_x size_box_y) ;

method display i j = match last with
Some (x,y,img) -> if x<>i || y<>j
then ( self#erase ; self#keep i j ; self#draw i j )
| None -> ( self#keep i j ; self#draw i j )
end ;;


Un objet graph_item conserve la portion d'image sur laquelle il est affich pour la restaurer lors de l'affichage suivant. De plus, si l'image n'a pas boug elle n'est pas raffiche.

# let foo_bitmap =  [|[| Graphics.black |]|] ;;
# class square_item x col =
object
inherit graph_item x x (Graphics.make_image foo_bitmap)
method private draw i j = Graphics.set_color col ;
Graphics.fill_rect (i+1) (j+1) (x-2) (x-2)
end ;;

# class disk_item r col =
object
inherit graph_item (2*r) (2*r) (Graphics.make_image foo_bitmap)
method private draw i j = Graphics.set_color col ;
Graphics.fill_circle (i+r) (j+r) (r-2)
end ;;

# class file_bitmap_item name =
let ch = open_in name
in let x = Marshal.from_channel ch
in let y = Marshal.from_channel ch
in let im = Marshal.from_channel ch
in let () = close_in ch
in object
inherit graph_item x y (Graphics.make_image im)
end ;;


Nous avons spcialis les graph_item en carrs, disques et bitmaps lus depuis un fichier.

Le robot graphique abstrait
est la fois un robot et un objet graphique.

# class virtual graph_robot i0 j0 =
object
inherit robot i0 j0
inherit graph_object
end ;;


Les robots graphiques fixes, fous et obstins
sont des objets graphiques spcialiss.

# class fix_graph_robot i0 j0 =
object
inherit fix_robot i0 j0
inherit disk_item 7 Graphics.green
end ;;

# class crazy_graph_robot i0 j0 =
object
inherit crazy_robot i0 j0
inherit file_bitmap_item "crazy_bitmap"
end ;;

# class obstinate_graph_robot i0 j0 =
object
inherit obstinate_robot i0 j0
inherit square_item 15 Graphics.black
end ;;


Le robot graphique interactif
utilise les primitives key_pressed et read_key du module Graphics pour l'acquisition du dplacement. On reconnatra les touches 8, 6, 2 et 4 du pav numrique (touche NumLock active). De cette faon, l'utilisateur n'est pas oblig de donner une indication de dplacement chaque interrogation du monde.

# class interactive_graph_robot i0 j0 =
object
inherit interactive_robot i0 j0
inherit file_bitmap_item "interactive_bitmap"
method private get_move () =
if not (Graphics.key_pressed ()) then Nothing
else match Graphics.read_key() with
'8' -> North | '2' -> South | '4' -> West | '6' -> East | _ -> Nothing
end ;;


Monde graphique

On obtient un monde interface graphique par hritage du monde pur en instanciant le paramtre 'a_robot avec la classe abstraite des robots graphiques graph_robot. Comme pour le monde en mode texte, le monde graphique redfinit la mthode run_robot de traitement d'un robot et la mthode d'activation gnrale run.

# let delay x = let t = Sys.time () in while (Sys.time ()) -. t < x do () done ;;

# class virtual graph_world l0 h0 =
object(self)
inherit [graph_robot] world l0 h0 as super
initializer
let gl = (l+2)*15 and gh = (h+2)*15 and lw=7 and cw=7
in Graphics.open_graph (" "^(string_of_int gl)^"x"^(string_of_int gh)) ;
Graphics.set_color (Graphics.rgb 170 170 170) ;
Graphics.fill_rect 0 lw gl lw ;
Graphics.fill_rect (gl-2*lw) 0 lw gh ;
Graphics.fill_rect 0 (gh-2*cw) gl cw ;
Graphics.fill_rect lw 0 lw gh

method run_robot r = let p = r#next_pos ()
in delay 0.001 ;
if (self#is_legal p) & (self#is_free p)
then ( r#set_pos p ; self#display_robot r)

method display_robot r = let (i,j) = r#get_pos
in r#display (i*15+15) (j*15+15)

method run() = List.iter self#display_robot robots ;
super#run()
end ;;


Notez que la fentre graphique est cre l'initialisation d'un objet de cette classe.

Le monde plan rectangulaire et graphique
s'obtient de la mme manire que pour le monde plan rectangulaire et textuel.

# class closed_graph_world l0 h0 =
object(self)
inherit graph_world l0 h0
method is_legal (i,j) = (0<=i) & (i<l) & (0<=j) & (j<h)
end ;;
class closed_graph_world :
int ->
int ->
object
val h : int
val l : int
val mutable robots : graph_robot list
method add : graph_robot -> unit
method display_robot : graph_robot -> unit
method is_free : int * int -> bool
method is_legal : int * int -> bool
method run : unit -> unit
method run_robot : graph_robot -> unit
end


On peut alors tester l'application graphique en tapant

let w = new closed_graph_world 10 10 ;;
w#add (new fix_graph_robot 3 3) ;;
w#add (new crazy_graph_robot 2 2) ;;
w#add (new obstinate_graph_robot 1 1) ;;
w#add (new interactive_graph_robot 5 5) ;;
w#run () ;;


Pour en faire plus

L'implantation de la mthode run_robot des diffrents mondes sous-entend que les robots sont potentiellement capables de se rendre en tout point du monde du moment que celui-ci est libre et lgal. De plus, rien n'interdit un robot de modifier sa position sans en prvenir le monde. Une amlioration possible consiste faire grer l'ensemble des positions des robots par le monde; lors du dplacement d'un robot, le monde vrifie d'une part si la nouvelle position est lgale mais aussi si elle constitue un dplacement autoris. Dans ce cas, le robot devra tre capable de demander au monde sa propre position; ce qui entrane que la classe des robots devra tre dpendante de la classe du monde. On pourra dfinir une classe robot prenant comme paramtre de type une classe de monde.

Cette modification permet alors de dfinir des robots capables d'interroger le monde qui les entoure et donc de se comporter en fonction de celui-ci. Nous pourrons raliser des robots qui suivent ou qui fuient d'autres robots, qui tentent de les bloquer, etc. L'tape suivante est de permettre aux robots de communiquer entre eux pour s'changer des informations et constituer ainsi des quipes de robots.

Les chapitres de la partie suivante de l'ouvrage permettent de librer l'excution des robots les unes des autres : soit en ayant recours aux Threads (voir page ??) pour que chacun s'excute sur un processus distinct, soit en profitant des possibilits de l'informatique distribue (voir page ??) pour que les robots soient des clients s'excutant sur des machines distantes qui annoncent leur dplacement ou demandent des informations un monde qui serait un serveur. Ce problme est trait la page ??.








Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora142.html0000644000000000000000000003745007421273602014500 0ustar Relations entre classes Prcdent Index Suivant

Relations entre classes

Les classes d'une application peuvent entretenir entre elles deux types de relations.
  1. Une relation d'agrgation, appele Has-a  :

    une classe C2 est dans la relation Has-a avec une classe C1 si C2 possde un champ du type de la classe C1. Cette relation se gnralise en C2 possde au moins un champ du type de la classe C1.
  2. Une relation d'hritage, appele Is-a :
    une classe C2 est sous-classe d'une classe C1 lorsque C2 est obtenue par extension du comportement de C1. C'est l'avantage majeur de la programmation par objet que de pouvoir tendre le comportement d'une classe existante tout en continuant utiliser le code crit pour la classe originale. Quand on tend une classe, la nouvelle classe hrite de tous les champs, de donnes et de mthodes, de la classe qu'elle tend.

Relation d'agrgation

Une classe C1 est en relation d'agrgation avec une classe C2, si au moins une de ses variables d'instance est du type de la classe C2. On indique l'arit de la relation quand elle est connue.

Exemple de relation d'agrgation

On dfinit une figure comme un ensemble de points. On dclare pour cela la classe picture (voir figure 15.2) dont un des champs est un tableau de points. La classe picture entretient donc la relation gnralise Has-a avec la classe point.

# class picture n =
object
val mutable ind = 0
val tab = Array.create n (new point(0,0))
method add p =
try tab.(ind)<-p ; ind <- ind + 1
with Invalid_argument("Array.set")
-> failwith ("picture.add:ind =" ^ (string_of_int ind))
method remove () = if (ind > 0) then ind <-ind-1
method to_string () =
let s = ref "["
in for i=0 to ind-1 do s:= !s ^ " " ^ tab.(i)#to_string() done ;
(!s) ^ "]"
end ;;
class picture :
int ->
object
val mutable ind : int
val tab : point array
method add : point -> unit
method remove : unit -> unit
method to_string : unit -> string
end


On construit une figure en crant une instance de la classe picture et en ajoutant les points dsirs.

# let pic = new picture 8;;
val pic : picture = <obj>
# pic#add p1; pic#add p2; pic#add p3;;
- : unit = ()
# pic#to_string ();;
- : string = "[ ( 0, 0) ( 3, 4) ( 3, 0)]"


Notation graphique de l'agrgation

La relation entre la classe picture et la classe point est reprsente graphiquement par la figure 15.2. Une flche, dont l'origine comporte un losange et l'extrmit forme une pointe vide, indique la relation d'agrgation. Dans cet exemple la classe picture possde entre 0 et un nombre quelconque de points.


Figure 15.2 : relation d'agrgation


On indique par ailleurs au dessus de la flche l'arit de la relation.

Relation d'hritage

C'est la relation essentielle de la programmation par objet. Quand une classe c2 hrite d'une classe c1 elle rcupre tous les champs de la classe anctre. Elle peut alors dfinir de nouveaux champs et mme redfinir des mthodes hrites pour les spcialiser. Comme la classe anctre n'a pas t modifie, les applications s'en servant n'ont pas tre adaptes aux changements apports la nouvelle classe.

La syntaxe de la relation d'hritage est la suivante :

Syntaxe


inherit nom1 p1 ...pn [ as nom2 ]
Les paramtres p1, ..., pn sont ceux attendus par le constructeur de la classe nom1. Il est possible d'associer un nom la classe anctre pour accder aux mthodes de celle-ci. On utilise pour cela le mot cl as. Cette possibilit est particulirement utile lorsque la classe fille redfinit une mthode de la classe anctre (voir page ??).

Exemple d'hritage simple

L'exemple classique est d'tendre la classe point en ajoutant un attribut de couleur aux points. On dfinit alors la classe colored_point qui hrite de la classe point. La couleur est reprsente par le champ c de type string. On ajoute une mthode get_color qui retourne la valeur de ce champ. La conversion vers une chane de caractres doit alors tenir compte de cet attribut et sera donc redfinie.

# class colored_point (x,y) c =
object
inherit point (x,y)
val mutable c = c
method get_color = c
method set_color nc = c <- nc
method to_string () = "( " ^ (string_of_int x) ^
", " ^ (string_of_int y) ^ ")" ^
" [" ^ c ^ "] "
end ;;
class colored_point :
int * int ->
string ->
object
val mutable c : string
val mutable x : int
val mutable y : int
method distance : unit -> float
method get_color : string
method get_x : int
method get_y : int
method moveto : int * int -> unit
method rmoveto : int * int -> unit
method set_color : string -> unit
method to_string : unit -> string
end


Les valeurs initiales pour la construction d'un colored_point sont le couple de coordonnes ncessaire pour la construction d'un point et la couleur du point color.

L'ensemble des mthodes hrites ou nouvellement dfinies ou redfinies correspond l'ensemble des comportements des instances de la classe.

# let pc = new colored_point (2,3) "blanc";;
val pc : colored_point = <obj>
# pc#get_color;;
- : string = "blanc"
# pc#get_x;;
- : int = 2
# pc#to_string();;
- : string = "( 2, 3) [blanc] "
# pc#distance;;
- : unit -> float = <fun>
On dit que la classe point est la classe anctre de la classe colored_point et que celle-ci est la classe fille de celle-l.

Warning


La redfinition d'une mthode par une classe fille doit respecter le type de la mthode dfinie dans la classe anctre.


Notation graphique de l'hritage

La relation d'hritage entre classes se note par une flche, dont la pointe est forme par un triangle ferm, allant de la classe fille vers la classe anctre. Dans la reprsentation graphique de l'hritage, on ne fait figurer dans la classe fille que les nouveaux champs, les nouvelles mthodes et les mthodes redfinies. La figure 15.3 illustre la relation entre la classe colored_point et son anctre point.




Figure 15.3 : Relation d'hritage


Comme il contient des mthodes supplmentaires, le type colored_point est diffrent du type point. Le test d'galit entre deux instances de ces classes provoque l'affichage d'un long message d'erreur qui rappelle le type complet de chaque classe pour bien montrer leur diffrence.

# p1 = pc;;
Characters 6-8:
This expression has type
colored_point =
< distance : unit -> float; get_color : string; get_x : int; get_y :
int; moveto : int * int -> unit; rmoveto : int * int -> unit;
set_color : string -> unit; to_string : unit -> string >
but is here used with type
point =
< distance : unit -> float; get_x : int; get_y : int;
moveto : int * int -> unit; rmoveto : int * int -> unit;
to_string : unit -> string >
Only the first object type has a method get_color



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora179.html0000644000000000000000000005731207421273602014511 0ustar Exemple : bureau de Poste Prcdent Index Suivant

Exemple : bureau de Poste

Nous prsentons, pour finir ce chapitre, un exemple un peu plus complet de programme concurrent : la modlisation d'une file d'attente commune aux divers guichets d'un bureau de poste.

Comme souvent en programmation concurrente les problmes sont poss mtaphoriquement, mais remplacez les guichets du bureau de poste par un ensemble d'imprimantes et vous avez la solution d'un vrai problme d'informatique.

Voici la politique de gestion de service que nous proposons, elle n'a rien d'originale mais a fait ses preuves : chaque client prend en arrivant un numro d'attente ; lorsqu'un guichetier a fini de servir un client, il appelle un numro. Lorsque son numro est appel, le client se rend au guichet correspondant.

Organisation du dveloppement
Nous distinguerons dans notre dveloppement les ressources et les agents. Les premires sont : le distributeur de numros d'attente, l'afficheur d'appel et les guichets. Les seconds sont : les guichetiers et les clients. Les ressources sont modlises par des objets. Les agents le sont par des fonctions excutes par un processus lger.

Les ressources sont les donnes partages par les diffrents agents du systme. L'encapsulation de leurs donnes et actions dans un objet permet de centraliser, pour chacune, la gestion des mcanismes d'exclusion mutuelle. Lorsqu'un agent dsire modifier ou consulter l'tat d'un objet, il n'a pas connatre ni grer lui-mme les verrous, ce qui permet de simplifier l'organisation de l'accs aux donnes sensibles et d'viter des oublis lors du codage des agents.

Le matriel

Le distributeur
Le distributeur de numros d'attente comprend deux champs de donnes : un compteur et un verrou. La seule mthode offerte par le distributeur est la prise d'un nouveau numro.

# class distrib () =
object
val mutable n = 0
val m = Mutex.create()
method prendre () = let r = Mutex.lock m ; n <- n+1 ; n
in Mutex.unlock m ; r
end ;;
class distrib :
unit ->
object val m : Mutex.t val mutable n : int method prendre : unit -> int end
Le verrou interdit que deux clients prennent en mme temps un numro. Notez la faon dont on utilise une variable intermdiaire (r) pour garantir que le numro calcul dans la section critique est bien celui retourn par l'appel la mthode.

L'afficheur
L'afficheur des numros appels contient trois champs de donnes : un entier (le numro appel) ; un verrou et une variable de condition. Les deux mthodes sont : consultation du numro appel (attendre) et modification (appel).

# class affich () =
object
val mutable nc = 0
val m = Mutex.create()
val c = Condition.create()

method attendre n =
Mutex.lock m;
while n > nc do Condition.wait c m done;
Mutex.unlock m;

method appel () =
let r = Mutex.lock m ;
nc <- nc+1 ;
nc
in Condition.broadcast c ;
Mutex.unlock m ;
r
end ;;
La variable de condition est utilise pour mettre les clients en sommeil sur l'attente de leur numro. Ils sont tous rveills lorsque la mthode appel est invoque. L'accs en lecture ou criture du numro appel est protg par un mme verrou.

Le guichet
Le guichet comprend cinq champs de donnes : un numro de guichet fixe (variable ng) ; le numro du client attendu (variable nc) ; un boolen (variable libre) ; un verrou et une variable de condition. Il offre huit mthodes, dont deux prives : deux mthodes d'accs simple (mthodes get_ng et get_nc) ; un groupe de trois mthodes simulant l'attente du guichetier entre deux clients (mthode prive attendre et mthodes publiques attendre_arrivee, attendre_depart) ; un groupe de trois mthodes simulant l'occupation du guichet (mthode prive set_libre et mthodes arriver, partir).

# class guichet (i:int) =
object(self)
val ng = i
val mutable nc = 0
val mutable libre = true
val m = Mutex.create()
val c = Condition.create()

method get_ng = ng
method get_nc = nc

method private attendre f =
Mutex.lock m ;
while f () do Condition.wait c m done ;
Mutex.unlock m

method attendre_arrivee n = nc <- n ; self#attendre (fun () -> libre)
method attendre_depart () = self#attendre (fun () -> not libre)

method private set_libre b =
Mutex.lock m ;
libre <- b ;
Condition.signal c ;
Mutex.unlock m
method arriver () = self#set_libre false
method partir () = self#set_libre true

end ;;


Un bureau de poste
On regroupe ces trois ressources dans un enregistrement :

# type bureau = { d : distrib ; a : affich ;  gs : guichet array } ;;



Clients et guichetiers

Le comportement de l'ensemble du systme dpendra des trois paramtres suivants :

# let delai_service = 1.7 ;;
# let delai_arrivee = 1.7 ;;
# let delai_guichet = 0.5 ;;


Ils reprsentent chacun la valeur maximale du paramtre dont la valeur effective sera tire au hasard dans la limite des bornes fixes. Le premier paramtre modlise le temps de service d'un client ; le deuxime le dlai d'arrive des clients dans le bureau de poste ; le dernier le temps que met un guichetier pour appeler un nouveau client aprs que le dernier soit parti.

Le guichetier
Le travail d'un guichetier consiste boucler indfiniment sur la squence suivante :
  1. Appeler un numro.
  2. Attendre l'arrive du client porteur du numro appel.
  3. Attendre le dpart du client occupant son guichet.
En rajoutant quelques affichages, nous obtenons la fonction :

# let guichetier ((a:affich), (g:guichet)) =
while true do
let n = a#appel ()
in Printf.printf"Guichet %d appelle %d\n" g#get_ng n ;
g#attendre_arrivee n ;
g#attendre_depart () ;
Thread.delay (Random.float delai_guichet)
done ;;
val guichetier : affich * guichet -> unit = <fun>


Le client
Un client excute la squence suivante :
  1. Prendre un numro d'attente.
  2. Attendre que son numro soit appel.
  3. Se rendre au guichet ayant appel son numro pour se faire servir.
La seule activit un peu complexe du client est de chercher le guichet o il est attendu.

On se donne, pour ce, la fonction auxiliaire :

# let chercher_guichet n gs =
let i = ref 0 in while gs.(!i)#get_nc <> n do incr i done ; !i ;;
val chercher_guichet : 'a -> < get_nc : 'a; .. > array -> int = <fun>


En rajoutant quelques affichages, la fonction principale du client est :

# let client b =
let n = b.d#prendre()
in Printf.printf "Arrive client %d\n" n ; flush stdout ;
b.a#attendre n ;
let ig = chercher_guichet n b.gs
in b.gs.(ig)#arriver () ;
Printf.printf "Le client %d occupe le guichet %d\n" n ig ;
flush stdout ;
Thread.delay (Random.float delai_service) ;
b.gs.(ig)#partir () ;
Printf.printf "Le client %d s'en va\n" n ; flush stdout ;;
val client : bureau -> unit = <fun>


L'ensemble

Le programme principal de l'application cre un bureau de poste et ses guichetiers (chaque guichetier est un processus) puis lance un processus charg d'engendrer indfiniment des clients (chaque client est aussi un processus).

# let main () =
let b =
{ d = new distrib();
a = new affich();
gs = (let gs0 = Array.create 5 (new guichet 0) in
for i=0 to 4 do gs0.(i) <- new guichet i done;
gs0)
}
in for i=0 to 4 do ignore (Thread.create guichetier (b.a, b.gs.(i))) done ;
let creer_clients b = while true do
ignore (Thread.create client b) ;
Thread.delay (Random.float delai_arrivee)
done
in ignore (Thread.create creer_clients b) ;
Thread.sleep () ;;
val main : unit -> unit = <fun>
La dernire instruction endort le processus associ au programme afin de passer tout de suite la main aux autres processus actifs de l'application.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora026.gif0000644000000000000000000000761607073350152014302 0ustar GIF89a'UUU999rrr!,'X0I8ͻ`(di&lp,rmxzpH,Ȥr)1)Z*vzxL&jnQ|N1~o9QJE.B X_ [~?Wzڍ8y} `A @`a!|HQ!C4T  !vBY"|'$YЁZ t $ېs =c=:%2 ehP]ԩgXYM鐦GʝKݻxRVA۱0 W}r!45C IwKمˋx3̚A;⽗牆B0i*K G]kC׊ 3&[nzsУ+oC^]>,8E، .A˓G_M ,]o`gc RaZuǑ{f`nǂ w^F4AQa8 䖊hء3rӆeWZlʱ|C݊,CO0I_0&dQA)ZeFёhntV ^aqwH-8gq (UpR4&@]Q\}VN!螞Y_@!**CM4b婥&6Fh#~9Ny*쯔^+H߱ꩪᚬtl*;Ű~)]}[6nmxmE Ŋ㼏BKƽZ+?,pfk '2K0Gl2pe#`Tƈl  D{Ѽbf`J>rsJQ4ڠDʟN DX&>CˢtqL u(W|1OS<ƒfcO g۬Z=e|Kؾa$57Aп7(8'~x-9/^Qxpns٦wvBK:&3j̻꿣"TM|"?ICR{r7NϿ_}B9qp}/@>dR[ ۻ?wEh'ӡ%dUcFݴm~ HU{X0n{22gf#AD3l<ݐհa|iIڃAG6d+Dʼn)ir4)4U-1w3afV"i$2kϔEQH8eLQ]b>>- @ P+.cΤH u%|QIB$Cx Och gS\ם8YH *j{J0!ނk13:&GbJD|b*""wHrHfqSi QZ9O N&RnQ@u*EgvxJX+f|bQإ M13tE__1kQ ~ŮglIwkI-Nrn4%$8Bg|7&ԐATĒmk+0B#ҥ0K7KX-c@0C_n G1.įFn9xБKJh0Dok`E[w73*l8gA+ce r}^mZݶЮOTuPm 5a q.e"=Շ+q/YN{ q1G}~;k8{MjwہWYsۓSi | pV{fgZWx[u'vQxw<.85> Zen'vX\$؀u8n7%8(;*Hn88`qc72+j"QB=#rD3Y"X a#&`WgWIwP%iZPu!5fDmNdtgxLe`t'HҐN`oɒ^lRgΗo V`xO)a}BȴBj$CʴaaR\y;34DiE#RɁ?xq)ciEĚTĚW4d ؇}TOykrfyZ{f|ËQ2 DFixP+!1Oe֝Č_mvtAI0&#{OrPpj Mr{8mro?wghdp:tRzTP47&,ǜ"k},bVWԘ6zqgsɘc VjZ6*$$£m)&fFbxǚj4픠I9g4fE5!أEQ2s4e"BoFFzXM8v`,qɖmZW/s[iq,DQ? =Yө>TJv3wy\hӃQTeBPڧM w툉GP&Uc"a&;qi+|{{QYyڪD*P8g:y*}իԥʦ:ʩﺎ!7SIY*+{k KS ڊ˧?8;جg+]# z~ܚ>!)~ኮryWSɮb ?kG˱. <<{=(KڱVQ[ZْoW8aH6;S jۭ6@g{V Yk+R I+Y+;ocaml-book-1.0/fr/html/book-ora011.gif0000644000000000000000000010536407073350152014273 0ustar GIF89a 0A(A 3a0,(玂Yauyau(QY0 Q77ADY7(ahh Yy0,H3q77HhAUyHLA7QyA 7h]qHeyHQy 0AHUQHD0YQyHhuYYhq]A07aYhYYhquqhQǶ7]qםH]qA7(hYAY]qyayY禪aL0l 00AY熖߮qyAaHqߦqhqylQyhYHYa(Aq y7DyayyhQ(Ƕ7Q0]7Lhy (7Haǽ(Y!i, H*\ȰÇ#Jpŋ@qC#c 9F&(S2’e -< 3`xL-chX-*4oپ=uTQUH$YrgT[ `|UN-t2w|DJ(%CL2MtSIT#-ǡ|5Ruk_oDem[!\ [H 蔎lXf_ bh&ITJ1 33tY%V-PQjQ`95f 9kFf7:E)IptHtmuvY a k9&GM$dkLDM)eҗXRQaZnuH[+PZNicuE %IhiIV@`&`qt(oUiwwM(]fH͸ZjnWゴ]U5HRSQzEm{&ʾ +kq]؟{iZ(go,hSh8h{8h]h̕6 kO12Z$?yFlmOUmZebUZ.Å6UH!آ^V DYK4sQD5n jf'P2 7{BN#GtopHgV҆ d_ Ig:smΆ>z\JU8]2Kyp&erYVf)xY6nEoBYݣ!S~.㩒uafº0&4*bɝV݄j9HCZpEZukdDsZ8S'ܚbZAK=CK̭|䌦T/!܉wꧯT2"k@bBI(*bft!TXZXx08\[zJL=>l#LnYQ_-F XӴYXd Eo;,ɍ5eő,}+́N2)t`! yqi5-rK7l zie&d >wb+7V Ї$p$[/TB\v57ŝoҗ3&Eɧ:USәzJ̫\J(`qIc C0 C( `=‚%^T"Sz4{rt1u #J2K8ӗsIn: Ai&%Eo޴{y%=#eV7_ц\c Swsxm>n&FdN`avd_# m| ,#f8F->UltKƆgsW6?Rp!n@>fEu7+1gf/s' DZ9uc`}(`-8;:0SrXB$hrZx2~x@T4USfbС`TYAՖ@f9DžTaR&4Vm\PPrE VA`"{Aܑ!b1WrwH 3 Eג:Ye*'Q'atץjPb2@ g\pY>%ǩ0} \FKH%"{nV "&A]74] 3qޣ@k2Y;w`4q4+o"bsC5; }|>A C0\ P\`$7U"?h\qB >A3NvUv{⦪/`A6FLW7fjvأ'r%MAbgxP>I&Ek(g  fP 6ĝ4qNWΦNjGh(Q828.2E^a3idji%0*Ȉwy\ V0}A{0I>p`0K0ye%J7s:%\3k*Du3Yo rk;ZVfuC54y1P\p0 p4P xFw }МAy@SzL9>&WR&eK4#ccT*".7gN[Vxph@HP%wY}PAuIR*# V{pCwf)4/EoDc湯>~NɍTU& ٪gB,82)AЏpo+HPW`Z EYеE8;IuqȳX[#"[lKk;܈Qoc]VspFb[4"3\pkp>0>@ I> ՉZ?_Ks"VebA.['|E]rRQ NTE+1n2Kh <"29Py zC&I *mYоQ*^ IGA@?< l,:N܇'x'i<~d:v%_b[38g Җ )x0@]` }R1ЀĈ, pk ۾Ed0mT*ŅGŠ)Gtr#wa)y3CZ<;hȯ" Ia4` +M9'5{8PRgbp ]pzXrb|Pjͽɀ`Q*̈Wp]VMC@:Qc9(@r)= 47=Դ4u[sNsHM\+0 2`k>0{oRPhmAV ωȀa Gxp#c5SQe4(49Rs@:\4Bb)B{Ly֧f<|mpZ}[Iƈyj> GA)XSMӶb| P+GC=Y`u/'Bk6Vu1gT^GQ EΚ81.3`(n+wy+ F\m^>p`)d`fkȌx& 1   0o$. WԉoΟCrAZݒ(S|`+/wG8 0$*ۑ>`Cn\ BL0 yP 8LPE G]q%<}¡$R )})m',\8nZ1IE# ZМ،YM ('b0*HkCо`P|PGcoypXlhJL NʽCAz$v4/e`q)],,C`]p]NǎGxE֌ho>@mIpw@?R, D٥]uw-S1SE +`!0Ê1)009ʩ,r1"k+޲O?N [° @je5ИC$XB$@"j PHAT] ::U v()$Lm `-i PL h*خ7ٿ LYȼ|֬, 2e/;.װp :L1*B P P 672j , 5V iMnF ja V$D8 ;\qͽʲS|vgLZSs-@qs 8XC,$l4XCG<"-p)S>Nq 0ġF0 7,`>H&uJm|ZiR3jLq _%&pX VV׹0Y j&p>t7{@x 8IMMd"B4LJ&H`1 )P!FvҚ0ۑhfNOQb㥴\I\:>j]S0ǝ *` : QГ $ 6O&A@K|$'[&D"PIVsR@ Z0@0@ \X Є#.v{ᴘ" -i9 Ls4K\5INLzs+M < p/$թU$@QDV~2`&VP t+B߈y `@P 4C.KDdKX9+dRE?;Hp"z %JdU2 ua 85M@UzBl)ib{mes<:2A)+s`bp+pDBd;|;˅a5тhpWh} 0Bh#3Ѝ ,WȄHȔ3׵a( >Rt$ ([A3+Od첖 2{ <>Y$ S%%ga0 xy( q* ӕLB7Ve vѹaxbM\3 P$J">"dkf Ǒ[ldV+sfRЦMr} ByKS< Yhm`=`q\CU&(vXqHI7\eD3&D9CU~c 4Je[iS@]w[R/^c<<`&; bp$ H* +ss8R o^cam6<#b>L<@t#pkpVXd]Sf(je`rN;zk,cI#w2Lp X KI=̟cbəD_p\< .(#|50k+#.! $yqëN5N"-X 09`yI?\Puip I,(?:X;$+ X()=9Nh @ xJvC,.X"q?7ȧ pNbJ he27C2)srbi.L.+"` z 3 6 P(Bp4X78’)0"04yUS0(8ƾ)b 蜝].x+ y m zAxDd;''x?(,Ȃ,h=YH5+L X-3a<.$.E86@)#/SJВ̒;kY3;ì92[8 M- ?4=@yKU2X͌JSDK׸ e5x &PP >#؃6x>, .H@+\eV#-j$ff~$PN!~yV_D b&&@DwZq#<#؁ l.i ,|*("ChE ޢ99ž 7XP|R:p%9d__tA\[f &2휦;0. 2h 9I@H+"4*%,#S< #n;VH*H|MɈȃâeK̥5mAlVTbJ@ATw  @+PpD1 ; @@ >ƻn H5ۼ!SIpsV<`_N̘A4e jmA #f!H%HYim-Ѐ? `$6DEB^aCH$<N+ļk3 +%0'UEXjCu,+d*Cy#5h&-h(Uh;}Д ɼq 41۰D" y;HǐS4@[$0)  d,#W|oP;z;zg|,PZ;H" >3X*U#y. S RZIf"Y$7`(@$= ae@ 0,`2D?QF&EiP?"Mf VL,ըJv=SH+zD4>D\G a"`(`d {!"a1c̃ X nHZ`$)dk 1T(ҥAeT:Ҭa~U:hs~YB$xhGL>*1 &C H0  M D GdtYAA$цJEpQn4DV|@ 8S!Y'qA11PLl\VZ ЁduǕ3El d a6,  \_Hg dA$`uцeqCyЙE@PH(40}PbI f4DIg$ vDuqQ>$E%a!erAuT1~ (pFn,H-q˃ydPX4gl)ЙHvqhEA R+7Q*P$#AI} qIe GqMi5eVBheUQ7ʪAd \h(@E |X D)t$aIHЧ'X~04*4D8`Y{xiĀVxmHAspI2]qrD_y\w#]R9%@h@ A|%!a7G"F9)l!F.5Y!FQ1 Vi@$$Q lenE)Iŏ0 @ $$!bTFF1CPay]0(0<(@D4Fqb3N!(+0|=4ppAw0 &d0)( B.A Q47"`D` !1L51HAȀ`RAID 0 p AHL+PةpMG*ouG P)\BڰtH&b<`DB ܄v1B`_3 T;@rBՄAC < "@ Q@ v6n`CF&Pجf%;gIǡvЄ @X8JA$8 A  x!R> 0E8` 5 [Aܠ@* @ fPa"BD 4;4T9LW> ~EIhT~0 nAL"6pa(^T$H1Zpi B: bˆv!&a] `EHed 2"Ma Ĵ2',>"; IB`L"1` Ci78Bn a"Fڇ( 7{BX1M RHpPFD٢6hybrJGk,"AA= +B)t <ȁ6 Z|P@ N%-"}& +CZ pia@'W $R /Vp`& @[#&h (:98Iw-:*kcսZj@L8:A8Np | `)@ ,1^JN#W )y4AD)t;ڧhk.!AAUKc5hlNTBts֞8o8B`BբOp `2T [e< (hCP 9m> 6&CЄٗ ,"2Te;2`4πCOt  ؈ṕHQ8HuTЀ$L , p@HA#U5npǀD}i  H'0@K0nHXޓDCb\ 0c /"xA OD aAF\L&NTfe># `Os@t!ԁQb \AW8W M M @ L$J` TrnҸlQDALL'FdJT4эAv]2D8U@U @$4'~.Rc]瑊Y c*ɀc@z5؋x@ @KB ě`@N ,dpY/j%"]b쀀H<p=1Ԡ@]@ ( NB\MN~pd56X؀zN L @h l @T pdKt 11JPp QXeH_I hD ًIˍʼn2^f}2|2@5pݐeVtD h8^Q4xUpA | AADѐĕM!iDBVFR ߹يl"eyb H\m"BTBllX|^&2*`qv]XQ(QXirlVAɂ\A Mk d@BIЎ ϙT@F \cD/"YּR&]DX mi:MUIHtKg$z!5b5[h(F h <Gp@ d @|AM\Re]V@|/~8U \yR5HN, XĪv.nAN%JTǬ)09A@I<@d̀L|H8_/RX^ exBOI$=!y c4Ed& yl!N5 ۮj1Um*upy@XM R$ AX 0A\dm%Il=*ֽHĆAy|FE̥2* +$gnYc C5rvc i T@8 @@āA} ^.f]*`Fl[$91rlNIWJFA $f@H ʔrf ` %rXe9Ȁ ^Th@B$ A ID0\~XKbKX-rΪEs;řA.!m֚AX4KtAF`A@dM w![o3cuf0^t Sp2"wk8@ ăq0HǶ\PlAc7AU1Dx\16Jh@$RiJF>ۆ\6kygcz'6zs6FpIMp*$nTNl tX XF@rIFMX l$/k G6! @F,i GNdnH4@ pywMg#S)Dw,)M@D% MD (RAA)`8@ 4{JrNN5%Mj(R THVEA[XTY@k攎tx2Aqm,܁H((|(\WDt@r ̉;VY/|inݽF&`3ynτ|'"Ad)F~AwN^}BYE D (@pAl@HiDD ,H@q HҢp/dudl1nFMŃd&-m`R$,Xy'ԧ>@P2g9L Ȅ *)RIfLE\1cǍ:@8@ā +QPP#H ц pPFB p7`hQ Hkhq@P $YSQ-A$E,QA$Aŀ2%mDvp H`CiPf!B LqtML}F+ tあnPXR $#`"P50j ?֬u $QJ԰In5 Tv'Hu=]4* !$X 4 ďޓ@0Db(K32#' 6$"JA@vP! ;v <*@ @3b .0` "!Ȝ$J,bJ^H7prl4Lg)@T[F+("X$}Ё*5m {IІ!Q"308[ P=$J`Bt ;wµ$'Ll jޤIl ^` $  f TeDƨ9L\A*#<@(Ȁ =pHB P-B9N--p! xH*ӊ4'01@ i'% X EH⣔ d[RQڔ4$`@ E@, @!^Z+,aP B/=tfCN$E bvPU] ]BL$l`FR-*hCF@ -(IZV'.AZ_bCT*b@3fh$ A$3{ޓ%d$d;шGWB@ o!! fȃV\0Da*K 0pa<4AW0iAe?^J.إPY z4vY@iYK [BdTgb#Hrj1!R@Ǻp\ -B 2C5! A00ei%ʢpE !*L(펢1@-\͚<,&א.+@< @k n-mY-T7]}:Ș$iC6F "@`!eB$00$XaiT ;tw6DJYڀ9y-@zdI\ 0 rbPR( q0 0H Dsx$=@X8@B@a 4@=_6'WЀT 3FM p8 F߈lkH&D[2 0)bѤFbަ 0@C A@zp /A: HH"PCL8-7X摊H(&8tnȒ pIh$\XdO)(тNY1k} =JG>wL6n:gW x@L8  w0Ch125̈́HE /0 6p"P`kiV>܆X|[bZ0ZI +$$vlBZ:C,"v>S@( & &kd,@ <@ @6hb Ѐ¾-D` @:Ѓc \ _hl,`* VĄH =< !T@= T`ؚd<, ]k`ƃM**P * KoGUoC. ~ [O oN @rR6@q@ & d v Z@ZV,b.dǁVlj ZjⒾv=k d2#,am62?e# r@`@Jva& $@ n)RJ$"C*D钤M jl,@-| "+D+`HEEq Z/l{2a8)A%b@  ؊b *G ld  ( )P i~9:@wMx;ժv`LfkIb`:`;d, +2i喫*, v0 di'3пYa!0@EI7F`/\_5 N  V..] `bgEjI!Yu lX Ť Jخ\fEv& -Gx̨b tF+ !TBYk@F h հ+ &`@wT'zixyQA  js2J @v` ^2M䪲* @Ԋ./y Y IzI| PLJcti,KQ]+,&*$`4gJ|.#4IW) ^Kդ*JI@. -.9 &y \c,سѢ "ibeJC:>e K+<{Q/O)`  D`j0`2gQB`z<#Z 10@`p~ @ @%@%\@VpV"ր Ө$oH[6 G׀x#@`-Pj(Z @blԭ/ZfI ތ7+c"XȠwe\@5 Hv4z9`.@۠Dy<`?D`d 揜cQ˕ p@U8$3*`yD+: g*o]S$lBfCZ Z TNj ^` `(Xr,.,hWZ 8=^=* gBX rl"(YXaE/A2Lf޹"esSD8@ ., ̻* @`b%I&[޸``U1[x(⾵ c#.M$sv6NG 6<< Ǭ a?ڃ^p¬/@LC&A&XYe @ @ z`YXWCC )XdӦf0F5npAD6?:\ぃ-$h549*HDnhɢeFG`S m^( J RK-HQa$kx,ThS$9T2s9LRwo߹L%#M011c!Laa&w E P$!+@E0PnzÃ#t$e*P[M$vg~e#NKGmx@kTE![@Rp"a'w&V XQl(0@`E = {l= FEf졆tS@9ԝm\RќKkQ]熖8yXau_ik )  mt]Y+8E]}}\"2L=vb>ؘBX)@  XGIH`*+4| 8Pjxr,A->07IPTs"`O}udgkRKB!b $P ,Dz^2`珩hjbs `PmA0T@ pWA@` f E;# )`szb A:I p ;H:R3A P `e 6KB̷/ n)Ovp1i+v% A fJ2Bp .z.\AA F@P  A@|88!LbA EC<` K![^5*`؅xDZ؁4@יcc aoZ  8M/ 22 8C $ 0vaԓ58t XĴ@V;)ĐKVR$>YdH`th⁞IO9K5) ]X `\GJGNSjLWxnJ2c@VṮ!ˆ|p*)A @IR@p i=IBxa ʊ} TH>@p%|R(\P $mVirj5<#`@QY#pZX$ <&a rb21u* NX &\@T- 0 &.TWܭE1[\u`dE(@=M_P]K0)tkF. Vd 'V /}%F*7PD>|PrgNE65Z hb {3"\;L njP,3qpgr JdLUHtY ePR)m*u+ AL“8wPjA {C,`+-R@A-P[ 0e~Ai1gaK^Qg#pYQid4qvux&"'zE7kp/a65n%sAt+P~w~ 75`(Mppk}`+5~ QI^]%sFl^mkWpKb/;@<C8z[p˒Em b'w yӲW#' '(&jPLlk 2RxEX ]mTp6\~Gu 'O  $+%(OsV 0`pBp`~aPR%a Qg#TxKӷe0J!r ,q0- m(MH8C{(~w b5y "p\p #y ?7q2`2eX@m`kk"J\Hp}%J~ >XIYuM@44mY4Bu`&HP7Lm*Xn),i4Z@*'A@\58xGG(Nspp(/(M`piGP@Ha,\[AX@rK5䊫'$&<0S #It`c`r]89"YA6$`2`gAA(&E u1)+lqc$+愁S;])I,H#hE!hq[rA GNF7;fBZf!&"Eze`Z8VD{99@N{|0_k!0`]GEǞ Q!y٥vT]M~P=bE4(F;F@_'RmOM͖4&`;] jF`A%(X!ZE.bB[hGN83PC6.bB?eesVI yw84m mPlnE&˓H0X+/I}p2;0Z"+0 Y{AYYeOza1^'b8 Pgݤ|pEdEBI'`гk[?G7!SN ?u~;},``pX{`a03[15  Y V@:QSh;þ8&qnȚLTZ[%MJvyxGTk56jAm@(IX;A`dS"X!$Q0NX "G`l(od*yyPw p@1:fJq` ,jb,>;>A_iu]pT:\?J#H.. Z&w7vigE@P;dTkPkq b (@f:7*hR ("yqv*RP˺2E}p-`[J15"Q`"2S-0C80N1-R[Uތbo+To[>+/KXUP `>]G;h6v8ʞ(K,-Ap|* Y0`#hGnY<"u!\J,fD)$5Ȗ`Mj(nB[ 6\0& 0f q#M@6fxG#=H8sp24" p5ppp!s>`vhpcB9^Che1`Xt$FpdO%UETbp25]. M3+$,GXV Jk!'+%GvQB~1aX@i$Vm"MH29@[@l[]@;LRkWpKkǩC11wVc#ֲ$A}{.b/#AvymbKIYn`%i{-@ܐxgx#pzp'pdpyy{tPBk< f<~t|sLOXnp Se[D5! 6n`HPA`IpܽkD"N")xp]?-~- B~;dLPhq(s9`_eH3Ihz41P 7bM4a,-H5~ H[bM&]n3J! hjw`EE'+ vTlU5#1-=!s!++~P9r8.p.I0O ,N zYU'm#E ;RkЕ@*-%AL#k 8C, 5~poJX_}PaT.okp,7YGvt -1A" [ A17#f0{~0!J9[0К>6}arR.D<..#-2@}BNb\R`&1Y` o[oҕa%lMbsB-)PtW0/fZ*f+8_p {hk,^y'KƷ.VDpz2}E]l*Y`6W M+hQ`Hn  fbfL *L̑)s͙Lt0S!i9"A @`pHbQS!{ Ph@AB3jxpO.((H03X׮$tѷH5A 9މZ"Tl0 )  XuYd!$XC&*F5x PِySG0aĀ Bxr .` C8+A .Vî5&!.H z Ĭh5-b5[$-BHb;`V`A.`:記i:~itXc.,@$,)\.Š. @= T +DXÁH#D! "Z.5<` 6#k 1 b :TH=Vx<2%K# U "\SRPRI2)v6lۀ`?ΦZ 1A.#Ԡ@" ,!+ԸJ)hŒ$B ~(jȍ `k#$ T @ :0㱏`T0 R`;:؁!$Bc t- :(⮔0o *@µ kqrB&x)%.$x`8A)Ɯ#pV  Fha"ZH5CSlȂSV.@>`&mW,8Vm6*+(Q#-~XA QVM"2),NWa$@jcvjV3b/VVTHP7` = T@\l@87k 0 . ,+:IL%4 Qt4@8Ѓlj@PH"8.A 5؇;a?<۵LY  @$b %{$^م/$R"OAC؄2R>^~ `QŖO7 4 z&Q+ \aA+.TBc` xH$B/H{f( I@F4 :*4y/Ъ/L \8Fvy-pJÑOp DB V:L3 ph5' 񣀋 .TYiz\nJ`R.ZC2|v3!ᨭ0sg(ܱ `ld ~d @ (XC,A_{1 vpg J* Y^fCЀ;Pk.Jbku& ܒ 㲦 `p(6=y(/IkZ;x9.] 701ᑏ@6. x iÚ axP (;v)0h  P+w5 $5Hp+[ >x.[B7% !!?6?Y K7@ "#Z qʐ*vL5v9@ 00 #.0i;i7(x1H03@0!!1X] &C3[c` ̋392Ÿ9@(Ǻ ð @` &(HIP= hjAh2 2HX1 xEh>8+R 3h2ƾE[$Ю? *8cOq0tC̐9 FiI6=nl(,ljhJ x3tЍ & C 8838  "@#@+<*06K#X{9:#BdCZ&08h:XŒM& XZ(%`S =#Y%,pN N-4`- ځ x +:+EЀDiʀc .{K65px?p L5a%c2#K!(h$H*|]+xF 3 4*C!38Xe X%x@ -ѩL3@-+X3@X_[<0;5`$%?CCI4̂1C3$E& D%6  b.#16% Ȟ: L @`0:Dرh ȋ؃S YPct1L=Hx#@ۉH>ؚ$(!H$`]\؃ x< (1S2' a1G7P8o $ꙅl"0B|W4h检0h8H x8 @ (&` =(!H!-2ؾ&ޓ|t#%[@1!pp%N P 8(nJӹ0LV pт2- MD:wM (h>@& 8<25p5DD1#T+R2"&l7X[C$3VЂ5{p5$83@0`  -hI x%cHcU `NxĮR[%POy a1C%I*`TAE 9! ߌ \],%,.#0Sfc 5Px!ݠ?H`Vh+*%fkf>c867@ P:( a#|^%(>Ђ'nc\5qyOVRm"X!.H\=6e\1. 'U0 o\;9;Fik|{i.2&a%0V! vZL(u 1}2<#˫54hЃ*hPPx?p%>(22p" > !tZLMm.]#shr$@ЀK`@EI{".Wcفw %%jc6<80"'Pd$-H&[=` Y&.k2$&I%ԁQ&HX-hL .P9*vdVUڈ.h ċ `ql~l#G>$J #?DrpY5BB[KQ=Zl`j\^0ЖE*EݱhK~4IYKZD~k6=^?c@#5P:\&hW?IЧ$)u8$Rx%S%X!9xnesK_q(p-1[>i'ĸ>X^$'Mb] _REBf`FE+u&TmF x`4Q`H!L%҇4D3դa -H-OA$Qu!yP˹A4Vt@OkL w]yduWKttЅ5p@Abp xLTwީ0yvb ~4Z aB$Q(K-0Qma&(g,$Bt@fO*!EMp yP\%yו#Blq\8WPFKPJ x !X @pFhZ}x!-tIq.f"zGYAHnhl0nt0 qJpLB),XVdyY%ߠxș!!bxppwHJAcI&1SkMDWEm! Vk VJuC-4uB,$R4-( MĐ|0%P05h}0Xk4"Aτ"P7mAW@ nd *%nXC[:ϝ `X6,LlXb4 .DwF@P5Y|6Mt A&=xXS5) "PQ{l!TH&)r41pHT}V3A`4`?X%)H \lޭ.:hZ@ۂ,() E uru3K xK <6.l 1i̠Ie/!ؿlkNtV) |Px!@B !? 󜇻;CRlia1U<a(M[3xz 7 H6OiAYSJLˋ] t d"2P,sN F<:/eK&0h@ r HDŽ*@%p`tƅ&SCDB[&e ü'YyԸȢ ` J_0=Q:ibN~* TL3[>< o#>_<_;ocaml-book-1.0/fr/html/book-ora025.gif0000644000000000000000000000655607073350152014303 0ustar GIF89aLUUU@@@!,LI8ͻ`(dihlp,tmx|0|rl:ШtJZجvzLuznA~Ͽ+rvc-uJEeIŸ+cٜsE,+H_;@o8pPc=x+iqF`!S gc907IYr/Pf͟@eeA*eusʥPn)M/juTBٳhӪ]'Ԭjn1Ӯ_zp +XP}ɓ˘3k̹ϠCMӨbH j԰c˞M۠)4Gp9“p N㜁[<呐KNuKn:'We} _j/Bd}N+3wG'%FRw8]y&9w V d= .a%_!Ȉ(Pb,a@bkDKո8r~YTBFw#C(_1ҏJ$}OnVW*bD@m `wb;*,rfTjH$_('-^Aʈ̩cT:$nAg#q験q䦉TZϥf(vj秨>J1ꗢy :(y`pjԹ*""Crk]ze&^š[5pӤej-yz[;k:kz붯+JunFoZ(bo S:ۨR@*r @[oT!)G*||?.w+A!ḻ$Ŋp*D>$BDCiTO9< {Aw rN"5.3Զr8JMp/=\ ^Ӆwv*p-*5I- 5],oy2Twxߢ3+y>z+xjy햳oN(n)^Tn09 oU9AZǕ~3ys[᜖-oV=rؽz#܌? ZTA]NiQh@B#,#Ib{I!>x-a,di ᧺*τbXCfd?axvȰNh'rv.!~WH2FM㖃6oy8x܂iD*VjLc> 8I@Б $$?B>p/&9EjrvjH@Y@Dt$VH +]ʵ%qR겄 W2fL&;oe[O ~ˮumӄ[vkn v-q[*wmnm"׹ԕqkl%wNw/ ċބveeڛX{q5-/w `W-j+d9#BRkab` ;,UD%1vw(zk(Lnr4(KTn܌,GU\20cyeۃ浚uͻr|ft6ݬۮs| KApa|,:.Nr xãJ ÒgIx\Poqǿy{娧[;9+~bޚ<9q7𡗽+Yo> > O{}`~=Qdzuf<68Fd jTf OKn~y;bss% W9ucWw?JxyywRus{sGf (h4ugWwz؁sjgGvREg7Ȃ|eǃ~>"8tC("EhzGЄN3p'VXLU[ȅDVxbdhowc8Vl(gHbh\`5ipȆqȇnspȈv؈N(KHGhC>:ȉ6d`'\GXg;61NJ,'FfƋ&Ffƌ挛&Ff~ytƍpkf&aF\fWRMƎHC>&9F4f/*$Ə& FfŐ%Eeԅϥő%EenW,8r'mxyv-yL{8(WkxZ}=ؕQxgyikY{mIoi|Cd)xXzE97j yؗ~~y٘hPT/9YSo&iRؙ9va闫N Ě HC"(FI d2D CӴITu9!#ZJ9۹Si+5YEH 湞ٞe(9IY ٟٟ٩:Zz j;ocaml-book-1.0/fr/html/book-ora144.html0000644000000000000000000016056107421273602014502 0ustar Types et gnricit Prcdent Index Suivant

Types et gnricit

L'intrt de la programmation par objet vient d'une part de la modlisation d'un problme par les relations d'agrgation et d'hritage, mais aussi de la possibilit de rutilisation et de modification de comportement des classes. L'extension objet d'Objective CAML doit de surcrot conserver les proprits de typage statique du langage.

Les classes abstraites permettent de factoriser du code mais aussi de regrouper dans un mme << protocole de communication >> leurs sous-classes : une classe abstraite fixe les noms et types des messages que pourront recevoir les instances de ses hritires. Cette dernire notion sera encore plus apprcie avec l'hritage multiple.

La notion de type objet ouvert ou, plus simplement, de type ouvert, dfinissant les mthodes requises, autorise la manipulation d'instances par des fonctions gnriques. Cependant, il faudra parfois prciser des contraintes de type. Ce sera indispensable avec les classes paramtres qui reprennent la gnricit du polymorphisme paramtrique dans le cadre des classes. Ce dernier trait de la couche objet d'Objective CAML lui permet d'tre vraiment gnrique.

Classes et mthodes abstraites

Les classes abstraites sont des classes dont certaines mthodes sont dclares mais ne possdent pas de corps. Ces mthodes sont alors dites abstraites. Il n'est pas possible d'instancier une classe abstraite : new est interdit. On utilise le mot cl virtual pour signifier l'abstraction d'une classe ou d'une mthode.

Syntaxe


class virtual nom = object ...end
Une classe doit imprativement tre dclare abstraite ds lors que l'une de ses mthodes l'est. On dclare une mthode abstraite en donnant uniquement son type attendu.

Syntaxe


method virtual nom : type


Lorsqu'une une sous-classe d'une classe abstraite redfinit toutes les mthodes abstraites de son anctre, alors elle peut devenir concrte, sinon elle doit elle aussi tre dclare abstraite.

Nous voulons construire un ensemble d'objets affichables disposant tous d'une mthode print qui affichera le contenu de l'objet traduit en chane de caractres. Ces objets devront donc possder une mthode to_string. Nous dfinissons cet effet la classe printable. Suivant la nature des objets considrs, la chane construire pourra varier. La mthode to_string sera donc abstraite dans la dclaration de printable. Du coup, cette classe est galement abstraite.

# class virtual printable () =
object(self)
method virtual to_string : unit -> string
method print () = print_string (self#to_string())
end ;;
class virtual printable :
unit ->
object
method print : unit -> unit
method virtual to_string : unit -> string
end
Notons que le caractre abstrait de la classe et de sa mthode to_string est manifeste dans le type obtenu.

partir de cette classe on cherche dfinir la hirarchie de classes de la figure 15.4.



Figure 15.4 : relations entre classes d'objets affichables


On redfinit facilement les classes point, colored_point et picture. Il suffit d'ajouter dans leurs dclarations la ligne inherit printable () pour les doter par hritage de la mthode print.



# let p = new point (1,1) in p#print() ;;
( 1, 1)- : unit = ()
# let pc = new colored_point (2,2) "bleu" in pc#print() ;;
( 2, 2) de couleur bleu- : unit = ()
# let t = new picture 3 in t#add (new point (1,1)) ;
t#add (new point (3,2)) ;
t#add (new point (1,4)) ;
t#print() ;;
[ ( 1, 1) ( 3, 2) ( 1, 4)]- : unit = ()


La sous-classe rectangle suivante hrite de printable et dfinit la mthode to_string. Les variables d'instance llc et ruc dsignent respectivement le point en bas gauche et le point en haut droite du rectangle.

# class rectangle (p1,p2) =
object
inherit printable ()
val mutable llc = (p1 : point)
val mutable ruc = (p2 : point)
method to_string () = "[" ^ p1#to_string() ^ "," ^ p2#to_string() ^ "]"
end ;;
class rectangle :
point * point ->
object
val mutable llc : point
val mutable ruc : point
method print : unit -> unit
method to_string : unit -> string
end


La classe rectangle hrite de la classe abstraite printable, et donc rcupre la mthode print. Elle possde deux variables d'instance point : les coins infrieur gauche (llc) et suprieur droit (ruc). Sa mthode to_string envoie le message to_string ses variables d'instance point.

# let r = new rectangle (new point (2,3), new point (4,5));;
val r : rectangle = <obj>
# r#print();;
[( 2, 3),( 4, 5)]- : unit = ()


Classes, types et objets

Rappelons que le type d'un objet est le type de ses mthodes. Par exemple le type point, infr lors de la dclaration de la classe point, est une abrviation du type :
  point =
    < distance : unit -> float; get_x : int; get_y : int;
      moveto : int * int -> unit; rmoveto : int * int -> unit;
      to_string : unit -> string  >
C'est un type objet ferm. C'est--dire que la totalit des mthodes et des types associs qui le composent est dtermine ; il n'y en a pas d'autre. Le mcanisme d'infrence de types, lors d'une dclaration de classe, calcule le type ferm associ cette classe.

Types ouverts

L'envoi d'un message un objet fait partie intgrante du langage, on peut donc dfinir une fonction envoyant un message quelconque un objet dont le type est non prcis.

# let f x = x#get_x ;;
val f : < get_x : 'a; .. > -> 'a = <fun>


Le type infr pour l'argument de f est un type objet, puisque l'on envoie un message x, mais ce type objet est ouvert. Le paramtre x de la fonction f doit au moins possder une mthode get_x. Comme le rsultat de l'envoi de ce message n'est pas utilis dans la fonction f, son type est le plus gnral possible (c'est--dire une variable de type 'a). Le mcanisme d'infrence de types laisse donc la possibilit d'utiliser la fonction f avec n'importe quel objet possdant une mthode get_x. L'ouverture du type de x est signifie par les deux points (..) la fin du type < get_x : 'a; .. >

# f (new point(2,3)) ;;
- : int = 2
# f (new colored_point(2,3) "vert meraude") ;;
- : int = 2
# class c () =
object
method get_x = "J'ai une mthode get_x"
end ;;
class c : unit -> object method get_x : string end
# f (new c ()) ;;
- : string = "J'ai une m\233thode get_x"


L'infrence de types pour les classes peut engendrer des types ouverts, en particulier pour les valeurs initiales de construction des instances. L'exemple suivant construit une classe couple, dont les valeurs initiales a et b possdent une mthode to_string.

# class couple (a,b) =
object
val p0 = a
val p1 = b
method to_string() = p0#to_string() ^ p1#to_string()
method copy () = new couple (p0,p1)
end ;;
class couple :
(< to_string : unit -> string; .. > as 'a) *
(< to_string : unit -> string; .. > as 'b) ->
object
val p0 : 'a
val p1 : 'b
method copy : unit -> couple
method to_string : unit -> string
end
Les types de a et b sont deux types ouverts possdant la mthode to_string. On note que ces deux types sont considrs comme diffrents. Ils sont nots respectivement comme as 'a et as 'b. Les variables de types 'a et 'b sont contraintes par le type ouvert engendr.

On dsigne le type ouvert construit partir d'un type ferm obj_type en utilisant le symbole dise :

Syntaxe


#obj_type
Le type obtenu contient toutes les mthodes du type obj_type et se termine par deux points.

Contrainte de type

Nous avons montr au chapitre sur la programmation fonctionnelle (voir page ??) comment contraindre une expression possder un type plus prcis que celui engendr par l'infrence. Les types objet, ferms ou ouverts, peuvent tre utiliss pour effectuer de telles contraintes. On peut vouloir a priori ouvrir le type d'un objet dfini pour lui appliquer une mthode venir. On utilise alors une contrainte de type objet ouvert :

Syntaxe


(nom:#type)
Ce qui permet d'crire :

# let g (x : #point) = x#amess;;
val g :
< amess : 'a; distance : unit -> float; get_x : int; get_y : int;
moveto : int * int -> unit; print : unit -> unit;
rmoveto : int * int -> unit; to_string : unit -> string; .. > ->
'a = <fun>
La contrainte de type avec #point force x avoir au moins toutes les mthodes de point, et l'envoi du message amess ajoute une mthode au type du paramtre x.

Comme dans le reste du langage, l'extension objet d'Objective CAML offre un typage statique obtenu par infrence. Lorsque ce mcanisme ne dispose pas de l'information suffisante pour dterminer le type d'une expression, il lui assigne une variable de type. Nous venons de voir que ce phnomne est aussi valable pour le typage des objets. Il peut engendrer parfois des situations de blocage que l'utilisateur devra rsoudre en donnant explicitement des informations de type.

# class a_point p0 =
object
val p = p0
method to_string() = p#to_string()
end ;;
Characters 6-89:
Some type variables are unbound in this type:
class a_point :
(< to_string : unit -> 'b; .. > as 'a) ->
object val p : 'a method to_string : unit -> 'b end
The method to_string has type unit -> 'a where 'a is unbound


On sort de ce blocage en prcisant que le paramtre p0 est de type #point.

# class a_point (p0 : #point) =
object
val p = p0
method to_string() = p#to_string()
end ;;
class a_point :
(#point as 'a) -> object val p : 'a method to_string : unit -> string end


Pour viter de placer les contraintes de type plusieurs endroits dans la dclaration d'une classe, on utilise la syntaxe suivante :

Syntaxe


constraint type1 = type2
L'exemple prcdent peut s'crire en indiquant que le paramtre p0 est de type 'a, puis en posant une contrainte de type sur la variable 'a.

# class a_point (p0 : 'a) =
object
constraint 'a = #point
val p = p0
method to_string() = p#to_string()
end ;;
class a_point :
(#point as 'a) -> object val p : 'a method to_string : unit -> string end


Plusieurs contraintes de type peuvent tre poses dans une dclaration de classe.

Warning


Un type ouvert ne peut pas apparatre comme type d'une mthode.


Cette contrainte forte provient du fait qu'un type ouvert contient une variable de type non encore instancie provenant de la suite du type. Comme on ne peut avoir une variable de type libre dans une dclaration de type, une mthode contenant un tel type est rejete par l'infrence de types.

# class b_point p0 =
object
inherit a_point p0
method get = p
end ;;
Characters 6-77:
Some type variables are unbound in this type:
class b_point :
(#point as 'a) ->
object val p : 'a method get : 'a method to_string : unit -> string end
The method get has type #point where .. is unbound


Le type de get est en fait, cause de la contrainte constraint 'a = #point, le type ouvert #point. Celui-ci contient la variable de type libre dnote par le double point (..) ce qui n'est pas autoris.

Hritage et type de self

Il existe une exception pour l'apparition d'une variable de type dans le type des mthodes. Il s'agit du cas o une variable dsigne le type de l'objet lui-mme (self). Prenons l'exemple d'une mthode d'galit qui teste si un point est gal un autre.

# class point_eq (x,y) =
object (self : 'a)
inherit point (x,y)
method eq (p:'a) = (self#get_x = p#get_x) && (self#get_y = p#get_y)
end ;;
class point_eq :
int * int ->
object ('a)
val mutable x : int
val mutable y : int
method distance : unit -> float
method eq : 'a -> bool
method get_x : int
method get_y : int
method moveto : int * int -> unit
method print : unit -> unit
method rmoveto : int * int -> unit
method to_string : unit -> string
end
Le type de la mthode eq est 'a -> bool, mais la variable de type dsigne le type de l'instance lors de sa construction.

On peut hriter de la classe point_eq et redfinir la mthode eq dont le type est toujours paramtr par le type de l'instance.

# class colored_point_eq (xc,yc) c =
object (self : 'a)
inherit point_eq (xc,yc) as super
val c = (c:string)
method get_c = c
method eq (pc : 'a) = (self#get_x = pc#get_x) && (self#get_y = pc#get_y)
&& (self#get_c = pc#get_c)
end ;;
class colored_point_eq :
int * int ->
string ->
object ('a)
val c : string
val mutable x : int
val mutable y : int
method distance : unit -> float
method eq : 'a -> bool
method get_c : string
method get_x : int
method get_y : int
method moveto : int * int -> unit
method print : unit -> unit
method rmoveto : int * int -> unit
method to_string : unit -> string
end


La mthode eq, de la classe colored_point_eq, est toujours du type 'a -> bool, mais, cette fois, la variable 'a dsigne le type d'une instance de la classe colored_point_eq. La dfinition de eq de la classe colored_point_eq masque celle hrite. Les mthodes contenant le type de l'instance dans leur type sont appeles mthodes binaires. Elles creront des limitations la relation de sous-typage dcrite la page ??.

Hritage multiple

L'hritage multiple permet d'hriter des champs de donnes et des mthodes de plusieurs classes. En cas de noms de champs ou de mthodes identiques, seulement la dernire dclaration, dans l'ordre des dclarations d'hritages, sera conserve. Nanmoins, on peut rfrencer une mthode d'une des classes anctres en associant des noms diffrents chaque classe dont on hrite. Cela n'est pas valable pour les variables d'instance. Si une classe hrite masque une variable d'instance d'une classe prcdemment hrite, cette dernire variable n'est plus accessible directement. Les diffrentes classes hrites n'ont pas forcment de lien d'hritage entre elles. L'intrt de l'hritage multiple est d'augmenter la rutilisabilit des classes.

On dfinit la classe abstraite geometric_object qui dclare deux mthodes virtuelles compute_area et compute_circ pour le calcul de la surface et du primtre.

# class virtual geometric_object () =
object
method virtual compute_area : unit -> float
method virtual compute_circ : unit -> float
end;;


On redfinit alors la classe rectangle de la manire suivante :

# class rectangle_bis ((p1,p2) :'a) =
object
constraint 'a = point * point
inherit printable ()
inherit geometric_object ()
val mutable llc = p1
val mutable ruc = p2
method to_string () =
"["^p1#to_string()^","^p2#to_string()^"]"
method compute_area() =
float ( abs(ruc#get_x - llc#get_x) * abs(ruc#get_y - llc#get_y))
method compute_circ() =
float ( (abs(ruc#get_x - llc#get_x) + abs(ruc#get_y - llc#get_y)) * 2)
end;;
class rectangle_bis :
point * point ->
object
val mutable llc : point
val mutable ruc : point
method compute_area : unit -> float
method compute_circ : unit -> float
method print : unit -> unit
method to_string : unit -> string
end


Cette implantation de classes respecte le graphe d'hritage de la figure 15.5.



Figure 15.5 : hritage multiple


Pour ne pas avoir rcrire la mthode de la classe rectangle, on peut hriter directement de rectangle comme le montre le schma de la figure 15.6.



Figure 15.6 : hritage multiple (suite)


Dans ce cas l, seules les mthodes abstraites de la classe abstraite geometric_object doivent tre dfinies dans rectangle_ter.

# class rectangle_ter (p2 :'a) =
object
constraint 'a = point * point
inherit rectangle p2
inherit geometric_object ()
method compute_area() =
float ( abs(ruc#get_x - llc#get_x) * abs(ruc#get_y - llc#get_y))
method compute_circ() =
float ( (abs(ruc#get_x - llc#get_x) + abs(ruc#get_y - llc#get_y)) * 2)
end;;


Toujours dans la mme veine, les dveloppements de la hirarchie printable et geometric_object auraient pu tre spars jusqu'au moment o il devenait utile d'avoir une classe possdant les deux comportements. Le schma de la figure 15.7 montre les relations ainsi dfinies.



Figure 15.7 : hritage multiple (fin)


Si on suppose que les classes printable_rect et geometric_rect dfinissent des variables d'instance pour les coins d'un rectangle, on se retrouve dans la classe rectangle avec quatre points (deux par coin).

class rectangle_quarte (p1,p2) =
inherit printable_rect (p1,p2) as super_print
inherit geometric_rect (p1,p2) as super_geo
end;;
Dans le cas o des mthodes de mme type existent dans les deux classes ..._rect, alors seule la dernire est visible. Nanmoins, grce au nommage des classes anctres (super_...), il est toujours possible d'invoquer une mthode de l'une ou l'autre des anctres.

L'hritage multiple permet donc une factorisation du code en intgrant les mthodes dj crites de diffrentes classes anctres pour construire une nouvelle entit. Le prix payer est la taille des objets construits plus gros que ncessaire : duplication de champs, hritage de champs inutiles une application donne, etc... De plus, en cas de duplication, comme dans notre dernier exemple, il faut tablir manuellement la communication entre ces champs (mise jour, etc...). Dans le dernier exemple sur la classe rectangle, on obtient les variables d'instance de la classe rectangle_graph et rectangle_geo. Si l'une de ces classes possde une mthode qui modifie ces variables (comme un facteur d'chelle) alors il est ncessaire de rpercuter ces modifications aux variables hrites de l'autre classe. Cette trop lourde communication entre variables d'instance hrites souligne souvent une mauvaise modlisation du problme pos.

Classes paramtres

Les classes paramtres permettent d'utiliser le polymorphisme paramtrique d'Objective CAML dans les classes. Une dclaration de classe peut tre paramtre par des variables de type, comme dans une dclaration de types d'Objective CAML. Cela offre de nouvelles possibilits de gnricit, pour la rutilisation du code, et s'intgre dans le typage la ML quand l'infrence de types produit des types paramtrs.

La syntaxe diffre lgrement de la dclaration de types paramtrs, les paramtres de type sont entre crochets.

Syntaxe


class ['a, 'b, ...] nom = object ...end
Nanmoins le type Objective CAML est not comme l'habitude : ('a,'b,...) nom.

Par exemple, si l'on dsire construire une classe pair pour dcrire les paires, une solution nave consiste poser :

# class pair x0 y0 =
object
val x = x0
val y = y0
method fst = x
method snd = y
end ;;
Characters 6-106:
Some type variables are unbound in this type:
class pair :
'a ->
'b -> object val x : 'a val y : 'b method fst : 'a method snd : 'b end
The method fst has type 'a where 'a is unbound


On retrouve l'erreur de typage voque la dfinition de la classe a_point (page ??). Le message d'erreur indique que la variable de type 'a assigne au paramtre x0 (et donc x et fst) n'est pas lie.

Pour obtenir un typage correct, comme dans le cas des types paramtrs, il faut paramtrer la classe pair avec deux variables de type et forcer le type des paramtres de construction x0 et y0 :

# class ['a,'b] pair (x0:'a) (y0:'b) =
object
val x = x0
val y = y0
method fst = x
method snd = y
end ;;
class ['a, 'b] pair :
'a ->
'b -> object val x : 'a val y : 'b method fst : 'a method snd : 'b end
L'affichage de l'infrence de types indique une interface de classe paramtre par les variables de type 'a et 'b.

Quand on construit une valeur d'une classe paramtre, les paramtres de type sont instancis par les types des paramtres de construction :

# let p = new pair 2 'X';;
val p : (int, char) pair = <obj>
# p#fst;;
- : int = 2
# let q = new pair 3.12 true;;
val q : (float, bool) pair = <obj>
# q#snd;;
- : bool = true


Remarque


Dans les dclarations de classe, les paramtres de type sont nots entre crochets, alors qu'au niveau de l'affichage des types, ils se retrouvent entre parenthses.


Hritage de classes paramtres

L'hritage d'une classe paramtre doit indiquer les paramtres de cette classe. On dfinit une classe acc_pair, qui hrite de ('a,'b) pair et ajoute deux mthodes d'accs aux champs , get1 et get2,

# class ['a,'b] acc_pair (x0 : 'a) (y0 : 'b) =
object
inherit ['a,'b] pair x0 y0
method get1 z = if x = z then y else raise Not_found
method get2 z = if y = z then x else raise Not_found
end;;
class ['a, 'b] acc_pair :
'a ->
'b ->
object
val x : 'a
val y : 'b
method fst : 'a
method get1 : 'a -> 'b
method get2 : 'b -> 'a
method snd : 'b
end
# let p = new acc_pair 3 true;;
val p : (int, bool) acc_pair = <obj>
# p#get1 3;;
- : bool = true


On peut prciser les paramtres de type de la classe paramtre hrite, par exemple pour une paire de points.

# class pair_point (p1,p2) =
object
inherit [point,point] pair p1 p2
end;;
class pair_point :
point * point ->
object
val x : point
val y : point
method fst : point
method snd : point
end


La classe pair_point n'a plus besoin de paramtres de type, car les paramtres 'a et 'b sont compltement dtermins.

Pour construire des paires d'objets affichables, c'est--dire possdant une mthode print, on rutilise la classe abstraite printable (voir page ??), puis on dfinit la classe printable_pair qui hrite de pair.

# class printable_pair (x0 ) (y0 ) =
object
inherit [printable, printable] acc_pair x0 y0
method print () = x#print(); y#print ()
end;;


Cette implantation permet effectivement de construire des paires d'instances de printable, mais ne peut pas tre utilise pour des objets d'une autre classe et possdant une mthode print.

Un premier essai consiste ouvrir le type printable utilis comme paramtre de type de acc_pair :

# class printable_pair (x0 ) (y0 ) =
object
inherit [ #printable, #printable ] acc_pair x0 y0
method print () = x#print(); y#print ()
end;;
Characters 6-149:
Some type variables are unbound in this type:
class printable_pair :
(#printable as 'a) ->
(#printable as 'b) ->
object
val x : 'a
val y : 'b
method fst : 'a
method get1 : 'a -> 'b
method get2 : 'b -> 'a
method print : unit -> unit
method snd : 'b
end
The method fst has type #printable where .. is unbound
Cet essai choue car les mthodes fst et snd contiennent un type ouvert.

Nous allons donc conserver les paramtres de type de la classe, tout en les contraignant au type ouvert #printable.

# class ['a,'b] printable_pair (x0 ) (y0 ) =
object
constraint 'a = #printable
constraint 'b = #printable
inherit ['a,'b] acc_pair x0 y0
method print () = x#print(); y#print ()
end;;
class ['a, 'b] printable_pair :
'a ->
'b ->
object
constraint 'a = #printable
constraint 'b = #printable
val x : 'a
val y : 'b
method fst : 'a
method get1 : 'a -> 'b
method get2 : 'b -> 'a
method print : unit -> unit
method snd : 'b
end


On construit alors une paire affichable contenant un point et un point color.

# let pp = new printable_pair
(new point (1,2)) (new colored_point (3,4) "vert");;
val pp : (point, colored_point) printable_pair = <obj>
# pp#print();;
( 1, 2)( 3, 4) de couleur vert- : unit = ()


Classes paramtres et typage

Une classe paramtre est, du point de vue des types, un type paramtr. Une valeur d'un tel type peut alors contenir des variables de type faibles.

# let r = new pair [] [];;
val r : ('_a list, '_b list) pair = <obj>
# r#fst;;
- : '_a list = []
# r#fst = [1;2];;
- : bool = false
# r;;
- : (int list, '_a list) pair = <obj>


Une classe paramtre est aussi vue comme un type objet ferm, donc rien n'empche de l'utiliser aussi comme type ouvert avec la notation dise.

# let compare_rien ( x : ('a, 'a) #pair) =
if x#fst = x#fst then x#mess else x#mess2;;
val compare_rien : < fst : 'a; mess : 'b; mess2 : 'b; snd : 'a; .. > -> 'b =
<fun>


Ce qui nous amne pouvoir construire des types paramtrs contenant des variables de type faibles, tout en tant des types objet ouverts.

# let jolitype x ( y : ('a, 'a) #pair) = if x = y#fst then y else y;;
val jolitype : 'a -> (('a, 'a) #pair as 'b) -> 'b = <fun>


Si on applique cette fonction un seul paramtre, on obtient une fermeture dont les variables de type deviennent faibles. Un type ouvert, comme #pair, contient encore une partie instancier reprsente par les deux points (..). En cela un type ouvert est un paramtre de type dont une partie est dj connue. Lors de l'affaiblissement d'un tel type suite une application partielle, l'afficheur prcise que la variable de type reprsentant ce type ouvert est affaiblie. La notation est alors _#pair.

# let g = jolitype 3;;
val g : ((int, int) _#pair as 'a) -> 'a = <fun>


Si maintenant on applique la fonction g une paire, on modifie son type faible.

# g (new acc_pair 2 3);;
- : (int, int) acc_pair = <obj>
# g;;
- : (int, int) acc_pair -> (int, int) acc_pair = <fun>


On ne peut plus alors utiliser g sur des paires simples.

# g (new pair 1 1);;
Characters 4-16:
This expression has type (int, int) pair = < fst : int; snd : int >
but is here used with type
(int, int) acc_pair =
< fst : int; get1 : int -> int; get2 : int -> int; snd : int >
Only the second object type has a method get1


Enfin comme les paramtres de la classe paramtre peuvent eux aussi s'affaiblir, on obtient l'exemple suivant.

# let h = jolitype [];;
val h : (('_b list, '_b list) _#pair as 'a) -> 'a = <fun>
# let h2 = h (new pair [] [1;2]);;
val h2 : (int list, int list) pair = <obj>
# h;;
- : (int list, int list) pair -> (int list, int list) pair = <fun>


Le type du paramtre de h n'est plus ouvert. L'application suivante n'est pas typable car l'argument n'est pas de type pair.

# h (new acc_pair [] [4;5]);;
Characters 4-25:
This expression has type
('a list, int list) acc_pair =
< fst : 'a list; get1 : 'a list -> int list; get2 : int list -> 'a list;
snd : int list >
but is here used with type
(int list, int list) pair = < fst : int list; snd : int list >
Only the first object type has a method get1


Remarque


Les classes paramtres d'Objective CAML sont absolument ncessaires ds que l'on manipule des mthodes dont le type comporte une variable de type autre que le type de self.



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora041.html0000644000000000000000000014205107421273601014467 0ustar Exercices Prcdent Index Suivant

Exercices

Arbres binaires

On reprsente les arbres binaires sous forme de vecteurs. Soit un arbre a de hauteur h, la taille du vecteur sera 2(h+1)-1. Soit un noeud en position i, le sous-arbre gauche de ce noeud est dans l'intervalle d'indices [i+1 , i+1+2h] et son sous-arbre droit dans l'intervalle [i+1+2h+1 , 2(h+1)-1]. Cette reprsentation est intressante si l'arbre est presque compltement rempli. Les tiquettes de tels arbres doivent possder une valeur spciale indiquant que le noeud n'existe pas. On reprsentera donc des arbres tiquets par des valeurs de type 'a et par des vecteurs de type 'a array.

  1. crire une fonction qui prend en entre un arbre binaire du type 'a arbre_bin

    # let fill_array tree tab empty =
    let rec aux i p = function
    Empty -> tab.(i) <- empty
    | Node (l,e,r) ->
    tab.(i) <- e ;
    aux (i+1) (p/2) l ;
    aux (i+p) (p/2) r
    in aux 0 (((Array.length tab)+1)/2) tree ;;
    val fill_array : 'a arbre_bin -> 'a array -> 'a -> unit = <fun>

    # type 'a arbre_bin =
    Empty
    | Node of 'a arbre_bin * 'a * 'a arbre_bin ;;
    type 'a arbre_bin = | Empty | Node of 'a arbre_bin * 'a * 'a arbre_bin
    dfini la page ??, un tableau, que l'on supposera de taille suffisante, et qui stocke les tiquettes contenues dans l'arbre dans le tableau selon la discipline de placement dfinie ci-dessus.

  2. crire la fonction de cration d'une feuille (arbre de hauteur 0).

    # let leaf empty = [| empty |] ;;
    val leaf : 'a -> 'a array = <fun>


  3. crire une fonction qui construit un nouvel arbre partir d'une tiquette et de deux autres arbres.

    # let node elt left right =
    let ll = Array.length left and lr = Array.length right in
    let l = max ll lr in
    let res = Array.create (2*l+1) elt in
    Array.blit left 0 res 1 ll ;
    Array.blit right 0 res (ll+1) lr ;
    res ;;
    val node : 'a -> 'a array -> 'a array -> 'a array = <fun>


  4. crire une fonction de conversion du type 'a arbre_bin en un tableau.

    # let rec make_array empty = function
    Empty -> leaf empty
    | Node (l,e,r) -> node e (make_array empty l) (make_array empty r) ;;
    val make_array : 'a -> 'a arbre_bin -> 'a array = <fun>


  5. crire une fonction de parcours infixe de tels arbres.

    # let infixe tab empty f =
    let rec aux i p =
    if tab.(i)<>empty then ( aux (i+1) (p/2) ; f tab.(i) ; aux (i+p) (p/2) )
    in aux 0 (((Array.length tab)+1)/2) ;;
    val infixe : 'a array -> 'a -> ('a -> 'b) -> unit = <fun>


  6. L'utiliser pour l'affichage .

    # let print_tab_int tab empty =
    infixe tab empty (fun x -> print_int x ; print_string " - ") ;;
    val print_tab_int : int array -> int -> unit = <fun>


  7. Que dire du parcours prfixe de tels arbres? Le parcours prfice correspond au parcours simple du tableau.

    # let prefixe tab empty f =
    for i=0 to (Array.length tab)-1 do if tab.(i)<>empty then f tab.(i) done ;;
    val prefixe : 'a array -> 'a -> ('a -> unit) -> unit = <fun>

Correcteur orthographique

Cet exercice reprend les arbres lexicaux, exercice du chapitre 2, page ??, pour construire un correcteur orthographique.

  1. Construire un dictionnaire partir d'un fichier en ASCII o chaque ligne contient un mot. On crira pour cela une fonction qui prend un nom de fichier et retourne le dictionnaire correspondant.

    # type noeud_lex = Lettre of char * bool * arbre_lex
    and arbre_lex = noeud_lex list;;
    type noeud_lex = | Lettre of char * bool * arbre_lex
    type arbre_lex = noeud_lex list
    # type mot = string;;
    type mot = string
    # let rec existe m d =
    let aux sm i n =
    match d with
    [] -> false
    | (Lettre (c,b,l))::q ->
    if c = sm.[i] then
    if n = 1 then b
    else existe (String.sub sm (i+1) (n-1)) l
    else existe sm q
    in aux m 0 (String.length m);;
    val existe : string -> arbre_lex -> bool = <fun>

    # let rec ajoute m d =
    let aux sm i n =
    if n = 0 then d else
    match d with
    [] -> [Lettre (sm.[i], n = 1, ajoute (String.sub sm (i+1) (n-1)) [])]
    | (Lettre(c,b,l))::q ->
    if c = sm.[i] then
    if n = 1 then (Lettre(c,true,l))::q
    else Lettre(c,b,ajoute (String.sub sm (i+1) (n-1)) l)::q
    else (Lettre(c,b,l))::(ajoute sm q)
    in aux m 0 (String.length m);;
    val ajoute : string -> arbre_lex -> arbre_lex = <fun>

    # let rec verifie l d = match l with
    [] -> []
    | t::q -> if existe t d then t::(verifie q d)
    else verifie q d
    ;;
    val verifie : string list -> arbre_lex -> string list = <fun>

    # let string_of_char c = String.make 1 c;;
    val string_of_char : char -> string = <fun>

    # let rec filter p l = match l with
    [] -> []
    | t::q -> if p t then t::(filter p q)
    else filter p q;;
    val filter : ('a -> bool) -> 'a list -> 'a list = <fun>


    # let rec selecte n d =
    match d with
    [] -> []
    | (Lettre(c,b,l))::q ->
    if n = 1 then
    filter (function x -> x <> "!")
    (List.map (function (Lettre(c,b,_)) -> if b then string_of_char c else "!") d)
    else
    let r = selecte (n-1) l
    and r2 = selecte n q in
    let pr = List.map (function s -> (string_of_char c)^s) r
    in pr@r2;;
    val selecte : int -> arbre_lex -> string list = <fun>

    # let lire_fichier nom_fichier =
    let dico = ref []
    and canal = open_in nom_fichier in
    try
    while true do dico := ajoute (input_line canal) !dico done ;
    failwith "cas impossible"
    with
    End_of_file -> close_in canal ; !dico
    | x -> close_in canal ; raise x ;;
    val lire_fichier : string -> arbre_lex = <fun>


  2. crire une fonction mots qui prend une chane de caractres et construit la liste des mots de cette chane. Les sparateurs de mots sont l'espace, la tabulation, l'apostrophe et les guillemets.

    # let mots s =
    let est_sep = function ' '|'\t'|'\''|'"' -> true | _ -> false in
    let res = ref [] and p = ref ((String.length s)-1) in
    let n = ref !p in
    while !p>=0 && est_sep s.[!p] do decr p done ;
    n := !p ;
    while (!n>=0) do
    while !n>=0 && not (est_sep s.[!n]) do decr n done ;
    res := String.sub s ( !n +1) (!p - !n) :: !res ;
    while !n>=0 && est_sep s.[!n] do decr n done ;
    p := !n
    done ;
    !res ;;
    val mots : string -> string list = <fun>


  3. crire une fonction verifie qui prend un dictionnaire, une liste de mots et retourne la liste des mots qui n'apparaissent pas dans le dictionnaire.

    # let rec verifie dico = function
    [] -> []
    | m::l -> if existe m dico then verifie dico l else m::(verifie dico l) ;;
    val verifie : arbre_lex -> string list -> string list = <fun>


  4. crire une fonction occurrences qui prend une liste de mots et retourne une liste de couples associant chaque mot de la liste son nombre d'occurrences.

    # let rec ajoute x = function
    [] -> [(x,1)]
    | ((y,n) as p)::l -> if x=y then (y,n+1)::l else p::(ajoute x l) ;;
    val ajoute : 'a -> ('a * int) list -> ('a * int) list = <fun>

    # let rec ajoute_liste ld = function
    [] -> ld
    | n::l -> let res = ajoute_liste ld l in ajoute n res ;;
    val ajoute_liste : ('a * int) list -> 'a list -> ('a * int) list = <fun>

    # let occurences l = ajoute_liste [] l ;;
    val occurences : 'a list -> ('a * int) list = <fun>


  5. crire une fonction orthographe qui prend un dictionnaire, un nom de fichier contenant le texte analyser, et retourne la liste des mots incorrects avec leur nombre d'apparitions.

    # let orthographe dico nom =
    let f = open_in nom and res = ref [] in
    try
    while true do
    let s = input_line f in
    let ls = mots s in
    let lv = verifie dico ls in
    res := ajoute_liste !res lv
    done ;
    failwith "cas impossible"
    with
    End_of_file -> close_in f ; !res
    | x -> close_in f ; raise x ;;
    val orthographe : arbre_lex -> string -> (string * int) list = <fun>

Ensemble des nombres premiers

On cherche construire l'ensemble infini des nombres premiers sans le calculer compltement la manire des structures de donnes paresseuses.

  1. Dfinir le prdicat est_divisible qui prend un entier et une liste initiale de nombres premiers et qui dtermine si le nombre est divisible par un des entiers de la liste. On sait que si un nombre x possde un diviseur suprieur racine_carre(x) alors il en possde infrieur racine_carre(x). Nous nous contentons donc de ne tester que les lments de la liste qui sont infrieurs la racine carre de l'argument.

    # let rec est_divisible x = function
    [] -> false
    | n::l -> (x mod n)=0 || ( (n*n<=x) && (est_divisible x l)) ;;
    val est_divisible : int -> int list -> bool = <fun>


  2. Possdant une liste initiale de nombres premiers, crire la fonction suivant qui renvoie le plus petit nombre premier qui n'est pas dans cette liste. On calcule le dernier lment de la liste.

    # let rec dernier = function
    [] -> failwith "liste vide"
    | [x] -> x
    | _::l -> dernier l ;;
    val dernier : 'a list -> 'a = <fun>
    On cherche le premier nombre premier partir d'un certain entier en les testant de deux en deux.

    # let rec plus_petit_premier l n =
    if est_divisible n l then plus_petit_premier l (n+2) else n ;;
    val plus_petit_premier : int list -> int -> int = <fun>
    Et on assemble.

    # let suivant = function
    [] -> 2
    | [2] -> 3
    | l -> let pg = dernier l in plus_petit_premier l (pg+2) ;;
    val suivant : int list -> int = <fun>


  3. Dfinir la valeur ensprem reprsentant l'ensemble des nombres premiers en s'inspirant du type 'a enum donn page ??. Il sera utile pour cet ensemble de conserver les nombres entiers dj trouvs.

    # type 'a ens = {mutable i:'a ; f : 'a -> 'a } ;;
    type 'a ens = { mutable i: 'a; f: 'a -> 'a }
    # let next e = let x = e.i in e.i <- (e.f e.i) ; x ;;
    val next : 'a ens -> 'a = <fun>

    # let ensprem =
    let prec = ref [2] in
    let fonct _ = let n = suivant !prec in prec := !prec @ [n] ; n
    in { i = 2 ; f = fonct } ;;
    val ensprem : int ens = {i=2; f=<fun>}

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora022.html0000644000000000000000000000727607421273601014477 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

Le modle de calcul des langages fonctionnels est le l-calcul, qui a t invent par Alonzo Church en 1932. Le but de Church tait de dfinir une notion de calculabilit effective au moyen de la l-dfinissabilit. Plus tard, il apparut que la notion ainsi introduite tait quivalente aux notions de calculabilit au sens de Turing (machine de Turing) et de Gdel-Herbrand (fonctions rcursives). Cette concidence incite penser qu'il existe une notion de calculabilit universelle, indpendante des formalismes particuliers : c'est la thse de Church. Dans ce calcul, les deux seules constructions sont l'abstraction et l'application. Les structures de donnes (entiers, boolens, couples, ...) peuvent tre codes par des l-termes.

Les langages fonctionnels, dont le premier reprsentant a t Lisp, implantent ce modle et l'tendent principalement par des structures de donnes plus efficaces. Dans un souci d'efficacit, les premiers langages fonctionnels implantaient des modifications physiques de mmoire, ce qui entre autres foraient la stratgie d'valuation en valuation immdiate ou stricte. Dans cette stratgie, les arguments des fonctions sont valus avant d'tre passs la fonction. C'est en fait plus tard, pour d'autres langages comme Miranda, Haskell ou LML, que la stratgie d'valuation retarde (paresseuse ou par ncessit) a t implante pour les langages fonctionnels purs.

Le typage statique, avec infrence de types, a t promu par la famille ML au dbut des annes 80. La page

Lien


http://www.pps.jussieu.fr/~cousinea/Caml/caml_history.html
prsente un historique du langage ML. Son modle de calcul est alors le l-calcul typ, sous-ensemble du l-calcul. Il garantit qu'aucune erreur de typage n'arrivera pendant le droulement du programme. Nanmoins des programmes <<tout--fait corrects>> peuvent tre rejets par le systme de types de ML. Ces cas arrivent rarement et ces programmes peuvent toujours tre rcrits de manire conforme au systme de types.

Les deux langages fonctionnels les plus utiliss sont Lisp et ML, reprsentants des langages fonctionnels impurs. Pour approfondir l'approche fonctionnelle de la programmation, les livres [AS89] et [CM95] prsentent chacun un cours gnral d'informatique utilisant respectivement les langages Scheme (dialecte Lisp) et Caml-Light.




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora012.gif0000644000000000000000000000217107073350152014264 0ustar GIF89aj111bbbPPP@@@! ,j0I8ͻ dihl뾥$tmx|@$ Ȥr E#sJ2Ϩzkl`#dXlݚkǁi;AvH U}~>GWPcri"y Cdnf JZ9K fmdy`ĈM^vR#ˠS4֍e{Fסޯ?A4F[7=Ӓ\"_ARg͸^rSDĈUX"JBrDHx#(1dJ%Yf͛.bوϟ@ JѣH2PҧPJtkSjݪTI5\ÊMZ+cӪLtk-[)Mxu3d޿R{am^rqQsL9(V}V|͔٘;ۅ :tv,Z2NM:ًanާ#.oōǜ;y=˅Wz\꟭ŮZ垻7>h⬷A*ڲ?󓹲'@"lz>}TEzS)H} 8!yfO=_VO,Eb&^J+.T@b`5wT&(? %Wן!&՗~Xkީe"qUdsu5y}woj+5(rhttFYiԡuxx)Jzej`غ*j!G "{,*Z"쳧紸^ӶC+y2zU5k^kҕogNnL@pRo60xC%gE ,Kȑl<&@$d1S}\/' 'q/#3'P31#4\PCl2DLu$Ԓ(M4Y\5kC5ب 2v9?C3G0/4,2ɐ4ṁ&G"s*F*gNMjaC礛襧> ݶ믃 n;ocaml-book-1.0/fr/html/book-ora124.html0000644000000000000000000000602107421273602014466 0ustar Introduction Prcdent Index Suivant

Introduction

Les applications de ce chapitre cherchent illustrer les nombreux concepts de programmation prsents dans cette partie.

La premire application est la construction d'une bibliothque de composants graphiques, appele Upi (Une Petite Interface). On l'appliquera la ralisation d'un convertisseur Francs/Euros. Les composants dcrits ici ragissent aux interactions d'un utilisateur en dclenchant des fonctions de traitement. Bien que la difficult algorithmique de cette application reste simple, son intrt est de montrer la communication entre composants grce aux fermetures. En effet les diffrentes fonctions de traitement partagent certaines valeurs dans leur environnement. Il est ncessaire de connatre la bibliothque de base Graphics pour apprcier la construction de la bibliothque Upi (voir chapitre 5, page ??).

La deuxime application est une recherche du chemin de moindre cot dans un graphe orient. Elle suit l'algorithme de Dijkstra qui calcule tous les chemins de moindre cot d'un sommet source vers tous les autres sommets atteignables partir de cette source. Pour acclrer les recherches demandes, on implante un mcanisme de cache qui conserve les recherches dj effectues en utilisant un tableau de pointeurs faibles (voir page ??). Le GC peut librer des lments de ce tableau, mais ils sont toujours recalculables en cas de besoin. La visualisation d'un graphe utilise les boutons simples de la bibliothque Upi pour la slection des sommets origine et destination du parcours demand. On compare l'efficacit d'excution entre les versions avec et sans cache. Pour faciliter les mesures de temps de ces diffrentes versions, on passe en argument de l'application le fichier de description du graphe ainsi que l'origine et la destination du parcours recherch. On dotera le programme de recherche d'<< une petite interface graphique >>.




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora066.html0000644000000000000000000002447307421273601014505 0ustar tapes de compilation Prcdent Index Suivant

tapes de compilation

L'obtention d'un excutable passe par les tapes de traduction et d'assemblage dcrites par la figure 7.1.


  Programme source  
prtraite  
  Programme source  
compile  
  Programme assembleur  
assemble  
  Instructions machine  
relie  
  Code excutable  

Figure 7.1 : Schma pour la production d'un excutable


Le prtraitement remplace des parties de texte par d'autres textes grce un systme de macros. La compilation traduit le programme source en instructions assembleur. L'assemblage effectue la traduction vers des instructions machines. Enfin l'dition de liens permet d'ajouter la bibliothque d'excution, contenant principalement la gestion mmoire, et de faire la jonction avec le systme d'exploitation pour les primitives.



Les compilateurs d'Objective CAML

Nous donnons figure 7.2 le dtail des phases de gnration de code des compilateurs d'Objective CAML. On appelle langage intermdiaire (LI) la reprsentation interne au compilateur de la suite d'instructions engendrer.


  Suite de caractres  
analyse lexicale  
  Suite de lexmes  
analyse syntaxique  
  Arbre de syntaxe  
analyse smantique  
  Arbre de syntaxe dcor  
gnration de code intermdiaire  
  Suite d'instructions du LI  
optimisation du code intermdiaire  
  Suite d'instructions du LI  
gnration du code cible  
  Programme assembleur  

Figure 7.2 : tapes d'un compilateur


L'analyse lexicale transforme la suite de caractres en une suite d'units lexicales (lexmes). Les lexmes correspondent principalement aux entiers, nombres flottants, caractres, chanes de caractres et identificateurs. Le message Illegal character provient de cette analyse.

L'analyse syntaxique construit un arbre de syntaxe en vrifiant que la suite de lexmes est correcte par rapport la grammaire du langage. Les messages Syntax error indiquent que la phrase analyse ne respecte pas la grammaire du langage.

L'analyse smantique, qui parcourt l'arbre de syntaxe, s'intresse un autre aspect de la correction du programme. En Objective CAML cette analyse consiste principalement en l'infrence de types qui, si elle russit, produit le type le plus gnral d'une expression ou d'une dclaration. Les messages d'erreurs de typage proviennent de cette passe. De mme les indications qu'un membre d'une squence n'est pas du type unit sont dtermines ce moment l. D'autres indications sont donnes par cette phase, comme l'analyse des filtrages (filtrages non exhaustifs, partie de filtrage non utilise).

La gnration et l'optimisation du code intermdiaire n'engendrent pas de message d'erreur ou d'avertissement. Ces phases de manipulation d'une structure intermdiaire permettent de factoriser le dveloppement des diffrents compilateurs d'Objective CAML.

La gnration du programme cible est l'tape finale du processus de compilation. Elle diffre selon les compilateurs.

Description du compilateur de code-octet

La machine virtuelle d'Objective CAML s'appelle la Zinc (pour << Zinc Is Not Caml >>). Elle est due Xavier Leroy et est dcrite dans ([Ler90]). Son nom a t choisi pour marquer sa diffrence d'avec les premires implantations du langage Caml reposant sur la machine virtuelle CAM (pour Categorical Abstract Machine, voir [CCM87]).

Nous donnons figure 7.3 la description des trois lments permettant l'utilisation du compilateur de code-octet. La premire partie de cette figure comprend l'interprte de la machine Zinc li la bibliothque d'excution. La deuxime partie correspond au compilateur Objective CAML qui produit du code-octet pour la machine Zinc. La troisime partie contient l'ensemble des bibliothques venant avec le compilateur. Elles seront dcrites au chapitre 8.



Figure 7.3 : Machine virtuelle


La notation graphique utilise pour dcrire les composants de la figure 7.3 et leur agencement est standard dans le monde de la compilation. Une bote simple reprsente un fichier crit dans le langage indiqu dans la bote. Une bote double reprsente l'interprtation d'un langage par un programme crit dans un autre. Une bote triple indique qu'un langage source est compil vers un langage cible en utilisant un compilateur crit dans un troisime langage. La figure 7.4 donne la lgende de chacune des botes.




Figure 7.4 : Notation graphique pour les interprtes et les compilateurs


La lgende de la figure 7.3 est la suivante :
  • BC : code-octet de la Zinc ;
  • C : code C ;
  • .o : code objet, dpendant de l'architecture ;
  •  : micro-processeur ;
  • OC (v1 ou v2) : code Objective CAML.

Remarque


Le compilateur Objective CAML est crit pour sa plus grande partie en Objective CAML. La deuxime partie de la figure 7.3 montre comment passer d'une version v1 du compilateur vers une version v2.



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora210.html0000644000000000000000000001213707421273603014470 0ustar Option -rectypes Prcdent Index Suivant

Option -rectypes

Il existe une option du compilateur et de la boucle d'interaction qui permet de lever cette limitation aux objets des types cycliques.
$ ocamlc -rectypes  ...
$ ocamlopt -rectypes  ...
$ ocaml -rectypes 
Si nous reprenons les exemples prcdents dans une boucle d'interaction lance avec cette option, voici ce qu'on obtient.

# type 'a arbre = 'a * 'a arbre list ;;
type 'a arbre = 'a * 'a arbre list

# let rec hauteur = function
(_,[]) -> 1
| (_,fils) -> 1 + (max_list (List.map hauteur fils)) ;;
val hauteur : ('b * 'a list as 'a) -> int = <fun>


Les valeurs arbre_1, arbre_2 et arbre_3 dfinies auparavant n'ont pas le mme type mais ont toutes un type compatible avec celui de hauteur.

# hauteur arbre_1 ;;
- : int = 1
# hauteur arbre_2 ;;
- : int = 2
# hauteur arbre_3 ;;
- : int = 3


Le mot cl as fait partie du langage de type, ce titre il peut tre utilis dans une dclaration de type.

Syntaxe


type nom = typedef as 'var ;;


Nous pouvons dfinir le type arbre en utilisant cette syntaxe.

# type 'a arbre = ( 'a * 'sommet list ) as 'sommet ;;
type 'a arbre = 'a * 'a arbre list


Warning


Si ce mode peut se rvler utile en quelques occasions, il a tendance accepter le typage de trop de valeurs en leur donnant des types difficiles lire.


Sans l'option -rectypes, la fonction suivante aurait t rejete par le typeur.

# let inclus l1 l2 =
let rec mem x = function
[] -> false
| a::l -> (l=x) || (mem x a) (* erreur volontaire : inversion de a et l *)
in List.for_all (fun x -> mem x l2) l1 ;;
val inclus : ('a list as 'a) list list -> ('b list as 'b) -> bool = <fun>
Bien que l'examen du type permet de constater immdiatement qu'il y a une erreur, il n'y a plus de message d'erreur permettant de localiser cette erreur.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora117.html0000644000000000000000000006734107421273602014504 0ustar Crer et modifier des valeurs Objective CAML en C Prcdent Index Suivant

Crer et modifier des valeurs Objective CAML en C

L'appel d'une fonction C partir d'Objective CAML peut modifier ses arguments ou retourner une nouvelle valeur. Cette valeur doit correspondre un type Objective CAML. Pour les types simples, plusieurs macros C sont prdfinies pour faciliter les conversions. Pour une valeur d'un type structur, elle doit tre alloue de la bonne taille et ses champs remplis par des valeurs du bon type. Il est en effet facile de faire n'importe quoi en C, ce qui risque de provoquer un arrt de l'excution du programme Objective CAML appelant ce morceau de C.

Il est important que les nouvelles valeurs Objective CAML cres en C soient connues du GC d'Objective CAML. Ces valeurs, le temps de leur porte, doivent tre considres comme de nouvelles racines pour le GC. Pour cela plusieurs macros sont dfinies pour ajouter des racines conserver par le GC.

De plus, il est possible d'allouer partir de C des valeurs Objective CAML mais aussi C, dans le tas d'Objective CAML. Cela permet de profiter du mcanisme de rcupration automatique de mmoire d'Objective CAML dans les programmes C. Pour des valeurs dont le type n'est connu qu'en C, il est ncessaire d'indiquer au GC leur fonction de finalisation pour la libration d'un bloc de ce type.

Modification des valeurs Objective CAML

Valeurs immdiates

Les macros suivantes permettent de crer des valeurs immdiates de type value partir de valeur C ainsi que d'en modifier:


   
Val_long(l) partir d'un long int
Val_int(i) partir d'un int
Val_bool(x) partir d'un int (false si x=0, true sinon)
Val_true  
Val_false  
Val_unit  
   
Store_field(b,n,v) met la valeur v dans le champ n d'un bloc b
Store_double_field(b,n,d) idem pour un tableau de flottants

Figure 12.10 : Cration et modification de valeurs immdiates


Il est en outre possible d'utiliser l'une des macros Field, Byte et Byte_u pour affecter les champs d'un bloc : Store_field(b,n,v) Field(b,n) = v.

Voici un exemple pour inverser une chane de caractres :

#include <caml/mlvalues.h>
value swap_char(value v, int i, int j)
{ char c=Byte(v,i); Byte(v,i)=Byte(v,j); Byte(v,j)=c; }
value swap_string (value v)
{
int i,j,t = string_length(v) ;
for (i=0,j=t-1; i<t/2; i++,j--) swap_char(v,i,j) ;
return v ;
}

# external miroir : string -> string = "swap_string" ;;
external miroir : string -> string = "swap_string"
# miroir "abcdefg" ;;
- : string = "gfedcba"


Allocation de nouveaux blocs

Il est important d'utiliser les macros suivantes pour allouer des valeurs dans le tas d'Objective CAML, sans cela, les blocs n'auraient pas les informations ncessaires au GC pour les manipuler. La figure 12.11 liste les diffrentes fonctions d'allocation de blocs dans le tas Objective CAML.

   
alloc(n, t) retourne un nouveau bloc de taille n et de Tag t
alloc_tuple(n) idem mais avec le Tag 0
alloc_string(n) retourne une chane (non initialise) de longueur n
copy_string(s) retourne un bloc partir d'une chane C
copy_double(d) retourne un bloc partir d'un double
alloc_array(f, a) retourne un bloc tableau partir d'une fonction f
  de conversion et d'un tableau de pointeurs
copy_string_array(p) retourne un tableau de chane partir d'un
  (char**), le tableau p doit se finir par un
  pointeur nul

Figure 12.11 : Fonctions d'allocation de blocs


La macro alloc_array prend un tableau de pointeurs (a) se terminant par le pointeur nul et une fonction f qui prend un pointeur et rend une valeur. Elle rend un tableau de valeur obtenu en appliquant f chacun des pointeurs de a. La fonction make_str_array de l'exemple ci-dessous utilise la macro alloc_array pour crer des copies de chacune des chanes d'un tableau :

#include <caml/mlvalues.h>
value make_str (char *s) { return copy_string(s); }
value make_str_array (char **p) { return alloc_array(make_str,p) ; }
Il est aussi possible de dfinir des blocs de taille 0, c'est par exemple le cas d'un tableau Objective CAML vide. Un tel bloc est appel un atome.

# inspecte [| |] ;;
....bloc : taille=0 - tableau de valeurs (Tag=0) :
- : '_a array = [||]
Il est possible de l'invoquer en C par la macro Atom(t) o t est le tag du bloc cr. En fait, de tels blocs sont constants et ne sont pas allous dans la partie dynamique du tas Objective CAML.

Des valeurs C dans le tas Objective CAML

On peut souhaiter conserver dans le tas Objective CAML des valeurs qui ne proviennent pas de ce langage et qui ne sont donc pas soumis aux contraintes imposes par Objective CAML pour le GC. Dans ce cas, on utilisera les blocs abstraits.

L'exemple le plus naturel est de manipuler avec Objective CAML des entiers C utilisant 32 (ou 64) bits. Une telle valeur va tre conserve dans un bloc de taille 1 (la taille d'une valeur est celle d'un entier long) de tag Abstract_tag.

#include <caml/mlvalues.h>
#include <stdio.h>

value Cint_of_OCAMLint (value v)
{
value res = alloc(1,Abstract_tag) ;
Field(res,0) = Long_val(v) ;
return res ;
}

value OCAMLint_of_Cint (value v) { return Val_long(Field(v,0)) ; }

value Cplus (value v1,value v2)
{
value res = alloc(1,Abstract_tag) ;
Field(res,0) = Field(v1,0) + Field(v2,0) ;
return res ;
}

value printCint (value v)
{
printf ("%d",(long) Field(v,0)) ; fflush(stdout) ;
return Val_unit ;
}

# type cint
external cint_of_int : int -> cint = "Cint_of_OCAMLint"
external int_of_cint : cint -> int = "OCAMLint_of_Cint"
external plus_cint : cint -> cint -> cint = "Cplus"
external print_cint : cint -> unit = "printCint" ;;


Nous pouvons maintenant utiliser des entiers sans << bit de tags >>, donc dans le format courant en C tout en conservant le GC d'Objective CAML. Par contre, ce ne sont plus des valeurs immdiates ce qui sera plus coteux pour les oprations arithmtiques simples.

# let a = 1000000000 ;;
val a : int = 1000000000
# a+a ;;
- : int = -147483648
# let c = let b = cint_of_int a in plus_cint b b ;;
val c : cint = <abstr>
# print_cint c ; print_newline () ;;
2000000000
- : unit = ()
# int_of_cint c ;;
- : int = -147483648


Fonction de finalisation

Il est aussi possible de stocker dans un bloc abstrait des pointeurs vers des blocs allous hors du tas Objective CAML. Nous savons qu'une valeur Objective CAML qui n'est plus utilise par le programme est susceptible d'tre rcupre par le GC. Mais que devient alors un bloc du tas C rfrenc par une valeur qui a disparue ? Pour viter cette fuite de mmoire, nous pouvons associer une fonction dite de finalisation qui est excute par le GC avant de rcuprer le bloc abstrait.

Pour ce faire nous utiliserons un bloc marqu par le tag Final_tag. Un tel bloc contient en plus des valeurs souhaites, une fonction prenant comme argument le bloc lui-mme et qui est excute avant la rcupration de la valeur.

L'allocation de tels blocs se fait en utilisant alloc_final (n, f, used, max)  :
  • n est la taille du bloc. Attention, la premire case du bloc est utilise pour stocker la fonction de finalisation.
  • f est la fonction de finalisation : void f (value);
  • used : place mmoire (hors du tas Objective CAML) suppose utilise par la valeur.
  • max : place mmoire maximale que l'on accepte de ne pas rcuprer systmatiquement.
Pour tre plus efficace, le GC ne rcupre pas un bloc ds qu'il devient inutilis. Le rapport used/max permet de spcifier le nombre de blocs abstraits que le GC peut laisser allous alors qu'ils ne sont plus utilisables.

Le programme suivant manipule des tableaux d'entiers C allous dans le tas C par appel malloc. Pour que ces tableaux puissent tre bien rcuprs lors d'un GC d'Objective CAML, la fonction create cre un bloc contenant le pointeur vers le tableau d'entiers (type IntTab) et la fonction de finalisation finalize_it.

#include <malloc.h>
#include <stdio.h>
#include <caml/mlvalues.h>

typedef struct {
int size ;
long * tab ; } IntTab ;

IntTab *alloc_it (int s)
{
IntTab *res = malloc(sizeof(IntTab)) ;
res->size = s ;
res->tab = (long *) malloc(sizeof(long)*s) ;
return res ;
}
void free_it (IntTab *p) { free(p->tab) ; free(p) ; }
void put_it (int n,long q,IntTab *p) { p->tab[n] = q ; }
long get_it (int n,IntTab *p) { return p->tab[n]; }

void finalize_it (value v)
{
IntTab *p = (IntTab *) Field(v,1) ;
int i;
printf("recuperation d'un IntTab par finalisation [") ;
for (i=0;i<p->size;i++) printf("%d ",p->tab[i]) ;
printf("]\n"); fflush(stdout) ;
free_it ((IntTab *) Field(v,1)) ;
}
value create (value s)
{
value bloc ;
bloc = alloc_final (2, finalize_it,Int_val(s)*sizeof(IntTab),1000) ;
Field(bloc,1) = (value) alloc_it(Int_val(s)) ;
return bloc ;
}
value put (value n,value q,value t)
{
put_it (Int_val(n), Long_val(q), (IntTab *) Field(t,1)) ;
return Val_unit ;
}
value get (value n,value t)
{
long res = get_it (Int_val(n), (IntTab *) Field(t,1)) ;
return Val_long(res) ;
}
Les fonctions visibles partir d'Objective CAML seront : create, put et get.

# type c_int_array
external cia_create : int -> c_int_array = "create"
external cia_get : int -> c_int_array -> int = "get"
external cia_put : int-> int -> c_int_array -> unit = "put" ;;


On peut dsormais manipuler notre nouvelle structure de donnes :

# let tab = cia_create 10 and tab2 = cia_create 10
in for i=0 to 9 do cia_put i (i*2) tab done ;
for i=0 to 9 do print_int (cia_get i tab) ; print_string " " done ;
print_newline () ;
for i=0 to 9 do cia_put (9-i) (cia_get i tab) tab2 done ;
for i=0 to 9 do print_int (cia_get i tab2) ; print_string " " done ;;
0 2 4 6 8 10 12 14 16 18
18 16 14 12 10 8 6 4 2 0 - : unit = ()


On force un GC pour provoquer l'appel la fonction de finalisation :

# Gc.full_major () ;;
recuperation d'un IntTab par finalisation [18 16 14 12 10 8 6 4 2 0 ]
recuperation d'un IntTab par finalisation [0 2 4 6 8 10 12 14 16 18 ]
- : unit = ()
La fonction de finalisation peut aussi tre utilise pour fermer un fichier, terminer un processus, etc.

Paramtres, variables locales des fonctions C et GC

Il est possible d'invoquer le GC depuis une fonction C, les primitives pour le faire sont dfinies dans le fichier memory.h. La fonction void Garbage_collection_function () est la primitive C d'invocation du GC. Selon que le programme est compil en code-octet ou en code natif le GC mineur ou majeur est invoqu.

Examinons l'exemple suivant et voyons quelle erreur a pu se produire.

#include <caml/mlvalues.h>
#include <caml/memory.h>

value identite (value x)
{
Garbage_collection_function() ;
return x;
}

# external id : 'a -> 'a = "identite" ;;
external id : 'a -> 'a = "identite"
# id [1;2;3;4;5] ;;
- : int list = [539709628; 539709622; 539709616; 539709610; 539709604]
La liste passe en paramtre de id, donc de la fonction C identite, est susceptible d'tre dplace ou rcupre par le GC. Nous avons, dans notre exemple, forc l'appel au GC, mais toute allocation d'un bloc aurait pu en faire tout autant. La liste, anonyme, passe en argument id a t modifie par le passage du GC car elle n'est pas connue dans l'ensemble des racines. Pour viter cel, il faut utiliser une dclaration spcifiques des paramtres et des variables locales, qui sont des value, utiliss par les primitives C pour qu'ils soient correctement traits par le GC en utilisant les macro suivantes :
CAMLparam1(v) : pour une valeur,
CAMLparam2(v1,v2) : pour deux valeurs,
...   ...
CAMLparam5(v1,...,v5) : pour cinq valeurs,
CAMLparam0 ; : obligatoire quand il n'y a pas de valeurs.
Si il y a plus de cinq paramtres de type value, les cinq premiers sont dclars avec la macro CAMLparam5 et les suivants en utilisant le nombre de fois ncessaires les macros CAMLxparam1, ..., CAMLxparam5.

CAMLparam5(v1,...,v5);
CAMLxparam5(v6,...,v10);
CAMLxparam2(v11,v12); : pour douze valeurs

Les variables locales de type value doivent tre elles-aussi dclares auprs du GC en utilisant les macros CAMLlocal1, ..., CAMLlocal5. La dclaration d'un tableau de valeurs se fait avec la macro CAMLlocalN(tab,n) o n est la taille du tableau tab. Enfin, si la primitive retourne une value, nous utiliserons la macro CAMLreturn.

Voici donc la version correcte de l'exemple prcdent :

#include <caml/mlvalues.h>
#include <caml/memory.h>
value identite2 (value x)
{
CAMLparam1(x) ;
Garbage_collection_function() ;
CAMLreturn x;
}

# external id : 'a -> 'a = "identite2" ;;
external id : 'a -> 'a = "identite2"
# let a = id [1;2;3;4;5] ;;
val a : int list = [1; 2; 3; 4; 5]
Cette fois nous obtenons le rsultat attendu.

Appel d'une fermeture Objective CAML depuis C

Pour, dans une fonction C, appliquer une fermeture (i.e. une valeur fonctionnelle Objective CAML) un ou plusieurs arguments, nous utiliserons les macros dfinies dans le fichier callback.h.
callback(f,v) : une fermeture f applique un argument v,
callback2(f,v1,v2) : deux arguments,
callback3(f,v1,v2,v3) : trois arguments,
callbackN(f,n,tab) : n arguments stocks dans le tableau tab.
Toutes ces macros retournent une value rsultat de l'application.

Dclarations de fonctions Objective CAML

L'utilisation des macros callback ncessite d'avoir la fonction appliquer sous la forme d'une fermeture, c'est--dire d'une valeur qu'il aura fallu passer en argument de la primitive. Il est aussi possible d'enregistrer une fermeture depuis un programme Objective CAML en lui associant un nom et de la rcuprer dans un programme C grce son nom.

La fonction register du module Callback associe un nom (string) une fermeture (de type quelconque donc 'a). Cette fermeture peut-tre retrouve depuis C grce la fonction C caml_named_value qui prend une chane de caractres et rend un pointeur vers la fermeture associe au nom, si elle existe, ou le pointeur nul, sinon.

Voyons cela sur un petit exemple :

# let plus x y = x + y ;;
val plus : int -> int -> int = <fun>
# Callback.register "plus3_ocaml" (plus 3);;
- : unit = ()
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/callback.h>

value plus3_C (value v)
{
CAMLparam1(v);
CAMLlocal1(f);
f = * caml_named_value("plus3_ocaml") ;
CAMLreturn callback(f,v) ;
}

# external plusC : int -> int = "plus3_C" ;;
external plusC : int -> int = "plus3_C"
# plusC 1 ;;
- : int = 4
# Callback.register "plus3_ocaml" (plus 5);;
- : unit = ()
# plusC 1 ;;
- : int = 6
Il n'y a pas exactement correspondance entre la dclaration d'une primitive C par external et l'enregistrement d'une fermeture Objective CAML par la fonction register. Dans le premier cas, il s'agit d'une dclaration statique, le lien entre les deux noms se fait l'dition de lien. Alors que dans l'autre cas, il s'agit d'une liaison dynamique qui intervient durant l'excution. En particulier, rien n'interdit de remplacer la liaison nom--fermeture en remplaant la fermeture et en modifiant ainsi le comportement des fonctions C utilisant ce nom.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora083.gif0000644000000000000000000000370107073350152014274 0ustar GIF89aUUUrrr!,X0I8ͻdi[p,+=|mXVT)hNˤ\E8Xla&jK_\o\imlST8zv8I8B`f:p.pDiN:OcXxtsxTZO ZInUdEBG+ fde44/#%k/Ch ~p(BĊ(blpqcȤ}ae$9&큩,︼R4q>{)4'-p"ʍ-@fHDK%lRA")(dNBSUfwQblGPFoMC"q8&askqO"r xʻY.|x+gqR {r-2c_Q`LV Vef(kp[}hˍ7ļ>/vËONwѧW;FK>Co?>zɵGs 0݁`7jS6V!vnaFH胇T/S&=8ͅJLxȃHv ،XWI1 h`![QƉN|[p`$3cS1g+kz'o.g's&R"w֑{z'rh6"}Zd$[,jYNvP@XmIq@ARSK U:jiPu%V*CaCs~1V &MQfk XɢMjF&U稡s07FK0  cx;ګzopzP0;.+ ;l` KxO! ,$l(3{,e25یsCA3}팡AB[xQt,=# Hܴ85SfZot~ 6䎍fShڄ ʶer#v\wmu~O xGJznW{ĸ⋟o)" 0"n8ڪ2:4%#j Yf[p &Pژ l#Jiy,|b?DC2!jA%DZ D+Vq;U&%brjx}%7#V?xa^B/XIT*5zم+ܑ0C 3ȉs Xb2 Kg×D*YzKN:XE{jZEH1f0%]h&0yLROaQ]KN@MEsV$x `+{!gVW0% m5,1kˏM$DnD9IJR% xbINnQ#N9 Ds5iNy2-Ȫ6‹)3z+:pXNrfg斦Vx ck>y՛%р)ЕGЄQHJJpn> "DXsr̃-q=.UyOA߮Ph8bܠ' &i|7ÉC&uD㇕&pxCDnF(-3ܧ(^ W\10E 6qm`ͮ TvA7TśjD#.knJj0AZgL8=(MT5MY(NIL$;ocaml-book-1.0/fr/html/book-ora113.html0000644000000000000000000001021307421273602014462 0ustar Introduction Prcdent Index Suivant

Introduction

Le dveloppement d'applications dans un langage donn ncessite trs souvent de devoir intgrer des bibliothques provenant d'autres langages. Cela pour deux raisons principales :
  • utiliser des bibliothques impossibles crire dans ce langage, c'est--dire tendre les fonctionnalits ;
  • utiliser des bibliothques performantes mises en oeuvre dans d'autres langages.
Une application devient alors un assemblage de briques logicielles provenant de diffrents langages, o chaque brique spcialise a t crite dans le langage adquat pour traiter une partie du problme. Celles-ci interoprent entre elles pour la communication de valeurs et la demande de calculs.

Le langage Objective CAML possde un tel mcanisme d'interoprabilit avec le langage C. Il permet d'appeler partir d'Objective CAML des fonctions C, avec arguments, et de rcuprer en Objective CAML le rsultat du calcul. De mme l'inverse est possible. Un programme C peut dclencher un calcul en Objective CAML et travailler ensuite sur le rsultat.

Le choix du langage C apparat judicieux pour les raisons suivantes :
  • c'est un langage normalis (C ANSI) ;
  • il est utilis pour implanter les systmes d'exploitation (Unix, Windows, MacOS, etc. ) ;
  • de trs nombreuses bibliothques sont crites en C ;
  • la plupart des langages possdent une interface avec C, ainsi il sera possible de s'interfacer avec cet autre langage en passant par C.
On peut donc voir le langage C comme un espranto des langages de programmation.

La coopration entre C et Objective CAML soulve un certain nombre de difficults que nous passons en revue ci-dessous.
  • reprsentation mmoire des valeurs
    Par exemple les valeurs de base (int, char, float) des deux langages n'ont pas la mme reprsentation mmoire. Il sera ncessaire de les convertir dans les deux sens. Il en est de mme pour les valeurs structures comme les enregistrements, les types sommes ou les tableaux.

  • GC d'Objective CAML
    Bien qu'il soit possible d'implanter un tel mcanisme en C, celui-ci n'en possde pas dans la dfinition de sa norme. Il ne faut donc pas que l'appel d'une fonction C fasse des modifications mmoire incompatibles avec le GC d'Objective CAML.
  • ruptures de calcul
    Les deux langages ne possdent pas le mme mcanisme d'exceptions, ce qui introduit un problme pour le rcuprateur d'exceptions d'Objective CAML.
  • partage de ressources communes
    Comme par exemple les tampons d'entres-sorties qui ne sont pas partags et ne rendent donc pas compte de la squentialit des entres-sorties des deux mondes.
Les applications dveloppes en Objective CAML bnficient des qualits dues la sret du typage et de la gestion automatique de la mmoire. Une bonne utilisation de bibliothques C ou de l'interfaage via C avec d'autres langages ne doit pas mettre en danger cette sret. Il faudra donc suivre des rgles assez strictes pour que les deux langages cohabitent harmonieusement.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora110.html0000644000000000000000000000336107421273602014465 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a dcrit les diffrents outils d'analyses lexicales et syntaxiques d'Objective CAML. On peut citer dans l'ordre d'apparition :
  • le module Str pour filtrer des expressions rationnelles;
  • le module Genlex pour construire facilement des analyseurs lexicaux simples;
  • l'outil ocamllex, intgration type de l'outil lex;
  • l'outil ocamlyacc, intgration type de l'outil yacc;
  • les flots pour construire des analyseurs descendants, y compris contextuels.
Les outils ocamllex et ocamlyacc ont t utiliss pour dfinir une analyse syntaxique du langage Basic plus aisment modifiable que celle prsente page ??.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora073.html0000644000000000000000000000615107421273601014474 0ustar Introduction Prcdent Index Suivant

Introduction

Tout langage vient avec un ensemble de programmes rutilisables par le programmeur, appels bibliothques. La qualit et la diversit de celles-ci est souvent un des critres de facilit d'utilisation d'un langage. On peut distinguer deux catgories de bibliothques : celles qui fournissent des types et fonctions d'usage courant dfinissables dans le langage et celles qui offrent des fonctionnalits non dfinissables dans le langage. Les premires vitent simplement au programmeur avoir redfinir les utilitaires tels que les piles, les files d'attente, etc. Les secondes tendent les possibilits d'utilisation du langage en y incorporant de nouvelles fonctionnalits.

La distribution du langage Objective CAML vient avec de nombreuses bibliothques. Elles sont fournies sous forme de fichiers compils. Nanmoins, pour le lecteur curieux, les sources de ces bibliothques viennent avec les distributions des sources du langage.

En Objective CAML, l'ensemble des bibliothques est organis en modules qui sont autant d'units de compilation. Chacun d'entre eux contient des dclarations globales de types, d'exceptions et de valeurs qui peuvent tre utilises dans les programmes. On ne s'intressera pas dans ce chapitre la manire de construire de nouveaux modules, mais seulement l'utilisation des modules existants. Le chapitre 14 reprendra les concepts de module (unit logique) et d'unit de compilation en dcrivant le langage de modules d'Objective CAML, incluant les modules paramtrs. Pour ce qui est de la constitution de bibliothques incorporant du code autre qu'Objective CAML, le chapitre 12 dcrira comment intgrer aux programmes Objective CAML des traitements crits en C.

La distribution du langage Objective CAML comporte une bibliothque prcharge (module Pervasives), un ensemble de modules de base, appel bibliothque standard, et plusieurs autres bibliothques ajoutant des fonctionnalits au langage. Certaines bibliothques sont simplement voques dans ce chapitre ou dcrites dans les chapitres suivants.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora020.gif0000644000000000000000000000302207073350152014257 0ustar GIF89a!,ڋ<} `ʪ)LOqp f$*gI1\J5ॊNrGLlZ6bY܍9XeAQeX)Ipcyb1i3IZfGgswri+wI[RJhkLXd(IjMxM];Tֽ̗\}NZ4 Xt$10`;:E`9p 7(!dH{S< %K&b^φF,h'O_ap7LίAQ25 ?2-*1WrUZ}&'lۺ} b^yW\g[:Xkif.ysj+׺i}BP;2jclL$k R:~(cSYxV쮥-E+<mڤ\2a0`wE+"CѵЇ\iXmTbJJeD7i9b#7:,dspLhu.Ȫ|ݕ:"s6\̨: ,b5)/6n8[5-Bws.RU5[w[D!5l(].]$uBFfsSc "KȃoqϷ9~EʯqTن_i֧R.ZsnOs;9i#0U۩s~fȡʩ;W~Qyo.׺峠;{vMpXVdu㽾Џ X+3p{ %0-B qָ C` *Vv  ? D ?F=ͥ!+&0de:a"h; mL ng2"'ǘ\8DiYRXJ={0| _$UъWTX$.Vd;'FMsaț I샣7 NMvy!@^CR "3tw1A‘6i;Odä $Ó<}CJ$YKJWP}OG[nԥIKH2#&3dBd a3oLD^Ӕa5mxMdSܦCpJ$'yNt"Nd'y2xBMgyO|nMf;ocaml-book-1.0/fr/html/book-ora052.gif0000644000000000000000000000202107073350152014262 0ustar GIF89at!,t޼H扦  Lעk& DL*{ǥ 5 ԪӼj, yrjlN?[bgx'hxxHؗxHPY (9Bi *:y :Xxz*;Q!aIZ+;X̖z|0P|] ! n.n~~n=Ύ<(@N]?{}_t 2L QpC{=aQ|iX_9`i5*¢16ʊKٽ^w5 sgʥRm(ao\-nh~ $(,80Ȃ4x8h >ǏB?i$H&L0F)%TVX v%RH&viRfDf׌resމ |v {z>[ʖYtR5 89*eW 2ҽg5JӨ&㏨ʚXPLڭ P+xSkKlC뫥r9QxoE(V^y҂+mۖintK k6>tMbҴ)K ;ZRUH<*ewHص"QZϦZr,r,s4ל8@-D7'FHҖ,ʹ!NWuf$\k-vd]h_v[ǽ#tw8PZ;ocaml-book-1.0/fr/html/book-ora077.html0000644000000000000000000030444107421273601014503 0ustar Bibliothque standard Prcdent Index Suivant

Bibliothque standard

La bibliothque standard regroupe un ensemble de modules stables. Ceux-ci sont indpendants des systmes d'exploitation. Il y a actuellement 29 modules dans la bibliothque standard contenant 400 fonctions, 30 types dont la moiti sont abstraits, 8 exceptions, 10 sous-modules et 3 modules paramtrs. Il est clair que nous n'allons pas dtailler l'ensemble des dclarations de tous ces modules. En effet le manuel de rfrence [LRVD99] le fait dj trs bien. Seuls les modules prsentant un nouveau concept ou une relle difficult de manipulation seront dtaills.

La bibliothque standard peut tre dcoupe en quatre grandes parties :

  • structures linaires de donnes (15 modules), dont certains sont dj apparus dans la premire partie ;
  • entres-sorties (4 modules), pour le formatage des sorties, la persistance et la cration de cls pour la cryptographie ;
  • analyses lexicale et syntaxique (4 modules). Leur description est reporte au chapitre 11 (page ??) ;
  • interface avec le systme qui permet de communiquer et d'analyser les paramtres passs une commande, de naviguer dans les catalogues et d'accder aux fichiers.
ces quatre ensembles, on ajoute un cinquime contenant quelques utilitaires de manipulation ou de cration de donnes comme des fonctions de traitement de caractres ou un gnrateur de nombres pseudo-alatoires, etc.

Utilitaires

Les modules que nous avons baptiss << utilitaires >> concernent :
  • les caractres : module Char qui contient essentiellement des conversions ;
  • copie d'objets : Oo qui sera prsent au chapitre 15 (page ??), sur la programmation objet ;
  • valuation retarde : Lazy prsent en premire partie, page ?? ;
  • gnrateur de nombres alatoires : Random que nous dcrivons ci-dessous.

Gnration de nombres alatoires

Le module Random est un gnrateur de nombres pseudo-alatoires. Il implante une fonction de gnration de nombres alatoires partir d'un nombre ou d'une suite de nombres appel racine. Pour que cette fonction ne retourne pas toujours la mme suite de nombres, le programmeur doit lui passer une racine diffrente chaque initialisation. partir de cette racine la fonction engendre une suite de nombres non prvisible. Nanmoins une initialisation avec la mme racine crera la mme suite. Il faut donc trouver une ressource extrieure au programme, comme la date du jour en millisecondes, ou le temps pass depuis le dbut du programme, pour initialiser correctement le gnrateur.

Voici les fonctions du module :
  • initialisation : init de type int -> unit et full_init de type int array -> unit qui initialisent le gnrateur. La deuxime fonction prend un tableau de racines.
  • demande d'un nombre : bits de type unit -> int retourne un entier positif, int de type int -> int retourne un entier positif compris entre 0 et une borne passe en tant que paramtre, et float qui retourne un flottant en lui passant une borne.

Structures de donnes linaires

Les modules sur les structures de donnes linaires sont :
  • modules simples : Array, String, List, Sort, Stack, Queue, Buffer, Hashtbl (qui est aussi paramtr) et Weak ;
  • modules paramtrs : Hashtbl (de paramtre HashedType), Map et Set (de paramtre OrderedType).
Les modules paramtrs sont construits partir d'autres modules accroissant ainsi leur gnricit. La construction de modules paramtrs sera prsente au chapitre 14, page ??.

Structures linaires de donnes simples

Le nom du module dcrit le type de structures de donnes manipules par le module. Si le type est abstrait, c'est--dire si sa reprsentation est masque, la convention actuelle est de le nommer t l'intrieur du module. Ces modules implantent les structures suivantes :
  • module Array : vecteurs
  • module List : listes
  • module String : chanes de caractres
  • module Hashtbl : tables de hachage (type abstrait)
  • module Buffer : chanes de caractres extensibles (type abstrait)
  • module Stack : piles (type abstrait)
  • module Queue : files d'attente ou FIFO (type abstrait)
  • module Weak : vecteur de pointeurs faibles (type abstrait)
Signalons un dernier module manipulant des structures linaires :
  • module Sort : tri sur les listes et les vecteurs, fusion de listes
Famille de fonctions communes
Tous ces modules, l'exception du module Sort, dfinissent une structure de donnes, les fonctions de cration et d'accs aux lments de cette structures ainsi que des fonctions de manipulation incluant des conversions vers d'autres types. Seul le module List est sans modification physique. Nous ne donnerons pas la description complte de toutes ces fonctions. Nanmoins, nous allons nous intresser aux familles de fonctions que l'on rencontre dans ces modules, puis nous dtaillerons les modules List et Array qui sont les structures les plus communes en programmation fonctionnelle et imprative.

On retrouve peu ou prou les fonctionnalits suivantes dans tous les modules :
  • une fonction length qui prend une valeur du type et calcule un entier correspondant sa longueur ;
  • une fonction clear remettant la structure linaire vide, si elle est modifiable ;
  • une fonction d'ajout d'un lment, add en gnral, mais pouvant tre dnomme diffremment selon les habitudes (comme par exemple push pour les piles) ;
  • une fonction d'accs au i-me lment, souvent nomme get ;
  • une fonction de retrait d'un lment (souvent le premier) remove ou take.
De mme certaines conventions sur les fonctions de parcours et de traitement se retrouvent dans un certain nombre de modules :
  • map : qui applique une fonction sur tous les lments de la structure et retourne une nouvelle structure contenant les rsultats de ces appels ;
  • iter : proche de map mais ne tient pas compte des rsultats et retourne ().
Pour les structures dont les lments sont indics. on a :
  • fill : qui remplace (modifie en place) une partie de la structure par une valeur ;
  • blit : qui copie une partie d'une structure dans une autre structure du mme type ;
  • sub : qui copie une partie d'une structure et en cre une nouvelle.

Modules List et Array

Nous dcrivons les fonctions de ces deux bibliothques en mettant l'accent sur les similitudes et les particularits de chacune d'elles. Pour les fonctions communes aux deux modules, t dsigne le type 'a list ou 'a array. Lorsqu'une fonction est propre un module, nous utiliserons la notation pointe.

Fonctionnalits communes ou analogues
La premire d'entre elles est le calcul de la longueur.
length : 'a t -> int

Deux fonctions permettent de concatner deux structures ou toutes les structures d'une liste.
append : 'a t -> 'a t -> 'a t
concat : 'a t list -> 'a t

Chacun des deux modules possde une fonction d'accs un lment dsign par sa position dans la structure.
List.nth : 'a list -> int -> 'a
Array.get : 'a array -> int -> 'a
La fonction d'accs un lment d'indice i d'un vecteur t, dont l'usage est frquent, possde un raccourci syntaxique : t.(i).

Deux fonctions permettent d'appliquer un traitement (une fonction) tous les lments d'une structure.
iter : ('a -> unit) -> 'a t -> unit
map : ('a -> 'b) -> 'a t -> 'b t

On peut utiliser iter pour afficher le contenu d'une liste ou d'un vecteur.

# let print_content iter print_item xs =
iter (fun x -> print_string"("; print_item x; print_string")") xs;
print_newline() ;;
val print_content : (('a -> unit) -> 'b -> 'c) -> ('a -> 'd) -> 'b -> unit =
<fun>
# print_content List.iter print_int [1;2;3;4;5] ;;
(1)(2)(3)(4)(5)
- : unit = ()
# print_content Array.iter print_int [|1;2;3;4;5|] ;;
(1)(2)(3)(4)(5)
- : unit = ()


La fonction map construit une nouvelle structure contenant le rsultat de l'application. On peut le voir par exemple sur les vecteurs dont le contenu est modifiable :

# let a = [|1;2;3;4|] ;;
val a : int array = [|1; 2; 3; 4|]
# let b = Array.map succ a ;;
val b : int array = [|2; 3; 4; 5|]
# a, b;;
- : int array * int array = [|1; 2; 3; 4|], [|2; 3; 4; 5|]


Deux itrateurs permettent de composer l'application partielle d'une fonction chacun des lments d'une structure.
fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
fold_right : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'b
Il faut fournir ces itrateurs un cas de base qui donne la valeur par dfaut lorsque la structure est vide.

fold_left f r [v1; v2; ...; vn] = f ... ( f (f r v1) v2 ) ... vn
fold_right f [v1; v2; ...; vn] r = f v1 ( f v2 ... (f vn r) ... )

Ces fonctions permettent de transformer facilement des oprations binaires en oprations n-aires. Lorsque l'opration est commutative et associative, l'itration gauche ou droite est indiffrente  :

# List.fold_left (+) 0 [1;2;3;4] ;;
- : int = 10
# List.fold_right (+) [1;2;3;4] 0 ;;
- : int = 10
# List.fold_left List.append [0] [[1];[2];[3];[4]] ;;
- : int list = [0; 1; 2; 3; 4]
# List.fold_right List.append [[1];[2];[3];[4]] [0] ;;
- : int list = [1; 2; 3; 4; 0]


Remarquons que la liste vide est lment neutre gauche et droite pour la concatnation binaire. On retrouve donc, pour ce cas particulier, l'quivalence des deux expressions :

# List.fold_left List.append [] [[1];[2];[3];[4]] ;;
- : int list = [1; 2; 3; 4]
# List.fold_right List.append [[1];[2];[3];[4]] [] ;;
- : int list = [1; 2; 3; 4]
On a, en fait, retrouv la fonction List.concat.

Manipulations propres aux listes
Il est d'usage d'avoir sur les listes les fonctions suivantes que fournit le module List :
List.hd : 'a list -> 'a
    premier lment de la liste
List.tl : 'a list -> 'a
    liste prive de son premier lment
List.rev : 'a list -> 'a list
    retournement d'une liste
List.mem : 'a -> 'a list -> bool
    test d'appartenance
List.flatten : 'a list list -> 'a list
    aplatissement d'une liste de listes
List.rev_append : 'a list -> 'a list -> 'a list
    est gale append (rev l1) l2
Les deux premires fonctions sont partielles. Elles ne sont pas dfinies sur la liste vide et dclenchent une exception Failure. Il existe une variante de mem : memq qui utilise l'galit physique.

# let c = (1,2) ;;
val c : int * int = 1, 2
# let l = [c] ;;
val l : (int * int) list = [1, 2]
# List.memq (1,2) l ;;
- : bool = false
# List.memq c l ;;
- : bool = true


Le module List fournit deux itrateurs particuliers gnralisant la conjonction et la disjonction boolennes : List.for_all et List.exists que l'on dfinit par itration :

# let for_all f xs = List.fold_right (fun x -> fun b -> (f x) & b) xs true ;;
val for_all : ('a -> bool) -> 'a list -> bool = <fun>
# let exists f xs = List.fold_right (fun x -> fun b -> (f x) or b) xs false ;;
val exists : ('a -> bool) -> 'a list -> bool = <fun>


Il existe des variantes de tous les itrateurs du module List prenant en argument deux listes et les parcourant en parallle (iter2, map2, etc.). Si elles ne sont pas de mme longueur, l'exception Invalid_argument est dclenche.

La recherche d'lments d'une liste selon un critre donn sous forme de fonction boolenne peut se faire en utilisant les fonctions suivantes :
List.find : ('a -> bool) -> 'a list -> 'a
List.find_all : ('a -> bool) -> 'a list -> 'a list
La fonction find_all a un alias : filter.

Une variante de la fonction gnrale de recherche est la fonction de partitionnement d'une liste :
List.partition : ('a -> bool) -> 'a list -> 'a list * 'a list

Comme utilitaires souvent ncessaires, on trouve dans le module List deux fonctions permettant de diviser ou de crer des listes de couples  :
List.split : ('a * 'b) list -> 'a list * 'b list
List.combine : 'a list -> 'b list -> ('a * 'b) list

Enfin, une structure combinant listes et couples est souvent utilise : les listes d'associations. Elles servent stocker des valeurs associes des cls. Ce sont des listes de couples dont la premire composante joue le rle de cl et la seconde, celui d'information associe. On a pour traiter de telles donnes :
List.assoc : 'a -> ('a * 'b) list -> 'b
    extrait l'information associe une cl
List.mem_assoc : 'a -> ('a * 'b) list -> bool
    teste l'existence d'une cl
List.remove_assoc : 'a -> ('a * 'b) list -> ('a * 'b) list
    suppression d'un lment correspondant une cl
Ces fonctions ont chacune une variante utilisant l'galit physique au lieu de l'galit structurelle: List.assq, List.mem_assq et List.remove_assq.

Manipulations propres aux vecteurs
Les vecteurs que l'on utilise souvent en programmation imprative sont des structures physiquement modifiables. Le module Array fournit une fonction de modification de la valeur d'un lment :
Array.set : 'a array -> int -> 'a -> unit
Comme get, la fonction set possde un raccourci syntaxique : t.(i) <- a.

Il existe trois fonctions d'allocation de vecteurs :
Array.create : int -> 'a -> 'a array
    cre un vecteur d'une taille donne dont tous les lments sont initialiss avec une mme valeur
Array.make : int -> 'a -> 'a array
    alias de create
Array.init : int -> (int -> 'a) -> 'a array
    cre un vecteur d'une taille donne dont chaque lment est initialis avec le rsultat de l'application d'une fonction l'indice de l'lment initialis

Comme leur usage est courant, le module Array fournit deux fonctions de cration de matrices (vecteurs de vecteurs) :
Array.create_matrix : int -> int -> 'a -> 'a array array
Array.make_matix : int -> int -> 'a -> 'a array array

La fonction set se gnralise en une fonction modifiant les valeurs d'un intervalle dcrit par un indice de dpart et une longueur :
Array.fill : 'a array -> int -> int -> 'a -> unit

On peut copier l'intgralit d'un vecteur ou extraire un sous vecteur (dcrit par un indice de dbut et une longueur) pour obtenir une nouvelle structure :
Array.copy : 'a array -> 'a array
Array.sub : 'a array -> int -> int -> 'a array

La copie ou l'extraction peuvent aussi se faire vers un autre vecteur :
Array.blit : 'a array -> int -> 'a array -> int -> int -> unit
Le premier argument entier est l'indice dans le premier vecteur, le deuxime, l'indice dans le second vecteur et le troisime argument entier, le nombre de valeurs copies. Les trois fonctions blit, sub et fill dclenchent l'exception Invalid_argument.

L'usage privilgi des indices dans les fonctions de manipulation de vecteurs a conduit la dfinition de deux itrateurs particuliers :
Array.iteri : (int -> 'a -> unit) -> 'a array -> unit
Array.mapi : (int -> 'a -> 'b) -> 'a array -> 'b array

Elles appliquent une fonction dont le premier argument est l'indice de la case traite.

# let f i a = (string_of_int i) ^ ":" ^ (string_of_int a) in
Array.mapi f [| 4; 3; 2; 1; 0 |] ;;
- : string array = [|"0:4"; "1:3"; "2:2"; "3:1"; "4:0"|]


Le module Array ne fournit pas de fonction permettant de modifier en place le contenu des cases d'un vecteur avec le rsultat d'un calcul sur leur contenu, mais on peut facilement l'obtenir avec iteri :

# let iter_and_set f t =
Array.iteri (fun i -> fun x -> t.(i) <- f x) t ;;
val iter_and_set : ('a -> 'a) -> 'a array -> unit = <fun>
# let v = [|0;1;2;3;4|] ;;
val v : int array = [|0; 1; 2; 3; 4|]
# iter_and_set succ v ;;
- : unit = ()
# v ;;
- : int array = [|1; 2; 3; 4; 5|]


Enfin, le module Array fournit deux fonctions de conversion avec les listes :
Array.of_list : 'a list -> 'a array
Array.to_list : 'a array -> 'a list

Entres-sorties

La bibliothque standard comporte les quatre modules d'entres-sorties :
  • module Printf : pour le formatage en sortie;
  • Format : pour un formatage en bote grant automatiquement les sauts de ligne;
  • module Marshal : implantant un mcanisme de valeurs persistantes ;
  • module Digest : pour crer des cls uniques.
La description du module Marshal sera donne plus loin dans ce chapitre lorsque nous aborderons le traitement des donnes persistantes (voir page ??).

Module Printf

Le module Printf permet de formater l'affichage la manire de la fonction printf de la bibliothque du langage C. Le format d'affichage est reprsent sous forme d'une chane de caractres, qui sera dcode selon les conventions du printf de C, c'est--dire en spcialisant le caractre %. C'est ce caractre suivi d'une lettre qui indiquera le type de l'argument mettre cet emplacement. Le format suivant "(x=%d, y=%d)" indique qu'il faudra placer deux entiers la place des %d dans la chane de sortie.

Spcifications des formats
Un format dfinit des paramtres pour une chane d'affichage. Ceux-ci, de type de base : int, float, char et string, seront convertis en chanes et remplaceront leur occurrence dans la chane d'affichage. Les valeurs 77 et 43 fournie au format "(x=%d, y=%d)" engendreront la chane d'affichage complte "(x=77, y=43)". Les principales lettres indiquant le type de la conversion effectuer sont donnes figure 8.1.


Type Lettre Rsultat
entier d ou i dcimal sign
  u dcimal non sign
  x hexadcimal non sign en minuscule
  X idem en majuscule
caractre c caractre
chane s chane
flottant f dcimal
  e ou E en notation avec exposant
  g ou G idem
boolen b true ou false
spcial a ou t paramtre fonctionnel
    de type (out_channel -> 'a -> unit) -> 'a -> unit
    ou out_channel -> unit

Figure 8.1 : Convention de conversion


Le format permet aussi d'assurer le cadrage d'une conversion, ce qui permet l'alignement des valeurs afficher. On peut principalement indiquer la taille en caractres de la conversion. Pour cela on intercalera entre le caractre % et le type de conversion un nombre entier comme dans %10d qui indique une conversion essayant de se cadrer droite sur dix caractres. Si la taille de la conversion dpasse cette limite, il ne sera pas tenu compte de cette limite. Un nombre ngatif effectuera un cadrage gauche. Pour les conversions de nombres flottants, il est utile de pouvoir spcifier la prcision d'affichage. On intercalera alors un point suivi d'un nombre pour indiquer le nombre de caractres aprs la virgule comme dans %.5f qui indique cinq caractres droite du point dcimal.

Il existe deux lettres de format particulires : a et t qui indiquent un argument fonctionnel. Typiquement, une fonction d'affichage dfinie par l'utilisateur. C'est une spcificit d'Objective CAML.

Fonctions du module
Les types des cinq fonctions de ce module sont donns figure 8.2.


fprintf : out_channel -> ('a, out_channel, unit) format -> 'a
printf : ('a, out_channel, unit) format -> 'a
eprintf : ('a, out_channel, unit) format -> 'a
sprintf : ('a, unit, string) format -> 'a
bprintf : Buffer.t -> ('a, Buffer.t, string) format -> 'a

Figure 8.2 : Fonctions de formatage de Printf


La fonction fprintf prend un canal, un format et des arguments de type dcrit dans le format. Les fonctions printf et eprintf sont des versions spcialises sur la sortie standard et la sortie erreur. Enfin sprintf et bprintf n'affichent pas le rsultat de la conversion, mais retournent la chane correspondante.

Voici quelques exemples simples d'utilisation des formats.

# Printf.printf "(x=%d, y=%d)" 34 78 ;;
(x=34, y=78)- : unit = ()
# Printf.printf "nom = %s, age = %d" "mapom" 18 ;;
nom = mapom, age = 18- : unit = ()
# let s = Printf.sprintf "%10.5f\n%10.5f\n" (-.12.24) (2.30000008) ;;
val s : string = " -12.24000\n 2.30000\n"
# print_string s ;;
-12.24000
2.30000
- : unit = ()


L'exemple suivant construit une fonction d'affichage d'une matrice de flottants sous un format donn.

# let print_mat m =
Printf.printf "\n" ;
for i=0 to (Array.length m)-1 do
for j=0 to (Array.length m.(0))-1 do
Printf.printf "%10.3f" m.(i).(j)
done ;
Printf.printf "\n"
done ;;
val print_mat : float array array -> unit = <fun>
# print_mat (Array.create 4 [| 1.2; -.44.22; 35.2 |]) ;;

1.200 -44.220 35.200
1.200 -44.220 35.200
1.200 -44.220 35.200
1.200 -44.220 35.200
- : unit = ()


Remarque sur le type format
La description d'un format adopte la syntaxe des chanes de caractres, mais ce n'est pas une valeur de type string. Le dcodage d'un format, selon les conventions prcdentes, construit une valeur de type format o le paramtre 'a est instanci soit unit si le format ne mentionne pas de paramtre, soit par un type fonctionnel correspondant une fonction pouvant recevoir autant d'arguments que mentionns et retournant la valeur de type unit.

On peut illustrer ce traitement en appliquant partiellement la fonction printf un format :

# let p3 =
Printf.printf "dbut\n%d est val1\n%s est val2\n%f est val3\n" ;;
dbut
val p3 : int -> string -> float -> unit = <fun>
On obtient ainsi une fonction qui attend trois arguments. Remarquez que le mot dbut a dj t affich. Un autre format aurait donn un autre type de fonction :

# let p2 =
Printf.printf "dbut\n%f est val1\n%s est val2\n";;
dbut
val p2 : float -> string -> unit = <fun>
En fournissant petit petit ses arguments p3, on obtient les affichages au fur et mesure :

# let p31 = p3 45 ;;
45 est val1
val p31 : string -> float -> unit = <fun>
# let p32 = p31 "hello" ;;
hello est val2
val p32 : float -> unit = <fun>
# let p33 = p32 3.14 ;;
3.140000 est val3
val p33 : unit = ()
# p33 ;;
- : unit = ()
La dernire valeur obtenue n'affiche rien : c'est la valeur () du type unit.

On ne peut pas construire un format en utilisant des valeurs de type string :

# let f d =
Printf.printf (d^d);;
Characters 27-30:
This expression has type string but is here used with type
('a, out_channel, unit) format


Le compilateur ne peut connatre la valeur de la chane passe en argument. Il ne peut donc pas connatre le type qui sert instancier le paramtre 'a du type format.

D'autre part, les chanes de caractres sont des valeurs physiquement modifiables, il serait alors possible de modifier, par exemple, la partie %d par une autre lettre, modifiant ainsi dynamiquement un format d'affichage. C'est en contradiction avec la gnration statique de la fonction de conversion.

Module Digest

Une fonction de hachage convertit une chane de caractres de taille quelconque en une chane de caractres de taille fixe, le plus souvent plus petite. Ces fonctions de hachage retournent une empreinte (digest) de leur entre.

De telles fonctions sont utilises pour la construction de tables de hachage, comme dans le module Hashtbl, permettant de tester rapidement si un lment appartient une telle table grce un accs direct l'empreinte. Par exemple la fonction f_mod_n, qui effectue la somme des codes ASCII des caractres d'une chane modulo n, est une fonction de hachage. Si on cre un tableau de dimension n pour ranger ces chanes, de part l'empreinte on obtient un accs direct. Nanmoins deux chanes peuvent retourner la mme empreinte. Dans ces cas de collisions, on ajoute la table de hachage une extension pour stocker ces lments. S'il y a trop de collisions, alors l'accs la table de hachage est peu efficace. Si l'empreinte a une taille de n bits, alors la probabilit de collision entre deux chanes diffrentes est de 1/2n.

Une fonction de hachage sens unique possde une probabilit trs faible de collision. Il est ainsi difficile, tant donne une empreinte, de construire une chane possdant cette empreinte. La fonction prcdente f_mod_n n'en est, l'vidence, pas une. Les fonctions de hachage sens unique permettent l'authentification d'une chane, que cela soit pour un texte envoy sur Internet, un fichier, etc.

Le module Digest utilise l'algorithme MD5, pour Message Digest 5. Il retourne une empreinte sur 128 bits. Bien que l'algorithme soit public, il est impossible ( ce jour) d'effectuer une reconstruction partir d'une empreinte. Ce module dfinit le type Digest.t comme une abrviation du type string. La figure 8.3 dtaille les principales fonctions de ce module.


string : string -> t
    retourne l'empreinte d'une chane
file : string -> t
    retourne l'empreinte d'un fichier

Figure 8.3 : Fonctions du module Digest


On utilise la fonction string dans l'exemple suivant sur une petite chane et sur une grande construite partir de la premire. L'empreinte est toujours de taille fixe.

# let s = "Le petit chat est mort...";;
val s : string = "Le petit chat est mort..."
# Digest.string s;;
- : Digest.t = "[\138\236]5\015\156'\243\232>\176IdF\022"

# let r = ref s in
for i=1 to 100 do r:= s^ !r done;
Digest.string !r;;
- : Digest.t = "\148\022\201?j\141\159\143\168\241;\004\158\139\196\147"


La cration d'une empreinte sur des programmes permet de garantir le contenu de celui-ci vitant ainsi l'utilisation d'une mauvaise version. Par exemple, lors du chargement dynamique de code (voir page ??), une empreinte est utilise pour slectionner le fichier de code-octet charger.

# Digest.file "basic.ml" ;;
- : Digest.t = "\179\026\191\137\157Ly|^w7\183\164:\167q"


Persistance

La persistance est la conservation d'une valeur en dehors de l'excution courante d'un programme. C'est le cas quand on crit une valeur dans un fichier. Cette valeur est alors accessible tout programme ayant accs au fichier. L'criture et la lecture d'une valeur persistante rclament la dfinition d'un format de reprsentation ou codage des donnes. En effet, il faut savoir passer d'une structure complexe stocke en mmoire, telle un arbre, une structure linaire, une suite d'octets, stocke sur fichier. C'est pourquoi le codage des valeurs persistantes s'appelle linarisation1.

Ralisation et difficults de la linarisation

La mise en oeuvre d'un mcanisme de linarisation des donnes demande des choix et prsente des difficults que nous dcrivons ci-dessous.
  • lecture-criture de donnes. Comme la mmoire peut toujours tre vue comme un vecteur de mots, une valeur peut toujours correspondre la mmoire qu'elle occupe, quitte ne conserver que la partie utile en compactant alors la valeur.
  • partage ou copie. L'aplatissement d'une donne doit-il conserver le partage ? Typiquement un arbre binaire qui possde deux fils identiques (au sens de l'galit physique) peut indiquer, pour le deuxime fils, qu'il a dj sauvegard le premier. Cette caractristique influence la taille de la valeur sauvegarde et le temps mis pour le faire. D'autre part, en prsence de valeurs physiquement modifiables, cela peut changer le comportement de cette valeur aprs une rcupration selon que le partage a t conserv ou non.
  • structures circulaires. Dans le cas d'une valeur circulaire, la linarisation sans partage risque de boucler. Il sera ncessaire de conserver ce partage.
  • valeurs fonctionnelles. Les valeurs fonctionnelles, ou fermetures, se composent d'une partie environnement et d'une partie code. La partie code correspond au point d'entre (adresse) du code excuter. Que faut-il faire alors du code? Il est possible de stocker uniquement cette adresse, mais alors seul le mme programme trouvera un sens correct cette adresse. Il est aussi possible de sauver la suite d'instructions machine de cette fonction, mais il sera ncessaire d'avoir un mcanisme de chargement dynamique de code.
  • garantie du type la relecture. C'est la principale difficult de ce mcanisme. Le typage statique garantit que les valeurs types n'engendreront pas d'erreur de type l'excution. Mais cela n'est vrai que pour les valeurs appartenant au programme en cours d'excution. Quel type peut-on donner une valeur extrieure au programme, qui n'a donc pas t vue par le vrificateur de types? Pour seulement vrifier que la valeur relue possde le type monomorphe engendr par le compilateur, il faudrait qu'il y ait une transmission de ce type au moment de la sauvegarde, puis vrification au chargement. Avec de surcrot un mcanisme de gestion de versions des types, pour rester sr, mme en cas de redclaration d'un type dans le programme.

Module Marshal

Le mcanisme de linarisation du module Marshal permet, au choix, de retenir ou non le partage des valeurs traites. Il permet galement de traiter les fermetures, mais, dans ce cas, seul le pointeur de code est conserv.

Ce module comporte principalement les fonctions de linarisation vers un canal ou une chane et les fonctions de rcupration partir d'un canal ou d'une chane. Les fonctions de linarisation sont paramtrables. Le type suivant dclare les deux options possibles :
type external_flag = 
  No_sharing
| Closures;;
Le constructeur constant No_sharing indique de ne pas conserver le partage d'une valeur, par dfaut il l'est. Le constructeur Closures permet de traiter les fermetures en conservant son pointeur de code. Son absence dclenchera une exception si l'on essaie de conserver une valeur fonctionnelle.

Warning


Le constructeur Closures est inoprant en mode interactif. Il ne peut tre utilis qu'en mode ligne de commande.


Les fonctions d'criture et de lecture de ce module sont regroupes la figure 8.4.


to_channel : out_channel -> 'a -> extern_flag list -> unit
to_string : 'a -> extern_flag list -> string
to_buffer : string -> int -> int -> 'a -> extern_flag list -> unit
from_channel : in_channel -> 'a
from_string : string -> int -> 'a

Figure 8.4 : Fonctions du module Marshal


La fonction to_channel prend un canal de sortie, une valeur et une liste d'options et crit la valeur passe sur le canal. La fonction to_string produit une chane correspondant la valeur linarise, alors que to_buffer effectue le mme travail en modifiant une partie d'une chane passe en argument. La fonction from_channel lit sur un canal une valeur linarise et la retourne. La variante from_string prend en entre une chane et la position du premier caractre lire dans la chane. Plusieurs valeurs linarises peuvent tre stockes dans le mme fichier ou dans la mme chane. Dans le premier cas, elles pourront tre lues squentiellement. Dans le deuxime cas, il faudra prciser le bon dcalage par rapport au dbut de la chane pour dcoder la valeur dsire.

# let s = Marshal.to_string [1;2;3;4] [] in String.sub s 0 10;;
- : string = "\132\149\166\190\000\000\000\t\000\000"


Warning


L'utilisation de ce module fait perdre la sret du typage statique (voir infra, page ??).


La relecture d'un objet persistant cre une valeur de type indtermin :

# let x = Marshal.from_string (Marshal.to_string [1; 2; 3; 4] []) 0;;
val x : '_a = <poly>
Cette indtermination est marque, en Objective CAML, par la variable de type faible '_a. Il est recommand de spcifier le type attendu :

# let l =
let s = (Marshal.to_string [1; 2; 3; 4] []) in
(Marshal.from_string s 0 : int list) ;;
val l : int list = [1; 2; 3; 4]
Nous revenons page ?? sur ce point.

Remarque


La fonction output_value de la bibliothque prcharge correspond l'appel de to_channel avec une liste d'options vide. La fonction input_value du module Pervasives appelle directement la fonction from_channel. Celles-ci ont t conserves pour la compatibilit des anciens programmes.


Exemple : sauvegarde d'crans

On cherche sauvegarder le bitmap, reprsent comme matrice de couleurs, de toute la fentre graphique. La fonction save_screen rcupre le bitmap, le convertit en tableau de couleurs et le sauve dans un fichier dont le nom a t pass en paramtre.

# let save_screen name =
let i = Graphics.get_image 0 0 (Graphics.size_x ())
(Graphics.size_y ()) in
let j = Graphics.dump_image i in
let oc = open_out name in
output_value oc j;
close_out oc;;
val save_screen : string -> unit = <fun>


La fonction load_screen effectue l'opration inverse. Elle ouvre le fichier dont le nom est pass en paramtre, rcupre la valeur stocke l'intrieur, convertit cette matrice couleurs en bitmap puis affiche celui-ci.

# let load_screen name =
let ic = open_in name in
let dessin = ((input_value ic) : Graphics.color array array) in
close_in ic;
Graphics.close_graph();
Graphics.open_graph (":0 "^(string_of_int(Array.length dessin.(0)))
^"x"^(string_of_int(Array.length dessin)));
let dessin2 = Graphics.make_image dessin in
Graphics.draw_image dessin2 0 0; dessin2 ;;
val load_screen : string -> Graphics.image = <fun>


Warning


Les valeurs dont le type est abstrait ne peuvent pas tre persistantes.
C'est pour cela que l'exemple prcdent n'utilise pas le type Graphics.image qui est abstrait, mais bien le type color array array qui lui est concret. L'abstraction de types est prsente au chapitre 14.

Partage

La perte du partage d'une donne peut lui faire perdre compltement son intrt. Reprenons l'exemple du gnrateur de symboles de la page ??. Pour une raison quelconque, on dsire sauver les valeurs fonctionnelles new_s et reset_s, pour, par la suite, repartir de la valeur de leur compteur commun. On crit alors le programme suivant :
 
# let reset_s,new_s =
let c = ref 0 in
( function () -> c := 0 ) ,
( function s -> c:=!c+1; s^(string_of_int !c) ) ;;

# let save =
Marshal.to_string (new_s,reset_s) [Marshal.Closures;Marshal.No_sharing] ;;

# let (new_s1,reset_s1) =
(Marshal.from_string save 0 : ((string -> string ) * (unit -> unit))) ;;

# (* 1 *)
Printf.printf "new_s : \%s\n" (new_s "X");
Printf.printf "new_s : \%s\n" (new_s "X");
(* 2 *)
Printf.printf "new_s1 : \%s\n" (new_s1 "X");
(* 3 *)
reset_s1();
Printf.printf "new_s1 (aprs reset_s1) : \%s\n" (new_s1 "X") ;;
Characters 148-154:
Unbound value new_s1


Les deux premiers affichages en (* 1 *) sont cohrents par rapport la dfinition. L'affichage obtenu en (* 2 *) aprs relecture des fermetures semble galement correct (aprs X2 vient X3). Mais, en fait, le partage du compteur c entre les fonctions relues new_s1 et reset_s1 est perdu comme l'atteste l'affichage de X4 bien que l'on ait entre temps remis le compteur zro. Chaque fermeture a une copie du compteur et l'appel reset_s1 ne remet pas zro le compteur de new_s1. Il n'aurait donc pas fallu utiliser l'option No_sharing lors de la linarisation.

Il est en rgle gnrale ncessaire de conserver le partage. Nanmoins dans certains cas o la vitesse d'excution est importante, l'absence de partage effectue un parcours plus rapide la sauvegarde. L'exemple suivant montre une fonction qui effectue une copie d'une matrice. Dans ce cas l aussi il peut tre prfrable de casser le partage :

# let copie_mat_f (m : float array array) =
let s = Marshal.to_string m [Marshal.No_sharing] in
(Marshal.from_string s 0 : float array array);;
val copie_mat_f : float array array -> float array array = <fun>


On peut aussi l'utiliser pour la cration d'une matrice sans partage :

# let create_mat_f n m v =
let m = Array.create n (Array.create m v) in
copie_mat_f m;;
val create_mat_f : int -> int -> float -> float array array = <fun>
# let a = create_mat_f 3 4 3.14;;
val a : float array array =
[|[|3.14; 3.14; 3.14; 3.14|]; [|3.14; 3.14; 3.14; 3.14|];
[|3.14; 3.14; 3.14; 3.14|]|]
# a.(1).(2) <- 6.28;;
- : unit = ()
# a;;
- : float array array =
[|[|3.14; 3.14; 3.14; 3.14|]; [|3.14; 3.14; 6.28; 3.14|];
[|3.14; 3.14; 3.14; 3.14|]|]


Ce qui est un comportement plus habituel que celui de Array.create et ressemble celui de Array.create_matrix.

Taille des valeurs

Il peut tre utile de connatre la taille d'un persistant. Si le partage est conserv, cette taille reflte assez bien l'occupation mmoire d'une valeur. Bien que le codage optimise parfois la taille des valeurs immdiates2, l'information de taille de leur codage respectif permet de comparer diffrentes implantations d'une structure de donnes. D'autre part pour les programmes qui ne s'arrtent jamais, logiciels embarqus ou mme serveurs rseau, l'tude de la taille des donnes peut indiquer des fuites de mmoire. Le module Marshal a deux fonctions de calcul de la taille et une constante qui sont dcrits la figure 8.5.

header_size : int
data_size : string -> int -> int
total_size : string -> int -> int

Figure 8.5 : Fonctions de taille de Marshal


La taille totale d'un persistant est gale la taille de ses donnes additionne la taille de l'en-tte.

Nous donnons ci-dessous un petit exemple d'utilisation du codage MD5 pour comparer deux reprsentations des arbres binaires :

# let taille x = Marshal.data_size (Marshal.to_string x []) 0;;
val taille : 'a -> int = <fun>
# type 'a bintree1 = Empty1 | Node1 of 'a * 'a bintree1 * 'a bintree1 ;;
type 'a bintree1 = | Empty1 | Node1 of 'a * 'a bintree1 * 'a bintree1
# let t1 =
Node1(2, Node1(1, Node1(0, Empty1, Empty1), Empty1),
Node1(3, Empty1, Empty1)) ;;
val t1 : int bintree1 =
Node1
(2, Node1 (1, Node1 (0, Empty1, Empty1), Empty1),
Node1 (3, Empty1, Empty1))
# type 'a bintree2 =
Empty2 | Leaf2 of 'a | Node2 of 'a * 'a bintree2 * 'a bintree2 ;;
type 'a bintree2 =
| Empty2
| Leaf2 of 'a
| Node2 of 'a * 'a bintree2 * 'a bintree2
# let t2 =
Node2(2, Node2(1, Leaf2 0, Empty2), Leaf2 3) ;;
val t2 : int bintree2 = Node2 (2, Node2 (1, Leaf2 0, Empty2), Leaf2 3)
# let s1, s2 = taille t1, taille t2 ;;
val s1 : int = 13
val s2 : int = 9
Les valeurs donnes par la fonction taille refltent bien l'intuition que l'on peut avoir de la taille de t1 et t2.

Problme de typage

Le vrai problme avec les persistants est qu'il est possible de casser le systme de types d'Objective CAML. Les fonctions de cration retournent bien un type monomorphe (unit ou string). En revanche, les fonctions de rcupration retournent un type polymorphe 'a. Du point de vue des types, on pourra faire n'importe quel usage d'une valeur persistante. Voici le pire d'entre eux (voir chapitre 2, page ??) : crer une fonction copie_magique de type 'a -> 'b.

# let copie_magique a =
let s = Marshal.to_string a [Marshal.Closures] in
Marshal.from_string s 0;;
val copie_magique : 'a -> 'b = <fun>


L'utilisation d'une telle fonction provoque un arrt brutal de l'excution.
#  (copie_magique 3 : float) +. 3.1;;
Segmentation fault
En mode interactif (sous Linux), on sort mme de la boucle d'interaction avec un signal d'erreur systme correspondant une violation de mmoire.

Interface avec le systme

La bibliothque standard comporte six modules d'interface avec le systme :
  • module Sys : pour la communication entre le systme d'exploitation et le programme ;
  • module Arg : pour analyser les paramtres passs au programme sur la ligne de commande ;
  • module Filename : pour la navigation dans les rpertoires (indpendamment du systme) ;
  • module Printexc : pour l'interception et l'affichage des exceptions ;
  • module Gc : pour le contrle du mcanisme de rcupration automatique de mmoire qui est dcrit au chapitre 9 ;
  • module Callback : pour l'appel de fonctions Objective CAML partir de C qui est dcrit au chapitre 12.
Les quatre premiers modules sont dcrits ci-dessous.

Module Sys

Ce module apporte de bien utiles fonctions de communication avec le systme d'exploitation, ainsi que le traitement des signaux reus par un programme. Les valeurs de la figure 8.6 donnent des informations sur le systme.


OS_type : string
    type du systme
interactive : bool ref
    vrai si excution au toplevel
word_size : string
    taille d'un mot (32 ou 64 bits)
max_string_length : int
    taille maximale d'une chane
max_array_length : int
    taille maximale d'un vecteur
time : unit -> float
    donne le temps en secondes depuis le dbut du programme

Figure 8.6 : Informations sur le systme


La communication entre le programme et le systme passe par la ligne de commande, la valeur d'une variable de l'environnement d'excution et la possibilit de lancer un autre programme. Ces fonctions sont dcrites la figure 8.7.

argv : string array
    contient le vecteur de paramtres
getenv : string -> string
    demande de la valeur d'une variable
command : string -> int
    excution de la commande passe en argument

Figure 8.7 : Communication avec le systme


Les fonctions de la figure 8.8 permettent une navigation dans la hirarchie de fichiers.

file_exists : string -> bool
    retourne true si le fichier existe
remove : string -> unit
    destruction d'un fichier
rename : string -> string -> unit
    renommage d'un fichier
chdir : string -> unit
    change de catalogue courant
getcwd : unit -> string
    retourne le nom du catalogue courant

Figure 8.8 : Manipulation de fichiers


Enfin la gestion des signaux sera dcrite au chapitre sur la programmation systme (18).

Voici un petit programme qui reprend l'exemple sur la sauvegarde d'une fentre graphique sous forme d'un tableau de couleurs. La fonction main vrifie qu'elle n'est pas lance de la boucle d'interaction, lit les noms des fichiers visualiser sur la ligne de commande, teste s'ils existent et les visualise (avec la fonction load_screen). On intercale une attente clavier dans la fentre graphique entre deux visualisations.

# let main () =
if not (!Sys.interactive) then
for i = 0 to Array.length(Sys.argv) -1 do
let nom = Sys.argv.(i) in
if Sys.file_exists nom then
begin
ignore(load_screen nom);
ignore(Graphics.read_key)
end
done;;
val main : unit -> unit = <fun>


Module Arg

Le module Arg fixe une petite syntaxe pour les arguments de la ligne de commande. Il fournit une fonction permettant l'analyse syntaxique ainsi que la dfinition d'une action associe aux lments analyss.

Les divers lments de la ligne de commande sont spars par un ou plusieurs espaces. Ils constituent les valeurs stockes dans le tableau Sys.argv. Dans la syntaxe fixe par Arg, certains lments distingus commencent par le caractre moins (-). On les appelle des mots cls de la ligne de commande. On peut associer aux mots cls une action spcifique qui peut prendre en argument une valeur de type string, int ou float. La valeur de ces arguments est initialise avec la valeur trouve sur la ligne de commande juste aprs le mot cl. Il y a dans ce cas appel une fonction de conversion des chanes de caractres vers le type attendu. Les autres lments de la ligne de commande sont dits arguments anonymes. On leur associe une action commune qui prend leur valeur en argument. Une option non dfinie provoque l'affichage d'une petite documentation en ligne de la commande dont le contenu est dfini par l'utilisateur.

Les actions associes aux mots cls sont encapsules dans le type :

type spec =
| Unit of (unit -> unit) (* Call the function with unit argument*)
| Set of bool ref (* Set the reference to true*)
| Clear of bool ref (* Set the reference to false*)
| String of (string -> unit) (* Call the function with a string
argument *)
| Int of (int -> unit) (* Call the function with an int
argument *)
| Float of (float -> unit) (* Call the function with a float
argument *)
| Rest of (string -> unit) (* Stop interpreting keywords and call the
function with each remaining argument*)


La fonction d'analyse de la ligne de commande est :

# Arg.parse ;;
- : (string * Arg.spec * string) list -> (string -> unit) -> string -> unit =
<fun>


Son premier argument est une liste de triplets de la forme (key, spec, doc) tels que :
  • key est une chane de caractres correspondant un mot cl. Elle commence donc par le caractre rserv '_'.
  • spec est une valeur de type spec spcifiant l'action associe key.
  • doc est une chane de caractres dcrivant l'option key. Elle est affiche sur une erreur de syntaxe.
Le deuxime argument est la fonction de traitement des arguments anonymes de la ligne de commande. Le dernier argument est une chane de caractres affiche en tte de la documentation en ligne.

Le module Arg comprend galement :
  • Bad : exception prenant en argument une chane de caractres. Elle peut tre utilise par les fonctions de traitement.
  • usage : de type (string * Arg.spec * string) list -> string -> unit, cette fonction affiche la documentation en ligne. On lui fournira de prfrence d les mmes arguments que ceux de parse.
  • current : de type int ref qui contient une rfrence sur la valeur courante de l'indice dans le tableau Sys.argv. On peut donc modifier cette valeur si besoin est.
titre d'exemple, nous donnons la fonction read_args permettant d'initialiser la configuration du dmineur vu au chapitre 6, page ??. Les options possibles seront -col, -lig et -min. Elles seront suivies d'un entier prcisant, respectivement : le nombre de colonnes, le nombre de lignes et le nombre de mines dsires. Ces valeurs ne devront pas tre infrieures aux valeurs respectives par dfaut 10, 10 et 15.

Les fonctions de traitement sont :

# let set_nbcols cf n = cf := {!cf with nbcols = n} ;;
# let set_nbrows cf n = cf := {!cf with nbrows = n} ;;
# let set_nbmines cf n = cf := {!cf with nbmines = n} ;;


Elles sont toutes trois de type config ref -> int -> unit. La fonction d'analyse de la ligne de commande peut s'crire :

# let read_args() =
let cf = ref default_config in
let speclist =
[("-col", Arg.Int (set_nbcols cf), "nombre de colonnes (>=10)");
("-lig", Arg.Int (set_nbrows cf), "nombre de lignes (>=10)");
("-min", Arg.Int (set_nbmines cf), "nombre de mines (>=15)")]
in
let usage_msg = "usage : demin [-col n] [-lig n] [-min n]" in
Arg.parse speclist (fun s -> ()) usage_msg; !cf ;;
val read_args : unit -> config = <fun>


Elle calcule une configuration qui sera passe en argument la fonction d'ouverture de la fentre du jeu open_wcf lors du lancement du jeu. Chaque option est, comme son nom l'indique, optionnelle. Si elle ne figure pas sur la ligne de commande, le paramtre correspondant garde la valeur par dfaut. L'ordre des options n'a pas d'importance.

Module Filename

Le module Filename donne des oprations sur les noms de fichiers indpendantes des systmes d'exploitation. En effet les conventions de nommage des fichiers et des catalogues diffrent fortement entre Windows, Unix et MacOS.

Module Printexc

Ce module trs court (trois fonctions dcrites la figure 8.9) apporte un rcuprateur gnral d'exceptions. Cela est particulirement pratique pour les programmes excuts en mode commande3 pour tre sr de ne pas laisser s'chapper une exception qui arrterait le programme.

catch : ('a -> 'b) -> 'a -> 'b
    rcuprateur gnral
print : ('a -> 'b) -> 'a -> 'b
    affiche et redclenche l'exception
to_string : exn -> string
    convertit une exception en chane

Figure 8.9 : Rcupration d'exceptions


La fonction catch applique son premier argument son second. Cela lancera la fonction principale du programme. Si une exception arrive au niveau de catch, c'est dire n'est pas rcupre l'intrieur du programme, alors catch affichera son nom et sortira du programme. La fonction print a le mme comportement que catch mais redclenchera l'exception aprs l'affichage. Enfin la fonction to_string convertit une exception en chane de caractres. Elle est utilise par les deux fonctions prcdentes. Si on reprend la fonction main de visualisation de bitmaps, on crira alors une fonction d'encapsulation go de la manire suivante :

# let go () =
Printexc.catch main ();;
val go : unit -> unit = <fun>


Cela permet de terminer normalement le programme en affichant la valeur de l'exception non capture.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora044.gif0000644000000000000000000000340407073350152014271 0ustar GIF89aUUU!,ڋ޼Hʶ L/T=oL*KL <ɴV 1lNWjyMsΔ'HѷwXؐX9G)9taY8i*jZ * +)jIۨ;Y{[{:|YN _cppN Cg)Eq(}CqyCsN`/G33C$BR23E}ZWJ<̐GdH&\N> eRN*>$6f&u"EFX%i2fˁi `oIquyxz~gLxh. X "*&A1}=y$)&S*hS'CQ{'8UFPe9֬+^}շi8 ihMYB+kP6@VUX)j(_֊ TQ}MH*z{N;Vc+i{U0>aVmEbA0 x..nn{Yf{1zAc%3xD?P2^ c 8IV/ԉϬO֓O]Yku}} c|.j-p vrs-axtw NSx/?yKJQ~c޶z蛇Nz .Ȫz{N;X^{f{*|;A-_A/W=wgϽwyaG  %KK?/76gNDOȝFKseRRЈ*4 d 1h%6 JQ?fBVշ*4r<]P(OsJmPZĒQdW:\ \5<ĻkK?4Nljː37vыq (F4'`ylcUDK]YHшGpR/օ5U![Pַ8&F,ɇIh5F3bmr!`FjR-)c1-VwNݬ5DA+e e9]^ Mm $61-LgiG㤦<癎mӁg=~8ܼ@J}gRڇLTJG? Ґt$-IOҔ$̦(fj(=H)&PuSl4&~yM_HҔP<6uPLTVU ^q5 T]7V^OvZkch+jA:`><^, 4)1eyC$%e=Ӫv(;ocaml-book-1.0/fr/html/book-ora015.gif0000644000000000000000000000316207073350152014270 0ustar GIF89a!,޼zHቦʶkI! ;K̦ )ʁb9Z։gs7 m;g͛M?s8sˆx2iX(Ti鄉9JHEzid3zzc;d+C+z۲EV# X|J<ʫ|,=2]Me~^{>\>.oIm^zy߼jn"C;9W|بbljD Hģ*P#ˁ Yp9  edW ̒Aљb(MF)-ӕI~(=iUgQS1m qꊫ-Ůۊ+,gv )6Y )\xs˦*{qI0ĸǢ"[xaf5Z2fbQ̪Isڗfbym0f-wq_ gv~yr5K벊[W_}F=Wp-O}[{￱/M_yI7z]G_ٙUդ WzE!|aZ"FWBLUHtz)N Ҡ1p0⁣V4b! iߋG)i$2H^R"5NEeGfR]i ]>{ס`.aXԍI虛. y7}zehmbG\%Ǚo|6ߣ(qSzjf!OJ`u~~JaXjjxZ,T1֥R;jʛzm> nPfb윖[-'.1h+pZBoi .,{|f pm|[ Ho@f2peD])r*IF0@sBC<&0lsk8 MtE56/u+H[HRCWu2wv%bPPg[h7Do77`P7w7Bs 5o1y㉊|@Y䔏Nؚ'z"z<~zˎHFo~;?Ͼ _y[>|rɯ:ͫ|=[r}>ts+.k/<6,1?;q D@>Pzz4]s$mb p G=ٵ˃#_ gㅟ!0laHh~3a4yPKKCXK xDI>.nAIE&6bF6G7+f57NQcYG_@c121HBHIQ(D% I<$}2CINnG&e@Tcxe˚WK0 t$xVV~Z7b#f.82e}LiDafMkZɛ0lYCsz g5E Uȼ%;%EOmFP1O6S}X0iπ%,hI" j 2KQ2)驊O4W(Jݨ2EM=Vә2NJ+'@z2ɂ1mJ{4_ž/y\޼ x^cB! |U7/ b׽µIqwUa[!BqpARe?MτC!v@$iK{Ʈ45~2<"@~. 290t hnOr(Aq}2(/v0Kt.x+d=7>^.ˢGI4O[$C?c'.!2$ҋ%YҒb̜'Nh'L,&NL&:fzi=hR,3 "i,*K ͆Q2OԂ3QI#%}];Æbud"`צϊd&X;iMylEiY筎ߣZ?2N2uD3[J6FL3JEQp?iȅF{Jkƽn] R7$mdM%p,gW/^m;[z`IFɋf7ribFuVk"p~aG6J+5 *C}E5 i)XM[Z?L fhU؉~BtFb8?vlVTh 9Wv8S9ZL&r䜬aH O2qrf7WV%ڜݽs]>Òzs] Z͂kh?#:WZugD4뜨)յ6=4f/h+a;Whxiom^h1g MtN9<2ZY5B̮#j=VѦvCO*5>v% W .MarcYoalc*Kk9f12BuCYJjǷmR P}7ǩ@1o:W/*o\dc#2lwj8h֕{4J~n޶Z uP.9cڋF"x64,hJ9BQ%d"}驅:ä?p 7Tt>xt#->L4K=yЏҐ[J4UgȈu)ESxuu6ܸu1剡:6(l8vi u"WF͸lx2rGx$H^L)XVRk (3H}3PCjS9`>oqSG(`id}{Ho`vx+wy8')yq%yHF"qR'o5 1p)fpCJl(,CYCYvG THgg%6IA;);g Y:?FUXpbVc j@V|`ՎEܷ,*א1yZf74 ɉY/xxUȇ菃U%焩&Dx(XP儓7v;;)#;X{㨍\\h\Ù\Yt(踜~vǕ؜91(Ee"؝_ FFu[[C֞ᩝR7Ue: z [[Awe*u uz5^j%w4w#h/2*&4680;Jق0M r+$Njfڨ9RʤYâEzi fz ;hlڦn A2tZvzxz|rLzJeMPٌ_YI[xs3G:" Ҩ@|>odMXzrTiR:;"*^̘z8 ةJ5&j+s:_}"Id|ԙS=ʺԬ&!Zz'XY0Y'OzHZ'wu0wtwJyt9 Vʰ )Zo ";$T(*,%ñ0 ]/K57۲:98+.xb.#5IñD+ 3˴s/_"!TsDavKV+,Uiek%ZV{@՛ 6oTcKy{zڶr[k2nTYa T2q/S}Wt0x Y@CeٹE})dk;dNu2VkbT&')x54iǼͻ,X#~Xz*[KFr;k|+s2}"w[HQ{r{[EY⋽/)m lUeYdR{!"3oboغ\5~ejJ¾Si"ܚ ,3"¹nJ!8":C %E"6EO,Msۧ6{wV^\bR1{ƿU2 np<k0Wq|xzsġȂ<Ȅ\Ȇ|ȈȊȌȎȂL᳔\ ɖ|ɘL ;ocaml-book-1.0/fr/html/book-ora071.gif0000644000000000000000000002320007073350152014265 0ustar GIF89at*}}}{{{yyywwwuuusssWWW333pppnnnllljjjĊ!!Q,tQ*ma)`(_&^%]$\#["!Y X (zXȰ!C+#Jءŋhq# CBɒR\r˗0ajI  㠧O"@BhH"´Ӧ J*UիX.ʵ+ `Ê ٳhӢM۷p"Kݻvw/6@?+^ǐL2e3kܣϞMz4ӨSk;b˞M6ms{w .$Q+WysQP<*ɗY }`Պ_6m_?{=L0#wlbڀfh%xk fۃۄF܅܆9 $ljp ,GbvXmywz@ސyח|G~ h 6`\Vᗺe(fqr%)"l0FƜtIՎM@)hDG"1ɘAiQ a v`ØPg~fm .)5Z+Ʈ f 'o+l\v kh' z2A*Qʙajq\jcaJbmꬲ檯/ ,pm\Q m @ #E찄"puбs9+r{Ek& +2 kiy+  {.%b!;/n"v\:+,mkj!%_@&{Ϭ .P'.'0N= tB] 2Aݜ&Rob][w/-ٴ;;Q v|̄U/F 8Ox/~f߇>&,BM4k z!Ɖ>:7~:걪N+r`G0ىv˝(:wi @º ,{ >}|=8A UBvV E#C '!sCˇ[rm7!]~8_ 5ЀC= -2 hL61 @:1BfN( `Ͱbx1Bp![#,@N0IIV]"boH$*kmIVTņi1b]HKލfK4s0<082if"@P$PN  x~l"FY7V*[WN\e-o9F]沗>c0~.l@:=h28uD jvi TȤt8]8Er\eJw3#=h3Sf? ОLP ѢFTHhJ:B |$I?3nP) XWt1L%X29 NCQԾ&}j Kv Mlb IJ౐, | (ᲘU6YHhGKҚHHjWֲlgKᶸͭnw"OK\FQr2:ХQVrP5$iZpL3bҀ|Kͯ~L턂;'L [ΰ7{ GL(NW0gL8αw@L"HN&;PL*[Xβ.{`L2hN6pL:xs>πMBЈNF;ѐ'MJ[ϊ7N{ӠG LԨNWVЦ~gMZ֪^MlbSZׇof;Ŏv#lBζ-nMr[Nn@9z?݉H6~[V7a?xv ˽p{/|N0~'3QmmF'Wr[\Ƹ4~cNAܾ8As%oʁ>;EtoX:#ֺ>=28g R}[7N/ki{קnBv/xƻҊo|? ~/^yPWЇMotMz3z+o^weƽ%{ ݯ=|No~}{[M+Gѿx򎖹_~M<{?Z_g~Wrwz޷vrnҧ~L7Vv} |}g%֗gx$xz7x"jxWp0|-i,Xy7':~8hi7n׃>hDBy:HȄTjNS'8WHhAG{ǃ=bhaG[l}_kFx)y+hWv|g\~;szI7vOO׀(~_ZQ}X~Lj։~r`7nhׅt{Kh5Vwȁv~8X}J8_KW_¸wǘ|QWo!vJ֍}&Ry X{wxX&wu3VRp!X\LJ鸌g_숐XTwU yjsH i'ǏR85׍1 j?69y3iA)ȋKiMɄոn8Y▔xVY4,ْ @)BHei\ٕrh~ zIz)tYsIir/)i喷w_%=t q} LٙI⨙9)W+[ | وG8I)fyt h핛y`ɜ]i9oi$H霴ɝȹC7ȚiI )ٞ؏ًw-ٝʙ9Xgy9y9iYGZ}zu*#J .&(zp 91j:zӉw)t8:Jڣ+TXMwky>uk<X xTڞ\^J[zj (ٚǦm@!Bʠهc:)qezxj a )ٚpz%Np CJqfsvʩ٤L6WlʫM:8WypJ- ʬf-ryJ}|J:7&&ɚʮꚪJ)'q :xs5YzVrOy˝+ʑ*ܷ*,;ڲ $k[i2ےމ س;˛U &#Ii[֦VɖFNqp[^h?ɗR[hegKhi;nDY`(^ق}gkwg{˶kZ}[{*[;5ȸۘS9Yv{V{hg:u)۹kӚ+[ś;KZ;h۹{  F֫hU}˺KsGƂ۽c|W|Fː󋿨zYhɽ^) iP k콵f;v8$%Lv+L**٭)Tfvi*{'FEL,,[l͞ʧUwF,&F{W׌Ͱη|nL<klL,n|ϲ |ۼ!˾l{ȝ'ȨL*FM`KξVeȝs j&*<5|k| ]Ҝ,h"=8nMtOӄUmZIlumS,l |d~?])!MKԸvJrJ ppE:+{6ؖ+ô6٠]{;\m_0 ٨ٍۧ^! | ?|ۿ[Բ+|FQ;ܪ mh} ݥ-ZǸܙ K;v=m+Nݾ6:bw>xg X-Ļt M>"u. ˿5;Ҽ3n;~ιMϯkI^ޥrA \ommjg_FSo]iU[e=__U ysBG ōi ^N.yMg5}(ۡԣ&0 ]mԟ=뱳5ӈs^k,um^Þf}sބyȱNIňt؅pz׋)-c emlj%-RsM˛ nsJ޲՗(bF^ۓߪ .7\9jծO?չlWLj#ߪ,!nCTM$ɯ.C(\Jj37jJ NnZ|R?L\\]ib?Woۊj5j}%8v@)9IYIɹi*:jhz:i*K[{,+lKx̜(}\[]݉Y=U.Nm]i*[K <[|Ϥ +]"S ҆S-o*{ʠA".#hM>!=6E[J7Ar":i(|S䰞"#Ks*e-2f™4S5hE&J1e_KPGc Jmڝf&555^]uR prm\p L92c˅(S99KΖ4[c]u5ٓ`϶]eܪUVH!v{ϭo#fs警K~m=|-S2?$vO}y].H[ӏ]7 Aw DՃ `er!'x9M%w%=b%4\7$1JWK$Y@'dM FdU$Gf%U'厛i d]^嚦E`jxXfX:Ycs(眍y)$4KJ4.Ah R*ajG觎ԋ'CZ*Ya\G**~T4bY&5үVk$eбn*PhzLQbjI2ʬ j[Y4K.;jlRo{o9ѹ'uo$p< Iۯ$0f*%rpq#%abQA.p3%+ A'qgI[,<{3;OM5=-vtW՛)ZTDK]h[ں5wy#҂9o]$? w]ݮd4`t.7Sk6Qs}I2ݍMBpsc_)z7bg[堿(,>3EzAe aE{CkS÷"0D'd=ן'?N(MnEy_Vԧu-xC33vYN=:#W!oW BƐZb@xjUA"b59L o ""fL`ʒضuɉ_1E*2ŊbƯ[n" (F.qt [8oOesڵ88&aHtiT Fedg"5iG.Љ H3-z~>i^rF4V2' KXC$پYK[ ~<8ƛXVy*RnLmf$U 5.U@L9)/uTJY]8#C$U-@5~e 'ކ z T=NR2'z~rtKR ZInY+C@SQy/l$iHM͌.ZFS}T(63jUVC%*W͸ճfdj[/Ua*]ž2K{j_}*fŮmI_$(P [ 6՛9Q 2=6Ll(KW&s^gBZ3,f*Zz;L>ո&pEq; o7eu*j$\m([djdEɰʜ M~"NcպFGrċF!8&EtuIYki!l˟wUQ4+/WR09 ʕ.0%fK պa PJUqb; b/zxk<Gt8t}x!2&xe+/lbB ͈ifV1=%lNwW֋eDwHҜs'NoYςrK8C!9!Zu,E3:<֕ cHoH5FB## NPfP-Qs[=h8:iR׊;`MlUvHe=򱶷#kcCU9&ek(ק-idʂmD?[w淫Fzχ8z]o0' \jGxMieǷ]t#H\s!]ҡ[>2i\r\#uEi"iT\tr6oeQ4ɭpI1'3H!5;k*36`w; #bAC֛^E O, ]Sw |gaxj${yW3ܣMuo7;¤Elg7.ĭв/7~;xꨯ/b40YP=QZ7c$;"*?K#}{'w$W~ QUyXPE"Ȁ"V $vǂ]A3!ZVExuoQ2xQHX egS3rWr(;YW~Y [c_Sv8X^7.o^wc_RO Wz@Uk4+uv-rG5(e3g_LiiVJK`6rTOwu/؈NP'J?BxEb6fwhwDWfh~w$u،TKx.8ILES,MyX/ZI\,S~z8~^Yf %9D9uŨy# 1YH؎(tIx錳L|9ȍ6x] 4In^qfS2yٙd}ؕI Yyb2Jw 9 I|4㉝虡蚈'V7I^(9e|) ΉKeE:ݩ⹝}uO)k}nyWډRĹI*uujj ʠ *Q(~)&zpٟ*Ykj J]#R 1Z+h|(9M : +ǣY(G"`I Uy:H @ZȨQeTc ]?dʦPqE:>ajmjG?tZw i]DzFf_ VJ JJ|꩓ɩ~ZjO-jEꐬ46:#Jgjǘ碴Ċ %j* ʬ2zq:2 * ʭ:( J抮jʮb Jڣꯥۮ'*[k +  *{˱ !+#K%k')+˲-/ 1+3K5k79;˳=? A+CKEkGIK˴MހQ+SKSQPY[˵]_ a+cKekgik˶mW[Ppukwy{˷}^Kc+dk˸y[a;cKk{O빟 +;ocaml-book-1.0/fr/html/index.html0000644000000000000000000011515607421273601013646 0ustar













Dveloppement d'applications avec


Emmanuel CHAILLOUX - Pascal MANOURY - Bruno PAGANO




Avant-propos

Introduction

Chapitre 1   Comment obtenir Objective CAML

Partie I
Noyau du langage



Chapitre 2   Programmation fonctionnelle

Chapitre 3   Programmation imprative

Chapitre 4   Styles fonctionnel et impratif

Chapitre 5   Interface graphique

Chapitre 6   Applications

Partie II
Outils de Dveloppement



Chapitre 7   Modes de compilation et portabilit

Chapitre 8   Bibliothques

Chapitre 9   Rcuprateur automatique de mmoire

Chapitre 10   Outils d'analyse des programmes

Chapitre 11   Outils d'analyses lexicale et syntaxique

Chapitre 12   Interoprabilit avec C

Chapitre 13   Applications

Partie III
Organisation d'Applications



Chapitre 14   Programmation modulaire

Chapitre 15   Programmation par objets

Chapitre 16   Comparaison des modles d'organisation

Chapitre 17   Applications

Partie IV
Concurrence et rpartition



Chapitre 18   Communication et processus

Chapitre 19   Programmation concurrente

Chapitre 20   Programmation rpartie

Chapitre 21   Applications

Chapitre 22   Dveloppement d'applications en Objective CAML

Conclusion

Partie V
Annexes



Annexe A   Types cycliques

Annexe B   Objective CAML 2.99

Rfrences

Index







Parutions O'Reilly




Ce document a t traduit de LATEX par HEVEA et HACHA.
ocaml-book-1.0/fr/html/book-ora119.html0000644000000000000000000000550307421273602014476 0ustar Programme principal en C Prcdent Index Suivant

Programme principal en C

Jusqu' maintenant le point d'entre du programme tait en Objective CAML. Rien n'empche de le faire dmarrer en C. Pour cela il faudra dfinir, dans le programme C, la fonction main habituelle. Elle initialisera le runtime d'Objective CAML par la fonction caml_main(char **) qui prend en argument le tableau d'arguments correspondant la valeur du tableau Sys.argv d'Objective CAML. Le contrle peut tre pass ce dernier en utilisant un callback (voir page ??).

Inclure du code Objective CAML en C

Le compilateur Objective CAML permet d'engendrer des fichiers objets C (fichiers avec l'extension '.o') en lieu et place des fichiers objets Objective CAML (d'extension '.cmo' ou '.cmx'). Il suffit d'utiliser l'option -output-obj du compilateur et d'ajouter la bibliothque unix.cma.
ocamlc -output-obj unix.cma fichiers.ml
ocamlopt -output-obj unix.cmxa fichiers.ml
partir des fichiers sources en Objective CAML fichiers.ml, le fichier objet portant par dfaut le nom camlprog.o est engendr.

Un excutable est obtenu en effectuant l'dition de liens avec un compilateur C en ajoutant la bibliothque libcamlrun.a pour un programme compil en code-octet ou la bibliothque libasmrun.a pour la compilation en code natif.
cc camlprog.o fichiersC.o -lcamlrun 
cc camlprog.o fichiersC.o -lasmrun
L'invocation d'une fonction Objective CAML depuis le programme C se fait comme dcrit prcdemment par les fonctions callback. La seule diffrence est que l'initialisation et le lancement du runtime d'Objective CAML se fait par la fonction caml_startup la place de caml_main.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora024.html0000644000000000000000000000722207421273601014470 0ustar Introduction Prcdent Index Suivant

Introduction

la diffrence de la programmation fonctionnelle o on calcule une valeur par l'application d'une fonction ses arguments sans se soucier du droulement des oprations, la programmation imprative est plus proche de la reprsentation machine car elle introduit un tat mmoire que le droulement des actions d'un programme va modifier. On appelle instructions ces actions des programmes et un programme impratif est une suite, ou une squence, d'instructions. L'tat mmoire est susceptible d'tre modifi l'excution de chaque instruction. On considre les oprations d'entres-sorties comme des modifications de la mmoire, de la mmoire vido ou de fichiers.

Ce style de programmation est directement inspir de la programmation assembleur. On le retrouve dans les premiers langages volus gnralistes (Fortran, C, Pascal, etc. ). En Objective CAML les lments suivants du langage correspondent ce modle :
  • les structures de donnes physiquement modifiables, comme les tableaux ou les enregistrements champs modifiables;
  • les oprations d'entres-sorties;
  • les structures de contrle de l'excution comme les boucles et les exceptions.
Certains algorithmes s'crivent plus facilement dans ce style de programmation. On peut citer comme exemple le produit de deux matrices. Mme s'il est effectivement possible de le traduire dans une version purement fonctionnelle, o des listes remplacent les vecteurs, cela n'est ni naturel, ni efficace par rapport une criture imprative.

L'intrt d'intgrer ce modle dans un langage fonctionnel est de pouvoir crire certains algorithmes dans ce style de programmation quand ceux-ci s'y prtent. Les deux principaux dsavantages par rapport au style purement fonctionnel sont :
  • de compliquer le systme de types du langage et de rejeter certains programmes qui sans cela seraient considrs comme corrects;
  • de devoir tenir compte de la reprsentation mmoire et de l'ordre des calculs.
Nanmoins, avec quelques rgles de prudence dans l'criture des programmes, le choix entre plusieurs styles de programmation offre de plus grandes possibilits d'criture d'algorithmes, ce qui est l'objectif principal des langages de programmation. En outre, un programme crit dans un style proche de l'algorithme utilis sera plus simple donc aura plus de chances d'tre correct (ou, tout du moins, plus rapidement mis au point).

Pour ces raisons, le langage Objective CAML possde des types de donnes dont les valeurs sont physiquement modifiables, des structures de contrle de l'excution des programmes et une bibliothque d'entres-sorties dans un style impratif.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora002.html0000644000000000000000000001124307421273601014462 0ustar Objectifs Prcdent Index Suivant

Objectifs

Objective CAML est un langage de programmation : un de plus dira-t-on ! Ils sont en effet dj nombreux et pourtant il en apparat constamment de nouveaux. Au del de leurs disparits, la conception et la gense de chacun d'eux procdent d'une motivation partage : la volont d'abstraire.
S'abstraire de la machine
En premier lieu, un langage de programmation permet de ngliger l'aspect << mcanique >> de l'ordinateur; il autorise mme oublier le modle du microprocesseur ou le systme d'exploitation sur lequel sera excut le programme.
S'abstraire du modle opratoire
La notion de fonction que possde sous une forme ou une autre la plupart des langages est emprunte aux mathmatiques et non l'lectronique. D'une manire gnrale, les langages substituent des modles formels aux conceptions purement calculatoires. Ils y gagnent en expressivit.
Abstraire les erreurs
Il s'agit ici de la tentative de garantir la sret d'excution; un programme ne doit pas se terminer brutalement ou devenir incohrent en cas d'erreur. Un des moyens pour y parvenir est le typage statique fort des programmes et la mise en oeuvre d'un mcanisme d'exceptions.
Abstraire les composants (i)
Les langages de programmation donnent la possibilit de dcouper une application en diffrents composants logiciels, plus ou moins indpendants et autonomes. La modularit permet une structuration de plus haut niveau de l'ensemble d'une application complexe.
Abstraire les composants (ii)
L'existence d'units de programmes a ouvert la possibilit de leur rutilisation dans d'autres contextes que ceux de leur dveloppement. Les langages objets constituent une autre approche de la rutilisabilit permettant la ralisation trs rapide de prototypes.
Objective CAML est un langage rcent qui se place dans l'histoire des langages de programmation comme un lointain descendant de LISP ayant su tirer les enseignements de ses cousins en incorporant les principales caractristiques des autres langages. Il est dvelopp l'INRIA1 et s'appuie sur une longue exprience de conception de langages de la famille ML. Objective CAML est gnraliste pour l'expression d'algorithmes symboliques ou numriques. Il est orient objet et possde un systme de modules paramtrs. Il permet le dveloppement d'applications concurrentes ou distribues. Il possde une excellente sret l'excution grce son typage statique, son mcanisme d'exceptions et son rcuprateur automatique de mmoire. Il est performant tout en tant portable. Enfin, il dispose d'un riche environnement de dveloppement.

Objective CAML n'a jamais fait l'objet d'une prsentation au << grand public >>. C'est la tche laquelle se sont attels les auteurs en donnant cet ouvrage trois objectifs :
  1. Dcrire de manire approfondie le langage Objective CAML, ses bibliothques et son environnement de dveloppement.
  2. Montrer et expliquer quels sont les concepts qui se cachent derrire les styles de programmation utilisables avec Objective CAML.
  3. Illustrer par de nombreux exemples comment Objective CAML peut servir de langage de dveloppement pour diffrentes familles d'applications.
Le but des auteurs est de fournir des lments de choix entre les diffrents styles de programmation et les diffrentes possibilits d'organisation d'un programme pour, en adquation avec un problme donn, obtenir une maintenance facilite et une bonne rutilisabilit des composants logiciels dvelopps.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora012.html0000644000000000000000000001734207421273601014471 0ustar Prsentation de la partie I Prcdent Index Suivant

Prsentation de la partie I

La premire partie de cet ouvrage est une introduction complte au noyau du langage Objective CAML, en particulier le mcanisme d'valuation d'une expression, le typage statique et le modle mmoire des donnes.

Une expression est la description d'un calcul. L'valuation d'une expression retourne la fin du calcul une valeur. L'excution d'un programme en Objective CAML correspond au calcul d'une expression. Les fonctions, les structures de contrle de l'excution d'un programme, telles les conditionnelles ou les boucles, sont elles aussi des expressions.

Le typage statique garantit que le calcul d'une expression ne pourra pas dclencher une erreur de type l'excution. Dans les faits, l'application d'une fonction des arguments (ou paramtres effectifs) n'est accepte que s'ils ont tous un type compatible avec les paramtres formels indiqus dans la dfinition de la fonction. Le langage Objective CAML possde en outre une infrence de types : le compilateur dtermine automatiquement le type le plus gnral d'une expression.

Enfin la connaissance minimale de la reprsentation des donnes est indispensable au programmeur pour matriser les effets des modifications physiques sur les donnes.

Plan

Le chapitre 2 contient une prsentation complte de la partie purement fonctionnelle du langage et des contraintes dues au typage statique. La notion de calcul d'expression y est longuement illustre. Les structures de contrle suivantes sont dtailles : la conditionnelle, l'application et le filtrage de motif. Les diffrences entre type et domaine d'une fonction sont discutes afin d'introduire le mcanisme d'exceptions. Ce trait du langage sort du cadre fonctionnel et permet la gestion de ruptures de calcul.

Le chapitre 3 expose le style impratif. Les constructions y sont plus proches des langages classiques. Les structures de contrle associes comme la squence et l'itration y sont prsentes ainsi que les structures de donnes physiquement modifiables. L'interaction entre modifications physiques et partage de donnes est alors dtaille. L'infrence de types y est prcise en tenant compte de ces nouvelles constructions.

Le chapitre 4 compare les deux styles prcdents et surtout prsente diffrents styles mixtes. Ce mlange permettra en particulier de construire des structures de donnes paresseuses, y compris physiquement modifiables.

Le chapitre 5 montre l'utilisation de la bibliothque Graphics incluse dans la distribution du langage. Les notions de base de la programmation graphique y sont exposes et immdiatement mises en oeuvre. Il en est de mme pour la construction d'interfaces graphiques grce la gestion d'vnements minimale fournie par cette bibliothque.

Ces quatre premiers chapitres sont illustrs par un exemple complet, l'implantation
d'une calculatrice, qui volue de chapitre en chapitre.

Le chapitre 6 prsente trois applications compltes : une petite base de donnes, l'interprte d'un mini-BASIC et le jeu Dmineur. Les deux premiers exemples sont construits principalement dans un style fonctionnel, alors que le troisime l'est dans un style impratif.

Rudiments de syntaxe

Avant de commencer nous indiquons les premiers lments de syntaxe du langage. Un programme est une suite de phrases du langage. Une phrase est un lment de syntaxe complet directement excutable (expression, dclaration). La fin d'une phrase se termine par un double point-virgule (;;). On distingue trois types de dclarations qui sont repres par un mot cl diffrent :
dclaration de valeur : let
dclaration d'exception : exception
dclaration de type : type
Tous les exemples donns dans cette partie sont crire au niveau de la boucle d'interaction du langage.

Voici un premier (petit) programme Objective CAML, entr sous la boucle d'interaction, dont l'invite est le caractre dise (#), o sont dfinies une fonction fact calculant la factorielle d'un nombre, et son application l'entier 8.

# let rec fact n = if n < 2 then 1 else n * fact(n-1) ;;
val fact : int -> int = <fun>
# fact 8 ;;
- : int = 40320
Ce programme comprend deux phrases. La premire est la dclaration d'une valeur fonctionnelle et la seconde est une expression. On s'aperoit que la boucle d'interaction affiche trois informations qui sont : le nom de la dclaration, ou le caractre moins (-) dans le cas d'une expression ; le type infr ; et la valeur retourne. Dans le cas d'une valeur fonctionnelle, le systme affiche <fun>.

L'exemple suivant montre la manipulation de fonctions comme valeurs du langage. On y dfinit tout d'abord la fonction succ qui calcule le successeur d'un entier, puis la fonction compose qui compose deux fonctions. Celle-ci sera applique fact et succ.

# let succ x = x+1 ;;
val succ : int -> int = <fun>
# let compose f g x = f(g x) ;;
val compose : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun>
# compose fact succ 8 ;;
- : int = 362880
Ce dernier appel effectue le calcul fact(succ 8) et retourne le rsultat escompt. Notons que les fonctions fact et succ sont passes en paramtres compose au mme titre que l'entier 8.




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora178.html0000644000000000000000000002513207421273602014503 0ustar Communication synchrone Prcdent Index Suivant

Communication synchrone

Le module Event de la bibliothque des processus lgers implante la communication de valeurs quelconques entre deux processus via des canaux de communication particuliers. La communication effective de la valeur est synchronise au moyen d'vnements d'mission ou de rception.

Ce modle de communication synchronise sur des vnements permet de transfrer sur des canaux typs des valeurs du langage, y compris des fermetures, des objets ou des vnements. Il est dcrit dans [Rep92].

Synchronisation sur vnements de communication

Les vnements primitifs de communication sont :
  • send c v envoie une valeur v sur le canal c
  • receive c rceptionne une valeur sur la canal c
Pour qu'ils ralisent l'action physique qui leur est associe, deux vnements doivent tre synchroniss. Pour cela on introduit une opration de synchronisation (sync) sur les vnements. L'mission et la rception d'une valeur ne sont effectives que lorsque les deux processus communicants sont en phase. Si un seul processus cherche se synchroniser, l'opration est bloquante et attend que le deuxime processus effectue sa synchronisation. Cela implique qu'un metteur cherchant synchroniser l'envoi d'une valeur (sync (send c v)) peut se retrouver bloqu en attente d'une synchronisation du rcepteur (sync (receive c)).

Valeurs transmises

Les canaux de communication par o transitent les valeurs changes sont typs : seules les valeurs du type paramtre du canal y sont acceptes. Rien n'empche de crer de nombreux canaux par type de valeur communiquer. Comme cette communication s'effectue entre processus lgers Objective CAML, n'importe quelle valeur du langage peut tre envoye sur un canal de son type. Cela est valable pour les fermetures, les objets et aussi des vnements pour une demande de synchronisation dporte.

Module Event

Les valeurs encapsules dans les vnements de communication transitent par des canaux de communication du type abstrait 'a channel. La fonction de cration d'un canal est :

# Event.new_channel ;;
- : unit -> 'a Event.channel = <fun>


Les vnements d'mission et de rception sont crs par un appel aux fonctions :

# Event.send ;;
- : 'a Event.channel -> 'a -> unit Event.event = <fun>
# Event.receive ;;
- : 'a Event.channel -> 'a Event.event = <fun>


On peut considrer les fonctions send et receive comme des constructeurs du type abstrait 'a event. L'vnement construit partir de l'mission ne conserve pas une information de type de la valeur transmettre (type unit Event.event). Par contre l'vnement de rception en tient compte pour rcuprer la valeur pendant une synchronisation. Ces fonctions ne sont pas bloquantes dans la mesure o la transmission d'une valeur n'est ralise qu'au moment de la synchronisation de deux processus par la fonction :

# Event.sync ;;
- : 'a Event.event -> 'a = <fun>
Cette fonction peut tre est bloquante pour l'metteur et le rcepteur.

Il en existe une version non bloquante :

# Event.poll ;;
- : 'a Event.event -> 'a option = <fun>
Cette fonction vrifie qu'un autre processus est en attente de synchronisation. Si c'est le cas elle ralise les transmissions, et retourne la valeur Some v si v est la valeur associe l'vnement, et None sinon. Le message reu, extrait par la fonction sync peut tre le rsultat d'un processus plus ou moins compliqu mettant en oeuvre d'autres changes de messages.

Exemple de synchronisation
On dfinit trois processus lgers, le premier t1 envoie une chane de caractres sur le canal c (fonction g) partag par tous les processus. Les deux autres t2 et t3 attendent une valeur sur ce mme canal. Voici les fonctions excutes par les diffrents processus :

# let c = Event.new_channel ();;
val c : '_a Event.channel = <abstr>
# let f () =
let ids = string_of_int (Thread.id (Thread.self ()))
in print_string ("-------- avant -------" ^ ids) ; print_newline() ;
let e = Event.receive c
in print_string ("-------- pendant -------" ^ ids) ; print_newline() ;
let v = Event.sync e
in print_string (v ^ " " ^ ids ^ " ") ;
print_string ("-------- aprs -------" ^ ids) ; print_newline() ;;
val f : unit -> unit = <fun>
# let g () =
let ids = string_of_int (Thread.id (Thread.self ()))
in print_string ("Dbut de " ^ ids ^ "\n");
let e2 = Event.send c "hello"
in Event.sync e2 ;
print_string ("Fin de " ^ ids) ;
print_newline () ;;
val g : unit -> unit = <fun>


Les trois processus sont alors crs et excuts :

# let t1,t2,t3 = Thread.create f (), Thread.create f (), Thread.create g ();;
val t1 : Thread.t = <abstr>
val t2 : Thread.t = <abstr>
val t3 : Thread.t = <abstr>
# Thread.delay 1.0;;
Dbut de 5
-------- avant -------6
-------- pendant -------6
hello 6 -------- aprs -------6
-------- avant -------7
-------- pendant -------7
Fin de 5
- : unit = <unknown constructor>


L'mission peut tre bloquante. La trace de fin de t1 s'affiche aprs les traces de synchronisation de t2 et t3. Seul un des deux processus t1 ou t2 est vraiment termin, comme le montrent les appels suivants :

# Thread.kill t1;;
- : unit = ()
# Thread.kill t2;;
Uncaught exception: Failure("Thread.kill: killed thread")



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora181.html0000644000000000000000000000344307421273602014476 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a abord le domaine de la programmation concurrente o plusieurs processus interagissent soit sur une mmoire partage, soit par une communication synchrone. Le premier cas reprsente la concurrence pour la programmation imprative. En particulier, nous avons dtaill les mcanismes d'exclusion mutuelle dont la mise en oeuvre permet la synchronisation des processus pour l'accs la mmoire partage. La communication synchrone offre un modle pour la concurrence en programmation fonctionnelle. En particulier, la possibilit d'envoyer des fermetures et des vnements de synchronisation sur les canaux de communication autorise une composition des calculs des diffrents processus.

Les processus utiliss dans ce chapitre sont les processus lgers du module Thread d'Objective CAML.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora040.html0000644000000000000000000004653607421273601014501 0ustar Flots de donnes Prcdent Index Suivant

Flots de donnes

Les streams, ou flots, sont des squences, potentiellement infinies, d'lments de mme nature. L'valuation d'une partie d'un flot s'effectue la demande, c'est--dire quand cela est ncessaire pour le calcul en cours : c'est une structure de donnes paresseuse.

Le type stream est un type de donnes abstrait dont on ne connat pas l'implantation. On manipule les objets de ce type par des fonctions de construction et de destruction. Pour le confort de l'utilisateur, Objective CAML fournit des constructions syntaxiques simples pour construire des flots et accder leurs lments.

Warning


Les streams sont une extension du langage et ne font pas partie du noyau stable d'Objective CAML.


Construction

La syntaxe allge de construction des flots est inspire de celle des liste ou des tableaux. Le flot vide s'crit :

# [< >] ;;
- : 'a Stream.t = <abstr>


On peut construire un flot par numration de ses lments en les faisant prcder d'une apostrophe (caractre ')

# [< '0; '2; '4 >] ;;
- : int Stream.t = <abstr>


Les expressions qui ne sont pas prcdes d'une apostrophe sont considres comme des sous-flots :

# [< '0; [< '1; '2; '3 >]; '4 >] ;;
- : int Stream.t = <abstr>
# let s1 = [< '1; '2; '3 >] in [< s1; '4 >] ;;
- : int Stream.t = <abstr>
# let concat_stream a b = [< a ; b >] ;;
val concat_stream : 'a Stream.t -> 'a Stream.t -> 'a Stream.t = <fun>
# concat_stream [< '"if"; '"c";'"then";'"1" >] [< '"else";'"2" >] ;;
- : string Stream.t = <abstr>


Le module Stream fournit d'autres fonctions de construction. Par exemple, les fonctions of_channel et of_string retournent un flot contenant une suite de caractres, partir d'un canal d'entre ou d'une chane de caractres.

# Stream.of_channel ;;
- : in_channel -> char Stream.t = <fun>
# Stream.of_string ;;
- : string -> char Stream.t = <fun>


Le calcul retard des flots permet de manipuler des structures de donnes infinies d'une faon similaire au type 'a enum dfini la page ??. On dfinit le flot des entiers naturels par son premier lment et une fonction calculant le flot des lments suivants.

# let rec nat_stream n = [< 'n ; nat_stream (n+1) >] ;;
val nat_stream : int -> int Stream.t = <fun>
# let nat = nat_stream 0 ;;
val nat : int Stream.t = <abstr>


Destruction et filtrage de flots

La primitive next permet la fois d'valuer, de rcuprer et d'enlever le premier lment d'un flot :

# for i=0 to 10 do
print_int (Stream.next nat) ;
print_string " "
done ;;
0 1 2 3 4 5 6 7 8 9 10 - : unit = ()
# Stream.next nat ;;
- : int = 11
Lorsque le flot est puis, une exception est dclenche.

# Stream.next [< >] ;;
Uncaught exception: Stream.Failure


Pour manipuler les flots, Objective CAML offre une construction de filtrage ddie dit filtrage destructif. La valeur filtre est calcule et retire du flot. Il n'y a pas de notion d'exhaustivit du filtrage des flots et, parce que nous manipulons une structure paresseuse potentiellement infinie, on peut ne pas filtrer la totalit du flot. La syntaxe de filtrage est :

Syntaxe


match expr with parser [< 'p1 ...>] -> expr1 | ...


La fonction next peut s'crire :

# let next s = match s with parser [< 'x >] -> x ;;
val next : 'a Stream.t -> 'a = <fun>
# next nat;;
- : int = 12
Remarquez que l'numration des entiers reprend l o nous l'avions laisse.

la manire de l'abstraction, il existe une forme syntaxique filtrant un paramtre de type Stream.t.

Syntaxe


parser p -> ...
La fonction next peut alors se rcrire ainsi :

# let next = parser [<'x>] -> x ;;
val next : 'a Stream.t -> 'a = <fun>
# next nat ;;
- : int = 13


Il est possible de filtrer le flot vide, mais attention : le motif de flot [<>] filtre n'importe quel flot. En effet, un flot s est toujours gal au flot [< [<>]; s >]. Il faut en consquence inverser l'ordre usuel du filtrage :

# let rec it_stream f s =
match s with parser
[< 'x ; ss >] -> f x ; it_stream f ss
| [<>] -> () ;;
val it_stream : ('a -> 'b) -> 'a Stream.t -> unit = <fun>
# let print_int1 n = print_int n ; print_string" " ;;
val print_int1 : int -> unit = <fun>
# it_stream print_int1 [<'1; '2; '3>] ;;
1 2 3 - : unit = ()
En utilisant le fait que le filtrage est destructif, on peut galement crire :

# let rec it_stream f s =
match s with parser
[< 'x >] -> f x ; it_stream f s
| [<>] -> () ;;
val it_stream : ('a -> 'b) -> 'a Stream.t -> unit = <fun>
# it_stream print_int1 [<'1; '2; '3>] ;;
1 2 3 - : unit = ()


Si les flots sont paresseux, ils sont cependant de bonne volont et ne refusent jamais de fournir leur premier lment et lorsque celui-ci est fourni, il est perdu. Ceci a des consquences sur le filtrage. La fonction suivante est une tentative (voue l'chec) d'un affichage par couple d'un flot d'entiers, sauf ventuellement pour le dernier lment.

# let print_int2 n1 n2 =
print_string "(" ; print_int n1 ; print_string "," ;
print_int n2 ; print_string ")" ;;
val print_int2 : int -> int -> unit = <fun>
# let rec print_stream s =
match s with parser
[< 'x; 'y >] -> print_int2 x y; print_stream s
| [< 'z >] -> print_int1 z; print_stream s
| [<>] -> print_newline() ;;
val print_stream : int Stream.t -> unit = <fun>
# print_stream [<'1; '2; '3>];;
(1,2)Uncaught exception: Stream.Error("")


Les deux premiers lments du flot ont bien t affichs, mais lors de l'valuation de l'appel rcursif (print_stream [< 3 >]) le premier motif a trouv une valeur pour x, qui a donc t consomme, mais il ne restait plus rien pour y. C'est ce qui a provoqu l'erreur. En fait, le second filtre est inutile puisque, si le flot n'est pas vide, le premier motif peut toujours commencer l'valuation.

Pour obtenir le rsultat attendu, il faut squentialiser le filtrage :

# let rec print_stream s =
match s with parser
[< 'x >]
-> (match s with parser
[< 'y >] -> print_int2 x y; print_stream s
| [<>] -> print_int1 x; print_stream s)
| [<>] -> print_newline() ;;
val print_stream : int Stream.t -> unit = <fun>
# print_stream [<'1; '2; '3>];;
(1,2)3
- : unit = ()


Si le filtrage choue sur le premier lment d'un filtre alors, on retrouve le comportement connu du filtrage :

# let rec print_stream s =
match s with parser
[< '1; 'y >] -> print_int2 1 y; print_stream s
| [< 'z >] -> print_int1 z; print_stream s
| [<>] -> print_newline() ;;
val print_stream : int Stream.t -> unit = <fun>
# print_stream [<'1; '2; '3>] ;;
(1,2)3
- : unit = ()


Aux limites du filtrage

Par son caractre destructif, le filtrage des flots diffre du filtrage des types somme. Nous allons voir comment il peut en diffrer radicalement.

On peut crire, de faon naturelle, une fonction qui calcule la somme des lments d'un flot :

# let rec somme s =
match s with parser
[< 'n; ss >] -> n+(somme ss)
| [<>] -> 0 ;;
val somme : int Stream.t -> int = <fun>
# somme [<'1; '2; '3; '4>] ;;
- : int = 10
Mais on peut galement consommer le flot << de l'intrieur >> en nommant le rsultat obtenu :

# let rec somme s =
match s with parser
[< 'n; r = somme >] -> n+r
| [<>] -> 0 ;;
val somme : int Stream.t -> int = <fun>
# somme [<'1; '2; '3; '4>] ;;
- : int = 10


Nous tudierons d'autres utilisations importantes des flots dans le chapitre 11 consacr aux analyses lexicale et syntaxique. En particulier, nous verrons comment la consommation d'un flot de l'intrieur peut tre mise profit.








Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora080.gif0000644000000000000000000001042407073350152014271 0ustar GIF89a#UUU999rrr!,#h0I8ͻ`(dihzk,tm3Ap)`Ĥr0#٬Z@7kqشz5z\7GIesQ TzB-~]=w<.|Phve{T R*韓Vj6i"ii3jsjҳjj3kskkkĶ2lVhƣQкlԢ3mؒJ܊slmkJϸ+o"Rz=RkK([Kf@\I |cziy81V'M^&,&S_҈2Ei !Arf.j Qtb@Z I.$0agkG7@1yUwe`? pHCeEw}E7"6*ަ܌z,wqL3(]u!\g/] }Ϣ#w}]h5L4.Fp|/oHavXHr(sH{;Ur|GfZzjXֆl5NGsFR$h\j#7f3kcF▣D!3f"/TbT!X .Ă S E!UBz! %26DsHX>!(D\1 (L( OGs1~TQUN ERt"X2* R]M̍Z IcYt7qT[R Ā06a3\Bmfix& Nsk(.5-4:2K)#Si> bhI|R'΂8m&3;LaE<ċ,eٙFt΁妥9FiZ&9}742I1 Q:Ifʌj!Et3Mm.b Qٱkc7 -|۞$r(T匲4)7_#PPDcR+$##*v,O]˜HժZ58*Ҵ¬vajXF[]h] q퀫XbAy@Tj ZoA_5r6q!0;[Z ޑ8+C4+ 5 Ab8R˖gwuP|sLю6GsK0QƝ86[m'1,d,>gWk6*7v MT '̂Tn=*xۖýzxA7Jiv** Q4,lF7)] Iʳ>3x:36KeH}O$r*;A5lkH`cm8Wa,'=6Gִ:8dG=KU򔡗AmL=J拎ev Ϝf>9l~sR+(Έ":WMEs Be/[9Dѱ^163~*uQiOh؞h44 4(I([f&ImS/bCz9=W^g&ӂV|Z^23$Nj+l%چ.lM=ɭmPDtV mr7b+R BEWIIhN!бעIk*o|5f w.efS ãy:^8AQ#LI; 1ߒC&"rtl_D Sς^ S||Hj_C< Lx A LV)Hu^˄{|,5#NjoMTdv) ^۶|V!o>}3tq{Yƃ{ ';Xٳ_е˚Or'r5'My}0'| N^b6<"r`0p3Q*l<)|sfvz:qx0$4C$DW%CbFL 2ept7h>U=SB clv t6"4ĂzJA&Hwq] ZR2CGV~  1sFx"7!wulЇ`(yt71|=9npkX"fD% gE7rhx rW0xw =4c?bnAֈ'*>wQvhxB*WdHxdE8Ш-򧋁ոs9w˘r"Dxeh@UoH~C`⒏dXAd@EA FҐ+%807؋u/vE{ RAyMYvYF'B,Hף3ZZ>w|EWNlYf!# \fLZ"IDK+32_do}\cYE i3(iQ]q)MAx ʵ1dqSsj!?V\gmNM"3ZS_UlYn5|7sN$t$>t)=#+ea.ea%$(Ex/ę|ђd*29u1dGB9sAXȎ[vQ yKI?׉՝ 0H\M`>Jp{CT)9tXiAzR1#QmXs UYDX{+ᘫWLcd7 ˍ Etzꪰ,%ˆ +؊{y--ݪ3$ (2΂`rs *'KKer@ X麲\[h_h1 ZBc^J;F"ضk Ä[:z' 96ڧ^mzN8LQXJLƒMiŴ5?[WF9ȭeehw!i %Ed{~rljRi8>&-5R$2x.Ke`';G+ z̐w[&,g!|T:w 0! r̊%,/'춭b!)< $[j.lf?C4=(9,mFOLg' ,O Exercices Prcdent Index Suivant

Exercices

Piles en objet

On reprend l'exemple des piles vu prcdemment la sauce objet.
  1. Dfinir une classe intpile en utilisant les listes d'Objective CAML implantant des listes d'entiers et disposant des mthodes empile, depile, sommet et taille.

    # exception PileVide

    class intpile () =
    object
    val p = ref ([] : int list)
    method empile i = p := i:: !p
    method depile () = if !p = [] then raise PileVide else p := List.tl !p
    method sommet () = if !p = [] then raise PileVide else List.hd !p
    method taille () = List.length !p
    end ;;
    exception PileVide
    class intpile :
    unit ->
    object
    val p : int list ref
    method depile : unit -> unit
    method empile : int -> unit
    method sommet : unit -> int
    method taille : unit -> int
    end


  2. Crer une instance contenant 3 et 4 comme lments de la pile.

    # let p = new intpile () ;;
    val p : intpile = <obj>
    # p#empile 3 ;;
    - : unit = ()
    # p#empile 4 ;;
    - : unit = ()


  3. Dfinir une nouvelle classe pile contenant des lments rpondant la mthode print : unit -> unit.

    # class pile () =
    object
    val p = ref ([] : <print : unit -> unit> list)
    method empile i = p := i:: !p
    method depile () = if !p = [] then raise PileVide else p := List.tl !p
    method sommet () = if !p = [] then raise PileVide else List.hd !p
    method taille () = List.length !p
    end ;;
    class pile :
    unit ->
    object
    val p : < print : unit -> unit > list ref
    method depile : unit -> unit
    method empile : < print : unit -> unit > -> unit
    method sommet : unit -> < print : unit -> unit >
    method taille : unit -> int
    end


  4. Dfinir une classe paramtre ['a] pile avec les mmes mthodes.

    # class ['a] ppile () =
    object
    val p = ref ([] : 'a list)
    method empile i = p := i:: !p
    method depile () = if !p = [] then raise PileVide else p := List.tl !p
    method sommet () = if !p = [] then raise PileVide else (List.hd !p)
    method taille () = List.length !p
    end ;;
    class ['a] ppile :
    unit ->
    object
    val p : 'a list ref
    method depile : unit -> unit
    method empile : 'a -> unit
    method sommet : unit -> 'a
    method taille : unit -> int
    end


  5. Comparer les diffrentes classes de piles.

Liaison retarde

Cet exercice illustre la notion de liaison retarde indpendamment de la notion de sous-typage.

Soit le programme donn par la suite :
  1. Dessiner les relations entre classes

  2. Tracer les diffrents envois de messages.

  3. En supposant tre dans un mode caractre sans cho, indiquer ce qu'affiche le programme.
exception CrLf;;
class lecture_chaine (m) =
object (self)
val msg = m
val mutable res = ""

method lire_char =
let c = input_char stdin in
if (c != '\n') then begin
output_char stdout c; flush stdout
end;
String.make 1 c

method private lire_chaine_aux =
while true do
let s = self#lire_char in
if s = "\n" then raise CrLf
else res <- res ^ s;
done

method private lire_chaine_aux2 =
let s = self#lire_char in
if s = "\n" then raise CrLf
else begin res <- res ^ s; self#lire_chaine_aux2 end

method lire_chaine =
try
self#lire_chaine_aux
with End_of_file -> ()
| CrLf -> ()

method input = res <- ""; print_string msg; flush stdout;
self#lire_chaine

method get = res
end;;

class lecture_mdp (m) =
object (self)
inherit lecture_chaine m
method lire_char = let c = input_char stdin in
if (c != '\n') then begin
output_char stdout '*'; flush stdout
end;

let s = " " in s.[0] <- c; s
end;;

let login = new lecture_chaine("Login : ");;
let passwd = new lecture_mdp("Passwd : ");;
login#input;;
passwd#input;;
print_string (login#get);;print_newline();;
print_string (passwd#get);;print_newline();;


Classes abstraites et valuateur d'expressions

Cet exercice a pour but de montrer la factorisation de code ralise par l'utilisation de classes abstraites.

Les expressions arithmtiques construites sont toutes des instances d'une sous-classe de la classe abstraite expr_ar.
  1. Dfinir une classe abstraite expr_ar pour les expressions arithmtiques contenant deux mthodes abstraites : eval de type float et print de type unit qui respectivement value et affiche une expression arithmtique.

    # class virtual expr_ar () =
    object
    method virtual eval : unit -> float
    method virtual print : unit -> unit
    end ;;
    class virtual expr_ar :
    unit ->
    object
    method virtual eval : unit -> float
    method virtual print : unit -> unit
    end


  2. Dfinir une classe concrte constante sous-classe de expr_ar.

    # class constante x =
    object
    inherit expr_ar ()
    val c = x
    method eval () = c
    method print () = print_float c
    end ;;
    class constante :
    float ->
    object
    val c : float
    method eval : unit -> float
    method print : unit -> unit
    end

    (* autre solution : *)

    # class const x =
    object
    inherit expr_ar ()
    method eval () = x
    method print () = print_float x
    end ;;
    class const :
    float -> object method eval : unit -> float method print : unit -> unit end


  3. Dfinir une sous-classe abstraite bin_op de expr_ar implantant les mthodes eval et print en utilisant deux nouvelles mthodes abstraites oper de type (float * float) -> float ( utilise par eval) et symbole de type string (utilise par print).

    # class virtual bin_op g d =
    object (this)
    inherit expr_ar ()
    val fg = g
    val fd = d
    method virtual symbole : string
    method virtual oper : float * float -> float
    method eval () =
    let x = fg#eval()
    and y = fd#eval() in
    this#oper(x,y)
    method print () =
    fg#print () ;
    print_string (this#symbole) ;
    fd#print ()
    end ;;
    class virtual bin_op :
    (< eval : unit -> float; print : unit -> 'b; .. > as 'a) ->
    (< eval : unit -> float; print : unit -> unit; .. > as 'c) ->
    object
    val fd : 'c
    val fg : 'a
    method eval : unit -> float
    method virtual oper : float * float -> float
    method print : unit -> unit
    method virtual symbole : string
    end


  4. Dfinir les classes concrtes add et mul sous-classes de bin_op implantant chacune les mthodes oper et symbole.

    # class add x y =
    object
    inherit bin_op x y
    method symbole = "+"
    method oper(x,y) = x +. y
    end ;;
    class add :
    (< eval : unit -> float; print : unit -> 'b; .. > as 'a) ->
    (< eval : unit -> float; print : unit -> unit; .. > as 'c) ->
    object
    val fd : 'c
    val fg : 'a
    method eval : unit -> float
    method oper : float * float -> float
    method print : unit -> unit
    method symbole : string
    end

    # class mul x y =
    object
    inherit bin_op x y
    method symbole = "*"
    method oper(x,y) = x *. y
    end ;;
    class mul :
    (< eval : unit -> float; print : unit -> 'b; .. > as 'a) ->
    (< eval : unit -> float; print : unit -> unit; .. > as 'c) ->
    object
    val fd : 'c
    val fg : 'a
    method eval : unit -> float
    method oper : float * float -> float
    method print : unit -> unit
    method symbole : string
    end


  5. Dessiner l'arbre d'hritage.

  6. crire une fonction qui prenant une suite de Genlex.token construit un objet de type expr_ar.

    # open Genlex ;;
    # exception Found of expr_ar ;;
    exception Found of expr_ar

    # let rec create accu l =
    let r = match Stream.next l with
    Float f -> new constante f
    | Int i -> ( new constante (float i) :> expr_ar)
    | Kwd k ->
    let v1 = accu#sommet() in accu#depile();
    let v2 = accu#sommet() in accu#depile();
    ( match k with
    "+" -> ( new add v2 v1 :> expr_ar)
    | "*" -> ( new mul v2 v1 :> expr_ar)
    | ";" -> raise (Found (accu#sommet()))
    | _ -> failwith "aux : bas keyword" )
    | _ -> failwith "aux : bad case"
    in
    create (accu#empile (r :> expr_ar); accu) l ;;
    val create :
    < depile : unit -> 'a; empile : expr_ar -> 'b; sommet : unit -> expr_ar;
    .. > ->
    Genlex.token Stream.t -> 'c = <fun>

    # let gl = Genlex.make_lexer ["+"; "*"; ";"] ;;
    val gl : char Stream.t -> Genlex.token Stream.t = <fun>

    # let run () =
    let s = Stream.of_channel stdin in
    create (new ppile ()) (gl s) ;;
    val run : unit -> 'a = <fun>


  7. Tester ce programme en lisant l'entre standard, en utilisant l'analyseur lexical gnrique Genlex. On pourra entrer les expressions valuer sous forme post-fixe.

Jeu de la vie en objet

On dfinit les 2 classes suivantes :
  • cell : pour les cellules du monde, rpondant la mthode isAlive : unit -> bool
  • world : ayant un tableau de cell et rpondant aux messages :
    display : unit -> unit
    nextGen : unit -> unit
    setCell : int * int -> cell -> unit
    getCell : int * int -> cell
    
  1. crire la classe cell.

    # class cell a =
    object
    val mutable v = (a : bool)
    method isAlive = v
    end ;;
    class cell : bool -> object val mutable v : bool method isAlive : bool end


  2. crire une classe abstraite absWorld qui implante les mthodes display, getCell et setCell et laisse abstraite la mthode nextGen.

    # class virtual absWorld n m =
    object(self)
    val mutable tcell = Array.create_matrix n m (new cell false)
    val maxx = n
    val maxy = m
    val mutable gen = 0
    method private dessine(c) =
    if c#isAlive then print_string "*"
    else print_string "."
    method display() =
    for i = 0 to (maxx-1) do
    for j=0 to (maxy -1) do
    print_string " " ;
    self#dessine(tcell.(i).(j))
    done ;
    print_newline()
    done
    method getCell(i,j) = tcell.(i).(j)
    method setCell(i,j,c) = tcell.(i).(j) <- c
    method getCells = tcell
    end ;;
    class virtual absWorld :
    int ->
    int ->
    object
    val mutable gen : int
    val maxx : int
    val maxy : int
    val mutable tcell : cell array array
    method private dessine : cell -> unit
    method display : unit -> unit
    method getCell : int * int -> cell
    method getCells : cell array array
    method setCell : int * int * cell -> unit
    end


  3. crire une classe world, sous-classe de absWorld, qui implante la mthode nextGen selon les rgles de croissance.

    # class world n m =
    object(self)
    inherit absWorld n m
    method neighbors(x,y) =
    let r = ref 0 in
    for i=x-1 to x+1 do
    let k = (i+maxx) mod maxx in
    for j=y-1 to y+1 do
    let l = (j + maxy) mod maxy in
    if tcell.(k).(l)#isAlive then incr r
    done
    done;
    if tcell.(x).(y)#isAlive then decr r ;
    !r

    method nextGen() =
    let w2 = new world maxx maxy in
    for i=0 to maxx-1 do
    for j=0 to maxy -1 do
    let n = self#neighbors(i,j) in
    if tcell.(i).(j)#isAlive
    then (if (n = 2) || (n = 3) then w2#setCell(i,j,new cell true))
    else (if n = 3 then w2#setCell(i,j,new cell true))
    done
    done ;
    tcell <- w2#getCells ;
    gen <- gen + 1
    end ;;
    class world :
    int ->
    int ->
    object
    val mutable gen : int
    val maxx : int
    val maxy : int
    val mutable tcell : cell array array
    method private dessine : cell -> unit
    method display : unit -> unit
    method getCell : int * int -> cell
    method getCells : cell array array
    method neighbors : int * int -> int
    method nextGen : unit -> unit
    method setCell : int * int * cell -> unit
    end


  4. crire le programme principal qui cre un monde vide, lui ajoute certaines cellules, puis rentre dans une boucle d'interaction qui affiche le monde, attend une interaction, calcule ensuite la gnration suivante et repart dans la boucle.

    # exception Fin;;
    exception Fin

    # let main () =
    let a = 10 and b = 12 in
    let w = new world a b in
    w#setCell(4,4,new cell true) ;
    w#setCell(4,5,new cell true) ;
    w#setCell(4,6,new cell true) ;
    try
    while true do
    w#display() ;
    if ((read_line()) = "F") then raise Fin else w#nextGen()
    done
    with Fin -> () ;;
    val main : unit -> unit = <fun>

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora205.html0000644000000000000000000001066007421273603014473 0ustar Avenir des dveloppements en Objective CAML Prcdent Index Suivant

Avenir des dveloppements en Objective CAML

Il est difficile pour un nouveau langage d'exister s'il ne s'accompagne pas d'un dveloppement important d'une application (comme Unix pour C) ou d'un support commercial et industriel consquent (comme SUN pour JAVA). Les qualits propres du langage sont rarement suffisantes. Objective CAML possde de nombreuses qualits et quelques dfauts que nous avons dcrits au cours de ce chapitre. Pour sa part, Objective CAML est soutenu par l'INRIA o il est conu et implant au sein du projet CRISTAL. Issu de la recherche acadmique, Objective CAML y est utilis comme laboratoire exprimental pour tester de nouveaux paradigmes de programmation et comme langage d'implantation. Il est largement enseign dans les diffrents cycles universitaires et dans les classes prparatoires. Plusieurs milliers d'tudiants et d'lves apprennent chaque anne les concepts du langage et le pratiquent. En cela le langage Objective CAML a une place importante dans le monde acadmique. L'enseignement de l'informatique, en France, mais aussi aux Etats Unis, forme de nombreux programmeurs ce langage tant au niveau pratique que thorique.

En revanche, dans le monde industriel le mouvement est moins dynamique. notre connaissance, il n'y a pas une seule application commerciale, dveloppe en Objective CAML, vendue au grand public et vantant l'utilisation d'Objective CAML. Le seul exemple approchant est celui du langage SCOL de chez Cryo-Networks. Il y a pourtant un lger frmissement en ce sens. Les premiers appels d'offres des SSII d'applications en Objective CAML apparaissent. Sans esprer rapidement un effet boule de neige, il est significatif qu'une demande existe pour ce type de langage. Et sans esprer non plus un retour sur investissement trs court terme, il est important de s'y intresser.

C'est maintenant au langage et son environnement de dveloppement de montrer leur pertinence. Pour accompagner ce phnomne, il est sans doute ncessaire d'apporter certaines garanties quant l'volution du langage. ce titre, Objective CAML n'est qu'encore mergeant et doit faire le choix de sortir plus avant du monde acadmique. Mais cette << sortie dans le monde >> n'aura d'effet que si certaines rgles sont respectes :
  • garantir la prnit des dveloppements en assurant la compatibilit ascendante des prochaines versions du langage (la difficult tant la stabilit des nouveaux lments (objet, etc));
  • spcification du langage en liaison avec les dveloppeurs actuels en vue d'une future normalisation (ce qui permettrait la ralisation de plusieurs implantations pour garantir l'existence de plusieurs solutions) ;
  • concevoir un environnement de dveloppement contenant une interface graphique portable, un bus CORBA, des interfaces avec les SGBD, et surtout un environnement de mise au point plus convivial.
Certains des points voqus en particulier la normalisation peuvent rester du ressort de l'acadmie. D'autres n'offrent d'intrt que pour le monde industriel. Tout dpendra alors de leur degr d'entente. Il existe un prcdent montrant qu'un langage peut tre << libre >> et pourtant maintenu commercialement comme ce fut le cas pour le compilateur gnat du langage ADA et la socit ACT.

Lien


http://www.act-europe.fr





Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora072.html0000644000000000000000000001237207421273601014475 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

Les techniques de compilation vers des machines abstraites ont t utilises par les premires implantations de SmallTalk, puis ont t reprises pour les langages fonctionnels LISP ou ML. L'argument, rel, de perte d'efficacit due aux machines abstraites a rejet un temps dans l'ombre cette technique. Cependant, aujourd'hui, la langage JAVA en fait un argument de vente. Les machines abstraites possdent plusieurs avantages. Le premier est de faciliter le portage d'un compilateur vers diffrentes architectures. En effet la partie du compilateur rcrire pour un portage est bien cerne (l'interprte de la machine abstraite et une partie de la bibliothque d'excution). D'autre part elles permettent la portabilit du code produit. Il est possible de compiler une application sur une architecture systme-machine et de l'excuter sur une autre. Enfin elles permettent de simplifier l'criture du compilateur en ajoutant des instructions spcifiques pour le type de langage compiler. Dans le cas des langages fonctionnels, les machines abstraites permettent de crer facilement des fermetures (couple environnement, code) en ajoutant la machine abstraite la notion d'environnement d'excution.

Pour pallier la perte d'efficacit due l'utilisation d'un interprte de code-octet, on peut procder une expansion des instructions de la machine abstraite vers les instructions de la machine relle au moment de leur chargement. On trouve ce type d'expansion dans les implantations de Le Lisp (llm3) et de JAVA (JIT). Nanmoins ces performances ne peuvent galer celles d'un compilateur natif ou vers C.

Une difficult de la compilation des langages fonctionnels provient des fermetures. Ces dernires contiennent en effet le code excuter ainsi que l'environnement d'excution de ce code (voir page ??).
Les choix d'implantation des environnements et de l'accs aux valeurs qu'ils contiennent influencent fortement l'efficacit du code produit. Une optimisation importante de la gestion des environnements consiste obtenir un accs aux valeurs en temps constant : les variables sont vues comme des indices dans un tableau contenant leur valeur. Cela ncessite un prtraitement des expressions fonctionnelles. On en trouve un premier exemple dans la Functional Abstract Machine de L. Cardelli. La Zinc utilise galement cette technique. Une autre optimisation cruciale est d'viter de construire des fermetures inutiles. Bien qu'en ML toute fonction puisse tre vue comme une fonction un argument, il est indispensable de ne pas crer de fermetures intermdiaires lors de l'application de plusieurs arguments. Par exemple, quand on applique deux entiers la fonction add, il n'est pas utile de construire la premire fermeture correspondant la fonction add applique au premier argument. Il faut noter que la cration d'une fermeture ncessite l'allocation d'un certain espace mmoire pour l'environnement donc en allouer trop dclenchera plus souvent le rcuprateur automatique de mmoire (9). Or, la rcupration automatique de mmoire est le deuxime point sensible pour les performances.

Enfin la proprit d'auto-amorage (bootstrap) permet d'crire la plus grande partie du compilateur dans le langage qu'il doit compiler. Pour cela, comme la poule et l'oeuf, il est ncessaire de dfinir la partie minimale du langage permettant ensuite de l'augmenter. En fait cette proprit est difficilement apprciable pour classifier les langages et leurs implantations. Disons que cela sert pour montrer la capacit d'un langage tre langage d'implantation pour un compilateur. Un compilateur est un programme d'une bonne taille et l'auto-amorage est un bon test de la correction du compilateur qui permet d'valuer ses performances. Donnons comme rfrence le lien suivant :

Lien


http://caml.inria.fr/camlstone.txt
On y trouve les temps de compilation du langage Caml sur une cinquantaine de machines. Ces temps sont pour des versions plus anciennes qu'Objective CAML mais permettent de se faire une ide de l'efficacit d'un couple machine-systme pour Objective CAML.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora047.html0000644000000000000000000000466107421273601014501 0ustar Mise en oeuvre du module Graphics Prcdent Index Suivant

Mise en oeuvre du module Graphics

L'utilisation de la bibliothque Graphics diffre selon le systme et le mode de compilation utiliss. On ne s'intressera ici qu'aux applications utilisables partir de la boucle d'interaction d'Objective CAML. Pour les systmes Windows ou MacOS, l'environnement de travail interactif a dj charg la bibliothque. Pour les systmes Unix, il est ncessaire de construire un nouveau toplevel. Celui-ci dpend de l'emplacement de la bibliothque X11. Si celle-ci est place dans un des chemins habituels de recherche des bibliothques du langage C, alors la ligne de commande est la suivante :
ocamlmktop -custom -o montoplevel graphics.cma -cclib -lX11
Elle construit une nouvelle commande montoplevel o la bibliothque Graphics est intgre. Le lancement de la commande s'effectue de la manire suivante :
./montoplevel
Si par contre, comme sous Linux, la bibliothque X11 est place dans un autre catalogue, il faut l'indiquer la commande ocamlmktop :
ocamlmktop -custom -o montoplevel graphics.cma -cclib \ 
           -L/usr/X11/lib -cclib -lX11
Dans cet exemple, le fichier libX11.a est recherch dans le catalogue /usr/X11/lib.

Une description complte de la commande ocamlmktop est donne au chapitre 7.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora196.html0000644000000000000000000016607307421273602014515 0ustar Les robots de l'aube Prcdent Index Suivant

Les robots de l'aube

Comme nous l'avions promis dans la dernire application de la troisime partie (page ??), nous reprenons le problme des robots pour le traiter dans un cadre distribu o le monde est un serveur et o chaque robot est un processus indpendant pouvant s'excuter depuis une machine distante.

Cette application est un bon rsum des possibilits du langage Objective CAML puisque nous allons y exploiter et mlanger la plupart de ses traits. Outre le modle distribu qui nous est impos par l'exercice, nous avons recours la concurrence pour raliser le serveur de sorte que les multiples connexions soient traites indpendamment tout en s'appuyant sur une unique reprsentation mmoire du << monde >>. L'accs et la modification de l'tat des cases du monde devront en consquence tre protgs par des sections critiques.

Afin de rutiliser au maximum le code qui a dj t ralis pour les robots d'une part, et les architectures client-serveur d'autre part, nous employons de faon conjointe foncteurs et hritage de classes.

Cette application est assez minimaliste, mais nous verrons que son architecture se prte particulirement bien des extensions dans de multiples directions.

Monde-Serveur

Nous prenons une reprsentation du monde similaire celle que nous avions dveloppe dans la partie III. Le monde est une grille de taille finie et chaque case de cette grille ne peut tre occupe que par un seul robot. Un robot est connu par son nom et par sa position, le monde est dtermin par sa taille et par les robots qui y sjournent. Ces diffrentes informations sont reprsentes par les types suivants :

# type position = { x:int ; y:int } ;;

# type robot_info = { name : string ; mutable pos : position }
type world_info = { length : int ; width : int ;
mutable robots : robot_info list } ;;


Le monde aura servir deux sortes de clients :
  • des clients passifs qui se contentent d'pier les dplacements des diffrents robots. Ils nous serviront pour raliser les clients chargs des affichages. Nous les appellerons des espions.
  • des clients actifs, susceptibles de demander au serveur de les dplacer et donc de modifier son tat.
Ces deux catgories de clients et leur comportement vont dterminer l'ensemble des messages qu'changeront serveur et clients.

Un client en se connectant se dclare passif (Spy) ou actif (Enter). Un espion reoit en rponse sa connexion l'tat global du monde. Ensuite, il est tenu inform de tous les changements. Par contre, il ne peut pas soumettre de requte. Un robot qui se connecte doit donner ses caractristiques (son nom et sa position initiale souhaite), le monde lui confirme alors son arrive. Ensuite, il pourra demander des informations : sa propre position (GetPos) ou la liste des robots qui l'entourent (Look). Il peut aussi solliciter le monde pour se dplacer. Le protocole de requtes du monde des robots distribus est reprsent par le type suivant :

# type query =
| Spy (* requtes de dclaration initiale *)
| Enter of robot_info

| Move of position (* requtes des robots *)
| GetPos
| Look of int

| World of world_info (* messages dlivrs par le monde *)
| Pos of robot_info
| Exit of robot_info ;;


De ce protocole, et avec les foncteurs de la << bote outils distribue >> du chapitre prcdent, on tire immdiatement le serveur gnrique.

# module Pquery = Make_Protocole (struct type t = query end ) ;;
# module Squery = Server (Pquery) ;;


Il ne nous reste plus qu' prciser le comportement du serveur en implantant la mthode treat et en y incorporant les donnes qui, d'une part, reprsentent le monde et, d'autre part, permettent la gestion des connexions.

Plus prcisment, le serveur possde une variable world (de type world_info) qui est protge par le verrou sem (de type Mutex.t). Il possde aussi une variable spies qui est une liste de files de messages envoyer aux observateurs, raison d'une file par espion. Pour rveiller les processus chargs de l'envoi de ces messages, le serveur dispose aussi d'un signal (de type Condition.t).

Nous donnons une fonction auxiliaire dist de calcul de distance entre deux positions :

# let dist p q = max (abs (p.x-q.x)) (abs (p.y-q.y)) ;;
val dist : position -> position -> int = <fun>


La fonction critical encapsule dans une section critique un calcul de valeur :

# let critical m f a =
Mutex.lock m ; let r = f a in Mutex.unlock m ; r ;;
val critical : Mutex.t -> ('a -> 'b) -> 'a -> 'b = <fun>


Voici la dfinition de la classe server implantant le monde-serveur. Elle est longue, mais nous en donnons ci-aprs une explication synthtique.

# class server l w n np =
object (self)
inherit [query] Squery.server n np
val world = { length=l ; width=w ; robots=[] }
val sem = Mutex.create ()
val mutable spies = []
val signal = Condition.create ()

method lock = Mutex.lock sem
method unlock = Mutex.unlock sem

method legal_pos p = p.x>=0 && p.x<l && p.y>=0 && p.y<w

method free_pos p =
let is_not_here r = r.pos.x<>p.x || r.pos.y<>p.y
in critical sem (List.for_all is_not_here) world.robots

method legal_move r p =
let dist1 p = (dist r.pos p) <= 1
in (critical sem dist1 p) && self#legal_pos p && self#free_pos p


method queueing_message mes =
List.iter (Queue.add mes) spies ;
Condition.broadcast signal

method trace_loop s q =
let foo = Mutex.create () in
let f () =
try
spies <- q :: spies ;
self#send s (World world) ;
while true do
while Queue.length q = 0 do Condition.wait signal foo done ;
self#send s (Queue.take q)
done
with _ -> spies <- List.filter ((!=) q) spies ;
Unix.close s
in ignore (Thread.create f ())

method remove_robot r =
self#lock ;
world.robots <- List.filter ((<>) r) world.robots ;
self#queueing_message (Exit {r with name=r.name}) ;
self#unlock

method try_move_robot r p =
if self#legal_move r p
then begin
self#lock ;
r.pos <- p ;
self#queueing_message (Pos {r with name=r.name}) ;
self#unlock
end

method treat_robot s r =
let f () =
try
world.robots <- r :: world.robots ;
self#send s (Pos r) ;
self#queueing_message (Pos r) ;
while true do
Thread.delay 0.5 ;
match self#receive s with
Move p -> self#try_move_robot r p
| GetPos -> self#send s (Pos r)
| Look d ->
self#lock ;
let dist p = max (abs (p.x-r.pos.x)) (abs (p.y-r.pos.y)) in
let l = List.filter (fun x -> (dist x.pos)<=d) world.robots
in self#send s (World { world with robots = l }) ;
self#unlock
| _ -> ()
done
with _ -> self#unlock ;
self#remove_robot r ;
Unix.close s
in ignore (Thread.create f ())

method treat s =
match self#receive s with
Spy -> self#trace_loop s (Queue.create ())
| Enter r ->
( if not (self#legal_pos r.pos && self#free_pos r.pos) then
let i = ref 0 and j = ref 0 in
( try
for x=0 to l do
for y=0 to w do
let p = { x=x ; y=y }
in if self#legal_pos p && self#free_pos p
then ( i:=x ; j:=y; failwith "treat" )
done done ;
Unix.close s
with Failure "treat" -> r.pos <- { x= !i ; y= !j } )) ;
self#treat_robot s r
| _ -> Unix.close s

end ;;
class server :
int ->
int ->
int ->
int ->
object
val nb_pending : int
val port_num : int
val sem : Mutex.t
val signal : Condition.t
val sock : Unix.file_descr
val mutable spies : Pquery.t Queue.t list
val world : world_info
method free_pos : position -> bool
method legal_move : robot_info -> position -> bool
method legal_pos : position -> bool
method lock : unit
method queueing_message : Pquery.t -> unit
method receive : Unix.file_descr -> Pquery.t
method remove_robot : robot_info -> unit
method send : Unix.file_descr -> Pquery.t -> unit
method start : unit
method trace_loop : Unix.file_descr -> Pquery.t Queue.t -> unit
method treat : Unix.file_descr -> unit
method treat_robot : Unix.file_descr -> robot_info -> unit
method try_move_robot : robot_info -> position -> unit
method unlock : unit
end


La mthode treat s'emploie ds le dpart distinguer la nature du client. Suivant qu'il est actif ou passif, c'est un traitement particulier qui est appel : trace_loop pour un observateur, treat_robot pour un robot. Dans le second cas, on vrifie que la position initiale propose par le client est compatible avec l'tat du monde, sinon on tche de lui trouver une position initiale valide. Le reste du code peut se partager en quatre catgories :
  1. Les mthodes gnrales : ce sont celles que nous avions pour les mondes gnraux de la partie III. Principalement, il s'agit de vrifier qu'un dplacement est lgal pour un robot donn.
  2. La gestion des observateurs : chaque observateur est associ une socket par laquelle lui sont envoyes les donnes, une queue contenant tous les messages qui ne lui ont pas encore t envoys et un processus. La mthode trace_loop est une boucle sans fin qui vide la queue des messages en les mettant et qui s'endort quand elle est vide. Le remplissage est effectu sur toutes les files d'attente par la mthode queueing_message. Notons qu'aprs avoir ajout un message, le signal de rveil est envoy tous les processus.
  3. La gestion des robots : l encore, chaque robot est associ un processus qui lui est ddi. La mthode treat_robot est une boucle sans fin : elle attend la rception d'une requte, la traite et y rpond s'il y a lieu. Puis elle se remet en attente de la prochaine requte. Notons que ce sont les mthodes grant le robot qui font les appels la mthode queueing_message quand une modification de l'tat du monde a eu lieu. Si la connexion avec le robot est perdue, c'est dire si une exception est leve pendant la rception des requtes, le robot est considr comme se retirant et son dpart est signal aux observateurs.
  4. Les mthodes hrites : ce sont celles du serveur gnrique obtenu par application du foncteur Server au protocole de notre application.

Client-Observateur

Le foncteur Client nous donne les fonctions gnriques de connexion avec un serveur selon le protocole particulier qui nous occupe ici.

# module Cquery = Client (Pquery) ;;
module Cquery :
sig
module Com :
sig
val send : Unix.file_descr -> Pquery.t -> unit
val receive : Unix.file_descr -> Pquery.t
end
val connect : string -> int -> Unix.file_descr
val emit_simple : string -> int -> Pquery.t -> unit
val emit_answer : string -> int -> Pquery.t -> Pquery.t
end


Le comportement de l'espion est simple : il se connecte au serveur et affiche les informations que celui-ci lui transmet. L'espion dispose de trois fonctions d'affichage que nous donnons ci-dessous :

# let display_robot r =
Printf.printf "Le robot %s se trouve en (%d,%d)\n" r.name r.pos.x r.pos.y ;
flush stdout ;;
val display_robot : robot_info -> unit = <fun>

# let display_exit r = Printf.printf "Le robot %s se retire\n" r.name ;
flush stdout ;;
val display_exit : robot_info -> unit = <fun>

# let display_world w =
Printf.printf "Le monde est un damier de %d par %d \n" w.length w.width ;
List.iter display_robot w.robots ;
flush stdout ;;
val display_world : world_info -> unit = <fun>


La fonction principale du client-espion est :

# let trace_client name port =
let sock = Cquery.connect name port
in Cquery.Com.send sock Spy ;
( match Cquery.Com.receive sock with
World w -> display_world w
| _ -> failwith "le serveur ne suit pas le protocole" ) ;
while true do
match Cquery.Com.receive sock with
Pos r -> display_robot r
| Exit r -> display_exit r
|_ -> failwith "le serveur ne suit pas le protocole"
done ;;
val trace_client : string -> int -> unit = <fun>


Il y a deux faons de procder pour raliser un affichage graphique. La premire est simple mais peu conomique : puisqu'en dbut de connexion le serveur envoie la totalit de l'information, on peut se contenter d'ouvrir une nouvelle connexion intervalles rguliers, d'afficher le monde dans sa globalit et de refermer la connexion. L'autre possibilit consiste utiliser les informations envoyes par le serveur pour maintenir une copie de l'tat du monde. Il est alors ais de n'afficher que les modifications d'tat au fil de la rception des messages. C'est cette seconde solution que nous avons mise en oeuvre.

Client-Robot

Tels que nous les avions dfinis dans le prcdent chapitre (cf. page ??), les robots suivaient la signature suivante.

# module type ROBOT = 
sig
class robot : int -> int ->
object
val mutable i : int
val mutable j : int
method get_pos : int * int
method next_pos : unit -> int * int
method set_pos : int * int -> unit
end
end ;;


La partie que nous souhaitons conserver des diffrentes classes est celle qui justement variait d'un type de robot l'autre et qui dfinissait son comportement : la mthode next_pos.

De plus nous avons besoin d'une mthode pour connecter le robot un monde (start) et d'une boucle alternant calcul d'une nouvelle position et communication avec le serveur pour soumettre la position choisie.

Nous dfinissons un foncteur qui partant d'une classe implantant un robot virtuel, c'est--dire respectant la signature ROBOT, cre, par hritage, une nouvelle classe contenant les mthodes propres faire du robot un client autonome.

# module RobotClient (R : ROBOT) = 
struct
class robot robname x y hostname port =
object (self)
inherit R.robot x y as super
val mutable socket = Unix.stderr
val mutable rob = { name=robname ; pos={x=x;y=y} }

method private adjust_pos r =
rob.pos <- r.pos ; i <- r.pos.x ; j <- r.pos.y

method get_pos =
Cquery.Com.send socket GetPos ;
match Cquery.Com.receive socket with
Pos r -> self#adjust_pos r ; super#get_pos
| _ -> failwith "le serveur ne respecte pas le protocole"

method set_pos =
failwith "la mthode set_pos ne doit pas tre utilise"

method start =
socket <- Cquery.connect hostname port ;
Cquery.Com.send socket (Enter rob) ;
match Cquery.Com.receive socket with
Pos r -> self#adjust_pos r ; self#run
| _ -> failwith "le serveur ne respecte pas le protocole"

method run =
while true do
let (x,y) = self#next_pos ()
in Cquery.Com.send socket (Move {x=x;y=y}) ;
ignore (self#get_pos)
done
end
end ;;
module RobotClient :
functor(R : ROBOT) ->
sig
class robot :
string ->
int ->
int ->
string ->
int ->
object
val mutable i : int
val mutable j : int
val mutable rob : robot_info
val mutable socket : Unix.file_descr
method private adjust_pos : robot_info -> unit
method get_pos : int * int
method next_pos : unit -> int * int
method run : unit
method set_pos : int * int -> unit
method start : unit
end
end


Remarquons que la mthode get_pos a t redfinie comme une interrogation auprs du serveur car les variables d'instance i et j ne sont pas fiables puisqu'elles peuvent tre modifies sans l'accord du monde. Pour les mmes raisons, l'emploi de set_pos a t invalid de sorte que son appel dclenche systmatiquement une exception. Ce traitement peut apparatre brutal, mais il y a fort parier que si cette mthode est utilise par next_pos alors un dcalage entre la position relle (celle connue par le serveur) et la position suppose (celle connue par le client) apparatra.

Nous utilisons le foncteur RobotClient pour crer les diffrentes classes correspondant aux diffrents robots.

# module Fix = RobotClient (struct class robot = fix_robot end) ;;
# module Crazy = RobotClient (struct class robot = crazy_robot end) ;;
# module Obstinate = RobotClient (struct class robot = obstinate_robot end) ;;


Le petit programme suivant permet de lancer depuis une ligne de commande, le serveur ou les diffrents clients. Chacun d'eux est identifi par l'argument pass au programme.

# let port = 1200 in
if Array.length Sys.argv >=2 then
match Sys.argv.(1) with
"1" -> let s = new server 25 30 port 10 in s#start
| "2" -> trace_client "localhost" port
| "3" -> let o = new Fix.robot "fixe" 10 10 "localhost" port in o#start
| "4" -> let o = new Crazy.robot "fou" 10 10 "localhost" port in o#start
| "5" -> let o = new Obstinate.robot "obstine" 10 10 "localhost" port
in o#start
| _ -> () ;;


Pour en faire plus

Le monde des robots a l'imagination fertile. Avec les lments dj donns ici, on peut facilement raliser un << robot intelligent >> qui soit la fois robot et espion. Cela permettrait de faire cooprer les diffrents habitants du monde. On peut alors tendre l'application pour obtenir un petit jeu d'action comme << poules-renards-vipres >> dans lequel les renards chassent les poules, les vipres chassent les renards et les poules mangent les vipres.




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora076.html0000644000000000000000000000713707421273601014504 0ustar Bibliothque prcharge Prcdent Index Suivant

Bibliothque prcharge

Une premire bibliothque est toujours prcharge que cela soit au niveau de la boucle d'interaction ou en compilation en ligne; dans ce cas, elle est toujours lie. Elle correspond au module Pervasives qui est l'environnement initial du langage. Elle contient les dclarations de :
  • type : types de base (int, char, string, float, bool, unit, exn, 'a array, 'a list) et les types 'a option (voir page ??) et ('a, 'b, 'c) format (voir page ??).

  • exceptions : une dizaine d'exceptions dclenchables par la bibliothque d'excution. On peut citer principalement les exceptions gnralistes suivantes :
    • Failure of string qui est dclenche par la fonction failwith applique une chane.
    • Invalid_argument of string qui indique qu'un argument est impossible traiter par la fonction ayant dclench l'exception. La fonction invalid_arg applique une chane dclenche cette exception.
    • Sys_error of string, pour les entres-sorties, typiquement dans l'ouverture d'un fichier inexistant en lecture.
    • End_of_file pour la dtection de fin de fichier.
    • Division_by_zero sur les entiers.

    Ainsi que des exceptions plus internes comme :
    • Out_of_memory et Stack_overflow pour le dpassement mmoire du tas et de la pile. Il est noter que l'exception Out_of_memory n'est pas rcuprable par un programme. En effet quand elle se dclenche, il est trop tard pour allouer de nouveau de l'espace mmoire pour poursuivre le calcul.
      La rcupration de l'exception Stack_Overflow diffre si le programme a t compil en code-octet ou en natif. Dans ce dernier cas, il n'est pas possible de la rattraper.

  • fonctions : environ 140 dont la moiti correspondent des fonctions C de la bibliothque d'excution. On y trouvera les oprateurs de comparaison et de calcul, les fonctions sur les entiers et les flottants, des fonctions sur les chanes de caractres, sur les rfrences et les entres-sorties. Il est noter qu'un certain nombre de ces dclarations sont en fait des synonymes de dclarations dfinies dans d'autres modules. Elles sont nanmoins dclares ici pour des raisons historiques et d'implantation.

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora204.html0000644000000000000000000001307207421273603014472 0ustar Langages Objet : comparaison avec Java Prcdent Index Suivant

Langages Objet : comparaison avec Java

Bien qu'Objective CAML soit issu du monde fonctionnel, il est ncessaire de comparer son extension objet un reprsentant important des langages objets. Nous choisissons le langage Java qui bien que proche du point de vue de son implantation, diffre fortement par son modle objet et par son systme de types.

Le langage Java est un langage objets dvelopp par la socit SUN. Le site principal d'accs au langage est le suivant :

Lien


http://java.sun.com


Principales caractristiques

Le langage Java est un langage classes. L'hritage est simple et permet de redfinir ou de surcharger des mthodes hrites. Le typage est statique. Une classe hrite est en relation de sous-typage avec sa classe anctre.

Java ne possde pas de classe paramtre. On obtient deux types de polymorphisme : ad hoc pour la surcharge et d'inclusion pour la redfinition.

Il est multi-threading et permet de dvelopper des applications rparties soit en utilisant les sockets soit en invoquant une mthode sur des objets distants (Remote Method Invocation).

Les principes de son implantation sont proches de celle d'Objective CAML. Un programme Java est compil vers une machine abstraite (JVM). Le chargement du code est dynamique. Le code produit est indpendant des architectures machines en tant valu par un interprte de la machine virtuelle. Les types de donnes de base sont spcifis de sorte garantir une mme reprsentation sur toutes les architectures. La bibliothque d'excution est munie d'un GC.

Java possde d'importantes bibliothques de classes (environ 600 avec le JDK auxquelles s'ajoutent les nombreux dveloppements indpendants). Les principales bibliothques concernent les interfaces graphiques et les oprations d'entres-sorties intgrant la communication entre machines.

Diffrences avec Objective CAML

Les principales diffrences entre Java et Objective CAML tiennent leur systme de type, la redfinition et la surcharge des mthodes. La redfinition d'une mthode hrite doit utiliser des paramtres exactement de mme type. La surcharge d'une mthode permet d'aiguiller le choix de la mthode employer selon les types des paramtres d'appel. Dans l'exemple suivant la classe B hrite de la classe A. La classe B redfinit la premire version de la mthode to_string, mais surcharge la deuxime version. De mme la mthode eq est surcharge car le type du paramtre (ici B) n'est pas gal au type du paramtre de la mthode hrite (ici A). Au final la classe B possde deux mthodes eq et deux mthodes to_string.
class A {
  boolean eq (A o) {  return true;}
  String to_string (int n ) { }
}

class B extends A {
  boolean eq (B o) {  return true;}
  String to_string (int n ) { }
  String to_string (float x, float y)
}
Bien que la liaison soit retarde, la rsolution de la surcharge, c'est--dire la dtermination du type de la mthode utiliser, est effectue la compilation.

La deuxime diffrence importante provient de la possibilit de forcer le type d'un objet comme on le ferait en C. Dans l'exemple suivant, on dfinit deux objets a et b, respectivement de classe A et B. On dclare ensuite trois variables c, d et e en posant une contrainte de type sur les valeurs affectes.
{
  A a = new A ();
  B b = new B ();
  A c = (A) b;
  B d = (B) c;
  B e = (B) a;
}
Comme le type de b est sous-type du type de a, alors l'affectation de b c est accepte. Dans ce cas la contrainte de type peut tre omise. Par contre les deux contraintes suivantes ncessitent d'effectuer un test dynamique de type pour garantir que les valeurs dans c et dans a correspondent en fait des objets de la classe B. Dans ce programme cela est vrai pour c, mais faux pour a. Ce dernier cas dclenche alors une exception. Bien que cela soit pratique, en particulier pour les interfaces graphiques, ces contraintes de type peuvent entraner des dclenchements d'exceptions l'excution pour l'utilisation de types errons. En cela Java est un langage typ en partie statiquement et en partie dynamiquement. De plus l'absence de classe paramtre oblige bien souvent utiliser ce trait pour crire des classes gnriques.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora002.gif0000644000000000000000000000470707073350152014272 0ustar GIF89aUUU!,ڋ޼HBʲ L HL*̦ËDԪMUٮ *[NR s*~Z/7 gxE蠗Ơ X(ɉBwIizzDIhPV fW* x ,)l| B| ݅J5=t,ݺ->MZN>aKIN_3߂oϬHq2BN$\P#ZJ UL,!C)[\˙16ƱI3G2uLПDUsRp.͉sЧ-"J"VN* Wzb7-8h=eX޻ #cl 05^|ZHfwrc9kv`P94Q]4$k֤4D=gtvyq+7o˔S 7wՌ9.]w3 Ή ,_1!S{lħ4?:_9Hn@  w` - "|&Xf`rjuWUHʆ%(\@4΍-7hۏ#c S$<äָbMD\R波թezŘ9d[h xPѕBgũjng=IӟhU/*rZ 5,Quå_eEbrwM a:jJ& NX٬Br+嚫DO 1j4RQvYGvֳ˺T ~!"ݢ(eZ1QnL.Y$F0koi/ץ™ǃL‰@ 4ȕ~L Y:Ox&&r f&1!$C9sX_c' ͣY?3ƝE4M_>$WY h2IQ[[6 b}V7vھa_f8u dnڂX87͓Ꮻ6}s{ޕgقCR w5/ꬿG#`9,;ub'%}I#c͋>=s= v&5ߝFXW(YISG%p]nJE$|d)K\ޒ'sdK[U`.XKdT2-TY4L5s3$ v#pv_桠YN ^2NESb:Nys4Hy5S`a?OT  KO㢨'8anXFQ.4+6x)Pb@QQ4(%(=uQq|_LsϐR6szG5H%$;ICcTi 6QuWZn5b Tδm 5J]+[ӫm]8LN^ kW7f5;=ece$~s5,d[F;5$leɴ.eElMˀ@NMd͞VxRFrky*mouێumq6Vӳǽl6gZև=*g;vkxs[~%%jg;ۮ7zvxZ_ 5mzi ZV<~g|aڤTb6t XziIdn1f*kL)2e$7D^e6~ E96͒,g+Wvq>[y3'`Cҹe bн̱ FGt Mi&x.C-iOcS{d5Ư>oӚ&yќtS ];up--lۘȆ_&&6c]8ç{JjovR{嶏-u[[Nl6-oh{Fx4~»j|*^_s8N)>_(fyGq3{(9۽l|7hs $Nѿu#~Kם=#Q!ޣž֟ٯZ8zO ܘIЙ@v_qGu.}uU>g7evZ팇y^=;)oOD0I5|y.;ocaml-book-1.0/fr/html/book-ora079.html0000644000000000000000000012066607421273602014513 0ustar Exercices Prcdent Index Suivant

Exercices

Rsolution de systmes linaires

Cet exercice reprend celui de rsolution de systmes linaires prsent comme exercice du chapitre sur la programmation imprative (3).
  1. En utilisant le module Printf, crire une fonction affiche_systeme qui aligne les colonnes du systme.

    # type vect = float array
    type mat = vect array
    type syst = { m:mat ; v:vect } ;;
    type vect = float array
    type mat = vect array
    type syst = { m: mat; v: vect }

    # let affiche_systeme s =
    let w = Array.length s.m.(0)
    and h = Array.length s.m in
    let c = h / 2 in
    for i = 0 to h - 1 do
    Printf.printf "| ";
    for j = 0 to w - 1 do
    Printf.printf " %8.2f " s.m.(i).(j)
    done;
    Printf.printf " |";
    if i = c then Printf.printf " * " else Printf.printf " ";
    Printf.printf "| x_%-2d |" i;
    if i = c then Printf.printf " = " else Printf.printf " ";
    Printf.printf "| %8.2f |\n" s.v.(i)
    done;
    Printf.printf "\n" ;;
    val affiche_systeme : syst -> unit = <fun>


  2. Tester cette fonction sur les exemples donns page ??.

    # let ax3 = { m = [| [| 10.0 ; 7.0 ; 8.1 ; 7.2 |]
    ; [| 7.08 ; 5.04 ; 6.0 ; 5.0 |]
    ; [| 8.0 ; 5.98 ; 9.89 ; 9.0 |]
    ; [| 6.99 ; 4.99 ; 9.0 ; 9.98 |] |] ;
    v = [| 32.0 ; 23.0 ; 33.0 ; 31.0 |] } ;;
    val ax3 : syst =
    {m=
    [|[|10; 7; 8.1; 7.2|]; [|7.08; 5.04; 6; 5|]; [|8; 5.98; 9.89; ...|];
    ...|];
    v=...}
    # affiche_systeme ax3 ;;
    | 10.00 7.00 8.10 7.20 | | x_0 | | 32.00 |
    | 7.08 5.04 6.00 5.00 | | x_1 | | 23.00 |
    | 8.00 5.98 9.89 9.00 | * | x_2 | = | 33.00 |
    | 6.99 4.99 9.00 9.98 | | x_3 | | 31.00 |

    - : unit = ()

Recherche de nombres premiers

Le crible d'Eratosthne est un algorithme simple programmer pour la recherche de nombres premiers dans un intervalle d'entiers donn dont la borne infrieure doit tre un nombre premier. La mthode est la suivante :
  1. numrer, dans une liste, toutes les valeurs de l'intervalle.
  2. ter de la liste toutes les valeurs multiples du premier lment.
  3. ter de la liste et conserver ce premier lment ;
  4. Recommencer en 2. tant que la liste n'est pas vide.
Voici les tapes de ralisation d'un programme qui implante cet algorithme :
  1. crire une fonction interval qui construit un intervalle d'entiers reprsent sous la forme d'une liste. On reprend la fonction interval des exercices du chapitre prcdent.

    # let interval order next a b =
    let rec aux a =
    if not (order a b) then [a] else a :: aux (next a)
    in aux a ;;
    val interval : ('a -> 'b -> bool) -> ('a -> 'a) -> 'a -> 'b -> 'a list =
    <fun>


  2. crire la fonction eras qui calcule les nombres premiers d'un intervalle d'entiers commenant 2 selon l'algorithme du crible d'Eratosthne.

    # let rec eras l = match l with
    [] -> []
    | p::q -> p :: (eras (List.filter (fun x -> x mod p <> 0) q)) ;;
    val eras : int list -> int list = <fun>


    crire une fonction era_go qui prend un entier et retourne la liste de tous les nombres premiers plus petits que cet entier.

    # let era_go n = eras (interval (<) (fun x -> x + 1) 2 n) ;;
    val era_go : int -> int list = <fun>


  3. On cherche crire un excutable premiers que l'on lancera en tapant la commande premiers n, o n est un entier. Cet excutable affichera les nombres premiers plus petits que n. Pour cela il faut utiliser le module Sys et regarder s'il y a un paramtre pass. Fichier principal premiers.ml :

    # let usage () = Printf.printf "Usage: premiers n\n";;
    val usage : unit -> unit = <fun>

    # let main () =
    if Array.length (Sys.argv) < 2 then usage()
    else
    let n = int_of_string Sys.argv.(1) in
    if n < 2 then usage()
    else
    let r = era_go n in
    List.iter (fun x -> Printf.printf "%d " x) r;
    Printf.printf "\n" ;;
    val main : unit -> unit = <fun>

    main() ;;


    Construction de l'excutable :
    $ ocamlc -o premiers premiers.ml
    
    ou
    $ ocamlopt -o premiers premiers.ml
    
    Test de l'excutable :
    $ premiers 
    Usage: premiers n
    $ premiers 50
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 
    $ premiers 100
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 
    

Affichage de bitmaps

Les bitmaps sauvegards comme color array array sont volumineux. Comme les 24 bits de couleur sont rarement utiliss, il est possible alors de coder un bitmap de manire plus lgre. Pour cela on analysera le nombre de couleurs d'un bitmap. Si ce nombre est petit (par exemple infrieur 256) on pourra coder chaque pixel sur 1 octet, reprsentant le numro de la couleur dans une table des couleurs de ce bitmap.

  1. crire une fonction analyse_couleurs explorant une valeur de type color array array et qui retourne la liste de toutes les couleurs rencontres dans ce tableau de couleurs.

    # let analyse_couleurs c =
    let l = ref [] in
    for i = 0 to (Array.length c) - 1 do
    for j = 0 to (Array.length c.(0)) -1 do
    if not(List.mem c.(i).(j) !l) then l:= c.(i).(j) :: !l
    done ;
    done ;
    List.rev !l ;;
    val analyse_couleurs : 'a array array -> 'a list = <fun>


  2. partir de cette liste, construire une table de correspondance des couleurs. On prendra un vecteur de couleurs. L'indice du tableau correspondra au numro d'ordre de la couleur, son contenu la couleur. crire la fonction trouve_indice qui retourne l'indice d'une valeur stocke dans un tableau.

    # let construire_table l = Array.of_list l ;;
    val construire_table : 'a list -> 'a array = <fun>

    # exception Find of int;;
    exception Find of int

    # let trouve_indice c t =
    let aux () =
    for i=0 to Array.length t do
    if c = t.(i) then raise (Find i)
    done ;
    raise Not_found
    in
    try aux () with Find i -> i ;;
    val trouve_indice : 'a -> 'a array -> int = <fun>


  3. partir de cette table, crire une fonction de conversion, encode, d'un color array array en string. Chaque pixel est alors reprsent par un caractre.

    # let encode caa t =
    if Array.length t > 255 then failwith "trop de couleurs (> 255)"
    else
    let h = Array.length caa
    and w = Array.length caa.(0) in
    let s = String.create (h * w) in
    let ns = ref 0 in
    for i = 0 to h-1 do
    for j = 0 to w-1 do
    let ci = trouve_indice caa.(i).(j) t in
    s.[!ns] <- char_of_int ci ;
    incr ns
    done
    done ;
    s ;;
    val encode : 'a array array -> 'a array -> string = <fun>


  4. Dfinir un type image_tdc comportant une table de correspondance des couleurs et un vecteur de chanes permettant de coder de manire plus petite un bitmap (ou tableau de couleurs).

    # type image_tdc = { tdc : Graphics.color array; image : string;
    largeur : int; hauteur : int};;
    type image_tdc =
    { tdc: Graphics.color array;
    image: string;
    largeur: int;
    hauteur: int }


  5. crire la fonction to_image_tdc de conversion d'un color array array vers ce type.

    # let to_image_tdc caa =
    let t = construire_table (analyse_couleurs caa) in
    let s = encode caa t in
    { tdc = t; image = s;
    largeur = Array.length caa.(0); hauteur = Array.length caa} ;;
    val to_image_tdc : Graphics.color array array -> image_tdc = <fun>


  6. crire la fonction sauve_image_tdc de sauvegarde de ces valeurs vers un fichier.

    # let sauve_image_tdc im nom =
    let oc = open_out nom in
    Marshal.to_channel oc im [] ;;
    val sauve_image_tdc : 'a -> string -> unit = <fun>


  7. Comparer la taille du fichier obtenu par rapport la sauvegarde du tableau de couleurs quivalent. Elle est plus petite, d'un facteur 4!!!

  8. crire la fonction from_image_tdc de conversion inverse.

    # let from_image_tdc im =
    let r = Array.create_matrix im.hauteur im.largeur Graphics.black in
    let ns = ref 0 in
    for i = 0 to im.hauteur -1 do
    for j = 0 to im.largeur -1 do
    r.(i).(j) <- im.tdc.(int_of_char im.image.[!ns]) ;
    incr ns
    done
    done ;
    r ;;
    val from_image_tdc : image_tdc -> Graphics.color array array = <fun>


  9. L'utiliser pour visualiser une image sauvegarde dans un fichier sous forme d'une valeur de type bitmap_tdc.

    # let visu name =
    let ic = open_in name in
    let im = Marshal.from_channel ic in
    let caa = from_image_tdc im in
    let b = Graphics.make_image caa in
    let size = (string_of_int (Array.length caa.(0)))
    ^ "x" ^ (string_of_int (Array.length caa)) in
    Graphics.open_graph (":0 " ^ size) ;
    Graphics.draw_image b 0 0 ;
    b ;;
    val visu : string -> Graphics.image = <fun>

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora008.gif0000644000000000000000000000252407073350152014273 0ustar GIF89a!,ڋޜ~ݨ!:^p2K 0:ȦSyjGd-z*-76v '<_u>XGxHfH%0WiH8&tǤ iS8:Ykp K)k)[+X4|zU: v +4nJ~ɘh*΍N߃O{ DvnJˆ(n@9QĈC~3eF%˗ΞHE̙c8^j4i ,D i[bB%CLeW 2B֮!?kRV-n[Y;D)4˻~''aQ,TYLG ;W%|s\N7>o&镐o8x ӑY;- θ'FVM<8ҰJmy*9}P<̳U{w_~4D׳G{j`o]l__Հe% 'UNBxᄚX u$8≯=",!k08ی.hMHc 8Տ9%$(HHzURC6V957E)vbe~@~Pyb($jO %ɥf]ƹbz}$hp wҞ)hu(U:Z hjzh^(z*L~*fӨU*ι)aꇮ+li)l,׫'< OΦժ8@)䖋Ў覫rƋj~g)/6kp>~;k] "0z g)q|g0x"E<07'3TΜ8˦=Lw+t;;64V{e%T\ K1n 7"jgk= ֜Mpb:uo|wu"MF8݁=z}ǖtv{wL'v>쓱3V2Gev#!`]:yM}K(RF?0;i MtyW Calcul des dpendances Prcdent Index Suivant

Calcul des dpendances

Le calcul de dpendances d'un ensemble de fichiers d'implantation et d'interface d'une application Objective CAML a un double but. Le premier est d'obtenir une vision globale des interdpendances entre modules. Le deuxime est d'utiliser cette information pour ne recompiler que le strict ncessaire lors de modifications de certains fichiers.

La commande ocamldep prend un ensemble de fichiers .ml et .mli et affiche les dpendances entre fichiers dans le format des fichiers Makefile1.

Ces dpendances proviennent de l'utilisation de dclarations globales d'autres modules, soit en utilisant la notation pointe (ex: M1.f), soit en ouvrant un module (ex: open M1).

Supposons crits les fichiers dp.ml :

let print_vect v =
for i = 0 to Array.length v do
Printf.printf "%f " v.(i)
done;
print_newline();;


et d1.ml :

let init n e =
let v = Array.create 4 3.14 in
Dp.print_vect v;
v;;


La commande ocamldep qui l'on passe les noms de ces fichiers indiquera les dpendances suivantes :
$ ocamldep dp.ml d1.ml array.ml array.mli printf.ml printf.mli
dp.cmo: array.cmi printf.cmi 
dp.cmx: array.cmx printf.cmx 
d1.cmo: array.cmi dp.cmo 
d1.cmx: array.cmx dp.cmx 
array.cmo: array.cmi 
array.cmx: array.cmi 
printf.cmo: printf.cmi 
printf.cmx: printf.cmi 
Les dpendances sont calcules pour les compilateurs code-octet et natif. On lit le rsultat de la manire suivante : la production du fichier dp.cmo dpend des fichiers array.cmi et printf.cmi. Les fichiers d'extension .cmi dpendent des fichiers de mme nom d'extension .mli. Il en est de mme pour les .ml avec les .cmo et .cmx.

Les fichiers objet de la distribution n'apparaissent pas dans la liste des dpendances. En fait le rsultat prcdent de ocamldep s'il ne trouve pas des fichiers array.ml et printf.ml dans le catalogue courant, les trouvera dans le chemin des bibliothques de l'installation et produira :
$ ocamldep dp.ml d1.ml
d1.cmo: dp.cmo 
d1.cmx: dp.cmx 
Pour indiquer de nouveaux chemins de recherche de fichiers la commande ocamldep on utilisera l'option de commande -I catalogue qui ajoute un catalogue aux chemins de recherche.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora211.html0000644000000000000000000000404707421273603014472 0ustar Introduction Prcdent Index Suivant

Introduction

Indpendamment du dveloppement d'Objective CAML, de nombreuses extensions du langage sont apparues. L'une d'entre-elles, baptise O'Labl, est en cours d'intgration dans Objective CAML et sera sans doute prsente dans les prochaines versions Objective CAML 3.xx. Une version de test est distribue sous le nom de Objective CAML 2.99; elle est prsente sur le cdrom accompagnant cet ouvrage.

Lien


http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/


Cette annexe est consacre une rapide illustration des nouvelles possibilits apportes par :
  • les labels1;
  • les arguments optionnels;
  • les constructeurs polymorphes;
  • l'interface de dveloppement ocamlbrowser;
  • la bibliothque LablTk.
Cette version tant en cours d'valuation, elle n'est pas exempte de bogues et elle est susceptible d'voluer dans la version dfinitive.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora166.html0000644000000000000000000002545507421273602014510 0ustar Module Unix Prcdent Index Suivant

Module Unix

On trouve dans ce module l'interface avec les principales fonctions des bibliothques du systme Unix. Cependant, comme nous l'avons dj signal, une partie importante de ce module a t porte sous Windows et est donc accessible depuis ce dernier. Nous signalerons, lorsque ncessaire, les restrictions d'utilisation des fonctions prsentes. Un tableau rsumant ces restrictions est donn figure 18.1.

La bibliothque Unix fait partie des bibliothques non standard d'Objective CAML qu'il est ncessaire de lier par la commande -custom du compilateur (voir chapitre 7, page ??). Suivant l'usage que l'on destine au programme, on crit sous Unix l'une des commandes suivantes, pour obtenir du code-octet, du code natif ou une boucle d'interaction :
$ ocamlc -custom unix.cma  fichiers.ml -cclib -lunix
$ ocamlopt unix.cma fichiers.ml -cclib -lunix
$ ocamlmktop -custom -o unixtop unix.cma -cclib -lunix 
L'intrt de construire une boucle d'interaction (dont le nom sera ici unixtop) est de permettre un dveloppement incrmentiel. On obtient rapidement la compilation de chaque fonction avec son indication de type. On peut mme procder quelques tests fonctionnels.

Suivant l'Unix utilis, la bibliothque systme peut ne pas tre l'emplacement prvu par dfaut. Si ncessaire, on peut prciser le chemin d'accs des bibliothques avec l'option -ccopt (voir chapitre 7).

Sous Windows, les commandes de compilation deviennent :
$ ocamlc -custom unix.cma  fichiers.ml %CAMLLIB%\libunix.lib wsock32.lib
$ ocamlopt unix.cma fichiers.ml %CAMLLIB%\libunix.lib wsock32.lib
$ ocamlmktop -custom -o unixtop.exe unix.cma %CAMLLIB%\libunix.lib wsock32.lib
Le nom de la boucle d'interaction ainsi obtenue est unixtop.exe.

Gestion des erreurs

Les erreurs provoques par les appels systme dclenchent l'exception Unix_error qui peut tre traite depuis un programme Objective CAML. Cette erreur contient trois arguments : une valeur de type Unix.error qu'il est possible de traduire sous forme d'une chane de caractres par la fonction error_message, une chane contenant le nom de la fonction ayant provoqu l'erreur et une chane contenant ventuellement l'argument de la fonction lorsqu'il est de type string.

On peut ainsi dfinir une fonction gnrique d'appel de fonction et de traitement des erreurs :

# let capsule_unix fonct arg =
try (fonct arg) with
Unix.Unix_error (e,fm,argm) ->
Printf.printf "%s %s %s" (Unix.error_message e) fm argm ;;
val capsule_unix : ('a -> unit) -> 'a -> unit = <fun>
La fonction capsule_unix prend une fonction, son argument et applique l'une l'autre. Si une erreur Unix survient, un message explicatif est affich. Une fonction quivalente est dfinie dans le module Unix :

# Unix.handle_unix_error ;;
- : ('a -> 'b) -> 'a -> 'b = <fun>


Portabilit des appels systme

La figure 18.1 indique quelles fonctions, de communication et de gestion des processus, prsentes dans ce chapitre sont accessibles sous Windows. Les deux manques principaux sont la fonction fork pour la duplication du processus courant et la fonction kill d'mission de signaux.

Fonction Unix Windows Commentaire
openfile  
close  
dup  
dup2  
read  
write  
lseek  
execv  
execve  
execvp  
execvpe  
fork   utiliser create_process
getpid  
sleep  
wait    
waitpid uniquement pour un numro de
      processus donn
create_process  
create_process_env  
kill    
pipe  
mkfifo    
open_process   utilise l'interprte de commandes
      /bin/sh
close_process    

Figure 18.1 : Portabilit des fonctions du module Unix utilises dans ce chapitre


De plus la fonction wait d'attente de fin d'un processus fils n'est pas implante car fork ne l'est pas.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora067.html0000644000000000000000000005343507421273601014506 0ustar Modes de compilation Prcdent Index Suivant

Modes de compilation

La distribution d'un langage dpend du processeur et du systme d'exploitation. Pour chaque architecture (couple processeur, systme d'exploitation), une distribution d'Objective CAML contient la boucle d'interaction, le compilateur de code-octet et dans la plupart des cas le compilateur natif sur cette architecture.

Noms des commandes

La figure 7.5 donne les noms des commandes des diffrents compilateurs que l'on rencontre dans les distributions d'Objective CAML. Les quatre premires commandes sont disponibles pour toute distribution.


ocaml boucle d'interaction
ocamlrun interprte de code-octet
ocamlc compilateur en ligne de code-octet
ocamlopt compilateur en ligne de code natif
ocamlc.opt compilateur en ligne optimis de code-octet
ocamlopt.opt compilateur en ligne optimis de code natif
ocamlmktop constructeur de nouvelles boucles d'interaction

Figure 7.5 : Commandes de compilation


Les compilateurs optimiss ont t eux-mmes compils avec le compilateur natif, produisant un excutable plus rapide.

Unit de compilation

L'unit de compilation correspond la plus petite dcomposition d'un programme Objective CAML pouvant tre compile. Pour la boucle d'interaction, l'unit de compilation correspond une phrase du langage. Par contre pour les compilateurs en ligne, l'unit de compilation est un couple de fichiers  : le fichier source et le fichier interface. Le fichier interface est optionnel. S'il n'existe pas, alors toutes les dclarations globales du fichier source seront visibles par une autre unit de compilation. La construction des fichiers d'interface sera dcrite dans le chapitre sur la programmation modulaire (14). La liaison entre ces deux fichiers utilise les conventions de nommage des extensions de fichiers.

Nommage des extensions de fichiers

La figure 7.6 prsente les extensions des diffrents fichiers utiliss pour les programmes Objective CAML et C.


extension signification
.ml fichier source
.mli fichier interface
.cmo fichier objet (code-octet)
.cma fichier d'une bibliothque objet (code-octet)
.cmi fichier interface compil
.cmx fichier objet (natif)
.cmxa fichier d'une bibliothque objet (natif)
.c fichier source C
.o fichier objet C (natif)
.a fichier d'une bibliothque objet C (natif)

Figure 7.6 : Extension des fichiers


Les fichiers exemple.ml et exemple.mli forment une unit de compilation. Le fichier d'interface compil (exemple.cmi) est commun au compilateur de code-octet et au compilateur natif. Les fichiers provenant du monde C sont utiliss pour l'interfaage de Objective CAML avec des bibliothques C (12).

Compilateur de code-octet

La forme gnrale de la commande d'un compilateur en ligne est la suivante :



Objective CAML suit la mme rgle. On tapera, par exemple :
ocamlc -c exemple.ml
Les options passes au compilateur sur la ligne de commande suivent les conventions du systme Unix. Elles sont prcdes du caractre -. Les extensions des fichiers sont interprtes de la manire dcrite la figure 7.6. Dans l'exemple ci-dessus, le fichier exemple.ml est considr comme un fichier source Objective CAML et sera donc compil, ce qui produira les fichiers exemple.cmo et exemple.cmi car l'option -c indique au compilateur de n'engendrer que le code objet. Sans cette option, le compilateur ocamlc produit aussi un fichier excutable a.out, c'est-- dire qu'il effectue aussi l'dition de liens.

Le tableau de la figure 7.7 dcrit les principales options du compilateur de code-octet. Le tableau de la figure 7.8 indique les autres options possibles.

Principales options
-a construit une bibliothque
-c compile sans faire l'dition de liens
-o nom_executable spcifie le nom de l'excutable
-linkall indique de lier avec toutes les bibliothques utilises
-i affiche toutes les dclaration globales compiles
-pp commande utilise commande comme pr-processeur
-unsafe bascule en mode sans vrification des indices
-v affiche la version du compilateur
-w liste choisit selon liste le niveau des messages d'avertissement (voir fig. 7.9)
-impl fichier indique que fichier est un source Caml (.ml)
-intf fichier indique que fichier est une interface Caml (.mli)
-I catalogue ajoute catalogue dans la liste des catalogues

Figure 7.7 : Principales options du compilateur de code-octet



Autres options
processus lgers -thread (19, page ??)
mise au point -g, -noassert (10, page ??)
excutables autonomes -custom, -cclib, -ccopt, -cc (voir page ??)
runtime -make-runtime , -use-runtime
interface C -output-obj (12, page ??)

Figure 7.8 : Autres options du compilateur de code-octet


Pour obtenir l'affichage de l'ensemble des options du compilateur de code-octet, il suffit de lui passer l'option -help.

Les niveaux de message d'avertissement (warning) sont dcrits la figure 7.9. Un niveau de message est une bascule (active/dsactive) reprsente par une lettre. Une majuscule active ce niveau et une minuscule le dsactive.

Principaux niveaux  
A/a active/dsactive tous les messages
F/f application partielle dans une squence
P/p pour les filtrages incomplets
U/u pour les cas inutiles d'un filtrage
X/x active/dsactive tous les autres messages
pour la couche objet M/m et V/v (voir chapitre 15)

Figure 7.9 : Description des avertissements de compilation


Par dfaut le niveau maximal (A) est choisi par le compilateur.

Un exemple d'utilisation du compilateur de code-octet est donn figure 7.10.



Figure 7.10 : Session avec le compilateur de code-octet


Compilateur natif

Le compilateur natif possde un comportement proche de celui du compilateur de code-octet bien que les fichiers produits soient diffrents. Les options de compilation sont globalement les mmes que celles dcrites aux figures 7.7 et 7.8. Il faut cependant retirer les options lies au runtime de la figure 7.8. Les options spcifiques au compilateur natif sont donnes la figure 7.11. Les diffrents niveaux de warning sont les mmes.

-compact optimise en espace le code produit
-S conserve le code assembleur dans un fichier
-inline niveau indique le grain de traduction d'une fonction plat

Figure 7.11 : Options particulires pour le compilateur natif.


L'expansion en ligne (inlining) est une version labore de la macro-expansion du prtraitement. Elle remplace l'appel d'une fonction par le corps de la fonction o les arguments sont fixs. Plusieurs appels diffrents correspondront une nouvelle traduction du corps de la fonction. On vite ainsi d'excuter la squence d'appel de fonction, mais la taille du code peut s'en trouver accrue. Les principaux niveaux d'inlining sont :
  • 0 : l'expansion n'est autorise que lorsqu'elle n'accrot pas la taille du code ;
  • 1 : c'est la valeur par dfaut, elle accepte une lgre augmentation de la taille du code ;
  • n > 1 : augmente la tolrance d'accroissement de la taille du code expans.

Boucle d'interaction

La boucle d'interaction ne possde que deux options pour son lancement :
  • -I catalogue : qui ajoute le chemin indiqu en tte de la liste des chemins de recherche des fichiers sources ou compils ;
  • -unsafe : qui indique au compilateur de ne plus tester les dbordements d'indice de tableaux ou de chanes de caractres.
La boucle d'interaction possde plusieurs directives de compilation qui permettent de modifier interactivement son comportement. Elles sont dcrites la figure 7.12. Toutes les directives commencent par le caractre # et se terminent par les habituels ;;.

#quit;; sort de la boucle d'interaction
#directory catalogue ;; ajoute un chemin en tte
#cd catalogue ;; change de catalogue courant
#load fichier_objet ;; charge un fichier .cmo
#use fichier_source ;; compile et charge un fichier source
#print_depth profondeur ;; modifie la profondeur d'affichage
#print_length largeur ;; idem pour la largeur
#install_printer fonction ;; spcifie une fonction d'affichage
#remove_printer fonction ;; repasse l'afficheur standard
#trace fonction ;; trace les arguments d'une fonction
#untrace fonction ;; enlve la trace d'une fonction
#untrace_all ;; enlve toutes les traces

Figure 7.12 : Directives de la boucle d'interaction


Les directives sur les catalogues respectent les conventions du systme d'exploitation utilis.

Les directives de chargement n'ont pas exactement le mme comportement. La directive #use lit le fichier source indiqu comme s'il avait t tap directement en interactif. La directive #load charge le fichier d'extension .cmo indiqu. Dans ce dernier cas, les dclarations globales de ce fichier ne sont pas directement accessibles.
Pour cela il faut utiliser la notation pointe. Si le fichier exemple.ml contient la dclaration globale f, alors une fois charg le code-octet (#load "exemple.cmo";;), on accdera la valeur de f par Exemple.f, o la premire lettre du fichier est remplace par une majuscule. Cette notation vient du systme de modules d'Objective CAML (voir chapitre 14, page ??).

Les directives de paramtrage de la profondeur et de la largeur d'affichage permettent de contrler l'affichage des valeurs. Ce qui est utile quand des valeurs volumineuses doivent tre affiches.

Les directives de redfinition des fonctions d'impression pour des types donns permettent de dfinir leur prsentation. Pour qu'elles s'intgrent bien avec l'affichage par dfaut, il sera ncessaire d'utiliser la bibliothque Format (8) pour les dfinir.

Les directives de traage des arguments et du rsultat de fonctions sont particulirement utiles pour la mise au point des programmes. Elles seront manipules au chapitre sur l'analyse de programme (10).

La figure 7.13 montre une session avec la boucle d'interaction.



Figure 7.13 : Session avec la boucle d'interaction


Construction de nouvelles boucles d'interaction

La commande ocamlmktop permet de construire de nouvelles boucles d'interaction dans lesquelles un certain nombre de bibliothques ou modules sont prchargs. Outre le fait de ne plus avoir besoin d'effectuer les #load en dbut de session, cela est indispensable pour intgrer des bibliothques crites en C car il est alors ncessaire de modifier la bibliothque d'excution.

Les options de cette commande sont un sous-ensemble des options du compilateur de code-octet (ocamlc) :
-cclib libname, -ccopt option, -custom, -I catalogue et -o nom_executable
Le chapitre sur la programmation graphique (5, page ??) utilise cette commande pour construire un toplevel contenant la bibliothque Graphics de la manire suivante :
ocamlmktop -custom -o mytoplevel graphics.cma -cclib \ 
           -L/usr/X11/lib -cclib -lX11
Cette commande construit l'excutable de nom mytoplevel, contenant la bibliothque de code-octet graphics.cma. Cet excutable autonome (-custom (voir section suivante) sera li la bibliothque X11 (libX11.a) qui elle-mme sera recherche dans le chemin /usr/X11/lib.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora087.html0000644000000000000000000005445107421273602014510 0ustar Rcupration automatique Prcdent Index Suivant

Rcupration automatique

On distingue deux classes d'algorithmes de rcupration automatique de mmoire :
  • les compteurs de rfrences  : chaque zone alloue connat le nombre de rfrences sur elle-mme. Quand ce nombre vaut zro, la zone est libre.
  • les algorithmes explorateurs : partir d'un ensemble de racines (pointeurs), l'ensemble des valeurs accessibles est explor la manire d'un parcours de graphe orient.
Ce sont les algorithmes explorateurs qui sont le plus utiliss dans les langages de programmation. En effet, un GC compteur de rfrence alourdi le traitement (mise jour des compteurs) mme lorsqu'il n'y a pas besoin de rcuprer quoique ce soit.

Compteurs de rfrences

On associe chaque zone alloue un compteur. Ce compteur indique le nombre de pointeurs sur cet objet. Il est incrment chaque partage de l'objet. Il est dcrment quand un pointeur sur cet objet disparat. Quand ce compteur vaut 0, l'objet est rcupr.

L'avantage d'un tel systme provient de la libration immdiate des zones qui ne sont plus utilises. Outre l'alourdissement systmatique des manipulations, les GC compteurs de rfrences souffrent d'un autre inconvnient : ils ne savent pas traiter les objets circulaires. Supposons qu'Objective CAML possde un tel mcanisme. L'exemple suivant construit une valeur temporaire l, liste de caractres dont le dernier lment pointe sur la cellule contenant 'c'. C'est donc bien une valeur circulaire (figure 9.2).

# let rec l = 'c'::'a'::'m'::l in List.hd l ;;
- : char = 'c'


Figure 9.2 : reprsentation mmoire d'une liste circulaire


la fin du calcul de cette expression, chaque lment de la liste l a un compteur gal un (mme le premier car la queue pointe sur la tte). Cette valeur n'est plus accessible et n'est pourtant pas rcupre car son compteur de rfrence n'est pas nul. Dans les langages munis d'une rcupration par compteur de rfrences, comme le langage (Python), et autorisant la construction de valeurs circulaires, il est ncessaire d'adjoindre un algorithme explorateur de mmoire.

Algorithmes explorateurs

Les algorithmes explorateurs permettent un instant donn d'explorer le graphe des valeurs accessibles du tas. Cette exploration utilise un ensemble de racines indiquant le dbut du parcours. Ces racines sont extrieures au tas. Elles sont conserves le plus souvent dans une pile. Dans notre exemple de la figure 9.1, on peut supposer que les valeurs de u et v font partie des racines. Le parcours partir de ces racines construira le graphe des valeurs conserver : les cellules et pointeurs en gras de la figure 9.3.


Figure 9.3 : rcupration mmoire aprs un GC


Le parcours de ce graphe ncessite de savoir distinguer dans le tas les valeurs immdiates de pointeurs. Si une racine pointe sur un entier, il ne faut pas considrer cette valeur comme l'adresse d'une autre cellule. Dans les langages fonctionnels, cette distinction est obtenue en utilisant quelques bits des cellules du tas. On parle alors de bits d'tiquette (en anglais tag). C'est pourquoi les entiers d'Objective CAML n'utilisent que 31 bits. Cette option est dcrite au chapitre 12, page ??. Il existe d'autres solutions au problme de la distinction entre pointeurs et valeurs immdiates que nous dcrivons dans ce chapitre, page ??.

Les deux algorithmes les plus utiliss sont le Mark&Sweep, qui construit la liste des cellules libres du tas, et le Stop&Copy qui recopie les cellules encore vivantes dans une deuxime zone mmoire.

Le tas doit tre vu comme un vecteur de cases mmoire. La reprsentation de l'tat du tas de l'exemple de la figure 9.1 est illustr la figure 9.4.


Figure 9.4 : tat du tas


On value un algorithme explorateur par les caractristiques suivantes.
  • efficacit : la complexit en temps dpend-elle de la taille du tas ou seulement de la taille des cellules vivantes ?
  • facteur de rcupration : toute la mmoire libre est-elle disponible ?
  • compaction : toute la mmoire libre est-elle disponible en un seul bloc ?
  • localisation : les diffrentes cellules d'une valeur structure sont-elles proches ?
  • besoin de mmoire : l'algorithme ncessite-t'il d'utiliser une partie de la mmoire lors de son droulement ?
  • dplacement des valeurs : les valeurs changent-elles de place suite une GC?
La localisation vite les changements de page mmoire pour le parcours d'une valeur structure. La compaction vite la fragmentation du tas et autorise des allocations de la taille de la mmoire disponible. L'efficacit, le facteur de rcupration et les besoins supplmentaires en mmoire sont intimement lis pour les complexits en temps et en espace de l'algorithme.

Mark&Sweep

Le principe du Mark&Sweep est de tenir jour une liste des cellules libres du tas appele freelist. Lors d'une demande d'allocation, si la liste est vide ou s'il n'y a plus de cellules libres d'une taille suffisante alors un Mark&Sweep est effectu.

Il procde en deux tapes :
  1. marquage des zones mmoires utiles partir d'un ensemble de racines (phase dite Mark) ;
  2. puis rcupration des zones mmoire non marques en parcourant squentiellement la totalit du tas (phase dite Sweep).
On peut illustrer la gestion mmoire du Mark&Sweep en utilisant quatre << colorations >> des cellules du tas : le blanc, le gris1, le noir et le hachur. La phase de marquage utilise le noir et le gris ; la phase de rcupration utilise le hachur et l'allocation, le blanc.

La signification des couleurs noir et gris utilises par le marquage est la suivante :
  • gris : cellule marque dont les fils ne le sont pas encore ;
  • noir : cellule marque dont les fils immdiats le sont aussi.
Il sera ncessaire de conserver l'ensemble des cellules grises pour tre sr de tout explorer. la fin du marquage chaque cellule est blanche ou noire. Les cellules noires sont celles qui ont t atteintes depuis les racines. La figure 9.5 montre une tape intermdiaire du marquage pour l'exemple de la figure 9.4 : la racine u a t traite et le traitement de v en est son dbut.


Figure 9.5 : phase de marquage


C'est dans la phase de rcupration que la liste des cellules libres est construite. Elle opre les modifications suivantes de coloration :
  • noir devient blanc, la cellule vit ;
  • blanc devient hachur, la cellule est ajoute la freelist.
La figure 9.6 montre l'volution des couleurs du tas et la construction de la freelist.


Figure 9.6 : phase de rcupration


Les caractristiques du Mark&Sweep sont les suivantes :
  • dpend de la taille entire du tas (tape Sweep) ;
  • rcupre toute la mmoire disponible ;
  • ne compacte pas la mmoire ;
  • n'assure pas la localisation ;
  • ne dplace pas les donnes .
La phase de marquage est gnralement implante par une fonction rcursive. Elle utilise donc une partie de la pile d'excution. On peut donner une version entirement itrative de Mark&Sweep, n'utilisant pas une pile de profondeur inconnue, mais elle s'avre moins efficace que la version en partie rcursive.

Enfin, Mark&Sweep a besoin de connatre la taille des valeurs. Celle-ci est soit conserve dans les valeurs, soit est dduite des adresses mmoire par un dcoupage du tas en zone d'allocation d'objets de taille borne. Cet algorithme, implant ds les premiers Lisp, est encore largement utilis. Une partie du GC d'Objective CAML utilise un tel algorithme.

Stop&Copy

L'ide principale de ce GC est d'utiliser une mmoire secondaire pour recopier et compacter les zones mmoire conserver. Le tas est divis en deux parties : la zone utile (appele from-space) et la zone de recopie (appele to-space).



Figure 9.7 : dbut du Stop&Copy


L'algorithme est le suivant. partir d'un ensemble de racines, on recopie la partie utile de la zone from-space dans l'espace to-space; la nouvelle adresse d'une valeur dplace est conserve (le plus souvent dans son ancienne localisation) afin de mettre jour toutes les autres valeurs pointant sur cette valeur.



Figure 9.8 : recopie de from-space dans to-space


Le contenu des cellules recopies forme les nouvelles racines. Tant que toutes ne sont pas traites l'algorithme continue.



Figure 9.9 : nouvelles racines


En cas de partage, c'est--dire le dplacement d'une valeur dj dplace, on se contente de donner la nouvelle adresse.



Figure 9.10 : partage


la fin du GC, toutes les racines sont mises jour pour pointer sur les nouvelles adresses. Enfin, les rles des deux zones sont inverss en perspective du prochain GC.



Figure 9.11 : inversion des zones


Les principales caractristiques de ce GC sont les suivantes :
  • dpend uniquement de la taille des objets conserver;
  • seule la moiti de la mmoire est disponible;
  • compacte la mmoire;
  • possibilit de localisation (parcours en largeur);
  • n'utilise pas de mmoire supplmentaire (from-space+to-space);
  • algorithme non rcursif;
  • dplace les donnes dans la nouvelle zone mmoire;

Autres GC

Bien d'autres techniques, souvent issues des deux prcdentes, ont t utilises soit dans des applications particulires, comme par exemple la manipulation de grandes matrices en calcul formel, soit de manire gnrale et lie des techniques de compilation. Les GC gnrations permettent d'optimiser les techniques selon l'ge des valeurs. Les GC racines ambigus sont utiliss quand on ne diffrencie pas explicitement les valeurs immdiates des pointeurs (par exemple lorsqu'on compile vers C). Enfin les GC incrmentiels permettent d'viter un ralentissement perceptible lors du dclenchement du GC.

GC gnration

Les programmes fonctionnels sont des programmes qui allouent en gnral beaucoup et on constate que de trs nombreuses valeurs ont une dure de vie trs courte2. D'autre part, ds qu'une valeur a survcu plusieurs GC, elle a de grandes chances d'exister pour un bon moment. Pour viter le parcours complet du tas, comme dans un Mark&Sweep, chaque rcupration de mmoire, on souhaite pouvoir n'effectuer un GC uniquement sur les valeurs ayant survcu 1 ou quelques GC (c'est--dire des valeurs jeunes). C'est le plus souvent dans ces espaces mmoire que l'on rcuprera le plus de place. Pour cela les objets sont dats, soit par une marque de temps, soit par le nombre de GC auxquels ils ont survcu. Pour optimiser le GC on utilisera des algorithmes diffrents selon l'ge des valeurs :
  • Les GC sur les objets jeunes devront tre rapides et ne parcourir que les jeunes gnrations.
  • Les GC sur les objets anciens devront tre rares et bien rcuprer l'ensemble de la mmoire.
Une valeur qui vieillit sera de moins en moins partie prenante pour les GC les plus frquents. La difficult provient alors de ne pouvoir tenir compte uniquement de la partie mmoire des objets jeunes. Dans un langage fonctionnel pur, c'est--dire sans modification physique, les objets jeunes rfrencent des objets anciens par contre les objets anciens ne possdent pas de pointeurs vers les objets jeunes, car ils ont t crs prcdemment. Donc ces techniques s'y prtent bien l'exception des langages valuation retarde qui en fait peuvent calculer les composants d'une structure aprs celle-ci. Par contre pour les langages fonctionnels avec modifications physiques, il est toujours possible d'affecter une partie d'un objet ancien avec un objet jeune. Le problme est alors de conserver une zone mmoire jeune uniquement rfrence par une valeur ancienne. Pour cela il sera ncessaire de tenir jour une table des rfrences des objets anciens vers les objets jeunes, pour obtenir un GC correct. On tudiera dans la section suivante le cas d'Objective CAML.

GC racines ambigus

Jusqu' maintenant, toutes les techniques de GC supposaient savoir discriminer une valeur immdiate d'un pointeur. Il est noter que dans les langages fonctionnels polymorphes paramtriques la reprsentation des valeurs est uniforme et occupe en gnral un mot mmoire3. C'est ce qui permet d'avoir un code gnrique pour les fonctions polymorphes.

Cependant, cette restriction de l'espace des entiers peut ne pas tre acceptable. Dans ce cas, les GC racines ambigus permettent d'viter de marquer les objets valeurs immdiates comme les entiers. Dans ce cas toutes les valeurs utilisent un mot mmoire sans aucune marque. Pour viter le parcours d'une zone mmoire partir d'une racine contenant un entier, on utilise un algorithme de discrimination entre valeurs immdiates et pointeurs valides reposant sur les deux observations suivantes :
  • les adresses de dbut et de fin de tas sont connues donc toute valeur en dehors de ces bornes est une valeur immdiate;
  • les objets allous sont aligns sur une adresse de mot. Toute valeur ne correspondant pas un alignement correct sera aussi considre comme une valeur immdiate.
Ainsi toute valeur du tas valide du point de vue des adresses dans le tas sera compte comme pointeur du tas et le rcuprateur mmoire cherchera conserver cette zone, y compris dans le cas o cette valeur est, en fait, une valeur immdiate. Ce cas peut devenir fort rare en utilisant des pages mmoire spcifiques selon la taille des objets. Il n'est pas possible de garantir que tout le tas inutile sera ainsi rcupr. C'est le dfaut principal de cette technique. Mais on reste sr que seules sont rcupres des zones effectivement inutilises.

En rgle gnrale, les GC racines ambigus sont conservatifs, i.e. ils ne dplacent pas les objets. En effet, puisque le GC considre certaines valeurs immdiates comme un pointeur, il serait dommageable d'en changer la valeur. Nanmoins des spcialisations peuvent tre apportes dans la constitution de l'ensemble des racines et permettent de dplacer une partie des zones correspondant des racines sres.

Les techniques de GC racines ambigus sont souvent utilises quand on compile un langage fonctionnel vers le langage C, vu alors comme un assembleur portable. Elles permettent d'utiliser les valeurs immdiates C codes sur un mot mmoire.

GC incrmentiel

Un des reproches souvent fait aux GC est d'arrter l'excution du programme en cours pendant un temps perceptible l'utilisateur et non born. Le premier cas est gnant pour certaines applications. On peut citer les jeux d'action rapide o l'arrt du jeu pendant quelques secondes est trop souvent source d'injustice pour le joueur, l'excution repart sans prvenir. Le deuxime cas est source de perte de contrle pour des applications devant traiter un certain nombre d'vnements dans un temps limit. C'est typiquement le cas pour des programmes embarqus qui contrlent un appareil physique, comme un vhicule ou une machine outil. Ces applications temps rel, dans le sens o l'application doit rpondre dans un temps born, vitent le plus souvent d'utiliser des GC.

Les GC incrmentiels doivent pouvoir s'interrompre pendant l'une quelconque de leurs phases de traitement et pouvoir repartir en assurant la sret de la rcupration. Ils permettent de traiter de manire assez satisfaisante le premier cas et peuvent tre utiliss dans le deuxime cas en forant une discipline de programmation qui isole clairement les parties logicielles qui utilisent l'allocation dynamique de celles qui ne l'utilisent pas.

Reprenons l'exemple du Mark&Sweep et voyons les adaptations qu'il faut lui apporter pour le rendre incrmentiel. Il y en a essentiellement deux :
  1. comment tre sr d'avoir tout marqu pendant la phase de marquage ?
  2. comment allouer durant les phases de marquage ou de rcupration ?
Si le Mark&Sweep est interrompu pendant la phase Mark, il faut assurer que les cellules alloues entre l'interruption du marquage et sa reprise ne serons pas indment rcupres par le Sweep qui va suivre. Pour cela, on marque, par anticipation, en noir ou gris les cellules alloues pendant l'interruption.

Si le Mark&Sweep est interrompu pendant la phase Sweep, on peut procder de faon usuelle en remettant blanc les marques des cellules alloues. En effet, comme la phase Sweep parcourt squentiellement le tas, les cellules alloues pendant l'interruption sont localises avant le point de redmarrage du Sweep, elles ne seront pas rexamines avant le prochain cycle du GC.

La figure 9.12 montre une allocation pendant la phase de rcupration. La racine w est cre par :

# let w = 'f'::v;;
val w : char list = ['f'; 'z'; 'a'; 'm']


Figure 9.12 : allocation pendant la rcupration



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora008.html0000644000000000000000000000320607421273601014470 0ustar Tlchargement Prcdent Index Suivant

Tlchargement

Le tlchargement d'Objective CAML s'effectue via un navigateur HTTP l'adresse suivante :

Lien


http://caml.inria.fr/ocaml/distrib.html
On y trouve les distributions binaires pour Linux (Intel et PPC), pour Windows (NT, 95, 98) et pour MacOS (7, 8), ainsi que la documentation, en anglais, sous diffrents formats (PDF, PostScript et HTML). Par ailleurs les sources sont disponibles pour les trois systmes. Une fois la distribution dsire copie sur sa machine, il est temps de l'installer. Cette procdure diffre selon le systme d'exploitation utilis.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora042.gif0000644000000000000000000001061607073350152014272 0ustar GIF89aUUU999rrr!,X0I8ͻ`(dihVtmk52&dm tJ] eXm<nw\BZ 3y^)?uN\ fP[+/@;>i{|-H?DX0P2o.X B OPfpyƞ~ڡ$70A h0&$yV؀i FY tZfj0'4Pĭj+\sM>$2eZ'D 5 qkF-0V&\ 6Ht ./'w,Xrj3s|+Z&\T4Qz@Ժrjp9*15Jф)id f#J>ÕRj{#8x D:tLJ^Yds9w`tzqmwëWG$Ԥ^qݾ#6/_+ܤt9TAV|ǣ'r9X _gIh1t9ӄUD`r)pl1@JQJN|)洍WAs`ij DX6Jx CSX#&|QB g IC[Er!  F4 85&h$ EYÊ|78q"X) aIg 뱮 %!bt@FC hlJ*4Ρ b-IT8,)VF+%%HIڧ%Md8d8}p8+aP>2T%_2oڥ 86YeXH+|pU),*ꉚP #\8U=@)e]>A5 >YX\w G h@qN49Ǧ`Wph;zbq'^BL@ToRձ0`<2Sk1J( L/(g`t=aPo"K7 IhN1H';k$ˊIN_bkQoj}*$.+qOW{xM'8A>㕟|[w~qU,u:]Zwso_?k V~ײ8 { hS XXwxz"~G!Q Yp$XJ{vHlv.&81(4TM0x3%ȃ@(A,zNՃ;(JH>?(Db8-؄AȄION(SVeȅWxjXi  _8sErkHs؅& os f~fxi(ȁ&aXuj؆y󇃈H\1vrӉhxx`Smw8'сS!҇Xh e\bso!oRx蘎긎XQ!ȍh ؎؏-8ixZ| oUH I yi%FAN C"w'@ْ,Hx9Y{98:y;v3?y$($ڥ^QdʓJN/~éH @dzcڦ 41zOh*x LɧzykZʚA9jXjJcx5Ql/_*ᦝ*IgPgqhJ}v:1񉨼*gxe^zJ c H1)Ҭls* WB!4<LR`O  ڏ8UZ६ @i: ;AK);Q,X $$;*6x#(.u~4[6{8:<۳>@B;D[q(_ua"Ji oYLe .X0kDa2{>Bb1Mq cg+V=X !b+cIF3Kdsk pJːผa4RKGR\nrzUD\08X3hs3e5L!"uVjK~ Jyf1cYF89#K53"%McMY+`4z[ S;dCvomyS3PøVMվIIP[1ETbWQeg{4¶adq/sGmw q݆a= ѷ0RL$fU"\(,l/Q(@Y\¤cbATaKub KO<U54}MX7Ŵ̕=cb&90 [&hNӻ`lƛȻiSL$I+E/K'dP2: $QdLYvlilJRd. 8dRF<&e 9$RL(ZV,=5 1,zljwkW <òé"a<%7MS\Η;ocaml-book-1.0/fr/html/book-ora006.html0000644000000000000000000000254107421273601014467 0ustar Logiciels libres Prcdent Index Suivant

Logiciels libres

Les diffrents logiciels utiliss dans cet ouvrage sont des logiciels << libres >>1. On les trouve soit sur le cdrom accompagnant cet ouvrage, soit en tlchargement depuis le rseau Internet. C'est le cas d'Objective CAML, dvelopp l'Inria.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora139.html0000644000000000000000000001070007421273602014473 0ustar Introduction Prcdent Index Suivant

Introduction

Comme son nom le laisse supposer, le langage Objective CAML possde la notion d'objet. la diffrence de la programmation imprative qui est dirige par les traitements ou de la programmation fonctionnelle qui est dirige par les calculs, la programmation par objet est une programmation dirige par les donnes. L'utilisation des objets introduit une nouvelle structuration des programmes en classes, les objets tant des instances de celles-ci. Une classe regroupe des donnes et des traitements. Ces derniers, appels mthodes, dfinissent les comportements de l'objet. Le dclenchement d'un comportement est effectu par une requte sur l'objet appele un envoi de message. L'objet qui reoit ce message effectue l'action ou le calcul correspondant la mthode invoque dans le message : ce n'est pas une fonction qui est applique des arguments, mais un message (correspondant un nom de mthode) qui est envoy un objet. C'est l'objet lui-mme qu'il revient de dterminer quel est le code effectif excuter. Cette liaison retarde entre nom et code permet de faciliter la << modifiabilit >> des comportements; c'est--dire d'accrotre la rutilisabilit du code.

La programmation par objets permet de spcifier les relations entre classes ainsi que la communication entre objets travers les paramtres passs aux objets dans les messages. Elle offre une nouvelle modlisation des applications par les relations d'agrgation et d'hritage entre classes. Une classe qui hrite d'une autre classe possde toutes les dfinitions de la classe anctre, elle peut tendre les donnes et les mthodes, et mme redfinir des comportements hrits en respectant le typage. On utilisera une notation graphique1 pour reprsenter les relations entre classes.

L'extension objet d'Objective CAML s'intgre dans le systme de types du langage. Une dclaration d'une classe dfinit un type du nom de la classe. La construction d'une instance de cette classe retourne une valeur du type de la classe. Deux genres de polymorphisme coexistent. Le premier est le polymorphisme paramtrique, dj rencontr avec les types paramtrs, reprsent par les classes paramtres. Le deuxime, appel polymorphisme d'inclusion, utilise la relation de sous-typage entre objets et la liaison retarde. Si le type de la classe sc est sous-type du type de la classe c, tout objet de sc peut tre utilis la place d'un objet de c. Il faudra cependant que la contrainte entre sous-type et type soit explicite. Le polymorphisme d'inclusion permet de construire des listes htrognes o chaque lment est sous-type du type des lments de la liste. Comme la liaison est retarde, l'envoi du mme message tous les lments d'une telle liste peut activer des mthodes diffrentes, propres aux sous-classes.

Par contre Objective CAML n'intgre pas la notion de surcharge des mthodes qui autoriserait diffrentes dfinitions d'une mme mthode. Cette limitation est induite par l'infrence de types qui tomberait sur des ambiguts et demanderait au programmeur d'avoir fournir de trop nombreuses informations de type supplmentaires.

Il est noter qu'Objective CAML est le seul langage muni d'une extension objet avec polymorphisme paramtrique et d'inclusion qui reste compltement typ statiquement avec une infrence de types.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora070.html0000644000000000000000000034601707421273601014501 0ustar Exercices Prcdent Index Suivant

Exercices

Cration de toplevels et d'excutables autonomes

On reprend l'interprte de Basic pour crer une nouvelle boucle d'interaction.
  1. Dcouper l'application Basic en 4 fichiers d'extension .ml contenant respectivement : la syntaxe abstraite (syntax.ml), l'afficheur (pprint.ml), l'analyse syntaxique (alexsynt.ml) et l'valuation des instructions (eval.ml). Chaque dbut de fichier contiendra l'ouverture des modules ncessaires sa compilation. syntax.ml :

    type op_unr = OPPOSE | NON ;;

    type op_bin = PLUS | MOINS | MULT | DIV | MOD
    | EGAL | INF | INFEQ | SUP | SUPEQ | DIFF
    | ET | OU ;;

    type expression =
    ExpInt of int
    | ExpVar of string
    | ExpStr of string
    | ExpUnr of op_unr * expression
    | ExpBin of expression * op_bin * expression ;;

    type instruction =
    Rem of string
    | Goto of int
    | Print of expression
    | Input of string
    | If of expression * int
    | Let of string * expression ;;

    type ligne = { num : int ; inst : instruction } ;;

    type program = ligne list ;;

    type phrase = Ligne of ligne | List | Run | End ;;

    let priority_ou = function NON -> 1 | OPPOSE -> 7
    let priority_ob = function
    MULT | DIV -> 6
    | PLUS | MOINS -> 5
    | MOD -> 4
    | EGAL | INF | INFEQ | SUP | SUPEQ | DIFF -> 3
    | ET | OU -> 2 ;;

    let pp_opbin = function
    PLUS -> "+" | MULT -> "*" | MOD -> "%" | MOINS -> "-"
    | DIV -> "/" | EGAL -> " = " | INF -> " < "
    | INFEQ -> " <= " | SUP -> " > "
    | SUPEQ -> " >= " | DIFF -> " <> " | ET -> " & " | OU -> " | "
    let pp_opunr = function OPPOSE -> "-" | NON -> "!" ;;
    pprint.ml :
    open Syntax;;

    let parenthese x = "(" ^ x ^ ")";;

    let pp_expression =
    let rec ppg pr = function
    ExpInt n -> (string_of_int n)
    | ExpVar v -> v
    | ExpStr s -> "\"" ^ s ^ "\""
    | ExpUnr (op,e) ->
    let res = (pp_opunr op)^(ppg (priority_ou op) e)
    in if pr=0 then res else parenthese res
    | ExpBin (e1,op,e2) ->
    let pr2 = priority_ob op
    in let res = (ppg pr2 e1)^(pp_opbin op)^(ppd pr2 e2)
    (* parenthse si la priorite n'est pas suprieure *)
    in if pr2 >= pr then res else parenthese res
    and ppd pr exp = match exp with
    (* les sous-arbres droits ne diffrent *)
    (* que pour les oprateurs binaires *)
    ExpBin (e1,op,e2) ->
    let pr2 = priority_ob op
    in let res = (ppg pr2 e1)^(pp_opbin op)^(ppd pr2 e2)
    in if pr2 > pr then res else parenthese res
    | _ -> ppg pr exp
    in ppg 0 ;;

    let pp_instruction = function
    Rem s -> "REM " ^ s
    | Goto n -> "GOTO " ^ (string_of_int n)
    | Print e -> "PRINT " ^ (pp_expression e)
    | Input v -> "INPUT " ^ v
    | If (e,n) -> "IF "^(pp_expression e)^" THEN "^(string_of_int n)
    | Let (v,e) -> "LET " ^ v ^ " = " ^ (pp_expression e) ;;

    let pp_ligne l = (string_of_int l.num) ^ " " ^ (pp_instruction l.inst) ;;
    alexsynt.ml :
    open Syntax;;

    type lexeme = Lint of int
    | Lident of string
    | Lsymbol of string
    | Lstring of string
    | Lfin ;;

    type chaine_lexer = {chaine:string; mutable courant:int; taille:int } ;;

    let init_lex s = { chaine=s; courant=0 ; taille=String.length s } ;;

    let avance cl = cl.courant <- cl.courant+1 ;;

    let avance_n cl n = cl.courant <- cl.courant+n ;;

    let extrait pred cl =
    let st = cl.chaine and ct = cl.courant in
    let rec ext n = if n<cl.taille && (pred st.[n]) then ext (n+1) else n in
    let res = ext ct
    in cl.courant <- res ; String.sub cl.chaine ct (res-ct) ;;

    let extrait_int =
    let est_entier = function '0'..'9' -> true | _ -> false
    in function cl -> int_of_string (extrait est_entier cl)
    let extrait_ident =
    let est_alpha_num = function
    'a'..'z' | 'A'..'Z' | '0' .. '9' | '_' -> true
    | _ -> false
    in extrait est_alpha_num ;;

    exception LexerErreur ;;

    let rec lexer cl =
    let lexer_char c = match c with
    ' '
    | '\t' -> avance cl ; lexer cl
    | 'a'..'z'
    | 'A'..'Z' -> Lident (extrait_ident cl)
    | '0'..'9' -> Lint (extrait_int cl)
    | '"' -> avance cl ;
    let res = Lstring (extrait ((<>) '"') cl)
    in avance cl ; res
    | '+' | '-' | '*' | '/' | '%' | '&' | '|' | '!' | '=' | '(' | ')' ->
    avance cl; Lsymbol (String.make 1 c)
    | '<'
    | '>' -> avance cl;
    if cl.courant >= cl.taille then Lsymbol (String.make 1 c)
    else let cs = cl.chaine.[cl.courant]
    in ( match (c,cs) with
    ('<','=') -> avance cl; Lsymbol "<="
    | ('>','=') -> avance cl; Lsymbol ">="
    | ('<','>') -> avance cl; Lsymbol "<>"
    | _ -> Lsymbol (String.make 1 c) )
    | _ -> raise LexerErreur
    in
    if cl.courant >= cl.taille then Lfin
    else lexer_char cl.chaine.[cl.courant] ;;

    type exp_elem =
    Texp of expression (* expression *)
    | Tbin of op_bin (* oprateur binaire *)
    | Tunr of op_unr (* oprateur unaire *)
    | Tpg (* parenthse gauche *) ;;

    exception ParseErreur ;;

    let symb_unr = function
    "!" -> NON | "-" -> OPPOSE | _ -> raise ParseErreur
    let symb_bin = function
    "+" -> PLUS | "-" -> MOINS | "*" -> MULT | "/" -> DIV | "%" -> MOD
    | "=" -> EGAL | "<" -> INF | "<=" -> INFEQ | ">" -> SUP
    | ">=" -> SUPEQ | "<>" -> DIFF | "&" -> ET | "|" -> OU
    | _ -> raise ParseErreur
    let tsymb s = try Tbin (symb_bin s) with ParseErreur -> Tunr (symb_unr s) ;;

    let reduit pr = function
    (Texp e)::(Tunr op)::st when (priority_ou op) >= pr
    -> (Texp (ExpUnr (op,e)))::st
    | (Texp e1)::(Tbin op)::(Texp e2)::st when (priority_ob op) >= pr
    -> (Texp (ExpBin (e2,op,e1)))::st
    | _ -> raise ParseErreur ;;

    let rec empile_ou_reduit lex stack = match lex , stack with
    Lint n , _ -> (Texp (ExpInt n))::stack
    | Lident v , _ -> (Texp (ExpVar v))::stack
    | Lstring s , _ -> (Texp (ExpStr s))::stack
    | Lsymbol "(" , _ -> Tpg::stack
    | Lsymbol ")" , (Texp e)::Tpg::st -> (Texp e)::st
    | Lsymbol ")" , _ -> empile_ou_reduit lex (reduit 0 stack)
    | Lsymbol s , _
    -> let symbole =
    if s<>"-" then tsymb s
    (* lever l'ambigute du symbole ``-'' *)
    (* suivant la pile (i.e dernier exp_elem empile) *)
    else match stack
    with (Texp _)::_ -> Tbin MOINS
    | _ -> Tunr OPPOSE
    in ( match symbole with
    Tunr op -> (Tunr op)::stack
    | Tbin op ->
    ( try empile_ou_reduit lex (reduit (priority_ob op)
    stack )
    with ParseErreur -> (Tbin op)::stack )
    | _ -> raise ParseErreur )
    | _ , _ -> raise ParseErreur ;;

    let rec reduit_tout = function
    | [] -> raise ParseErreur
    | [Texp x] -> x
    | st -> reduit_tout (reduit 0 st) ;;

    let parse_exp fin cl =
    let p = ref 0
    in let rec parse_un stack =
    let l = ( p:=cl.courant ; lexer cl)
    in if not (fin l) then parse_un (empile_ou_reduit l stack)
    else ( cl.courant <- !p ; reduit_tout stack )
    in parse_un [] ;;

    let parse_inst cl = match lexer cl with
    Lident s -> ( match s with
    "REM" -> Rem (extrait (fun _ -> true) cl)
    | "GOTO" -> Goto (match lexer cl with
    Lint p -> p
    | _ -> raise ParseErreur)
    | "INPUT" -> Input (match lexer cl with
    Lident v -> v
    | _ -> raise ParseErreur)
    | "PRINT" -> Print (parse_exp ((=) Lfin) cl)
    | "LET" ->
    let l2 = lexer cl and l3 = lexer cl
    in ( match l2 ,l3 with
    (Lident v,Lsymbol "=") -> Let (v,parse_exp ((=) Lfin) cl)
    | _ -> raise ParseErreur )
    | "IF" ->
    let test = parse_exp ((=) (Lident "THEN")) cl
    in ( match ignore (lexer cl) ; lexer cl with
    Lint n -> If (test,n)
    | _ -> raise ParseErreur )
    | _ -> raise ParseErreur )
    | _ -> raise ParseErreur ;;

    let parse str =
    let cl = init_lex str
    in match lexer cl with
    Lint n -> Ligne { num=n ; inst=parse_inst cl }
    | Lident "LIST" -> List
    | Lident "RUN" -> Run
    | Lident "END" -> End
    | _ -> raise ParseErreur ;;
    eval.ml :
    open Syntax;;
    open Pprint;;
    open Alexsynt;;

    type valeur = Vint of int | Vstr of string | Vbool of bool ;;

    type environnement = (string * valeur) list ;;

    type etat = { ligne:int ; prog:program ; env:environnement } ;;

    exception RunErreur of int
    let runerr n = raise (RunErreur n) ;;

    let rec eval_exp n envt expr = match expr with
    ExpInt p -> Vint p
    | ExpVar v -> ( try List.assoc v envt with Not_found -> runerr n )
    | ExpUnr (OPPOSE,e) ->
    ( match eval_exp n envt e with
    Vint p -> Vint (-p)
    | _ -> runerr n )
    | ExpUnr (NON,e) ->
    ( match eval_exp n envt e with
    Vbool p -> Vbool (not p)
    | _ -> runerr n )
    | ExpStr s -> Vstr s
    | ExpBin (e1,op,e2)
    -> match eval_exp n envt e1 , op , eval_exp n envt e2 with
    Vint v1 , PLUS , Vint v2 -> Vint (v1 + v2)
    | Vint v1 , MOINS , Vint v2 -> Vint (v1 - v2)
    | Vint v1 , MULT , Vint v2 -> Vint (v1 * v2)
    | Vint v1 , DIV , Vint v2 when v2<>0 -> Vint (v1 / v2)
    | Vint v1 , MOD , Vint v2 when v2<>0 -> Vint (v1 mod v2)

    | Vint v1 , EGAL , Vint v2 -> Vbool (v1 = v2)
    | Vint v1 , DIFF , Vint v2 -> Vbool (v1 <> v2)
    | Vint v1 , INF , Vint v2 -> Vbool (v1 < v2)
    | Vint v1 , SUP , Vint v2 -> Vbool (v1 > v2)
    | Vint v1 , INFEQ , Vint v2 -> Vbool (v1 <= v2)
    | Vint v1 , SUPEQ , Vint v2 -> Vbool (v1 >= v2)

    | Vbool v1 , ET , Vbool v2 -> Vbool (v1 && v2)
    | Vbool v1 , OU , Vbool v2 -> Vbool (v1 || v2)

    | Vstr v1 , PLUS , Vstr v2 -> Vstr (v1 ^ v2)
    | _ , _ , _ -> runerr n ;;

    let rec ajoute v e env = match env with
    [] -> [v,e]
    | (w,f)::l -> if w=v then (v,e)::l else (w,f)::(ajoute v e l) ;;

    let rec goto_ligne n prog = match prog with
    [] -> runerr n
    | l::ll -> if l.num = n then prog
    else if l.num<n then goto_ligne n ll
    else runerr n ;;

    let print_valeur v = match v with
    Vint n -> print_int n
    | Vbool true -> print_string "true"
    | Vbool false -> print_string "false"
    | Vstr s -> print_string s ;;

    let eval_inst etat =
    let lc, ns =
    match goto_ligne etat.ligne etat.prog with
    [] -> failwith "programme vide"
    | lc::[] -> lc,-1
    | lc::ls::_ -> lc,ls.num
    in
    match lc.inst with
    Rem _ -> { etat with ligne=ns }
    | Print e -> print_valeur (eval_exp lc.num etat.env e) ;
    print_newline () ;
    { etat with ligne=ns }
    | Let(v,e) -> let ev = eval_exp lc.num etat.env e
    in { etat with ligne=ns; env=ajoute v ev etat.env }
    | Goto n -> { etat with ligne=n }
    | Input v -> let x = try read_int ()
    with Failure "int_of_string" -> 0
    in { etat with ligne=ns ; env=ajoute v (Vint x) etat.env }
    | If (t,n) -> match eval_exp lc.num etat.env t with
    Vbool true -> { etat with ligne=n }
    | Vbool false -> { etat with ligne=ns }
    | _ -> runerr n ;;

    let rec run etat =
    if etat.ligne = -1 then etat else run (eval_inst etat) ;;

    let rec inserer ligne p = match p with
    [] -> [ligne]
    | l::prog ->
    if l.num < ligne.num then l::(inserer ligne prog)
    else if l.num=ligne.num then ligne::prog
    else ligne::l::prog ;;

    let print_prog etat =
    let print_ligne x = print_string (pp_ligne x) ; print_newline ()
    in print_newline () ;
    List.iter print_ligne etat.prog ;
    print_newline () ;;

    let premiere_ligne = function [] -> 0 | i::_ -> i.num ;;

    exception Fin
    let une_commande etat =
    print_string "> " ; flush stdout ;
    try
    match parse (input_line stdin) with
    Ligne l -> { etat with prog=inserer l etat.prog }
    | List -> (print_prog etat ; etat )
    | Run -> run {etat with ligne = premiere_ligne etat.prog}
    | End -> raise Fin
    with
    LexerErreur -> print_string "Illegal character\n"; etat
    | ParseErreur -> print_string "syntax error\n"; etat
    | RunErreur n ->
    print_string "runtime error at line ";
    print_int n ;
    print_string "\n";
    etat ;;

    let go () =
    try
    print_string "Mini-BASIC version 0.1\n\n";
    let rec loop etat = loop (une_commande etat) in
    loop { ligne=0; prog=[]; env=[] }
    with Fin -> print_string "A bientt...\n";;





  2. Compiler sparment chacun des fichiers.
    $ ocamlc -c syntax.ml 
    $ ocamlc -c pprint.ml
    $ ocamlc -c alexsynt.ml
    $ ocamlc -c eval.ml
    


  3. Rajouter un fichier mainbasic.ml qui ne contiendra que le lancement de la fonction principale. mainbasic.ml :
    open Eval;;

    go ();;


  4. Crer un nouveau toplevel, de nom topbasic, qui dmarre l'interprte Basic. cration du toplevel :
    $ ocamlmktop -o topbasic syntax.cmo pprint.cmo alexsynt.cmo eval.cmo mainbasic.ml
    
    test du toplevel :
    $ topbasic
    Mini-BASIC version 0.1
    
    > 10 PRINT "DONNER UN NOMBRE"
    > 20 INPUT X
    > 30 PRINT X
    > LIST
    
    10  PRINT "DONNER UN NOMBRE"
    20  INPUT X
    30  PRINT X
    
    > RUN
    DONNER UN NOMBRE
    44
    44
    > END
    A bientt...
            Objective Caml version 2.04
    
    # 
    


  5. Crer un excutable autonome lanant l'interprte Basic. compilation et dition de liens :
    $ ocamlc -custom -o basic.exe syntax.cmo pprint.cmo alexsynt.cmo eval.cmo mainbasic.ml
    test de l'excutable autonome :
    $ basic.exe
    Mini-BASIC version 0.1
    
    > 10 PRINT "BONJOUR"
    > LIST
    
    10  PRINT "BONJOUR"
    
    > RUN
    BONJOUR
    > END
    A bientt...
    $
    

Comparaison de performances

On cherche comparer les performances du code produit par le compilateur de byte-code et par le compilateur natif. Pour cela on crira une application de tri sur des listes et des tableaux.
  1. crire une fonction polymorphe de tri sur les listes, la relation d'ordre sera passe comme paramtre de la fonction de tri. L'algorithme de tri est laiss au choix du lecteur. On peut imaginer soit un tri bulle, soit un tri rapide. On crira cette fonction dans le fichier sort.ml. On utilise les fichiers sort.mli et sort.ml de la distribution qui dfinissent les fonctions list et array de tri d'une liste et d'un tableau.

    sort.mli :

    (***********************************************************************)
    (* *)
    (* Objective Caml *)
    (* *)
    (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
    (* *)
    (* Copyright 1996 Institut National de Recherche en Informatique et *)
    (* Automatique. Distributed only by permission. *)
    (* *)
    (***********************************************************************)

    (* $Id: sort.mli,v 1.1 2000/01/21 09:40:00 emmanuel Exp $ *)

    (* Module [Sort]: sorting and merging lists *)

    val list : ('a -> 'a -> bool) -> 'a list -> 'a list
    (* Sort a list in increasing order according to an ordering predicate.
    The predicate should return [true] if its first argument is
    less than or equal to its second argument. *)

    val array : ('a -> 'a -> bool) -> 'a array -> unit
    (* Sort an array in increasing order according to an
    ordering predicate.
    The predicate should return [true] if its first argument is
    less than or equal to its second argument.
    The array is sorted in place. *)

    val merge : ('a -> 'a -> bool) -> 'a list -> 'a list -> 'a list
    (* Merge two lists according to the given predicate.
    Assuming the two argument lists are sorted according to the
    predicate, [merge] returns a sorted list containing the elements
    from the two lists. The behavior is undefined if the two
    argument lists were not sorted. *)


    sort.ml

    (***********************************************************************)
    (* *)
    (* Objective Caml *)
    (* *)
    (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
    (* *)
    (* Copyright 1996 Institut National de Recherche en Informatique et *)
    (* Automatique. Distributed only by permission. *)
    (* *)
    (***********************************************************************)

    (* $Id: sort.ml,v 1.1 2000/01/21 09:18:40 emmanuel Exp $ *)

    (* Merging and sorting *)

    open Array

    let rec merge order l1 l2 =
    match l1 with
    [] -> l2
    | h1 :: t1 ->
    match l2 with
    [] -> l1
    | h2 :: t2 ->
    if order h1 h2
    then h1 :: merge order t1 l2
    else h2 :: merge order l1 t2

    let list order l =
    let rec initlist = function
    [] -> []
    | [e] -> [[e]]
    | e1::e2::rest ->
    (if order e1 e2 then [e1;e2] else [e2;e1]) :: initlist rest in
    let rec merge2 = function
    l1::l2::rest -> merge order l1 l2 :: merge2 rest
    | x -> x in
    let rec mergeall = function
    [] -> []
    | [l] -> l
    | llist -> mergeall (merge2 llist) in
    mergeall(initlist l)

    let swap arr i j =
    let tmp = unsafe_get arr i in
    unsafe_set arr i (unsafe_get arr j);
    unsafe_set arr j tmp

    let array order arr =
    let rec qsort lo hi =
    if hi <= lo then ()
    else if hi - lo < 5 then begin
    (* Use insertion sort *)
    for i = lo + 1 to hi do
    let val_i = unsafe_get arr i in
    if order val_i (unsafe_get arr (i - 1)) then begin
    unsafe_set arr i (unsafe_get arr (i - 1));
    let j = ref (i - 1) in
    while !j >= 1 && order val_i (unsafe_get arr (!j - 1)) do
    unsafe_set arr !j (unsafe_get arr (!j - 1));
    decr j
    done;
    unsafe_set arr !j val_i
    end
    done
    end else begin
    let mid = (lo + hi) lsr 1 in
    (* Select median value from among LO, MID, and HI *)
    let pivotpos =
    let vlo = unsafe_get arr lo
    and vhi = unsafe_get arr hi
    and vmid = unsafe_get arr mid in
    if order vlo vmid then
    if order vmid vhi then mid
    else if order vlo vhi then hi else lo
    else
    if order vhi vmid then mid
    else if order vhi vlo then hi else lo in
    swap arr pivotpos hi;
    let pivot = unsafe_get arr hi in
    let i = ref lo and j = ref hi in
    while !i < !j do
    while !i < hi && order (unsafe_get arr !i) pivot do incr i done;
    while !j > lo && order pivot (unsafe_get arr !j) do decr j done;
    if !i < !j then swap arr !i !j
    done;
    swap arr !i hi;
    (* Recurse on larger half first *)
    if (!i - 1) - lo >= hi - (!i + 1) then begin
    qsort lo (!i - 1); qsort (!i + 1) hi
    end else begin
    qsort (!i + 1) hi; qsort lo (!i - 1)
    end
    end in
    qsort 0 (Array.length arr - 1)


  2. crire un programme principal, dans le fichier trilist.ml, qui utilise la fonction prcdente et l'applique sur une liste d'entiers en la triant en ordre croissant puis en ordre dcroissant. interval.ml :

    let interval order next a b = 
    let rec aux a =
    if not (order a b) then [a] else a :: aux (next a)
    in aux a;;





    trilist.ml :

    let main () = 
    let il = Interval.interval (>) (fun x -> x -1) 50000 20
    and il2 = Interval.interval (<) (fun x -> x + 1) 20 50000 in
    Sort.list (<) il, Sort.list (>) il2;;

    main();;


  3. Crer deux excutables autonomes, un avec le compilateur de byte-code et l'autre en natif. Mesurer les temps d'excution de ces deux programmes. On choisira des listes de tailles suffisamment importantes pour les mesures de temps.
    1. code-octet (Unix) : trilbyte.exe

      ocamlc -custom -o trilbyte.exe sort.mli sort.ml interval.ml trilist.ml
      
    2. natif (Unix) : trilopt.exe
      ocamlopt -o trilopt.exe sort.mli sort.ml interval.ml trilist.ml
      
    Performances :
    trilbyte.exe trilopt.exe
    2,55 secondes (user) 1,67 secondes (user)

    Le rapport trilopt.exe / trilbyte.exe est de 2/3.


  4. Rcrire le tri pour des tableaux, en utilisant toujours une fonction d'ordre en paramtre. Effectuer les mesures sur des tableaux remplis de la mme manire que les listes prcdentes. triarray.ml :


    let main () =
    let il = Array.of_list(Interval.interval (>) (fun x -> x -1) 50000 20)
    and il2 = Array.of_list(Interval.interval (<) (fun x -> x + 1) 20 50000) in
    Sort.array (<) il, Sort.array (>) il2;;

    main();;


    1. code-octet (Unix) : triabyte.exe
      ocamlc -custom -o triabyte.exe sort.mli sort.ml interval.ml triarray.ml
      
    2. natif (Unix) : triaopt.exe
      ocamlopt -o triaoptu.exe sort.mli sort.ml interval.ml triarray.ml
      
    Performances :
    triabyte.exe triaopt.exe
    515 s 106 s

    Le rapport triaopt.exe / triabyte.exe est de 1/5.


  5. Que peut-on dire des rsultats des mesures? Le compilateur natif apporte un gain de temps d'excution variable ( facteur 2/3 pour les liste et 1/5 pour les tableaux).

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora056.html0000644000000000000000000000146007421273603014475 0ustar Notes
1
Un type abstrait est un type dont la reprsentation interne n'est pas visible. La dclaration d'un tel type sera prsente dans le chapitre 14.
ocaml-book-1.0/fr/html/book-ora169.html0000644000000000000000000012023207421273602014500 0ustar Communication entre processus Prcdent Index Suivant

Communication entre processus

L'utilisation de processus dans le dveloppement d'une application permet la dlgation des tches. Nanmoins, comme nous l'avons dj voqu, ces tches peuvent ne pas tre indpendantes et il est ds lors ncessaire que les processus sachent communiquer entre eux.

Nous abordons dans ce paragraphe deux modes de communication entre processus : les tubes de communication (pipes) et les signaux. Ce chapitre ne fait pas un tour complet des possibilits de communication entre processus. Il n'est qu'une premire approche des applications dveloppes aux chapitres 19 et 20.

Tubes de communication

la manire des fichiers, il est possible de communiquer directement entre processus travers des tubes de communication.

Les tubes sont en quelque sorte des fichiers virtuels dans lesquels on peut lire et crire au moyen des fonctions d'entres-sorties read et write. Cependant ils sont de taille limite (cette limite dpendant des systmes) et leur discipline de remplissage et de vidage est celle des files d'attente : le premier entr est le premier sorti. Et quand nous disons << sorti >>, il faut prendre l'expression au pied de la lettre : la lecture de donnes dans un tube les supprime de celui-ci.

Cette discipline de file d'attente est ralise en associant deux descripteurs un tube : l'un correspond l'extrmit du tube dans laquelle on crit ; l'autre, l'extrmit dans laquelle on lit. Un tube est cr par la fonction :

# Unix.pipe ;;
- : unit -> Unix.file_descr * Unix.file_descr = <fun>
La premire composante du couple obtenu en rsultat est la sortie du tube utilise en lecture et la seconde, l'entre du tube utilise en criture. Tout processus en ayant connaissance peut fermer ces descripteurs.

La lecture dans un tube est bloquante sauf si tous les processus connaissant son descripteur d'entre (et donc, susceptible d'y crire) l'ont ferm. Dans ce dernier cas, la fonction read renvoie 0. Si un processus tente d'crire dans un tube plein, il est suspendu jusqu' ce qu'un autre processus ait effectu une lecture. Si un processus tente d'crire dans un tube alors que plus aucun autre processus n'est susceptible d'y lire (tous ont ferm le descripteur de sortie) alors le processus tentant d'crire reoit le signal sigpipe qui, sauf mention contraire, provoque sa terminaison.

L'exemple suivant montre l'utilisation des tubes dans lesquels des petits fils communiquent leur numro leur grand pre.
let sortie, entree = Unix.pipe();;

let write_pid entree =
try
let m = "(" ^ (string_of_int (Unix.getpid ())) ^ ")"
in ignore (Unix.write entree m 0 (String.length m)) ;
Unix.close entree
with
Unix.Unix_error(n,f,arg) ->
Printf.printf "%s(%s) : %s\n" f arg (Unix.error_message n) ;;

match Unix.fork () with
0 -> for i=0 to 5 do
match Unix.fork() with
0 -> write_pid entree ; exit 0
| _ -> ()
done ;
Unix.close entree
| _ -> Unix.close entree;
let s = ref "" and buff = String.create 5
in while true do
match Unix.read sortie buff 0 5 with
0 -> Printf.printf "Mes petits fils sont %s\n" !s ; exit 0
| n -> s := !s ^ (String.sub buff 0 n) ^ "."
done ;;


On obtient la trace :
Mes petits fils sont (1575.).(1576.).(1577.).(1578.).(1579.).(1580.).


Nous avons introduit des points entre chaque partie de chane lue. On peut ainsi lire sur la trace la succession des contenus du tube. On remarquera que la lecture peut ainsi tre dsynchronise : ds qu'une entre, ft-elle partielle, est produite, elle est consomme.

Les tubes nomms
Certains Unix acceptent de nommer les tubes comme s'il s'agissait de fichiers normaux. Il est alors possible de communiquer entre deux processus sans lien de parent en utilisant le nom du tube. La fonction suivante permet de crer le tube.

# Unix.mkfifo ;;
- : string -> Unix.file_perm -> unit = <fun>


Les descripteurs de fichier pour l'utiliser sont obtenus par openfile comme s'il s'agissait de fichiers, mais leur comportement est celui des pipes. En particulier, puisqu'il s'agit de files d'attente, on ne peut appliquer la fonction lseek un tube.

Warning


mkfifo n'est pas implante pour Windows.


Canaux de communication

Le module Unix fournit une fonction de haut niveau permettant le lancement d'un programme en lui associant des canaux d'entre ou de sortie avec le programme appelant :

# Unix.open_process ;;
- : string -> in_channel * out_channel = <fun>
L'argument est le nom du programme ou, plus prcisment, la ligne d'appel du programme telle qu'on la taperait pour l'interprte de commande. Elle pourra donc ventuellement contenir les arguments du programme lancer. Les deux valeurs de sortie sont les descripteurs de fichier associs aux entres-sorties standard du programme ainsi lanc qui est excut en parallle du programme appelant.

Warning


Le programme lanc par open_process est excut par appel l'interprte de commandes Unix /bin/sh.
L'utilisation de cette fonction n'est possible que sur les systmes connaissant cet interprte de commandes.


On peut mettre fin l'excution d'un programme lanc par open_process en utilisant :

# Unix.close_process ;;
- : in_channel * out_channel -> Unix.process_status = <fun>
L'argument est le couple des canaux associs un processus que l'on veut fermer. La valeur de retour est le statut d'excution du processus dont on a attendu la terminaison.

Il existe des variantes de ces deux fonctions n'ouvrant et ne fermant qu'un canal d'entre ou un canal de sortie :

# Unix.open_process_in ;;
- : string -> in_channel = <fun>
# Unix.close_process_in ;;
- : in_channel -> Unix.process_status = <fun>
# Unix.open_process_out ;;
- : string -> out_channel = <fun>
# Unix.close_process_out ;;
- : out_channel -> Unix.process_status = <fun>


Voici un petit exemple amusant d'utilisation de open_process : on lance ocaml dans ocaml !

# let n_print_string s = print_string s ; print_string "(* <-- *)" ;;
val n_print_string : string -> unit = <fun>
# let p () =
let oc_in, oc_out = Unix.open_process "/usr/local/bin/ocaml"
in n_print_string (input_line oc_in) ; print_newline() ;
n_print_string (input_line oc_in) ; print_newline() ;
print_char (input_char oc_in) ;
print_char (input_char oc_in) ;
flush stdout ;
let s = input_line stdin
in output_string oc_out s ;
output_string oc_out "#quit\059\059\n" ;
flush oc_out ;
let r = String.create 250 in
let n = input oc_in r 0 250
in n_print_string (String.sub r 0 n) ;
print_string "Merci de votre visite\n" ;
flush stdout ;
Unix.close_process (oc_in, oc_out) ;;
val p : unit -> Unix.process_status = <fun>


L'appel de la fonction p lance un toplevel d'Objective CAML. On remarquera que c'est la version 2.03 qui se trouve dans le catalogue /usr/local/bin. Les quatre premires oprations de lecture permettent de rcuprer l'en-tte qu'affiche le toplevel. La ligne let x = 1.2 +. 5.6;; est lue au clavier, puis envoye sur oc_out (le canal de sortie li l'entre standard du nouveau processus). Celui-ci type et value la phrase Objective CAML passe, et crit le rsultat dans sa sortie standard qui est lie au canal d'entre oc_in. Ce rsultat est alors lu par la fonction input et affich, ainsi que la chane "Merci de votre visite". On envoie d'autre part la directive #quit;; pour sortir du nouveau processus.
# p();;
        Objective Caml version 2.03

# let x = 1.2 +. 5.6;;
val x : float = 6.8
Merci de votre visite
- : Unix.process_status = Unix.WSIGNALED 13
# 

Signaux sous Unix

Une des possibilits pour communiquer avec un processus est de lui envoyer un signal. Un signal peut tre reu n'importe quel moment de l'excution du programme. La rception d'un signal provoque une interruption logicielle. L'excution d'un programme est interrompue pour traiter le signal reu, puis reprend l'endroit de son interruption. Les signaux sont en nombre fini et relativement restreint (32 avec Linux). L'information vhicule par un signal est rudimentaire : elle se borne l'identit (le numro) du signal. Les processus ont tous une raction prdfinie aux signaux. Nanmoins, celle-ci peut tre redfinie par le programmeur pour la plupart des signaux.

Les donnes et fonctions de traitement des signaux sont rparties entre les modules Sys et Unix. Le module Sys contient la dclaration d'un certain nombre de signaux rpondant la norme POSIX (dcrits dans [Rif90]) ainsi que des fonctions de traitement des signaux. Le module Unix dfinit la fonction kill d'mission d'un signal. L'utilisation des signaux sous Windows est restreinte au seul sigint.

Un signal peut avoir de multiples sources : le clavier ; une tentative errone d'accs mmoire, etc. Un processus peut mettre un signal destination d'un autre processus en ayant recours la fonction :

# Unix.kill ;;
- : int -> int -> unit = <fun>
Son premier paramtre est le PID du processus destinataire et le second est le signal qu'on veut lui envoyer.

Traitement des signaux

La raction associe un signal peut tre de trois ordres. chacun d'eux correspond un constructeur du type signal_behavior :
  • Signal_default : le comportement par dfaut dfini par le systme. Dans la plupart des cas c'est la terminaison du processus avec cration ou non d'un fichier d'tat du processus (fichier core).
  • Signal_ignore : le signal est ignor.
  • Signal_handle : le comportement est redfini par une fonction Objective CAML de type int -> unit que l'on passe en argument au constructeur. Lors du traitement du signal ainsi modifi, le numro de signal est pass la fonction de traitement.
la rception d'un signal, l'excution du processus rcepteur est droute vers la fonction de traitement du signal. La fonction permettant de redfinir le comportement associ un signal est fournie par le module Sys :

# Sys.set_signal;;
- : int -> Sys.signal_behavior -> unit = <fun>
Le premier argument est le signal redfinir et le second, le comportement assign.

Le module Sys fournit une autre fonction de modification du traitement des signaux :

# Sys.signal ;;
- : int -> Sys.signal_behavior -> Sys.signal_behavior = <fun>


Elle agit comme set_signal, sauf qu'en plus elle renvoie la valeur associe au signal avant la modification. On peut ainsi crire une fonction renvoyant (sans la modifier apparemment) la valeur comportementale associe un signal :

# let signal_behavior s =
let b = Sys.signal s Sys.Signal_default
in Sys.set_signal s b ; b ;;
val signal_behavior : int -> Sys.signal_behavior = <fun>
# signal_behavior Sys.sigint;;
- : Sys.signal_behavior = Sys.Signal_handle <fun>


Certains signaux ne peuvent pas voir leur comportement modifi. Notre fonction n'est donc pas utilisable pour n'importe quel signal :

# signal_behavior Sys.sigkill ;;
Uncaught exception: Sys_error("Invalid argument")


Quelques signaux

Nous illustrons ci-dessous l'utilisation de quelques signaux essentiels.

sigint
Ce signal est, en gnral, associ la combinaison de touches CTRL-C. Dans le petit exemple ci-dessous, nous modifions la raction ce signal de faon ce que le processus rcepteur ne s'interrompe qu' la troisime occurrence du signal.

Crons le fichier ctrlc.ml suivant :
let sigint_handle =
let n = ref 0
in function _ -> incr n ;
match !n with
1 -> print_string "Vous venez d'appuyer sur CTRL-C\n"
| 2 -> print_string "Vous avez encore appuy sur CTRL-C\n"
| 3 -> print_string "Si vous insistez ...\n" ; exit 1
| _ -> () ;;
Sys.set_signal Sys.sigint (Sys.Signal_handle sigint_handle) ;;
match Unix.fork () with
0 -> while true do () done
| pid -> Unix.sleep 1 ; Unix.kill pid Sys.sigint ;
Unix.sleep 1 ; Unix.kill pid Sys.sigint ;
Unix.sleep 1 ; Unix.kill pid Sys.sigint ;;


Ce programme simule l'appui de la combinaison de touches CTRL-C par l'envoi du signal sigint, on obtient la trace d'excution suivante :
$ ocamlc -i -o ctrlc ctrlc.ml
val sigint_handle : int -> unit
$ ctrlc
Vous venez d'appuyer sur CTRL-C
Vous avez encore appuy sur CTRL-C
Si vous insistez ...
sigalrm
Un autre signal couramment utilis est sigalrm qui est associ l'horloge de la machine. Il peut tre mit par la fonction :

# Unix.alarm ;;
- : int -> int = <fun>
L'argument spcifie le nombre de secondes d'attente avant l'mission du signal sigalrm. La valeur de retour est le nombre de secondes restant courir avant l'mission d'un prochain signal, ou si aucune alarme n'est en cours.

Nous allons utiliser cette fonction et le signal associ pour dfinir la fonction timeout qui lance l'excution d'une autre fonction et l'interrompt, si besoin est, au bout d'un temps donn. Plus prcisment, la fonction timeout prendra en argument une fonction f, l'argument arg attendu par f, la dure (time) du << timeout >> et la valeur (default_value) rendre si cette dernire est dpasse.

Dans timeout, les choses se passent ainsi :
  1. On modifie le comportement associ au signal sigalrm de faon dclencher l'exception Timeout.
  2. On a pris soin, au passage, de mmoriser le comportement original associ sigalrm pour dfinir une fonction capable de le restaurer.
  3. On dclenche l'horloge.
  4. On lance le calcul :
    1. Si tout s'est bien pass, on remet sigalrm dans son tat d'origine et on renvoie la valeur du calcul.
    2. Sinon, on restaure sigalrm et si la dure est dpasse, on renvoie la valeur par dfaut.
Voici les dfinitions correspondantes ainsi qu'un petit essai :

# exception Timeout ;;
exception Timeout
# let sigalrm_handler = Sys.Signal_handle (fun _ -> raise Timeout) ;;
val sigalrm_handler : Sys.signal_behavior = Sys.Signal_handle <fun>
# let timeout f arg time default_value =
let old_behavior = Sys.signal Sys.sigalrm sigalrm_handler in
let reset_sigalrm () = Sys.set_signal Sys.sigalrm old_behavior
in ignore (Unix.alarm time) ;
try let res = f arg in reset_sigalrm () ; res
with exc -> reset_sigalrm () ;
if exc=Timeout then default_value else raise exc ;;
val timeout : ('a -> 'b) -> 'a -> int -> 'b -> 'b = <fun>
# let itere n = for i = 1 to n do () done ; n ;;
val itere : int -> int = <fun>
Printf.printf "1re excution : %d\n" (timeout itere 10 1 (-1));
Printf.printf "2me excution : %d\n" (timeout itere 100000000 1 (-1)) ;;
1re excution : 10
2me excution : -1
- : unit = ()
sigusr1 et sigusr2
Ces deux signaux sont la disposition du programmeur pour les besoins de ses applications. Ils ne sont pas utiliss par le systme d'exploitation.

Dans cet exemple, la rception, par le fils, du signal sigusr1 provoque l'affichage du contenu de la variable i.
let i = ref 0  ;;
let affiche_i s = Printf.printf "signal recu (%d) -- i=%d\n" s !i ;
flush stdout ;;
Sys.set_signal Sys.sigusr1 (Sys.Signal_handle affiche_i) ;;

match Unix.fork () with
0 -> while true do incr i done
| pid -> Unix.sleep 0 ; Unix.kill pid Sys.sigusr1 ;
Unix.sleep 3 ; Unix.kill pid Sys.sigusr1 ;
Unix.sleep 1 ; Unix.kill pid Sys.sigkill




Voici la trace d'une excution de ce programme :
signal recu (10) -- i=0
signal recu (10) -- i=47580467
En examinant cette trace, on voit qu'aprs avoir excut une premire fois le code associ au signal sigusr1, le processus fils continue excuter la boucle et incrmenter i.

sigchld
Ce signal est mis vers son pre la terminaison d'un processus. Nous allons l'utiliser pour rendre un pre plus attentif au devenir de ses enfants. Voici comment :
  1. On dfinit une fonction de traitement du signal sigchld qui traite tous les enfants morts la rception de ce signal4 et termine le processus pre lorsque celui-ci n'a plus d'enfant (exception Unix_error). Pour ne pas bloquer le pre, si tous ses enfants ne sont pas morts, on utilise waitpid plutt que wait.
  2. Le programme principal, aprs avoir redfini la raction associe sigchld, boucle pour crer cinq fils. Ceci fait, le pre fait autre chose (boucle while true) jusqu' la mort de ses fils.
let rec sigchld_handle s =
try let pid, _ = Unix.waitpid [Unix.WNOHANG] 0
in if pid <> 0
then ( Printf.printf "%d est mort et enterr au signal %d\n" pid s ;
flush stdout ;
sigchld_handle s )
with Unix.Unix_error(_, "waitpid", _) -> exit 0 ;;

let i = ref 0
in Sys.set_signal Sys.sigchld (Sys.Signal_handle sigchld_handle) ;
while true do
match Unix.fork() with
0 -> let pid = Unix.getpid ()
in Printf.printf "Cration de %d\n" pid ; flush stdout ;
Unix.sleep (Random.int (5+ !i)) ;
Printf.printf "Terminaison de %d\n" pid ; flush stdout ;
exit 0
| _ -> incr i ; if !i = 5 then while true do () done
done ;;


On obtient la trace :
Cration de 1561
Cration de 1562
Cration de 1563
Cration de 1564
Cration de 1565
Terminaison de 1565
1565 est mort et enterr au signal 17
Terminaison de 1561
1561 est mort et enterr au signal 17
Terminaison de 1562
1562 est mort et enterr au signal 17
Terminaison de 1563
1563 est mort et enterr au signal 17
Terminaison de 1564
1564 est mort et enterr au signal 17







Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora105.html0000644000000000000000000000440007421273602014464 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

Ce chapitre prsente diffrents outils de la distribution d'Objective CAML pour l'analyse lexicale et l'analyse syntaxique. Cette dernire suppose en rgle gnrale que la premire a t effectue. Dans la premire section, nous prsentons un outil simple d'analyse lexicale fourni par le module Genlex. Nous entrons ensuite un peu plus dans le dtail de la dfinition des ensembles d'units lexicales en prsentant le formalisme des expressions rationnelles. Nous illustrons leur mise en oeuvre dans le module Str et l'outil ocamllex. Dans la deuxime section nous dfinissons les grammaires et dtaillons les rgles de production de phrase d'un langage pour prsenter deux types d'analyse : l'analyse descendante et l'analyse ascendante. Elles sont illustres par l'utilisation des Stream et par l'outil ocamlyacc. Les analyses prcdentes utilisent des grammaires sans contexte. Nous montrerons comment effectuer une analyse contextuelle avec les Streams. Dans la troisime section, nous reprenons l'exemple de l'interprte de BASIC, prsent page ??, en utilisant ocamllex et ocamlyacc pour implanter les fonctions d'analyse lexicale et syntaxique du langage.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora213.html0000644000000000000000000000413207421273603014467 0ustar Bibliothque LablTk Prcdent Index Suivant

Bibliothque LablTk

L'interface avec Tcl/Tk a t intgre la distribution d'Objective CAML 2.99 mais elle n'a pas t porte sur toutes les plate-formes. L'installation fournit trois nouvelles commandes :
  • labltk lance une boucle d'interaction qui intgre la bibliothque LablTk;
  • labltklink s'utilise en lieu et place de ocamlc pour raliser l'dition de liens de programmes utilisant cette bibliothque;
  • labltkopt est le pendant de labltklink pour la gnration de code natif.
Cette bibliothque dfinit un grand nombre de modules et utilise massivement les extensions de langage de O'Labl. La prsentation de ce module sort du cadre de cette annexe et nous invitons le lecteur intress se rfrer la documentation d'Objective CAML 2.99.

Il existe aussi une interface avec Gtk prsente sous la forme d'une hirarchie de classes, mais elle n'est pas encore dans la distribution. Elle devrait tre compatible avec Unix et Windows.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora031.html0000644000000000000000000017713007421273601014474 0ustar Exercices Prcdent Index Suivant

Exercices

Listes doublement chanes

La programmation fonctionnelle se prte bien la manipulation de structures de donnes non circulaires, comme par exemple les listes. Par contre pour les structures circulaires, il y a de relles difficults d'implantation. On se propose ici de dfinir des listes doublement chanes, c'est--dire que chaque lment d'une liste connat son successeur et son prdcesseur.
  1. Dfinir un type pour des listes doublement chanes paramtr, en utilisant au moins un enregistrement avec champs mutables. Une cellule est un enregistrement contenant une valeur, la liste des cellules suivantes et la liste des cellules prcdantes.

    # type 'a cell = {
    info : 'a ;
    mutable prev : 'a dlist ;
    mutable next : 'a dlist }


    Une liste est soit vide, soit une cellule.

    and 'a dlist = Empty | Cell of 'a cell ;;
    type 'a cell = { info: 'a; mutable prev: 'a dlist; mutable next: 'a dlist }
    type 'a dlist = | Empty | Cell of 'a cell


  2. crire les fonctions add et remove qui ajoute et enlve un lment dans une liste doublement chane. Ajouter une cellule contenant x en l'intercalant entre la cellule dsigne par dl et sa cellule prcdante.

    # let add x = function
    Empty -> Cell { info=x ; prev=Empty ; next=Empty }
    | Cell c as l ->
    let new_cell = { info=x ; prev=c.prev ; next=l } in
    let new_dlist = Cell new_cell in
    c.prev <- new_dlist ;
    ( match new_cell.prev with
    Empty -> ()
    | Cell pl -> pl.next <- new_dlist ) ;
    new_dlist ;;
    val add : 'a -> 'a dlist -> 'a dlist = <fun>
    Supprimer la cellule dsigne par dl en "recollant" la cellule prcdante avec la suivante.

    # let remove_cell = function
    Empty -> failwith "enleve"
    | Cell c -> match (c.prev , c.next) with
    Empty , Empty -> Empty
    | Cell c1 as l , Empty -> c1.next <- Empty ; l
    | Empty , ((Cell c2) as l) -> c2.prev <- Empty ; l
    | Cell c1 as l1 , (Cell c2 as l2) -> c1.next <- l2; c2.prev <- l1; l1 ;;
    val remove_cell : 'a dlist -> 'a dlist = <fun>


    Supprimer toutes les cellules contenant x.

    # let rec remove x l =
    let rec remove_left = function
    Empty -> ()
    | Cell c as l -> let pl = c.prev in
    if c.info = x then ignore (remove_cell l) ;
    remove_left pl
    and remove_right = function
    Empty -> ()
    | Cell c as l -> let nl = c.next in
    if c.info = x then ignore (remove_cell l) ;
    remove_right nl
    in match l with
    Empty -> Empty
    | Cell c as l -> if c.info = x then remove x (remove_cell l)
    else (remove_left c.prev ; remove_right c.next ; l) ;;
    val remove : 'a -> 'a dlist -> 'a dlist = <fun>

Rsolution de systmes linaires

Cet exercice porte sur le calcul matriciel. Il rsoud un systme d'quations selon la mthode de Gauss (dite du pivot). On note le systme d'quation A   X = Y avec A une matrice carre de dimension n, Y un vecteur de donnes de dimension n et X un vecteur inconnu de mme dimension.

Cette mthode consiste transformer le systme A   X = Y en un systme quivalent C   X = Z tel que la matrice C soit triangulaire suprieure. On diagonalise C pour obtenir la solution.
  1. Dfinir un type vect, un type mat et un type syst .

    # type vect = float array ;;
    type vect = float array
    # type mat = vect array ;;
    type mat = vect array
    # type syst = { m:mat ; v:vect } ;;
    type syst = { m: mat; v: vect }


  2. crire les fonctions utilitaires de manipulation de vecteurs : affichage l'cran d'un systme, addition de deux vecteurs, multiplication d'un vecteur par un scalaire. crire un flottant sur cinq caractres.

    # let my_print_float s =
    let x = string_of_float s
    in let y = match String.length x with
    5 -> x
    | n when n<5 -> (String.make (5-n) ' ') ^ x
    | n -> String.sub x 0 5
    in print_string y ;;
    val my_print_float : float -> unit = <fun>


    Afficher un systme.

    # let print_syst s =
    let l = Array.length s.m
    in for i=0 to l-1 do
    print_string " | " ;
    for j=0 to l-1 do
    my_print_float s.m.(i).(j) ;
    print_string " "
    done ;
    if i=l/2 then print_string " | * | x"
    else print_string " | | x" ;
    print_int (i+1) ;
    if i=l/2 then print_string " | = | "
    else print_string " | | " ;
    my_print_float s.v.(i) ;
    print_string " |" ;
    print_newline ()
    done ;;
    val print_syst : syst -> unit = <fun>

    # let add_vect v1 v2 =
    let l = Array.length v1
    in let res = Array.create l 0.0
    in for i=0 to l-1 do res.(i) <- v1.(i) +. v2.(i) done ;
    res ;;
    val add_vect : float array -> float array -> float array = <fun>

    # let mult_scal_vect x v =
    let l = Array.length v
    in let res = Array.create l 0.0
    in for i=0 to l-1 do res.(i) <- v.(i) *. x done ;
    res ;;
    val mult_scal_vect : float -> float array -> float array = <fun>


  3. crire les fonctions utilitaires de calcul sur les matrices : multiplication de deux matrices, produit d'une matrice par un vecteur.

    # let mult_mat m1 m2 =
    let l1 = Array.length m1 and l2 = Array.length m2.(0)
    and l3 = Array.length m2
    in let res = Array.create_matrix l1 l2 0.0
    in for i=0 to l1-1 do
    for j=0 to l2-1 do
    for k=0 to l3-1 do
    res.(i).(j) <- res.(i).(j) +. m1.(i).(k) *. m2.(k).(j)
    done done done ;
    res ;;
    val mult_mat : float array array -> float array array -> float array array =
    <fun>

    # let mult_mat_vect m v =
    let l1 = Array.length m and l2 = Array.length v
    in let res = Array.create l1 0.0
    in for i=0 to l1-1 do
    for j=0 to l2-1 do
    res.(i) <- res.(i) +. m.(i).(j) *. v.(j)
    done done ;
    res ;;
    val mult_mat_vect : float array array -> float array -> float array = <fun>


  4. crire les fonctions utilitaires de manipulation de systmes : division d'une ligne d'un systme par un pivot (Aii), permutation de deux lignes.

    # let div_syst s i =
    let p = s.m.(i).(i)
    in s.m.(i).(i) <- 1.0 ;
    for j=i+1 to (Array.length s.m.(0)) - 1 do
    s.m.(i).(j) <- s.m.(i).(j) /. p
    done ;
    s.v.(i) <- s.v.(i) /. p ;;
    val div_syst : syst -> int -> unit = <fun>

    # let permut_syst s i j =
    let aux1 = s.m.(i) and aux2 = s.v.(i)
    in s.m.(i) <- s.m.(j) ;
    s.v.(i) <- s.v.(j) ;
    s.m.(j) <- aux1 ;
    s.v.(j) <- aux2 ;;
    val permut_syst : syst -> int -> int -> unit = <fun>


  5. crire la diagonalisation d'un systme. En dduire une fonction rsolvant un systme linaire.

    # exception Not_linear ;;
    exception Not_linear

    # let trigonalize s =
    if s.m=[| |] || s.v=[| |] then raise Not_linear ;
    let l = Array.length s.m
    in if l<>Array.length s.m.(0) || l<>Array.length s.v then raise Not_linear ;
    for i=0 to l-1 do
    if s.m.(i).(i)=0.0 then
    begin
    let j = ref (i+1)
    in while !j<l && s.m.(!j).(i)=0.0 do incr j done ;
    if !j=l then raise Not_linear ;
    permut_syst s i !j
    end ;
    div_syst s i ;
    for j=i+1 to l-1 do
    s.v.(j) <- s.v.(j) -. s.m.(j).(i) *. s.v.(i) ;
    s.m.(j) <- add_vect s.m.(j) (mult_scal_vect (-. s.m.(j).(i)) s.m.(i))
    done
    done ;;
    val trigonalize : syst -> unit = <fun>

    # let solve s =
    trigonalize s ;
    let l = Array.length s.v
    in let res = Array.copy s.v
    in for i = l-1 downto 0 do
    let x = ref res.(i)
    in for j=i+1 to l-1 do x := !x -. s.m.(i).(j) *. res.(j) done ;
    res.(i) <- !x
    done ;
    res ;;
    val solve : syst -> float array = <fun>


  6. Tester vos fonctions sur les systmes suivants :

    AX =



    10 7 8 7
    7 5 6 5
    8 6 10 9
    7 5 9 10




    *



    x1
    x2
    x3
    x4




    =



    32
    23
    33
    31




    = Y

    AX =



    10 7 8 7
    7 5 6 5
    8 6 10 9
    7 5 9 10




    *



    x1
    x2
    x3
    x4




    =



    32.1
    22.9
    33.1
    30.9




    = Y

    AX =



    10 7 8.1 7.2
    7.08 5.04 6 5
    8 5.98 9.89 9
    6.99 4.99 9 9.98




    *



    x1
    x2
    x3
    x4




    =



    32
    23
    33
    31




    = Y

    # let ax1 = { m = [| [| 10.0 ; 7.0 ; 8.0 ; 7.0 |]
    ; [| 7.0 ; 5.0 ; 6.0 ; 5.0 |]
    ; [| 8.0 ; 6.0 ; 10.0 ; 9.0 |]
    ; [| 7.0 ; 5.0 ; 9.0 ; 10.0 |] |] ;
    v = [| 32.0 ; 23.0 ; 33.0 ; 31.0 |] } ;;
    val ax1 : syst =
    {m=[|[|10; 7; 8; 7|]; [|7; 5; 6; 5|]; [|8; 6; 10; ...|]; ...|]; v=...}

    # let r1 = solve ax1 ;;
    val r1 : float array = [|1; 1; 1; 1|]

    # let ax2 = { m = [| [| 10.0 ; 7.0 ; 8.0 ; 7.0 |]
    ; [| 7.0 ; 5.0 ; 6.0 ; 5.0 |]
    ; [| 8.0 ; 6.0 ; 10.0 ; 9.0 |]
    ; [| 7.0 ; 5.0 ; 9.0 ; 10.0 |] |] ;
    v = [| 32.1 ; 22.9 ; 33.1 ; 30.9 |] } ;;
    val ax2 : syst =
    {m=[|[|10; 7; 8; 7|]; [|7; 5; 6; 5|]; [|8; 6; 10; ...|]; ...|]; v=...}

    # let r2 = solve ax2 ;;
    val r2 : float array = [|9.2; -12.6; 4.5; -1.1|]

    # let ax3 = { m = [| [| 10.0 ; 7.0 ; 8.1 ; 7.2 |]
    ; [| 7.08 ; 5.04 ; 6.0 ; 5.0 |]
    ; [| 8.0 ; 5.98 ; 9.89 ; 9.0 |]
    ; [| 6.99 ; 4.99 ; 9.0 ; 9.98 |] |] ;
    v = [| 32.0 ; 23.0 ; 33.0 ; 31.0 |] } ;;
    val ax3 : syst =
    {m=
    [|[|10; 7; 8.1; 7.2|]; [|7.08; 5.04; 6; 5|]; [|8; 5.98; 9.89; ...|];
    ...|];
    v=...}

    # let r3 = solve ax3 ;;
    val r3 : float array = [|-80.9999999999; 137; -33.9999999999; 22|]


  7. Que peut-on dire des rsultats obtenus? Il ne faut jamais ngliger les erreurs d'arrondis. Une petite diffrence dans les donnes de dpart peuvent provoquer de grandes erreurs l'arriv.

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora030.html0000644000000000000000000004426607421273601014476 0ustar Calculatrice mmoire Prcdent Index Suivant

Calculatrice mmoire

On reprend maintenant l'exemple de la calculatrice dcrite dans le chapitre prcdent mais en la dotant cette fois d'une interface utilisateur rendant notre programme propre tre utilis comme une calculette de bureau. Cette boucle permet d'entrer les oprations directement et de voir s'afficher les rsultats sans avoir appliquer explicitement la fonction de transition pour chaque pression d'une touche.

Nous ajoutons quatre nouvelles touches : C qui remet 0 l'affichage, M qui met en mmoire un rsultat, m qui restitue cette mmoire et OFF qui << teint >> la calculatrice. Ceci correspond au type suivant :

# type touche = Plus | Moins | Fois | Par | Egal | Chiffre of int
| MemoireIn | MemoireOut | Clear | Off ;;


Il faut alors dfinir une fonction de traduction des caractres frapps au clavier en des valeurs de type touche. L'exception Touche_non_valide permet de traiter le cas des caractres ne reprsentant pas une touche de la calculette. La fonction code du module Char traduit un caractre en son code ASCII.

# exception Touche_non_valide ;;
exception Touche_non_valide
# let traduction c = match c with
'+' -> Plus
| '-' -> Moins
| '*' -> Fois
| '/' -> Par
| '=' -> Egal
| 'C' | 'c' -> Clear
| 'M' -> MemoireIn
| 'm' -> MemoireOut
| 'o' | 'O' -> Off
| '0'..'9' as c -> Chiffre ((Char.code c) - (Char.code '0'))
| _ -> raise Touche_non_valide ;;
val traduction : char -> touche = <fun>


Dans un style impratif, la fonction de transition ne calculera plus un nouvel tat, mais modifiera physiquement l'tat de la calculette. Il faut donc redfinir le type etat de faon ce que ses champs soient modifiables. Enfin, on dfinit l'exception Touche_off pour traiter l'activation de la touche OFF.

# type etat = {
mutable dce : int; (* dernier calcul effectu *)
mutable dta : bool; (* vrai si touche = chiffre *)
mutable doa : touche; (* dernier oprateur actionn *)
mutable vaf : int; (* valeur affiche *)
mutable mem : int (* mmoire de la calculette *) };;



# exception Touche_off ;;
exception Touche_off
# let transition et tou = match tou with
Clear -> et.vaf <- 0
| Chiffre n -> et.vaf <- ( if et.dta then et.vaf*10+n else n );
et.dta <- true
| MemoireIn -> et.dta <- false ;
et.mem <- et.vaf
| MemoireOut -> et.dta <- false ;
et.vaf <- et.mem
| Off -> raise Touche_off
| _ -> let dce = match et.doa with
Plus -> et.dce + et.vaf
| Moins -> et.dce - et.vaf
| Fois -> et.dce * et.vaf
| Par -> et.dce / et.vaf
| Egal -> et.vaf
| _ -> failwith "transition: filtre impossible"
in
et.dce <- dce ;
et.dta <- false ;
et.doa <- tou ;
et.vaf <- et.dce;;
val transition : etat -> touche -> unit = <fun>


On dfinit la fonction go qui lance la calculette. Sa valeur de retour est () puisque ne nous importe que l'effet produit par l'excution sur l'environnement (entre/sortie, modification de l'tat). Son argument est galement la constante () puisque la calculette est autonome (elle dfinit elle-mme son tat initial) et interactive (les donnes du calcul sont rentres au clavier au fur et mesure des besoins). Les transitions s'effectuent dans une boucle infinie (while true do) dont on pourra sortir par l'exception Touche_off.

# let go () = 
let etat = { dce=0; dta=false; doa=Egal; vaf=0; mem=0 }
in try
while true do
try
let entree = traduction (input_char stdin)
in transition etat entree ;
print_newline () ;
print_string "affichage : " ;
print_int etat.vaf ;
print_newline ()
with
Touche_non_valide -> () (* pas d'effet *)
done
with
Touche_off -> () ;;
val go : unit -> unit = <fun>


Notons que l'tat initial doit tre soit pass en paramtre, soit dclar localement l'intrieur de la fonction go pour qu'il soit initialis chaque application de cette fonction. Si nous avions utilis une valeur etat_initial comme dans le programme fonctionnel, la calculatrice repartirait dans le mme tat que celui qu'elle avait lorsqu'elle a t coupe. Il aurait t alors difficile d'utiliser deux calculatrices dans le mme programme.








Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora176.html0000644000000000000000000003404607421273602014505 0ustar Processus concurrents Prcdent Index Suivant

Processus concurrents

L'criture d'une application compose de plusieurs processus concurrents fait perdre la proprit de dterminisme des programmes squentiels. Pour des processus partageant une mme zone mmoire, le rsultat du programme suivant ne peut pas tre dduit de sa lecture.
programme principal
lex x = ref 1;;
processus P processus Q
x := !x + 1;; x := !x * 2;;
la fin de l'excution de P et Q, la rfrence x peut valoir 2, 3 ou 4, selon l'ordre de calcul de chaque processus.

Cet indterminisme vaut galement pour la terminaison. Comme l'tat mmoire dpend du droulement de chaque processus parallle, une application peut ne pas terminer pour une certaine excution et terminer dans une autre. Pour apporter un certain contrle l'excution, les processus doivent se synchroniser.

Pour des processus utilisant des mmoires distinctes, mais communiquant entre eux, leur interaction dpend du type de communication. On introduit pour l'exemple suivant deux primitives de communication : send qui envoie une valeur en indiquant le destinataire et receive qui reoit une valeur d'un processus. Soient deux processus communicants P et Q :
processus P processus Q
lex x = ref 1;; lex y = ref 1;;
send(Q,!x); y := !y + 3;
x := !x * 2; y := !y + receive(P);
send(Q,!x); send(P,!y);
x := !x + receive(Q); y := !y + receive(P);
Dans le cas d'une communication vanescente, le processus Q peut rater les missions de P. On retombe dans le non-dterminisme du modle prcdent.

Pour une communication asynchrone, le mdium du canal de communication conserve les diffrentes valeurs transmises. Seule la rception est bloquante. Le processus P peut tre en attente sur Q, bien que ce dernier n'ait pas encore lu les deux envois de P. Ce qui ne l'empche pas d'mettre.

Pour une communication synchrone, l'mission est elle aussi bloquante. Dans notre exemple le send(Q,!x) de P attend que Q soit en rception (receive(P)). Une fois l'information transmise, les deux processus continuent leur chemin. Malheureusement, dans notre exemple, P et Q se retrouvent sur une instruction d'mission bloquante, et le programme n'avancera plus.

On peut classer les applications concurrentes en cinq catgories suivant que les units de programme les composant sont :
  1. sans relation ;
  2. avec relation mais sans synchronisation ;
  3. avec relation d'exclusion mutuelle ;
  4. avec relation d'exclusion mutuelle et communication ;
  5. avec relation, sans exclusion mutuelle et avec communication synchrone.
La difficult de ralisation vient principalement des dernires catgories. Nous allons prsent voir comment rsoudre ces difficults en utilisant les bibliothques d'Objective CAML.

Compilation avec processus lgers

La bibliothque sur les threads d'Objective CAML est dcoupe en cinq modules dont les quatre premiers dfinissent chacun des types abstraits :
  • module Thread : cration et excution de processus lgers (type Thread.t);
  • module Mutex : cration, pose et libration de verrous (type Mutex.t);
  • module Condition : cration de conditions (signaux), attente et rveil sur condition (type Condition.t);
  • module Event : cration de canaux de communication (type 'a Event.channel), des valeurs y transitant (type 'a Event.event), et des fonctions de communication.
  • module ThreadUnix : redfinition des fonctions d'entres-sorties du module Unix pour qu'elles ne soient pas bloquantes.
Cette bibliothque ne fait pas partie de la bibliothque d'excution d'Objective CAML. Son utilisation ncessite soit de compiler ses programmes avec l'option -custom, soit de construire une nouvelle boucle d'interaction en utilisant les commandes :
$ ocamlc -thread -custom threads.cma  fichiers.ml -cclib -lthreads
$ ocamlmktop -thread -custom -o threadtop thread.cma -cclib -lthreads
La bibliothque Threads n'est utilisable avec le compilateur natif que si le systme d'exploitation implante des processus lgers conformes la norme POSIX 10031. On compile alors les excutables en ajoutant les bibliothques unix.a et pthread.a :
$ ocamlc -thread -custom threads.cma fichiers.ml -cclib -lthreads \
  -cclib -lunix -cclib -lpthread
$ ocamltop -thread -custom threads.cma fichiers.ml -cclib -lthreads \
  -cclib -lunix -cclib -lpthread
$ ocamlcopt -thread threads.cmxa fichiers.ml -cclib -lthreads \
  -cclib -lunix -cclib -lpthread

Module Thread

Le module Thread d'Objective CAML contient les primitives de cration et de gestion des processus lgers. Nous n'en ferons pas une prsentation exhaustive, en particulier les oprations d'entres-sorties sur les fichiers ont t dcrites au chapitre prcdent.

La cration d'un processus lger se fait par appel  :

# Thread.create ;;
- : ('a -> 'b) -> 'a -> Thread.t = <fun>
Le premier argument, de type ('a -> 'b), correspond la fonction excute par le processus cr ; le second argument, de type 'a, est l'argument attendu par la fonction excute ; le rsultat de l'appel est le descripteur associ au processus. Le processus ainsi cr est dtruit automatiquement lorsque la fonction associe termine.

Connaissant son descripteur, on peut demander l'excution d'un processus et en attendre la fin en utilisant la fonction join. En voici un exemple d'utilisation :

# let f_proc1 () = for i=0 to 10 do Printf.printf "(%d)" i; flush stdout done;
print_newline() ;;
val f_proc1 : unit -> unit = <fun>
# let t1 = Thread.create f_proc1 () ;;
val t1 : Thread.t = <abstr>
# Thread.join t1 ;;
(0)(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)
- : unit = <unknown constructor>


Warning


Le rsultat de l'excution d'un processus n'est pas rcupr par le processus pre, mais perdu quand le processus fils termine.


On peut galement interrompre brutalement le droulement d'un processus dont on connat le descripteur par la fonction kill. Crons, par exemple, un processus pour l'interrompre immdiatement :

# let n = ref 0 ;;
val n : int ref = {contents=0}
# let f_proc1 () = while true do incr n done ;;
val f_proc1 : unit -> unit = <fun>
# let go () = n := 0 ;
let t1 = Thread.create f_proc1 ()
in Thread.kill t1 ;
Printf.printf "n = %d\n" !n ;;
val go : unit -> unit = <fun>
# go () ;;
n = 0
- : unit = ()


Un processus peut mettre fin sa propre activit par la fonction :

# Thread.exit ;;
- : unit -> unit = <fun>


Il peut suspendre son activit pendant un temps donn par appel  :

# Thread.delay ;;
- : float -> unit = <fun>
L'argument indique le nombre de secondes d'attente.

Reprenons l'exemple prcdent en lui ajoutant des temporisations. Nous crons un premier processus t1 dont la fonction associe f_proc2 cre son tour un processus t2 qui excute f_proc1, puis f_proc2 attend un dlai de d secondes, met fin t2. la terminaison de t1, on affiche le contenu de n.

# let f_proc2 d =
n := 0 ;
let t2 = Thread.create f_proc1 ()
in Thread.delay d ;
Thread.kill t2 ;;
val f_proc2 : float -> unit = <fun>
# let t1 = Thread.create f_proc2 0.25
in Thread.join t1 ; Printf.printf "n = %d\n" !n ;;
n = 36123
- : unit = ()



Prcdent Index Suivant ocaml-book-1.0/fr/html/logocaml-petit.gif0000644000000000000000000001320107073350147015250 0ustar GIF89a||z~*:B$Zb,6&&4JR/jr466( :BLZbp*&~43BR\jf!4~B*2-BJFB$vr5$2>L'rn6>," zOrn7D:>&bft<>Z^l2:J $6:$&&BFTFN\&2,4<"<"*.% kn09NV,nv4=FG^ft pv2:8<<:4V^,D/VVC6nj-UV&@CF)+?V^l.:L&2D42&#" C2:,fn|>F-"*$>FX{v7>NVd*2BDBJL4*B!^b*4-nr3^bt&.*FJ';!Made with GIMP! ,|| H*\ȰÇ#JHŋ3jȱǏ 9"IB\Yp[^H1Pf48aA. X (OהQܪIV+2OH%"U*02` 9C6PIXxu۷P3.x%(L;u[boޡt&paˇ1qmÚ NϴQ[3NSgvVV9N`PmY0Χ&Qq( p[MuI'nowͧ+NWH6 G-l#~aomuT,$ T"U{Ej7!oeHMk *77HD~HCc9'a| AO7I(eEV |(&,( Y{b^!TIwQTb^'A#8 h(T(&KNБLaB%:'ECSb@C=O (RX c+UTbC!fGBp gAGo`.ĵ !a2fl 7p(w ܱѵ?D(CAX+ٴP xPF^l]cH+TF].c"P^oPD.!# [uxrm1cfF /U5{rTES蠅*bk-5fo4oCO% BBC%Cd9όsU/ \+"ެP(\ A$&kb <j,0oOʭ߁JxtC,A@ u<&z xq9`D Q㱉rˋ8󊳌S!&PH'`w*xp2$Љ' |d@Y"(/8p2a o"xT"5] X` &HdXGK9/e =YCe0aW3P;"(\X= ܨF p@e rXB/Ԋ4IDQ*Fq(aX+|xyi UU(@1X@THЖVu^"X\FEJS+ἸsE,@AŪViIm,=Z,b0n 8pA|ªU*|4vGx1H>H> P[Xa! Z73TjC.>&ɇ/-Td$s \D*b8u!(ojf Ç=`j.zzXoHHI!&!C ࠼ prY`x^1񕫨RFCtq7*`9#5Fn6ǨwA F0W'Ԋ,Q!ȄKwK[*v4+PGAZbPuO-fR٥}sA㷅`8xSwQE0ZMl˜хf1B ! `%ЈgeYpŚR;ٖ]9ZBac!j `ۤ,TZƀ" ȅ'Å)b Sܠ-Uk)Eb0^q=p⸅e@4N|B: NUn@LT/zq1ڛ  8]a1S^X\j& pH!8VP^G@HBNna>b ZoRn8ő1dr`;ME5F$ʄurET(X`aj!9bb2ƑjMj@ qyi uH ^ΏZ"@7-8 7vJ"w;]E;( #9D68-,T/0$ *oD 2k_}n! `l|wMwfF` y.Z0ސlp5P7j60 bf%x` |opG )0 @`B - n#j:>vpp\ d nB݇F(G B80 >4Xj7 IV0 qrp$ V 0Ơ -I@C `PphfWSvl v4K )M e lpY FK rt֕ c f7IuxzpsTŐB 0G 7`v|X;v"a3'7Sa`q++t(|nj : vf4p[0'(b4 iFVq T-`:`@pŀ Xjvpqf0 u$"@+ Q ;@Ep8!@b'5d`iCXY` xPR#H{qy!kudMTp]$@/,PӸ8J:`Z P݁G @1GIVY |@d@PT<@M˰A9um==vƄ`o#".pJ Y P{ P;AvFj U0ny*O3(/\ Xf7m:i-U PeT<4T%OA 8U)R@g-JQnQ $ pE A % QP 0 &^mX:06O50j c)Fp 0: J8$(lBxz|)0  g02 lo PBJues!#,X.I> TOR `Y q 7Ҟ_`L E>b?P3a#@pa0 . aT3$ѱPQ@CʦWBH KOkjnm0; Pd*0{!yw0GEɗq ׀o0<Р40X3 sܐkW^p:V6"d4 z 3g0 Cs%*pIb i305[E\ : K5pf`CX Ҫ; h-7|*t : Jaq@j T@ .`05 ltZ"a|@^b10ά,PePaǒ[ *) 0^".? 72̗o7{2rHF oŴQ ۾Hqh"0G* o0 }Gn:R !@\}%,B- m*Rrt]F' ^ `,w r (5w*Q " ]! P +I%`!S@ rw10c1 ^0bs#OQ ÀQɀ 5p -  0 ~W 5 0 0A` p z_0*]&`Ԁqu0 gW}g 0 rPX`܉Sp0oc0  0^~;ocaml-book-1.0/fr/html/book-ora120.html0000644000000000000000000011526507421273602014475 0ustar Exercices Prcdent Index Suivant

Exercices

Affichage polymorphe

On cherche dfinir une fonction d'affichage print de type 'a -> unit pouvant afficher n'importe quelle valeur Objective CAML. Pour cela on reprend la fonction inspecte pour l'amliorer.

  1. crire en C la fonction print_ws qui affiche les valeurs Objective CAML de la manire suivante :
    • valeurs immdiates : comme des entiers C
    • chanes : entre ""
    • flottants : habituels
    • tableau de flottants : entre [| |]
    • les fermetures : < code, env >
    • tout le reste comme un n-uplet, entre ( )
    La fonction parcourt les valeurs de manire rcursive. Une fonction C :
    #include <stdio.h>
    #include <caml/mlvalues.h>
    #include <caml/memory.h>
    
    value print_ws (value v) {
      CAMLparam1(v);
      int taille,i ;
      if (Is_long(v)) printf("%d", Long_val(v)); 
      else {
        taille=Wosize_val(v);
        switch (Tag_val(v)) 
          {
          case String_tag :
     printf("\"%s\"", String_val(v));  
     break;
          case Double_tag:  
     printf("%g", Double_val(v));
     break;
          case Double_array_tag : 
     printf ("[|"); 
            if (taille>0) printf("%g", Double_field(v,0));
     for (i=1;i<(taille/2);i++)  printf("; %g", Double_field(v,i));
     printf("|]");
     break;
          case Abstract_tag :
          case Final_tag : 
     printf("<abstract>"); 
     break;
          case Closure_tag : 
     printf("<%d, ",Code_val(v)) ;
     if (taille>1) print_ws(Field(v,1)) ;
     for (i=2;i<taille;i++) {
       printf("; ") ;
       print_ws(Field(v,i));
     }
     printf(">");
     break;
          default:  
     if (Tag_val(v)>=No_scan_tag) printf("?"); 
     else {
       printf("(");
       if (taille>0) print_ws(Field(v,0));
       for (i=1;i<taille;i++) {
         printf(", ");
         print_ws(Field(v,i));
       }
       printf(")");
     }
          }
      }
      fflush(stdout);
      return Val_unit;
    }
    
    appele depuis Objective CAML:

    # external print_ws : 'a -> unit = "print_ws" ;;
    external print_ws : 'a -> unit = "print_ws"


  2. Pour viter de boucler sur une valeur circulaire et pour montrer le partage, modifier cette fonction pour tenir compte des adresses dj rencontres. Si une adresse apparat plusieurs fois, alors lui donner un nom au premier affichage ( v = nom) et utiliser ce nom par la suite quand vous la rencontrez de nouveau.

    # type adresse ;;
    type adresse
    1. Dfinir une structure de donnes pour stocker les adresses, indiquer quand elles occurrent plusieurs fois et leur associer un nom.

      # let (gensym,init_gensym) =
      let i = ref 0 in
      (function () -> incr i ; "val_" ^ (string_of_int !i))
      , (function () -> i:=0) ;;
      val gensym : unit -> string = <fun>
      val init_gensym : unit -> unit = <fun>

      # type occurence =
      Une_fois
      | Plusieurs_fois
      | Deja_nomme of string ;;
      type occurence = | Une_fois | Plusieurs_fois | Deja_nomme of string

      # let table = Hashtbl.create 17 ;;
      val table : ('_a, '_b) Hashtbl.t = <abstr>

      # let ajoute (adr : adresse) =
      try
      match Hashtbl.find table adr with
      Une_fois -> Hashtbl.remove table adr ;
      Hashtbl.add table adr Plusieurs_fois ;
      true
      | _ -> true
      with
      Not_found -> Hashtbl.add table adr Une_fois ; false ;;
      val ajoute : adresse -> bool = <fun>

      # let multiple_occ adr =
      match Hashtbl.find table adr with
      Une_fois -> false
      | _ -> true ;;
      val multiple_occ : adresse -> bool = <fun>

      # let deja_nomme adr =
      match Hashtbl.find table adr with
      Une_fois -> failwith "deja_nomme"
      | Plusieurs_fois -> Hashtbl.remove table adr ;
      Hashtbl.add table adr (Deja_nomme (gensym ())) ;
      false
      | _ -> true ;;
      val deja_nomme : adresse -> bool = <fun>

      # let nom_de adr =
      match Hashtbl.find table adr with
      Deja_nomme s -> s
      | _ -> raise Not_found ;;
      val nom_de : adresse -> string = <fun>
    2. La valeur est parcourue une premire fois pour explorer toutes les adresses et les enregistrer dans la table. La partie Objective CAML:

      # Callback.register "add" ajoute ;;
      - : unit = ()
      # Callback.register "multiple?" multiple_occ ;;
      - : unit = ()
      # Callback.register "named?" deja_nomme ;;
      - : unit = ()
      # Callback.register "name" nom_de ;;
      - : unit = ()

      # external explore_value : 'a -> unit = "explore" ;;
      external explore_value : 'a -> unit = "explore"
      La partie C :
      #include <caml/callback.h>
      
      value explore (value v) {
        CAMLparam1(v);
        int taille,i ;
        if (Is_long(v))  return Val_unit;
        if (Bool_val(callback(*caml_named_value("add"),v))) return Val_unit;
        taille=Wosize_val(v);
        switch (Tag_val(v)) 
          {
            case String_tag :
            case Double_tag:  
            case Double_array_tag : 
            case Abstract_tag :
            case Final_tag : 
              break;
            case Closure_tag : 
              for (i=1;i<taille;i++) explore(Field(v,i));
              break;
            default:  
              if (Tag_val(v)>=No_scan_tag) break ;
              for (i=1;i<taille;i++) explore(Field(v,i));
          }
        return Val_unit;
      }
      
    3. Le second parcours ralise l'affichage proprement dit en nommant les adresses la premire de leurs occurrences.

      # external print_rec : 'a -> unit = "print_gen" ;;
      external print_rec : 'a -> unit = "print_gen"
      value print_gen (value v)
      {
        CAMLparam1(v);
        int taille,i ;
        if (Is_long(v))  return print_ws(v) ;
        if (Bool_val(callback(*caml_named_value("multiple?"),v))) {
          if (Bool_val(callback(*caml_named_value("named?"),v))) {
            printf("%s",String_val(callback(*caml_named_value("name"),v))) ;
            return Val_unit ;
          }
          printf("%s = { ",String_val(callback(*caml_named_value("name"),v))) ;
        }
        taille=Wosize_val(v);
        switch (Tag_val(v)) 
          {
            case String_tag :
            case Double_tag:  
            case Double_array_tag : 
            case Abstract_tag :
            case Final_tag : 
       print_ws(v);
       break;
            case Closure_tag : 
       printf("<%d, ",Code_val(v)) ;
       if (taille>1) print_gen(Field(v,1)) ;
       for (i=2;i<taille;i++) {
         printf("; ") ;
         print_gen(Field(v,i));
       }
       printf(">");
       break;
            default:  
       if (Tag_val(v)>=No_scan_tag) printf("?"); 
       else {
         printf("(");
         if (taille>0) print_gen(Field(v,0));
         for (i=1;i<taille;i++) {
           printf(", ");
           print_gen(Field(v,i));
         }
         printf(")");
       }
          }
        if (Bool_val(callback(*caml_named_value("multiple?"),v)))  printf(" }") ; 
        fflush(stdout);
        return Val_unit;
      }
      
    4. Dfinir la fonction print runissant le tout.

      external print_rec : 'a -> unit = "print_gen" ;;
      let print v =
      Hashtbl.clear table ;
      init_gensym () ;
      explore_value v ;
      print_rec v ;;

Produit de matrices : partage ou copie

  1. Dfinir un type abstrait float_matrix pour des matrices de flottants.

    # type float_matrix ;;
    type float_matrix


  2. Dfinir un type C pour de telles matrices.
    typedef struct { int size_x , size_y ;
                     float * mat ;
                   } Matrix ;
    


  3. crire une fonction en C de conversion de valeur de type float array array vers une valeur de type float_matrix.
    value conversion_to_C (value faa) {
      CAMLparam1(faa) ;
      CAMLlocal1(vres) ;
      Matrix * res ;
      int size_x, size_y ;  
    
      /* taille du vecteur de vecteur */
      size_x = Wosize_val(faa) ;
      if (size_x>0) size_y = Wosize_val(Field(faa,0))/2 ;
    
      /* allocation de la valeur float_matrix */
      vres=alloc(sizeof(Matrix),Abstract_tag) ;
      res=(Matrix *) vres ;
      res->size_x = size_x ;
      res->size_y = size_y ;
      if (size_x*size_y==0) res->mat=0 ;
      else {
        int i,j ;
        float * tab ;
        value vect ;
        res->mat=tab=(float *) alloc(sizeof(float)*size_x*size_y,Abstract_tag) ;
        for (i=0;i<size_x;i++) {
          vect = Field(faa,i) ;      
          for (j=0;j<size_y;j++) *(tab++) = Double_field(vect,j) ;
        }
      }
      CAMLreturn vres ;
    }
    


  4. crire la fonction en C effectuant la conversion inverse.
    value conversion_to_Caml (value matrix) {
      CAMLparam1(matrix) ;
      CAMLlocal2(res,aux) ;
      Matrix* mat = (Matrix *) matrix ;
      float * tab = mat->mat ;
      int size = mat->size_x*mat->size_y ;
      int i,j ;
      res=alloc(mat->size_x,0);
      for (i=0;i<mat->size_x;i++) {
        aux = alloc(2*mat->size_y,Double_array_tag) ;
        Field(res,i) = aux ;
        for (j=0;j<mat->size_y;j++) Store_double_field(aux,j,*(tab++)) ;
      }
      CAMLreturn res ;
    }
    


  5. Ajouter en C les fonctions pour la somme et le produit de telles matrices.
    value plus (value arg1,value arg2) {
      CAMLparam2(arg1,arg2) ;
      CAMLlocal1(vres) ;
      Matrix *m1=(Matrix*) arg1, *m2=(Matrix*) arg2,*res;
      float *tab;
      int i,size=m1->size_x*m1->size_y ;
      vres=alloc(sizeof(Matrix),Abstract_tag) ;
      res =(Matrix*) vres;
      res->size_x=m1->size_x;
      res->size_y=m1->size_y;
      res->mat=tab=(float *) alloc(sizeof(float)*size,Abstract_tag) ;
      for (i=0;i<size;i++) tab[i]=m1->mat[i]+m2->mat[i] ;
      CAMLreturn vres;
    }
    
    value prod (value arg1,value arg2) {
      CAMLparam2(arg1,arg2) ;
      CAMLlocal1(vres) ;
      Matrix *m1=(Matrix*) arg1, *m2=(Matrix*) arg2,*res;
      float *tab;
      int i,j,k;
      vres=alloc(sizeof(Matrix),Abstract_tag) ;
      res =(Matrix*) vres;
      res->size_x=m1->size_x;
      res->size_y=m2->size_y;
      res->mat=tab=(float *) alloc(sizeof(float)*res->size_x*res->size_y,
                                   Abstract_tag) ;
      for (i=0;i<res->size_x;i++) 
        for (j=0;j<res->size_y;j++) {
          float acc=0 ;
          for (k=0;k<m1->size_y;k++) 
     acc += m1->mat[i*m1->size_x+k] * m1->mat[k*m2->size_x+j] ;
          tab[i*m1->size_x+j]=acc ;
        }
      CAMLreturn vres;
    }
    


  6. Les interfacer avec Objective CAML et les utiliser.

    # external to_matrix : float array array -> float_matrix = "conversion_to_C" ;;
    # external of_matrix : float_matrix -> float array array = "conversion_to_Caml";;
    # external somme : float_matrix -> float_matrix -> float_matrix = "plus" ;;
    # external produit : float_matrix -> float_matrix -> float_matrix = "prod" ;;

Compter les mots : programme principal en C

La commande Unix wc compte le nombre de caractres, de mots et de lignes d'un fichier. Cet exercice a pour but de raliser cette commande en ne comptant qu'une fois les mots qui sont rpts plusieurs fois.
  1. crire le programme wc en C comptant simplement les mots, les lignes et les caractres d'un fichier dont le nom est pass en argument. Le fichier wc.c :
    #include <stdio.h>
    
    void read_file (char *path) {
      FILE *fd=fopen(path,"r");
      int car=0;
      char buffer[80], *buff;
      int nb_car=0, nb_mots=0, nb_lignes=0;
    
      buff=buffer; *buff=0;
      if (!fd) exit(-1) ;
      while ((car=getc(fd))!=EOF) {
        nb_car++ ;
        if (car=='\n') nb_lignes++ ;
        if (car==' ' || car=='\n' ||(buff-buffer)>=80) {
          if (buff!=buffer) { nb_mots++; buff=0; buff=buffer; }
        }
        else *(buff++)=car; 
      }
      printf(" %d - %d - %d : %s\n",nb_lignes,nb_mots,nb_car,path) ;
    }
    
    int main (int argc,char **argv)
    {
      if (argc>1) read_file(argv[1]);
      return 0;
    }
    


  2. crire en Objective CAML une fonction ajoute_mot qui l'aide d'une table de hachage conserve le nombre de fois o la fonction a t invoque avec pour argument une mme chane de caractres.

    # let table = Hashtbl.create 17 ;;
    val table : ('_a, '_b) Hashtbl.t = <abstr>

    # let ajoute_mot (m:string) =
    try let p = Hashtbl.find table m in incr p
    with Not_found -> Hashtbl.add table m (ref 1) ;;
    val ajoute_mot : string -> unit = <fun>


  3. crire deux fonctions nb_mots_repetes et nb_mots_differents comptant respectivement les mots rpts et les mots diffrents partir de la table construite par ajoute_mot.

    # let nb_mots_repetes () =
    let i =ref 0 in
    Hashtbl.iter (fun _ n -> if !n>1 then incr i) table ;
    !i ;;
    val nb_mots_repetes : unit -> int = <fun>

    # let nb_mots_differents () =
    let i =ref 0 in
    Hashtbl.iter (fun _ _ -> incr i) table ;
    !i ;;
    val nb_mots_differents : unit -> int = <fun>


  4. Enregistrer les trois fonctions prcdentes de sorte qu'elles puissent tre utilises depuis un programme C.

    # Callback.register "add word" ajoute_mot ;
    Callback.register "rep words" nb_mots_repetes ;
    Callback.register "diff words" nb_mots_differents ;;
    - : unit = ()


  5. Rcrire la fonction principale du programme wc pour que soit affich le nombre de mots diffrents en lieu et place du nombre de mots. Il est ncessaire d'inclure les interfaces suivantes :
    #include <caml/mlvalues.h>
    #include <caml/callback.h>
    
    void read_file (char *path) {
      FILE *fd = fopen(path,"r") ;
      int car=0 ;
      char buffer[80],*buff ;
      int nb_car=0, nb_mots, nb_lignes=0; 
    
      buff=buffer; *buff=0;
      if (!fd) exit(-1) ;
      while ((car=getc(fd))!=EOF) {
        nb_car++ ;
        if (car=='\n') nb_lignes++ ;
        if (car==' ' || car=='\n' ||(buff-buffer)>=80) {
          if (buff!=buffer) { 
            nb_mots++; 
     *buff=0;
     buff=buffer;
     callback(*caml_named_value("add word"),copy_string(buffer));
          }
        }
        else *(buff++)=car; 
      }
      nb_mots=Int_val(callback(*caml_named_value("diff words"),Val_unit)); 
      printf(" %d - %d - %d\n",nb_lignes,nb_mots,nb_car) ;
    }
    


  6. Donner la fonction main et les commandes ncessaires pour compiler ce programme comme un programme Objective CAML.
    int main (int argc,char **argv)
    {
      caml_main(argv);
      if (argc>1) read_file(argv[1]);
      return 0;
    }
    
    Pour compiler en code-octet :
    $ cc -c -I /usr/local/lib/ocaml/ wc.c
    $ ocamlc -custom mots.ml wc.o
    
    Pour compiler en code-natif :
    $ cc -c -I /usr/local/lib/ocaml/ wc.c
    $ ocamlopt mots.ml wc.o
    


  7. Donner la fonction main et les commandes ncessaires pour compiler ce programme comme un programme C.
    int main (int argc,char **argv)
    {
      caml_startup(argv);
      if (argc>1) read_file(argv[1]);
      return 0;
    }
    
    Pour compiler en code-octet :
    $ ocamlc -output-obj mots.ml -o mots.o
    $ gcc -c -I /usr/local/lib/ocaml/ wc.c
    $ gcc mots.o wc.o -L /usr/local/lib/ocaml/ -lcamlrun -lcurses
    
    Pour compiler en code-natif :
    $ ocamlopt -output-obj mots.ml -o motsprog.o 
    $ gcc -c -I /usr/local/lib/ocaml/ wc.c
    $ gcc motsprog.o wc.o -L /usr/local/lib/ocaml/ -lasmrun 
    

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora058.gif0000644000000000000000000000234307073350152014277 0ustar GIF89aUUU!,t޼HTʶnK3~ DDLڎʦ|J(*bI-+812*5q 9~zKnW>z>ْALG}\#(nBO8kM>Ԛ~f))z ?aImSplzh`& qVB-]T a~]aٗC$ y < y! 0YPZ"'iUfyؖ\4XfX '\-u(mGe2[rAe {NJJg(gTh `pm.qƠbz(&,***xjj]*kB:k` [.l" [*_J˥lbrkoKa*:HꖮtSB’K56J*rjVzmTPo%\| s #,҃3iW%ou2V)WT-?k#KksA#:+:pABLЀ*bev;1b&;AҦihv5axU*i+(NL c7*l #hy ^.j|Πy.5赊鲢^1ޓ!?=f>AԷ dwSB7nJz" qgnk%LjňtrhGcщ{9>[G;oZlZVW@_UI@a-UtNw7A]%Y?;ocaml-book-1.0/fr/html/book-ora084.gif0000644000000000000000000001070007073350152014272 0ustar GIF89aWt֝0r,ݪD½3 L}TrTtt;LD;33fLLʈ=&njdU$vڝ,ڙ,$ WWWrLfflll333DΖ3’;[ [B}TLTdDLΌT{ʛj nnnҙ.],3Ƅʖ3[l d7 Ν3"""DəfŲ$Ҕƒ;n,!, H*\ȰÇ#JHbQ2jsǏB iI;RLY:bʜI8sϟ Ѣ*u঩Ӧ Jeժ jU׮Š5ٲҪ%۷pќK2xK߾(È Vimb˭z;V\nc|_ l$[A#rUtyHF pH`G+- c{1kk!\2*g \`F#Tmv+l!tm}|߀ny$x4xxD.9UG4RcEd0YVwyx!Ey,Vו@,m/oj#77O{Wog|Zk_<槯⣿~ӟïG ^׽9ދ`ط? .m A #> l`UBo wO> X< HLbh&:PH*ZX̢.z`D܅hL#H!4T+#B!:x̣GqۂTB kp`F:Sf!XL 8z[*.n kDZhF>0"B^Z$,bKr | g0UXr$ /8>Qr4 5d!hEH/ϙY:d$H'Kuo%*K"D޷Wt )Rusք )N񙳝Aˎbe.uQS-CPr d_j3q$A e _#V!RfC/ZRPEgxSj貺]3iYֶ..s]󺷾n-r<6e#&"`(!v5BB=km۩ `@;(T~涡N T_@T@ľp{D ~&u Ȃ1? H!  n(Q.&񅝃|^L H&ooZ{y50gNޏ#,mR|o'r+AJ@ڎrWK@ f <$ w§|,*QD@)( V8F1xfC &7j`^3 }œ|J@ QoyK?ĽO}I /(AmZ;O`HS?qBda t }|5w@@$ m }{#J@@oh=2G=G^#x="p =@*<-j\$0W ^g} g8aJ J| @Јp( `XZ`x  T" p˅ԣ #rFDž 08 }'~@C8w2 L'ESpp5؆KX@`v % 6~Ÿ5uk>h m,`v6\ X ٘m,0xk\g@HH(Ѕ(Ǎq1'vK&) 9swy\gֈ` )kXha@؊NSU9aE#ɕh v] hIؐok\X=xt 9  ( ] sjCI(p؅t Wr8y sboy ozy<]{y=ɘr*ؘ:bg邴\jY}^=cixWj7)jɝdٗ }_ө>ڈ䙑)d i9_Iii)`m(AT}IuѦkz l ڠjl:Vl϶*ml"Zb$Z<(Jd'5. <0Fi٢8>3ӣ>z;@K3DPG:IʖKMZOQ*SU*\FY[z]JĥFb:dZfzg;_kmoqjsJuwy{}ZL:ZF[]׵G FH<_4 :کjXI+5D߂L婪2ITHCZCP  jjGGRNo#$IWZЮ>z*P :3jdu%PI1jKJZ*ЭB :֫SPW1jT4{Lτ #^;(*CBԳ@B+dDDz%:0T[V{XZ\۵^`b;d[fa3j۶npr;t[v{x{li{;[}۶{;#uk;ocaml-book-1.0/fr/html/book-ora146.html0000644000000000000000000003453207421273602014502 0ustar Style fonctionnel Prcdent Index Suivant

Style fonctionnel

Le style de la programmation par objets est le plus souvent impratif. Un message est envoy un objet qui modifie physiquement son tat interne (ses champs de donnes). Nanmoins il est aussi possible d'aborder la programmation par objets par le style fonctionnel. L'envoi d'un message un objet retourne un nouvel objet.

Copie d'objets

Objective CAML offre une construction syntaxique particulire qui permet de retourner une copie de l'objet self dans laquelle les valeurs de certains champs de donnes sont changs.

Syntaxe


{< nom1=expr1;...; nomn=exprn >}
On peut ainsi dfinir des points fonctionnels pour lesquels les mthodes de mouvements relatifs n'ont pas d'effet de bord, mais renvoient un nouveau point.

# class f_point p =
object
inherit point p
method f_rmoveto_x (dx) = {< x = x + dx >}
method f_rmoveto_y (dy) = {< y = y + dy >}
end ;;
class f_point :
int * int ->
object ('a)
val mutable x : int
val mutable y : int
method distance : unit -> float
method f_rmoveto_x : int -> 'a
method f_rmoveto_y : int -> 'a
method get_x : int
method get_y : int
method moveto : int * int -> unit
method print : unit -> unit
method rmoveto : int * int -> unit
method to_string : unit -> string
end


L'envoi des nouvelles mthodes de dplacement ne modifie pas l'objet receveur.

# let p = new f_point (1,1) ;;
val p : f_point = <obj>
# print_string (p#to_string()) ;;
( 1, 1)- : unit = ()
# let q = p#f_rmoveto_x 2 ;;
val q : f_point = <obj>
# print_string (p#to_string()) ;;
( 1, 1)- : unit = ()
# print_string (q#to_string()) ;;
( 3, 1)- : unit = ()


Comme ces mthodes construisent un objet, il est possible d'envoyer directement un message au rsultat de la mthode f_rmoveto_x.

# print_string ((p#f_rmoveto_x 3)#to_string()) ;;
( 4, 1)- : unit = ()


Les mthodes f_rmoveto_x et f_rmoveto_y ont un type rsultat du type de l'instance de la classe dfinie. C'est ce qu'indique le 'a du type de f_rmoveto_x.

# class f_colored_point (xc, yc) (c:string) =
object
inherit f_point(xc, yc)
val color = c
method get_c = color
end ;;
class f_colored_point :
int * int ->
string ->
object ('a)
val color : string
val mutable x : int
val mutable y : int
method distance : unit -> float
method f_rmoveto_x : int -> 'a
method f_rmoveto_y : int -> 'a
method get_c : string
method get_x : int
method get_y : int
method moveto : int * int -> unit
method print : unit -> unit
method rmoveto : int * int -> unit
method to_string : unit -> string
end


L'envoi de f_rmoveto_x une instance de f_colored_point retourne une nouvelle instance de f_colored_point.

# let fpc = new f_colored_point (2,3) "bleu" ;;
val fpc : f_colored_point = <obj>
# let fpc2 = fpc#f_rmoveto_x 4 ;;
val fpc2 : f_colored_point = <obj>
# fpc2#get_c;;
- : string = "bleu"


On peut galement obtenir une copie de n'importe quel objet en utilisant la primitive copy du module Oo :

# Oo.copy ;;
- : (< .. > as 'a) -> 'a = <fun>
# let q = Oo.copy p ;;
val q : f_point = <obj>
# print_string (p#to_string()) ;;
( 1, 1)- : unit = ()
# print_string (q#to_string()) ;;
( 1, 1)- : unit = ()
# p#moveto(4,5) ;;
- : unit = ()
# print_string (p#to_string()) ;;
( 4, 5)- : unit = ()
# print_string (q#to_string()) ;;
( 1, 1)- : unit = ()


Exemple : classe pour les listes

Une mthode fonctionnelle peut utiliser l'objet lui-mme self pour le calcul de sa valeur de retour. Nous illustrons ce point en dfinissant une hirarchie simple de classes pour reprsenter les listes d'entiers.

On commence par dfinir la classe abstraite paramtre par le type des lments des listes.

# class virtual ['a] o_list () =
object
method virtual empty : unit -> bool
method virtual cons : 'a -> 'a o_list
method virtual head : 'a
method virtual tail : 'a o_list
end;;


On dfinit la classe des listes non vides.

# class ['a] o_cons (n ,l) =
object (self)
inherit ['a] o_list ()
val car = n
val cdr = l
method empty () = false
method cons x = new o_cons (x, (self : 'a #o_list :> 'a o_list))
method head = car
method tail = cdr
end;;
class ['a] o_cons :
'a * 'a o_list ->
object
val car : 'a
val cdr : 'a o_list
method cons : 'a -> 'a o_list
method empty : unit -> bool
method head : 'a
method tail : 'a o_list
end


Il est noter que la mthode cons retourne une nouvelle instance de 'a o_cons. Pour ce faire le type de self est contraint 'a #o_list puis sous-typ en 'a o_list. Sans le sous-typage, on obtient un type ouvert ('a #o_list) qui apparat dans le type des mthodes, ce qui est rigoureusement interdit (voir page ??). Sans la contrainte ajoute le type de self ne peut pas tre sous-type de 'a o_list.

On obtient de cette faon le type attendu pour la mthode cons. Et maintenant que l'on connat le principe, on dfinit la classe des listes vides.

# exception EmptyList ;;
# class ['a] o_nil () =
object(self)
inherit ['a] o_list ()
method empty () = true
method cons x = new o_cons (x, (self : 'a #o_list :> 'a o_list))
method head = raise EmptyList
method tail = raise EmptyList
end ;;


On construit tout d'abord une instance de la liste vide, pour ensuite crer une liste d'entiers.

# let i = new o_nil ();;
val i : '_a o_nil = <obj>
# let l = new o_cons (3,i);;
val l : int o_list = <obj>
# l#head;;
- : int = 3
# l#tail#empty();;
- : bool = true
La dernire expression envoie le message tail sur la liste contenant l'entier 3, qui dclenche la mthode tail de la classe 'a o_cons. Sur ce rsultat (la liste vide i) est ensuite envoy le message empty() qui retourne true. C'est bien la mthode empty de la classe 'a o_nil qui est excute.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora200.html0000644000000000000000000002233407421273602014466 0ustar lments d'valuation Prcdent Index Suivant

lments d'valuation

La distribution d'Objective CAML fournit deux compilateurs en ligne, un engendrant du code-octet et l'autre produisant des instructions pour les processeurs les plus courants. La boucle d'interaction utilise le compilateur de code-octet. En outre, la distribution offre de nombreuses bibliothques sous forme de modules et quelques outils pour le calcul de dpendances entre modules, le profilage et la mise au point. Enfin grce son interface avec le langage C, il est possible de lier des programmes Objective CAML et des programmes C. Les langages offrant galement une interface avec C, comme le JNI (Java Native Interface) de Java deviennent ainsi accessibles.

Le manuel de rfrence donne la syntaxe du langage, dcrit les outils de dveloppement et les signatures des bibliothques. Cet ensemble (langage, outils, bibliothques, documentation) forment un environnement de dveloppement.

Langage

Spcification et implantation

Il y a deux manires d'aborder un nouveau langage. Une premire est de lire la spcification du langage pour avoir une vision globale. Une deuxime est de se plonger dans le guide d'utilisateur du langage en suivant les concepts illustrs par des exemples. Objective CAML ne possde ni l'un ni l'autre, ce qui rend son abord de manire autonome relativement difficile. L'absence de spcification formelle (comme en possde SML) ou descriptive (comme pour ADA) est un handicap pour comprendre le fonctionnement d'un programme. Une autre consquence de l'absence de spcification est l'impossibilit d'obtenir un label de normalisation du langage (ISO, ANSI ou IEEE). Cela limite fortement la construction de nouvelles implantations munies d'autres environnements. Heureusement l'implantation de l'INRIA est de bonne qualit et surtout les sources de la distribution sont disponibles.

Syntaxe

Les particularits syntaxiques sont une difficult non ngligeable pour aborder Objective CAML. Elles s'expliquent par l'origine fonctionnelle du langage, mais aussi par des facteurs historiques, voire anecdotiques.

La syntaxe de l'application d'une fonction ses arguments est dfinie par la simple juxtaposition, comme dans f 1 2. L'absence de parenthse perturbe aussi bien le nophyte que le programmeur C ou le programmeur Lisp confirm. Nanmoins, cette difficult ne vaut vraiment que pour la lecture du code d'un programmeur chiche en parenthses. Rien n'empche le programmeur nophyte en Objective CAML d'utiliser un parenthsage plus explicite et d'crire (f 1 2).

Au del du noyau fonctionnel, Objective CAML adopte une syntaxe rompant parfois avec l'usage : l'accs aux lments d'un tableau utilise la notation t.(i) et non les usuels crochets ; l'invocation de mthode se note avec un dise (caractre #) et non le point, etc. Ce particularisme ne facilite pas la prise en main d'Objective CAML.

Enfin, les syntaxes d'Objective CAML et de son anctre Caml ont subi de nombreuses modifications depuis leurs premires implantations. Ce qui n'a pas plaid en faveur de la prennit des applications dveloppes.

Pour ne pas rester sur une note ngative, la syntaxe de filtrage de motifs, hrite de la famille ML, qu'intgre Objective CAML structure agrablement et avec simplicit les dfinitions de fonctions par cas.

Typage statique

La caractristique fondamentale du langage Objective CAML demeure le typage statique des expressions et dclarations du langage. Il garantit qu'aucune erreur de type ne survient durant l'excution d'un programme. L'infrence statique de type a t conue pour les langages fonctionnels de la famille ML, et Objective CAML a su maintenir la plus grande partie de cette discipline de typage pour les extensions imprative et objet. Cependant, dans le cas de l'emploi d'objets, le programmeur doit parfois pauler l'infrence de types par des contraintes explicites de typage. Mme alors, Objective CAML conserve un typage statique des expressions et des dfinitions, ce qui constitue un gage incontest de sret d'excution : un programme Objective CAML ne retournera pas l'exception << mthode non trouve >>, ce qui n'est pas le cas des langages objets dynamiquement typs.

Le polymorphisme paramtrique des types d'Objective CAML permet l'implantation d'algorithmes gnraux. Il est reconduit dans la couche objet o les classes paramtres produisent un code gnrique, et non une expansion de code comme en engendrent les templates d'autres langages. lui seul, le polymorphisme paramtrique est une composante importante de la rutilisabilit de code.

L'extension objet ajoute une notion de polymorphisme d'inclusion qui est obtenu en spcifiant les relations de sous-typage entre objets. Il concilie la rutilisabilit de code, qui fait la force de la relation d'hritage entre classes, avec la sret du typage statique.

Bibliothques et outils

Les bibliothques fournies avec la distribution couvrent de larges besoins. Le programmeur y retrouve, en standard, l'implantation des structures de donnes les plus usuelles avec leurs fonctions de base. Citons : piles, files d'attente, tables de hachage, AVL. On y trouve galement des outils plus avancs comme le traitement des flots de donnes. Ces bibliothques s'enrichissent au fil des versions du langage.

La bibliothque Unix autorise une programmation de plus bas niveau tant pour les entres-sorties, que pour la gestion des processus. Elle n'est pas identique pour toutes les plate-formes ce qui limite en partie son utilisation.

La bibliothque d'arithmtique exacte et la bibliothque d'expressions rgulires facilitent le dveloppement d'applications spcifiques.

On peut regretter que la bibliothque graphique portable offre peu de fonctionnalits permettant la construction d'interfaces graphiques.

Des bibliothques C permettent un interfaage ais entre ce langage et Objective CAML. Ici, la libre disponibilit de sources bien structures et dment commentes est un facteur d'ouverture certain vers l'extrieur et les bibliothques diverses et varies que l'on peut y trouver.

Parmi les outils fournis, on peut citer plus spcialement ceux d'analyses lexicale et syntaxique indispensables lorsque l'on doit traiter des donnes textuelles complexes. Bass sur les classiques lex et yacc, ils s'intgrent parfaitement aux types somme et la fonctionnalit d'Objective CAML, les rendant ainsi plus simples d'utilisation que leurs ans.

Enfin, quelle que soit la solidit qu'apportent ses traits, l'utilisation d'un langage << en condition relle >> n'vite jamais les phases de mise au point d'un programme. La distribution Objective CAML 2.04 ne fournit pas d'environnement intgr de dveloppement. Certes, l'utilisation de la boucle d'interaction permet de procder rapidement la compilation et aux tests unitaires des fonctions (ce qui est un avantage indniable), mais cette utilisation variera selon les systmes et les programmeurs : copier-coller sous X-Windows, appel de l'interprte de commande sous emacs, demande d'valuation d'un tampon sous Windows. La version suivante (voir annexe B) propose un premier environnement pour Unix contenant un navigateur d'interfaces de modules et un diteur structur li la boucle d'interaction. Enfin le debugger de la distribution reste d'emploi difficile (en particulier, cause de l'aspect fonctionnel et du polymorphisme paramtrique du langage) et limit au systme Unix.

Documentation

La documentation de la distribution est constitue par le manuel de rfrence en version imprimable (PostScript) ou en ligne (HTML). Ce manuel n'est en aucun cas une introduction au langage. Par contre il est indispensable pour connatre la teneur des bibliothques ou les paramtres des commandes. On trouve du matriel pdagogique la page de Caml de l'Inria, mais il porte principalement sur le langage Caml-Light. Il manque donc un manuel d'apprentissage complet du langage, nous esprons que cet ouvrage comble ce manque.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora069.gif0000644000000000000000000000474007073350152014304 0ustar GIF89a UUU99UUrrr!, I8ͻ`(dyhltMǢ&(#[vK$RVj3;bU Tcos۷MB,fzo|laIq{&`is@pU*O)Z(~ϲW%֪\JUXDĸb]PLJحC+KVۻn;p( ~[WmR UA* *+G2 . N̸Е#WD%Oq!gYn\ѕ"y,HS O3Wk%P L,ק]ӤSߦ]"DUkC\*^1Q̟zhcZ7,l@^7>p]5C*F^ [+_oQ#9tgNA:%́2`]6^:U}PtmN6<{otQJ9+G_|_vن E]JX~`Gra%~HSz#~M؞&S؊0>AcyPE"s>1C!JL>ًPH(W2aeqM^n)X&|h)$keҙtc9vhxgs:ho`*|y聀9'epBbWi6*jIU*k*pzk nVxG mL fUJ/V-V߮b=kĸ;֩@K.yISBǯ;z{bMaꯟױ09<H@r |<#+@uPc :ed%E6Ј23;ؘ R莋>QC9 ~u!3@ٞ|M.͒F f.M]RaAEGsߤvx~$N^`*TU0Dq#6.]PI>aJ2;]B궧SIsU~;WJ*uEu=D2X0EO4=L}ǙAقvrh&m=t<~ˋ-a~-<"WJ,f@d)OjAg}#Q 'tBC0h,{.0mb'Wl Cq`)QgTZ Vnc-RnL X{VΕ)Y ΰ]*X =,vJxFV=,[S VDiyZukm{[h v5qx{T&W wUqaFum{S͊e[(nS (w^b1WjB/_S=0)}y#_U!Q0', ʢM/TWc1/I<0X?}1%vwv׻]sv] 9 DFg:&s<$OMG^Qvkf-UV,c Ft2i ؓ֬G`F:g60em֖ #bSfֱuBo'acte*i>\*?KǠR-53BmkcT7i D޵p ع5=[eۚ٥uvMSi-vZa- 6}u{~-o֛ھw%m|{^K/j?eG!T%px55* % >yC .c~6y9БQ7YCrIp UӴF1 ֦R6tMLb2[-k7j78S#9׮FձKͺwٮv.x&y`<)ɫg)3fs^|y.z%Ҍ;ocaml-book-1.0/fr/html/book-ora128.html0000644000000000000000000001374407421273602014504 0ustar Prsentation de la partie III Prcdent Index Suivant

Prsentation de la partie III

La troisime partie de cet ouvrage se consacre la ralisation d'applications et dcrit deux styles d'organisation d'applications : modules et objets. Le but est de modliser facilement l'application pour un dveloppement incrmentiel et rapide, une maintenance facilite par une bonne volutivit et la possibilit de rutiliser de grandes parties pour de futurs dveloppements.

Nous avons dj prsent les modules prdfinis (8) du langage vus comme units de compilation. Le langage de modules d'Objective CAML permet d'une part de dfinir de nouveaux modules simples, pour construire ses propres bibliothques, y compris avec des types abstraits, et d'autre part de dfinir des modules paramtrs par d'autres modules, appels foncteurs. L'intrt de ce paramtrage est de pouvoir << appliquer >> un module sur diffrents modules arguments pour crer des modules spcifiques. La communication entre modules est alors explicite via la signature du module paramtre, contenant le type des dclarations globales de celui-ci. Rien n'empche cependant d'appliquer un foncteur un module de signature plus importante qui reste nanmoins compatible avec celle indique au paramtre.

D'autre part le langage Objective CAML possde une extension objet. La programmation objet permet tout d'abord une modlisation de la communication entre objets. On n'applique plus une fonction ses arguments, mais on envoie un message (une requte) un objet qui saura le traiter. L'objet, instance d'une classe (structure de regroupement donnes/mthodes), excutera alors le code correspondant. La relation principale entre les classes est l'hritage qui permet de dcrire des sous-classes qui possdent dj toutes les dclarations de la classe anctre. La liaison retarde entre le nom d'un message et le code correspondant dans l'objet s'effectue l'excution du programme. Nanmoins le typage d'Objective CAML garantit qu'un objet receveur possdera toujours une mthode de ce nom, sinon l'infrence de types aura provoqu une erreur la compilation. La deuxime relation importante est celle de sous-typage, o un objet d'une certaine classe pourra toujours tre utilis la place d'un objet d'une autre classe. On introduit par l un nouveau type de polymorphisme : le polymorphisme d'inclusion.

Enfin la construction d'interface graphique, aborde au chapitre 5, utilise diffrents modles de gestion d'vnements. On assemble dans une interface plusieurs composants sur lesquels l'utilisateur ou le systme peut produire des vnements. L'association d'un composant et du traitement d'un ou plusieurs vnements se produisant sur celui-ci permet d'ajouter et de modifier facilement de telles interfaces. L'association composant-vnement-traitement peut revtir plusieurs formes : une fonction (dite callback), ou un hritage avec redfinition des mthodes de traitement, ou enfin un enregistrement d'un objet traitant (modle par dlgation).

Le chapitre 14 est une prsentation de la programmation modulaire. Les diffrentes terminologies provenant des types abstraits de donnes et des langages de modules sont exposes et illustres par des modules simples. Le langage des modules est ensuite dtaill . La correspondance entre modules simples ou non et units de compilation est explicite.

Le chapitre 15 contient une introduction la programmation objet. Elle apporte une nouvelle structuration des programmes Objective CAML, alternative aux modules. Ce chapitre montre comment les notions de programmation objet (hritage simple et multiple, classes abstraites, classes paramtres, liaison retarde) s'articulent autour du systme de types du langage et l'tendent par la relation de sous-typage au polymorphisme d'inclusion.

Le chapitre 16 compare les deux organisations prcdentes en prcisant les lments de choix tout en montrant comment simuler l'une par l'autre. Il traite diffrents cas d'organisations mixtes. Le mlange permet d'enrichir chacune des deux organisations en particulier avec les classes paramtres utilisant un type abstrait d'un module.

Le chapitre 17 prsente deux applications : des jeux deux joueurs et la construction d'un monde de robots virtuels. Le premier exemple est organis en plusieurs modules paramtrs. On y utilise en particulier un module paramtr par la reprsentation d'un jeu pour l'algorithme minimax ab. Il est ensuite appliqu deux jeux : Puissance 4 et Stone Henge. Le deuxime exemple utilise une modlisation objet d'un monde et d'un robot abstrait dont on drive, par hritage, plusieurs simulations. Cet exemple sera repris au chapitre 21.




Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora118.html0000644000000000000000000002106507421273602014476 0ustar Gestion des exceptions en C et en Objective CAML Prcdent Index Suivant

Gestion des exceptions en C et en Objective CAML

Les mcanismes de rupture et reprise de calcul varient selon les langages : C utilise le couple setjmp-longjmp) et les exceptions Objective CAML, la construction (try .. with, raise). Ces mcanismes ne sont, bien sr, pas compatibles. C'est--dire qu'ils ne conservent pas les mmes informations la pose d'un rcuprateur. Le fait de pouvoir imbriquer des rcuprateurs d'exceptions de nature diffrente et d'en outrepasser certains pose de relles difficults d'implantation pour garantir la sret de leur utilisation. C'est pour cela que seules les exceptions Objective CAML sont traites. Nanmoins rien n'empche de les dclencher ou de les rcuprer en C.

Toutes les macros et les fonctions utilises dans cette partie sont dfinies dans le fichier fail.h.

Dclencher une exception prdfinie

Il est facilement possible de dclencher, depuis une fonction C, les exceptions Failure ou Invalid_argument du module Pervasives en utilisant les fonctions suivantes :
failwith(s) : dclenche l'exception Failure(s)
invalid_argument(s) : dclenche l'exception Invalid_argument(s).
Dans les deux cas, s est une chane de caractres C (char *).

Dclencher une exception quelconque

Le mcanisme pour lever une exception quelconque dfinie en Objective CAML est semblable celui utilis pour appliquer une fermeture. Il faut dans un premier temps enregistrer l'exception par la fonction register_exception du module Callback. On rcupre ensuite la valeur avec la fonction caml_named_value (voir page ??). On dclenche l'exception avec l'une des fonctions suivantes :
raise_constant(e) lve l'exception e,
raise_with_arg(e,v) lve l'exception e avec v pour argument,
raise_with_string(e,s) idem, mais la valeur est une copie de la chane s.

#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/fail.h>

value division (value v1,value v2)
{
CAMLparam2(v1,v2);
if (!Long_val(v2))
raise_with_arg(*caml_named_value("exception"),v1) ;
CAMLreturn Val_long(Long_val(v1)/Long_val(v2)) ;
}

# external divise : int -> int -> int = "division" ;;
external divise : int -> int -> int = "division"
# exception Division_par_zero of int ;;
exception Division_par_zero of int
# Callback.register_exception "exception" (Division_par_zero 0) ;;
- : unit = ()
# divise 20 4 ;;
- : int = 5
# divise 22 0 ;;
Uncaught exception: Division_par_zero(22)


Rattraper une exception

Il est possible de rattraper une exception depuis une fonction C, mais seulement si elle a t dclenche par l'application d'une fermeture venue d'Objective CAML et non directement depuis une fonction C. Les macros callback_exn, callback2_exn, callback3_exn et callbackN_exn fonctionnent comme les macros callback simples, sauf que si l'application a dclench une exception, celle-ci est rendue comme rsultat. Le rsultat d'un callback_exn peut tre test par le prdicat Is_exception_result(v) qui retourne 1 si la valeur provient du dclenchement d'une exception et 0 sinon. On retrouve le constructeur de l'exception par appel la fonction Extract_exception(v).

La fonction C divise_affiche appelle une fonction divise et teste si le retour est une exception, en utilisant l'appel callback2_exn. Si c'est le cas, elle affiche une trace et la redclenche, sinon elle affiche le rsultat.

#include <stdio.h>
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/callback.h>
#include <caml/fail.h>

value divise_affiche (value v1,value v2)
{
CAMLparam2(v1,v2) ;
CAMLlocal3(div,dbz,res) ;
div = * caml_named_value("divise") ;
dbz = * caml_named_value("div_by_0") ;
res = callback2_exn (div,v1,v2) ;
if (Is_exception_result(res))
{
value exn=Extract_exception(res);
if (Field(exn,0)==dbz) printf("division par 0\n") ;
else printf("autre exception\n");
fflush(stdout);
if (Wosize_val(exn)==1) raise_constant(Field(exn,0)) ;
else raise_with_arg(Field(exn,0),Field(exn,1)) ;
}
printf("resultat = %d\n",Long_val(res)) ;
fflush(stdout) ;
CAMLreturn Val_unit ;
}

# Callback.register "divise" (/) ;;
- : unit = ()
# Callback.register_exception "div_by_0" Division_by_zero ;;
- : unit = ()
# external divise_ml : int -> int -> unit = "divise_affiche" ;;
external divise_ml : int -> int -> unit = "divise_affiche"
# divise_ml 42 3 ;;
resultat = 14
- : unit = ()
# divise_ml 21 0 ;;
division par 0
Uncaught exception: Division_by_zero
Il est donc possible de dclencher une exception en C et de la rattraper en Objective CAML; de mme, il est possible de dclencher une exception depuis Objective CAML et la rattraper en C. En revanche, un programme C ne peut pas lui seul dclencher et rattraper une exception d'Objective CAML.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora013.gif0000644000000000000000000000152707073350152014271 0ustar GIF89akPPP!,kH0IXͻ`(di(@p,tmxpH,hl:2dF,mJ%`_xv/4y;ϳ|NE^ op2z{Ui|j.},y~ƿ˲ɶЫRը`ڞ؜ߔݯu U?9ǰÇ Gٵ3j|a%H$Et"?Bj F0~iגJ0 N6kaR%(O5?4e&w,zUhvZ:"X9{i١iRfu۪[5Ww]yuUjYXz"k-PKl \ń?+$޸v)82hgl4c-hgv=9ڥ|snϻ6`lq5?fbtՏ_.(f6=-?|~[|ñV%nH #dG܁%_Far 6zXٱ`%2Q5@bو!bfxa2x# x^M>G$CGy8$A!VNZ$]Fe}#e&i*& g.r静xfv&;ocaml-book-1.0/fr/html/book-ora082.gif0000644000000000000000000000461407073350152014277 0ustar GIF89a>UUU999!,>H0I8ͻ`(`hlp tmJ/H lJΨ72lMr7 FҬn_n$0Htf h?9+9)R٪` BvͱqF7?(SJor[ҹڕ7o) rz =;Ì̓8[*[͐' ZGK_&lG ?AdUm$#9\6973t޿s˒0qC.3Ŏ =~zEB~1Q×~}H|wG %~wz~a 5*f Fa (bz@އ\v8*bm.:/Q8#NaAQs1 A9%-Oơ2֑G'IFd[fMZDTPʹM'ݔ @d&%u(B5@>N򉥟X&)X%HV)Tu娢<(*]U)^Ք'YQc/zUY4 I`rT'3g^YlqeL:;jlKh%EHPڹmj{*d ESM;S+nxuGIHahnPn{k|P㫥Ɣ"{mlI2ewJ1Rl+.ys,7ވfr@`˞Bz܁U}״&T7wrf)W-Ws^uc'4kWG7gtHx|[yw ^+߻ߞ919kِˁy/7^^\g itNWNCTz뮳j.c׶Cgꕿ{Kğ~ꯏv 3/8Ͽis+?=(`q$ rƂr+碸o G9 I5-^;^HMg|.apÝm;8D!IH|W5Vړ&.Ɛ}aE-֔F1ЊՋɆjW&U&=bdX{PeMx "UZ(%U䨳?i%j-ˈG!gb'IF I8 \be.T1$rɅK^,@)ČFi/D|_gGB_`71Yh6ۣ\vaT9_&N(C0,ydʦO==iO#N u@yD FWHŽz e5()J_R6Tiz)d O ,)E H4 p?gPE'žF@`RG*T6D QVԥokU՛v}X$T'կ/p*Y!VZ+WUլk+$j׳|zi\ֿv/?Cl UScuXP&`eSnv! hGK>cjW;7s.qwʨL?(^eڇ– p;*kpc;2⌎9֒{o%'3[措B"t (X[7i(2¥jD zKmJ=q„'MjS}Q%ۚRWGzHuZoVgbק"f ,хh5oE+Dw"/ H񎭘 @7G6Yr],]QU2{Mx(8*E^ov`5)*1`.]eHyrV+0L49\b< ߌ<9<;Kny5":9́e]y'y~.mXxj-&1Wj թgsC9șrkc;~ol;l֠.}iXHF5yMϮʝ0 nL^umr_7]_3{U\:.p12ݣ7v]m;{f|;LKvV7׍O|,%S U|/:iM_Q4N$Qny@nZ#^{{1j\EGz1wONk;^m?)w ^!/=EЎ6AGSdFnڧvUF{ذ}F;ocaml-book-1.0/fr/html/book-ora216.html0000644000000000000000000002637307421273603014505 0ustar Bibliographie Prcdent Index

Bibliographie

[AC96]
Aponte (Mara-Virginia) et Castagna (Giuseppe). -- Programmation modulaire avec surcharge et liaison tardive. In : Journes Francophones des Langages Applicatifs. INRIA. -- janvier 1996.

[AHU85]
Aho (Alfred), Hopcroft (John) et Ullman (Jeffrey). -- Structures de donnes et algorithmes. -- InterEditions, 1985.

[And91]
Andrews (G.). -- Concurrent Programming : Principles and practices. -- Benjamin Cumming, 1991.

[Ari86]
Ari (Ben). -- Processus concurrents. -- Masson, 1986.

[AS89]
Abelson (Harold) et Sussman (Gerald J.). -- Structures et interprtation des programmes informatiques. -- InterEditions, 1989.

[ASU89]
Aho (Alfred), Sethi (Ravi) et Ullman (Jeffrey). -- Compilateurs : principes, techniques et outils. -- InterEditions, 1989.

[CCM87]
Cousineau (Guy), Curien (Pierre-Louis) et Mauny (Michel). -- The Categorical Abstract Machine. Science of Computer Programming, vol. 8, 1987, pp. 173--202.

[CCS96]
Chailloux (Emmanuel), Cousineau (Guy) et Surez (Ascnder). -- Programmation fonctionnelle de graphismes pour la production d'illustrations techniques. Technique et Science Informatiques, 1996.

[CDM96]
Card (Rmy), Dumas (ric) et Mvel (Franck). -- Programmation Linux 2.0, API systme et fonctionnement du noyau. -- Eyrolles, 1996, seconde dition.

[Ciz98]
Cizault (Gisle). -- IPv6 : Thorie et pratique. -- O'Reilly, 1998.

[CKL96]
Chailloux (Emmanuel), Kirsch (Laurent) et Lucas (Stphane). -- Caml2sml, un outil d'aide la traduction de Caml vers Sml. In : Journes Francophones des Langages Applicatifs. INRIA. -- janvier 1996.

[CL99]
Conchon (Sylvain) et Le Fessant (Fabrice). -- JoCaml: mobile agents for Objective-Caml. In : International Symposium on Agent Systems and Applications. -- 1999.

[CM95]
Cousineau (Guy) et Mauny (Michel). -- Approche Fonctionnelle de la Programmation. -- EdiScience, 1995.

[CP95]
Caspi (Paul) et Pouzet (Marc). -- A functional extension to lustre. In : 8th International Symposium on Languages for Intensional Programming. World Scientific. -- Sydney, mai 1995.

[DDLP98]
Danelutto (Marco), Di Cosmo (Roberto), Leroy (Xavier) et Pelagatti (Susanna). -- Parallel functional programming with skeletons: the ocamlp3l experiment. In : ML Workshop. ACM SIGPLAN. -- 1998.

[DEMN98]
Ducournau (Roland), Euzenat (Jrme), Masini (Grald) et Napoli (Amedeo) (dit par). -- Langages et modles objets: tat et perspectives de la recherche. -- INRIA, 1998.

[Eng98]
Engel (Emmanuel). -- Extensions sres et praticables du systme de types de ML en prsence d'un langage de modules et de traits impratifs. -- Thse de doctorat, Universit Paris-Sud, Orsay, France, mai 1998.

[FC95]
Foisy (Christian) et Chailloux (Emmanuel). -- Caml Flight: a Portable SPMD Extension of ML for Distributed Memory Multiprocessors. In : Conference on High Performance Functional Computing. -- avril 1995.

[FF98]
Findler (Robert B.) et Flatt (Matthew). -- Modular Object-Oriented Programming with Units and Mixins. In : International Conference on Functional Programming. ACM. -- 1998.

[FGS90]
Froidevaux (Christine), Gaudel (Marie-Claude) et Soria (Michle). -- Types de donnes et algorithmes. -- Paris, EdiSciences, 1990.

[FW00]
Furuse (Jun) et Weis (Pierre). -- Entres/Sorties de valeurs en Caml. In : JFLA'2000 : Journes Francophones des Langages Applicatifs. INRIA. -- Mont Saint-Michel, janvier 2000.

[GHJV98]
Gamma (Erich), Helm (Richard), Johnson (Ralph) et Vlissides (John). -- Design Pattern : catalogue de modles de conception rutilisables. -- MIT, 1998.

[HFa96]
Hartel (Pieter), Feeley (Marc) et al. -- Benchmarking implementations of functional languages with ``Pseudoknot'', a float-intensive benchmark. Journal of Functional Programming, vol. 6, n 4, 1996.

[HS90]
Harbison (Samuel P.) et Steele (Guy L.). -- Langage C : manuel de rfrence. -- Paris, Masson, 1990, seconde dition.

[Jon98]
Jones (Richard). -- Garbage Collection : Algorithms for Automatic Dynamic Memory Management. -- John Wiley & Sons, 1998.

[Ler90]
Leroy (Xavier). -- The ZINC experiment: an economical implementation of the ML language. -- Technical report n 117, INRIA, 1990.

[Ler92]
Leroy (Xavier). -- Programmation du systme unix en caml light. -- Rapport technique n RR-147, INRIA, dcembre 1992.

[LMB92]
Levine (John R.), Mason (Tony) et Brown (Doug). -- lex & yacc. -- O'Reilly, 1992, seconde dition.

[Lou98]
Loulergue (Frdric). -- BSML : programmation BSP purement fonctionnelle. In : RENPAR'10, Rencontres francophones du paralllisme. Universit de Strasbourg. -- Juin 1998.

[LRVD99]
Leroy (Xavier), Rmy (Didier), Vouillon (Jrme) et Doligez (Damien). -- The Objective Caml system release 2.04. -- Rapport technique, INRIA, dcembre 1999.

[MdR92]
Mauny (Michel) et de Rauglaudre (Daniel). -- Parser in ML. -- Rapport technique n RR-1659, INRIA, avril 1992.

[MNC+89]
Masini (Grald), Napoli (Amedeo), Colnet (Dominique), Lonard (Daniel) et Tombre (Karl). -- Les langages objets. -- Paris, InterEditions, 1989.

[MT91]
Milner (Robin) et Tofte (Mads). -- Commentary on Standard ML. -- MIT, 1991.

[MTH90]
Milner (Robin), Tofte (Mads) et Harper (Robert). -- The Definition of Standard ML. -- MIT, 1990.

[Rep92]
Reppy (John). -- Higher-Order Concurrency. -- Thse de PhD, Cornell University, juin 1992.

[Rif90]
Rifflet (Jean-Marie). -- La communication sous Unix. -- EdiSciences, 1990.

[Rob89]
Robert (Eric S.). -- Implementing exceptions in C. -- Rapport technique n SRC-40, Digital Equipment, 1989.

[Rou96]
Rouaix (Franois). -- A Web navigator with applets in Caml. In : Proceedings of the 5th International World Wide Web Conference, in Computer Networks and Telecommunications Networking. pp. 1365--1371. -- Elsevier, May 1996.

[RV98]
Rmy (Didier) et Vouillon (Jrme). -- Objective ML: An effective object-oriented extension to ML. Theory And Practice of Object Systems, vol. 4, n 1, 1998, pp. 27--50. -- A preliminary version appeared in the proceedings of the 24th ACM Conference on Principles of Programming Languages, 1997.

[Spi90]
Spir (Eric). -- Gestion Dynamique de la Mmoire dans les Langages de Programmation. -- InterEdition, 1990.

[Tho99]
Thompson (Simon). -- Haskell: The Craft of Functional Programming. -- Addison Wesley, 1999, seconde dition.

[Tur85]
Turner (David A.). -- Miranda: A non-strict functional language with polymorphic types. In : Proceedings International Conference on Functional Programming Languages and Computer Architecture, d. par Jouannaud (J.). pp. 1--16. -- New York, NY, septembre 1985.

[Wil92]
Wilson (Paul. R.). -- Uniprocessors Garbage Collection Techniques. In : International Workshop on Memory Management, d. par Springer-Verlag. ACM SIGPLAN, pp. 1--42. -- septembre 1992.

[Wri93]
Wright (Andrew K.). -- Polymorphism for Imperative Languages without Imperative Types. -- Rapport technique n 93-200, Rice University, fvrier 1993.

Prcdent Index ocaml-book-1.0/fr/html/book-ora004.html0000644000000000000000000001453507421273601014473 0ustar Plan de l'ouvrage Prcdent Index Suivant

Plan de l'ouvrage

Le prsent ouvrage est constitu de quatre parties principales, entoures de deux chapitres et agrment de deux annexes, d'une bibliographie, d'un index des lments du langage et d'un index des concepts de programmation.

Chapitre 1 :
Ce chapitre est une rapide description de l'installation de la version 2.04 du langage Objective CAML pour les systmes les plus courants (Windows, Unix et MacOS).

Partie I : Noyau du langage
La premire partie est une prsentation complte des lments de base du langage Objective CAML. Le chapitre 2 est une plonge dans le noyau fonctionnel du langage. Le chapitre 3 est le pendant du prcdent et dcrit la partie imprative du langage. Le chapitre 4 compare les styles fonctionnel et impratif << purs >> puis prsente leur utilisation conjointe. Le chapitre 5 prsente la bibliothque graphique. Le chapitre 6 expose trois applications : gestion d'une base de donnes simple, un interprte d'un mini-Basic et un jeu solitaire bien connu, le dmineur.
Partie II : Outils de dveloppement
La deuxime partie de l'ouvrage dcrit les diffrents outils pour la ralisation d'applications. Le chapitre 7 compare les diffrents modes de compilation que sont la boucle d'interaction et les compilateurs en ligne de commande de code-octet et natif. Le chapitre 8 prsente les principales bibliothques fournies avec la distribution du langage. Le chapitre 9 explique les mcanismes de rcupration automatique de mmoire et dtaille celui utilis par Objective CAML. Le chapitre 10 explique l'utilisation des outils de mise au point et d'analyse de programmes. Le chapitre 11 s'intresse aux outils d'analyses lexicale et syntaxique. Le chapitre 12 montre comment interfacer des programmes Objective CAML avec C. Le chapitre 13 construit une bibliothque et une application. Cette bibliothque offre des outils pour la construction d'interfaces graphiques. L'application est une recherche de chemins de moindre cot dans un graphe, son interface graphique utilise la bibliothque prcdente.
Partie III : Organisation d'applications
La troisime partie dcrit les deux organisations d'un programme que permettent les modules et les objets. Le chapitre 14 est une prsentation des modules simples et paramtrs du langage. Le chapitre 15 introduit l'extension objet d'Objective CAML. Le chapitre 16 compare ces deux types d'organisation et indique l'intrt de leur mlange pour augmenter l'extensibilit des programmes. Le chapitre17 dcrit deux applications consquentes : les jeux deux joueurs qui mettent en oeuvre plusieurs modules paramtrs utiliss pour deux jeux diffrents, et une simulation d'un monde de robots montrant la communication entre objets.
Partie IV : Concurrence et rpartition
La quatrime partie introduit les programmations concurrente et rpartie en dtaillant la communication entre processus, lgers ou non, et sur le rseau Internet. Le chapitre 18 montre le lien direct entre le langage et les bibliothques systme, en particulier les notions de processus et de communication. Le chapitre 19 amne l'indterminisme de la programmation concurrente en prsentant les processus lgers d'Objective CAML. Le chapitre 20 discute de la communication entre processus, via les prises de communication rseau (sockets), dans le modle de mmoire rpartie. Le chapitre 21 prsente tout d'abord une bote outils pour applications client-serveur. Elle est utilise ensuite pour tendre les robots de la partie prcdente au modle client-serveur. Enfin, nous adaptons certains programmes dj rencontrs sous forme de serveur HTTP.
Chapitre 22
Ce dernier chapitre fait le point sur le dveloppement d'applications en Objective CAML et prsente les applications les plus connues des langages de la famille ML.
Annexes
La premire annexe explique la notion de types cycliques utilise dans le typage des objets. La deuxime annexe dcrit les volutions du langage prsentes dans la version exprimentale 2.99. Celles-ci seront probablement intgres dans les prochaines versions d'Objective CAML (3.xx).
Chaque chapitre comprend une prsentation gnrale du thme abord, un plan du chapitre, les diffrentes sections de celui-ci, des noncs d'exercices raliser, un rsum et une ultime section intitule << Pour en savoir plus >> qui indique les rfrences bibliographiques du thme abord.










Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora060.html0000644000000000000000000026035207421273601014475 0ustar Dmineur Prcdent Index Suivant

Dmineur

Rappelons brivement les principes du jeu : explorer un champ de mines sans marcher sur l'une d'elles.
Un champ de mines est un tableau deux dimensions dont quelques cases contiennent des mines caches alors que les autres sont vides. En dbut de partie toutes les cases sont recouvertes et le joueur doit les dcouvrir l'une aprs l'autre. Le joueur est victorieux quand il a russi dcouvrir toutes les cases ne contenant pas de mines.

chaque tape du jeu, le joueur peut au choix ouvrir une case, ou la marquer comme mine. S'il ouvre une case et que celle-ci contient une mine, il saute et perd la partie. Sinon, la case change d'aspect et s'y inscrit le nombre des cases adjacentes qui sont mines (donc au maximum 8). S'il choisit de marquer la case, il ne peut plus ensuite l'ouvrir sans avoir au pralable retir la marque.



Figure 6.5 : Copie d'cran


Nous dcomposons l'implantation de ce jeu en trois parties.
  1. La description d'un jeu abstrait comprenant la reprsentation interne du champ de mines et les fonctions manipulant cette reprsentation.
  2. La description graphique du jeu avec ses fonctions propres pour le rendu de l'affichage des cases.
  3. Le moteur grant les interactions entre le programme et l'utilisateur.

Le champ de mines abstrait

Cette partie ne s'intresse qu'au champ de mines considr comme une pure abstraction, nous ne nous occupons pas de son affichage.



Configuration
Un champ de mines est caractris par ses dimensions et le nombre de mines qu'il contient. Nous regroupons ces trois paramtres dans un enregistrement et dfinissons une configuration par dfaut : 1010 cases et 15 mines.

# type config = {
nbcols : int ;
nbrows : int ;
nbmines : int };;
# let default_config = { nbcols=10; nbrows=10; nbmines=15 } ;;


Le champ de mines
Il apparat naturel qu'un champ de mines soit cod par un tableau deux dimensions. Il nous faut encore prciser ce que sont ces cases en dterminant les informations qu'il est ncessaire de connatre pour chacune d'elle. L'tat d'une case peut tre :
  • la case est-elle mine ?
  • la case a-t-elle t dcouverte ?
  • la case a-t-elle t marque ?
  • combien y a-t-il de cases mines parmi les cases adjacentes ?
Cette dernire information n'est pas indispensable puisqu'il est possible de la calculer au moment opportun, mais il est plus simple de faire ce calcul une fois pour toutes en dbut de partie.

Nous reprsentons une case par un enregistrement contenant ces quatre informations.

# type cell = {
mutable mined : bool ;
mutable seen : bool ;
mutable flag : bool ;
mutable nbm : int
} ;;
Le tableau deux dimensions du champ de mine est un tableau de tableaux de cases :

# type board = cell array array ;;


Un itrateur
Dans la suite du programme nous aurons de multiples occasions besoin d'appliquer une fonction chacune des cases du champ de mines. Pour le faire gnriquement, nous dfinissons l'itrateur iter_on_cell appliquant la fonction f donne en argument chaque case du tableau dfini par la configuration cf.

# let iter_on_cell cf f =
for i=0 to cf.nbcols-1 do for j=0 to cf.nbrows-1 do f (i,j) done done ;;
val iter_on_cell : config -> (int * int -> 'a) -> unit = <fun>


Nous avons ici un bon exemple du mlange des styles fonctionnel et impratif, puisque pour itrer une fonction oprant par effet de bord (puisque sans rsultat) sur toutes les coordonnes d'un tableau nous utilisons une fonctionnelle (une fonction prenant en argument une autre fonction).

Initialisation
Nous dterminons alatoirement l'ensemble des cases mines. Si l et c sont respectivement le nombre de lignes et le nombre de colonnes du champ de mines et m le nombre de mines, il faut engendrer une liste de m nombres diffrents compris entre 1 et l c. On supposera que m l c pour dfinir l'algorithme, mais le programme devra explicitement vrifier cette condition.

L'ide la plus naturelle et la plus simple est de commencer avec une liste vide, de tirer un nombre au hasard, de l'introduire dans la liste s'il n'y apparat pas dj; et de recommencer jusqu' ce que la liste contienne m nombres. Pour cela nous utiliserons les fonctions suivantes prises dans les modules Random et Sys :
  • Random.int : int -> int qui tire selon un gnrateur de nombres alatoires un nombre compris entre 0 et n-1 si n est l'argument pass ;
  • Random.init : int -> unit, qui initialise le gnrateur de nombres alatoires ;
  • Sys.time : unit -> float, qui retourne le nombre de millisecondes de temps processeur utilis depuis le dbut de l'excution du programme. Cette fonction sera utilise pour initialiser le gnrateur de nombres alatoires de manire diffrente chaque partie.
Les modules auxquels appartiennent ces fonctions sont dtaills au chapitre 8, pages ?? et ??.

La fonction de tirage alatoire des mines prend en argument le nombre de cases (lc) ainsi que le nombre de mines tirer (m) et retourne une liste de positions linarises des m mines.

# let random_list_mines lc m =
let cell_list = ref []
in while (List.length !cell_list) < m do
let n = Random.int lc in
if not (List.mem n !cell_list) then cell_list := n :: !cell_list
done ;
!cell_list ;;
val random_list_mines : int -> int -> int list = <fun>


Telle qu'elle est crite, nous ne sommes pas capable de dire en combien d'tapes cette fonction se termine. Si notre gnrateur alatoire est fiable, nous pouvons uniquement assurer que la probabilit qu'elle ne termine pas est nulle. Ce qui nous donne la proposition paradoxale : << la fonction termine si elle tourne infiniment >>.
Cependant, la pratique n'a jamais mis en dfaut notre fonction. Aussi nous contenterons nous de cette dfinition incertaine pour engendrer la liste des cases mines.

Il nous faut initialiser le gnrateur de nombres alatoires pour que chaque excution du jeu ne se fasse pas avec le mme champ de mines. On utilise le temps processeur depuis le dbut de l'excution du programme pour initialiser le gnrateur de nombres alatoires.

# let generate_seed () =
let t = Sys.time () in
let n = int_of_float (t*.1000.0)
in Random.init(n mod 100000) ;;
val generate_seed : unit -> unit = <fun>
Dans la pratique on s'aperoit qu'un mme programme prendra souvent le mme temps d'excution ce qui entrane un tirage unique de la fonction generate_seed. On utilisera de prfrence la fonction Unix.time (voir chapitre 18).

Aussi bien lors de l'initialisation du champ de mines que lors du jeu, nous aurons besoin de connatre l'ensemble des cases adjacentes une case donne (fonction neighbours). L'numration de cet ensemble doit prendre en compte les cases des bords qui ont moins de voisins que celles du milieu du jeu (fonction valid).

# let valid cf (i,j) = i>=0 && i<cf.nbcols && j>=0 && j<cf.nbrows ;;
val valid : config -> int * int -> bool = <fun>
# let neighbours cf (x,y) =
let ngb = [x-1,y-1; x-1,y; x-1,y+1; x,y-1; x,y+1; x+1,y-1; x+1,y; x+1,y+1]
in List.filter (valid cf) ngb ;;
val neighbours : config -> int * int -> (int * int) list = <fun>
La fonction initialize_board cre le champ de mines initial. Elle procde en quatre tapes :
  1. gnration de la liste des cases mines;
  2. cration d'un tableau deux dimensions contenant des cellules diffrentes;
  3. marquage des cases mines;
  4. dcompte des cases mines adjacentes chaque case non mine.
La fonction initialize_board utilise un certain nombre de fonctions locales que nous dcrivons brivement.
cell_init
: cre une valeur initiale ;
copy_cell_init
: affecte une case du tableau avec une nouvelle copie de la valeur initiale ;
set_mined
: dpose une mine dans une case ;
count_mined_adj
: compte le nombre de cases mines adjacentes une case donne ;
set_count
met jour le nombre de cases mines adjacentes une case non mine.

# let initialize_board cf =
let cell_init () = { mined=false; seen=false; flag=false; nbm=0 } in
let copy_cell_init b (i,j) = b.(i).(j) <- cell_init() in
let set_mined b n = b.(n / cf.nbrows).(n mod cf.nbrows).mined <- true
in
let count_mined_adj b (i,j) =
let x = ref 0 in
let inc_if_mined (i,j) = if b.(i).(j).mined then incr x
in List.iter inc_if_mined (neighbours cf (i,j)) ;
!x
in
let set_count b (i,j) =
if not b.(i).(j).mined
then b.(i).(j).nbm <- count_mined_adj b (i,j)
in
let list_mined = random_list_mines (cf.nbcols*cf.nbrows) cf.nbmines in
let board = Array.make_matrix cf.nbcols cf.nbrows (cell_init ())
in iter_on_cell cf (copy_cell_init board) ;
List.iter (set_mined board) list_mined ;
iter_on_cell cf (set_count board) ;
board ;;
val initialize_board : config -> cell array array = <fun>


Dcouverte
Durant une partie, lorsqu'un joueur dcouvre une case dont aucun voisin n'est min, il sait qu'il peut dcouvrir toutes les cases adjacentes sans risque, et ainsi de suite tant qu'il dcouvre d'autres cases sans voisin min. Afin de soulager le joueur de cette tape fastidieuse (phase du jeu n'entranant pas la moindre rflexion), notre Dmineur dcouvre lui-mme toutes ces cases. Pour ce faire, nous crivons la fonction cells_to_see qui renvoie la liste de toutes les cases dcouvrir quand une case est ouverte.

L'algorithme est simple exprimer : si la case dcouverte a des voisins mins alors la liste se rsume cette unique case; sinon, c'est l'ensemble de ses voisins plus les listes des cases dcouvrir quand on dcouvre chacun des voisins. La difficult est d'crire un programme qui ne boucle pas car bien videment une case est un voisin de son voisin. Il faut donc faire en sorte de ne pas examiner plusieurs fois les mmes cases.
Pour garder une trace des cases examines, on utilise le tableau de boolens visited. Il est de mme dimension que le champ de mines, on y marque avec la valeur true qu'une case a dj t examine. On applique rcursivement la recherche des cases dcouvrir uniquement sur les cases non marques.

On utilise la fonction auxiliaire relevant qui calcule, partir de la liste des voisins d'une case deux sous-listes. Chacune de ces sous-listes ne contient que des voisins ni mins, ni dj dcouverts, ni marqus par le joueur et ni dj visits. La premire sous-liste contient les voisins ayant au moins un voisin min ; la seconde contient les voisins n'ayant pas de voisin min. On en profite pour marquer comme visites toutes ces cases. Remarquons que nous excluons les cases marques par le joueur mme si elles ne sont pas mines, car le sens de cette marque est d'interdire la dcouverte de la case.

La fonction locale cells_to_see_rec ralise la boucle rcursive de recherche. Elle construit la liste des cases dcouvrir et prend en argument la liste des cases examiner qui est maintenue jour. On dmarre avec la liste contenant uniquement la dernire case dcouverte aprs l'avoir marque comme visite.

# let cells_to_see bd cf (i,j) =
let visited = Array.make_matrix cf.nbcols cf.nbrows false in
let rec relevant = function
[] -> ([],[])
| ((x,y) as c)::l ->
let cell=bd.(x).(y)
in if cell.mined || cell.flag || cell.seen || visited.(x).(y)
then relevant l
else let (l1,l2) = relevant l
in visited.(x).(y) <- true ;
if cell.nbm=0 then (l1,c::l2) else (c::l1,l2)
in
let rec cells_to_see_rec = function
[] -> []
| ((x,y) as c)::l ->
if bd.(x).(y).nbm<>0 then c :: (cells_to_see_rec l)
else let (l1,l2) = relevant (neighbours cf c)
in (c :: l1) @ (cells_to_see_rec (l2 @ l))
in visited.(i).(j) <- true ;
cells_to_see_rec [(i,j)] ;;
val cells_to_see :
cell array array -> config -> int * int -> (int * int) list = <fun>


premire vue, l'argument de cells_to_seen_rec peut crotre entre deux appels alors que la rcurrence se fait sur cet argument. On peut donc se demander si cette fonction termine toujours ?
L'utilisation du tableau visited nous garantit qu'une case dj marque ne peut tre dans le rsultat de relevant. Or les cases venant augmenter la liste des cases examiner proviennent toutes de relevant. De plus, cette fonction marque toutes les cases qu'elle renvoie. Ceci nous garantit qu'une case ne pourra tre renvoye qu'une seule fois par relevant donc n'tre qu'une seule fois dans la liste des cases examiner. Le nombre de cases possibles tant fini, il en dcoule que la fonction termine.

Ceci clt la partie hors affichage du Dmineur, profitons-en pour faire un point sur le style de programmation que nous avons adopt. Le choix de structures modifiables (tableaux et champs mutables) nous a impos un style impratif utilisant des boucles et des affectations. Pourtant, pour traiter les problmes auxiliaires nous avons fait usage de listes et les fonctions de traitement adoptent un style fonctionnel. En fait, le style de programmation nous a t dict par les structures de donnes que nous souhaitions manipuler. Le cas de la fonction cells_to_seen est symptomatique de cela : c'est une fonction manipulant des listes, et il est apparu naturel d'adopter un style fonctionnel. Pourtant, nous utilisons un tableau pour identifier les cases dj traites et nous drogeons la fonctionnalit pour maintenir ce tableau. Nous aurions pu dans un pur style fonctionnel maintenir une liste des cases traites et vrifier si une case apparaissait ou non dans cette liste. Mais le cot de cette mthode aurait t important (la recherche de la prsence d'un lment est linaire suivant la taille de la liste alors qu'avec un tableau elle est constante), et elle ne simplifiait pas le programme.

Affichage du jeu Dmineur

Cette partie est dpendante des structures de donnes reprsentant l'tat du jeu (voir page ??). Il s'agit principalement d'afficher les diffrents composants de la fentre du Dmineur suivant la figure 6.6. On utilise pour cet affichage les fonctions sur les botes vues page ??.



Figure 6.6 : La fentre principale du Dmineur


Les paramtres suivants dfinissent les caractristiques des diffrents composants de la fentre graphique.


# let b0 = 3 ;;
# let l1 = 15 ;;
# let l2 = l1 ;;
# let l4 = 20 + 2*b0 ;;
# let l3 = l4*default_config.nbcols + 2*b0 ;;
# let l5 = 40 + 2*b0 ;;
 

# let h1 = l1 ;;
# let h2 = 30 ;;
# let h3 = l5+20 + 2*b0 ;;
# let h4 = h2 ;;
# let h5 = 20 + 2*b0 ;;
# let h6 = l5 + 2*b0 ;;
 

Nous les utilisons pour tendre la configuration de base du dmineur (valeur de type config). Nous dfinissons l'enregistrement window_config ci-dessous. Le champ cf contient la configuration minimale. On associe une bote chacun des composants de l'affichage : fentre globale (champ g_bcf), champ de mines (champ f_bcf), fentre de dialogue (champ m_bcf) avec deux sous botes (champs m1_bcf et m2_bcf), bouton de marquage (champ s_bcf), case courante (champ cc_bcf).

# type window_config = {
cf : config ;
g_bcf : box_config ;
f_bcf : box_config ;
m_bcf : box_config ;
m1_bcf : box_config ;
m2_bcf : box_config ;
s_bcf : box_config ;
mutable cc_bcf : box_config ;
cell : int*int -> (int*int) ;
coor : int*int -> (int*int)
} ;;


De plus, une valeur de type window_config fournit deux fonctions :
  • cell : qui prend les coordonnes d'une case et rend les coordonnes de la bote correspondante.
  • coor : qui prend les coordonnes d'un pixel de la fentre et rend les coordonnes de la case correspondante.
Configuration
On dfinit ensuite une fonction construisant une configuration graphique (de type window_config) en fonction d'une configuration minimale (de type config) et des paramtres dfinis plus haut. Les valeurs des paramtres de certains composants dpendent les uns des autres. Par exemple, la largeur de la bote globale dpend de la largeur du champ de mines qui dpend son tour du nombre de colonnes. Pour ne pas recalculer plusieurs fois une mme valeur, nous renseignons incrmentalement les champs des composants. Cette phase d'initialisation d'un ensemble graphique est toujours un peu fastidieuse en l'absence de primitives ou d'outils adquats.

# let make_box x y w h bw r =
{ x=x; y=y; w=w; h=h; bw=bw; r=r; b1_col=grey1; b2_col=grey3; b_col=grey2 } ;;
val make_box : int -> int -> int -> int -> int -> relief -> box_config =
<fun>
# let make_wcf cf =
let wcols = b0 + cf.nbcols*l4 + b0
and hrows = b0 + cf.nbrows*h5 + b0 in
let g_bcf = let gw = (b0 + l1 + wcols + l2 + b0)
and gh = (b0 + h1 + hrows + h2 + h3 + h4 + b0)
in make_box 0 0 gw gh b0 Top
and f_bcf = make_box l1 h1 wcols hrows b0 Bot in
let m_bcf = make_box ((g_bcf.w - l3) / 2) (b0+h1+hrows+h2) l3 h3 b0 Bot in
let m1_bcf = make_box (m_bcf.x + b0) (b0 + h1 + hrows + h2)
((l3-l5)/2-(2*b0)) (h3-(2*b0)) 5 Flat in
let s_bcf = make_box (m1_bcf.x + m1_bcf.w)
(m1_bcf.y + (h3-h6) / 2) l5 h6 b0 Top in
let m2_bcf = make_box (s_bcf.x + s_bcf.w)
m1_bcf.y m1_bcf.w m1_bcf.h 5 Flat in
let cc_bcf = make_box 0 0 l4 h5 b0 Top
in { cf = cf;
g_bcf = g_bcf; f_bcf=f_bcf; m_bcf=m_bcf; m1_bcf=m1_bcf;
s_bcf=s_bcf; m2_bcf=m2_bcf; cc_bcf = cc_bcf;
cell = (fun (i,j) -> ( l1+b0+l4*i , h1+b0+h5*j)) ;
coor = (fun (x,y) -> ( (x-l1)/l4 , (y-h1)/h5 )) } ;;
val make_wcf : config -> window_config = <fun>


Affichages des cases
Il s'agit maintenant de dfinir les fonctions ralisant l'affichage des cases dans les diffrents cas possibles. Une case pourra tre ouverte ou ferme et pourra contenir ou non une information. On affichera toujours (la bote de) la cellule courante de la configuration du jeu (champs cc_bcf).

Nous dfinissons donc deux fonctions modifiant la configuration de la cellule courante ; l'une la fermant, l'autre l'ouvrant.

# let close_ccell wcf i j =
let x,y = wcf.cell (i,j)
in wcf.cc_bcf <- {wcf.cc_bcf with x=x; y=y; r=Top} ;;
val close_ccell : window_config -> int -> int -> unit = <fun>
# let open_ccell wcf i j =
let x,y = wcf.cell (i,j)
in wcf.cc_bcf <- {wcf.cc_bcf with x=x; y=y; r=Flat} ;;
val open_ccell : window_config -> int -> int -> unit = <fun>


Suivant la phase de jeu, nous serons amens afficher une information sur les cases. Nous dfinissons, pour chaque cas, une fonction particulire.
  • Affichage d'une case ferme :

    # let draw_closed_cc wcf i j =
    close_ccell wcf i j;
    draw_box wcf.cc_bcf ;;
    val draw_closed_cc : window_config -> int -> int -> unit = <fun>


  • Affichage d'une case ouverte avec son compte de mines :

    # let draw_num_cc wcf i j n =
    open_ccell wcf i j ;
    draw_box wcf.cc_bcf ;
    if n<>0 then draw_string_in_box Center (string_of_int n)
    wcf.cc_bcf Graphics.white ;;
    val draw_num_cc : window_config -> int -> int -> int -> unit = <fun>


  • Affichage d'une case contenant une bombe :

    # let draw_bomb_cc wcf i j =
    open_ccell wcf i j ;
    let cc = wcf.cc_bcf
    in draw_box wcf.cc_bcf ;
    Graphics.set_color Graphics.black ;
    Graphics.fill_circle (cc.x+cc.w/2) (cc.y+cc.h/2) (cc.h/3) ;;
    val draw_bomb_cc : window_config -> int -> int -> unit = <fun>


  • Affichage d'une case mine et marque :

    # let draw_flag_cc wcf i j =
    close_ccell wcf i j ;
    draw_box wcf.cc_bcf ;
    draw_string_in_box Center "!" wcf.cc_bcf Graphics.blue ;;
    val draw_flag_cc : window_config -> int -> int -> unit = <fun>


  • Affichage d'une case marque mauvais escient :

    # let draw_cross_cc wcf i j =
    let x,y = wcf.cell (i,j)
    and w,h = wcf.cc_bcf.w, wcf.cc_bcf.h in
    let a=x+w/4 and b=x+3*w/4
    and c=y+h/4 and d=y+3*h/4
    in Graphics.set_color Graphics.red ;
    Graphics.set_line_width 3 ;
    Graphics.moveto a d ; Graphics.lineto b c ;
    Graphics.moveto a c ; Graphics.lineto b d ;
    Graphics.set_line_width 1 ;;
    val draw_cross_cc : window_config -> int -> int -> unit = <fun>
En cours de jeu, le choix de la fonction d'affichage d'une case est ralis par :

# let draw_cell wcf bd i j =
let cell = bd.(i).(j)
in match (cell.flag, cell.seen , cell.mined ) with
(true,_,_) -> draw_flag_cc wcf i j
| (_,false,_) -> draw_closed_cc wcf i j
| (_,_,true) -> draw_bomb_cc wcf i j
| _ -> draw_num_cc wcf i j cell.nbm ;;
val draw_cell : window_config -> cell array array -> int -> int -> unit =
<fun>


Une fonction spcifique affichera l'ensemble des cases en fin de partie. Elle diffre lgrement de la prcdente car toutes les cases sont considres comme devant tre dcouvertes. De plus, on signale d'une croix rouge les cases marques tort par le joueur.

# let draw_cell_end wcf bd i j =
let cell = bd.(i).(j)
in match (cell.flag, cell.mined ) with
(true,true) -> draw_flag_cc wcf i j
| (true,false) -> draw_num_cc wcf i j cell.nbm; draw_cross_cc wcf i j
| (false,true) -> draw_bomb_cc wcf i j
| (false,false) -> draw_num_cc wcf i j cell.nbm ;;
val draw_cell_end : window_config -> cell array array -> int -> int -> unit =
<fun>


Affichage des autres composants
L'tat du mode marquage est matrialis par une bote en creux ou en relief contenant le mot ON ou OFF :

# let draw_flag_switch wcf on =
if on then wcf.s_bcf.r <- Bot else wcf.s_bcf.r <- Top ;
draw_box wcf.s_bcf ;
if on then draw_string_in_box Center "ON" wcf.s_bcf Graphics.red
else draw_string_in_box Center "OFF" wcf.s_bcf Graphics.blue ;;
val draw_flag_switch : window_config -> bool -> unit = <fun>


Au dessus du bouton de marquage, on affiche son rle :

# let draw_mark_title wcf =
let m = "Marquage" in
let w,h = Graphics.text_size m in
let x = (wcf.g_bcf.w-w)/2
and y0 = wcf.m_bcf.y+wcf.m_bcf.h in
let y = y0+(wcf.g_bcf.h-(y0+h))/2
in Graphics.moveto x y ;
Graphics.draw_string m ;;
val draw_mark_title : window_config -> unit = <fun>


Tout au long de la partie, le nombre de cases restant dcouvrir et le nombre de cases marques sont affichs dans la bote de message, de part et d'autre du bouton de marquage.

# let print_score wcf nbcto nbfc =
erase_box wcf.m1_bcf ;
draw_string_in_box Center (string_of_int nbcto) wcf.m1_bcf Graphics.blue ;
erase_box wcf.m2_bcf ;
draw_string_in_box Center (string_of_int (wcf.cf.nbmines-nbfc)) wcf.m2_bcf
( if nbfc>wcf.cf.nbmines then Graphics.red else Graphics.blue ) ;;
val print_score : window_config -> int -> int -> unit = <fun>


Pour dessiner le champ de mines initial, il faut dessiner (nombre de lignes) (nombre de colonnes) fois une mme case ferme. C'est toujours le mme dessin, mais il peut tre assez long faire puisqu'il faut dessiner et remplir en plus d'un rectangle, quatre trapzes. Pour acclrer cette initialisation, nous avons recours la technique consistant ne dessiner qu'une seule case, la capturer sous forme d'un bitmap et le reproduire chaque emplacement d'une case.

# let draw_field_initial wcf =
draw_closed_cc wcf 0 0 ;
let cc = wcf.cc_bcf in
let bitmap = draw_box cc ; Graphics.get_image cc.x cc.y cc.w cc.h in
let draw_bitmap (i,j) = let x,y=wcf.cell (i,j)
in Graphics.draw_image bitmap x y
in iter_on_cell wcf.cf draw_bitmap ;;
val draw_field_initial : window_config -> unit = <fun>


En fin de partie, on dcouvre la totalit du champ de mines en marquant d'une croix rouge les cases marques mauvais escient :

# let draw_field_end wcf bd =
iter_on_cell wcf.cf (fun (i,j) -> draw_cell_end wcf bd i j) ;;
val draw_field_end : window_config -> cell array array -> unit = <fun>


Enfin, la fonction principale d'affichage de dbut de partie ouvre le contexte graphique et affiche l'tat initial des diffrents composants.

# let open_wcf wcf =
Graphics.open_graph ( " " ^ (string_of_int wcf.g_bcf.w) ^ "x" ^
(string_of_int wcf.g_bcf.h) ) ;
draw_box wcf.g_bcf ;
draw_box wcf.m_bcf ;
draw_flag_switch wcf false ;
draw_box wcf.f_bcf ;
draw_field_initial wcf ;
draw_mark_title wcf ;
print_score wcf ((wcf.cf.nbrows*wcf.cf.nbcols)-wcf.cf.nbmines) 0 ;;
val open_wcf : window_config -> unit = <fun>


Notons que toutes les primitives d'affichage sont paramtres par une configuration graphique de type window_config. Le fait d'avoir procd de la sorte les rend indpendantes des choix de disposition des lments composant notre Dmineur. Si nous souhaitons modifier cette disposition, le code reste utilisable tel quel, seule la configuration doit tre mise jour.

Interaction entre le programme et le joueur

Dfinissons les diffrentes possibilits offertes au joueur :
  • il peut cliquer sur la case de slection pour changer de mode (dcouverte ou marquage),
  • ou cliquer sur l'une des cases pour la dcouvrir ou la marquer,
  • ou il peut appuyer sur la touche 'q' pour quitter le jeu.
Rappelons qu'un vnement de Graphics (Graphics.event) doit tre associ un enregistrement (Graphics.status) contenant les informations courantes sur la souris et le clavier l'instant o se produit l'vnement. Une interaction souris peut avoir lieu soit sur le bouton de marquage, soit sur une case du champ de mines. Tout autre vnement souris doit tre ignor. Afin de diffrencier ces vnements souris, nous nous donnons le type :

# type clickon = Out | Cell of (int*int) | SelectBox ;;


D'autre part, l'appui sur le bouton de la souris et son relchement sont deux vnements distincts. Pour qu'un clic soit valide, nous imposons que les deux vnements aient eu lieu sur le mme composant (bouton de marquage ou case du champ de mines).

# let locate_click wcf st1 st2 =
let clickon_of st =
let x = st.Graphics.mouse_x and y = st.Graphics.mouse_y
in if x>=wcf.s_bcf.x && x<=wcf.s_bcf.x+wcf.s_bcf.w &&
y>=wcf.s_bcf.y && y<=wcf.s_bcf.y+wcf.s_bcf.h
then SelectBox
else let (x2,y2) = wcf.coor (x,y)
in if x2>=0 && x2<wcf.cf.nbcols && y2>=0 && y2<wcf.cf.nbrows
then Cell (x2,y2) else Out
in
let r1=clickon_of st1 and r2=clickon_of st2
in if r1=r2 then r1 else Out ;;
val locate_click :
window_config -> Graphics.status -> Graphics.status -> clickon = <fun>


Le coeur du programme est la boucle d'attente et de traitement des vnements dfinie dans la fonction loop. Elle s'inspire de la fonction squel dcrite la page ??, mais spcifie mieux les types d'vnement souris. Cette boucle est interrompue :
  • par appui de la touche q (ou Q) que l'on interprte comme la volont du joueur de terminer la partie ;
  • lorsque le joueur dcouvre une case mine, auquel cas il a perdu ;
  • lorsque le joueur a dcouvert toutes les cases non mines, auquel cas il a gagn.
On rassemble dans le type enregistrement demin_cf les diffrentes informations utiles aux fonctions de traitement de l'interaction :

# type demin_cf =
{ wcf : window_config; bd : cell array array;
mutable nb_marked_cells : int;
mutable nb_hidden_cells : int;
mutable flag_switch_on : bool } ;;
Les champs correspondent :
  • wcf : la configuration graphique ;
  • bd : le tableau de cellules ;
  • flag_switch_on : un boolen indiquant si le jeu est en mode marquage ou non ;
  • nb_marked_cells : le nombre de cases marques ;
  • nb_hidden_cells : le nombre de cases non mines restant dcouvrir ;
La boucle principale s'crit de la manire suivante :

# let loop d f_init f_key f_mouse f_end =
try
while true do
let st = Graphics.wait_next_event
[Graphics.Button_down;Graphics.Key_pressed]
in if st.Graphics.keypressed then f_key st.Graphics.key
else let st2 = Graphics.wait_next_event [Graphics.Button_up]
in f_mouse (locate_click d.wcf st st2)
done
with Fin -> ();;
val loop : demin_cf -> 'a -> (char -> 'b) -> (clickon -> 'b) -> 'c -> unit =
<fun>


Les fonctions d'initialisation, de fin et de gestion clavier sont fort simples.

# let d_init d () = open_wcf d.wcf
let d_end () = Graphics.close_graph()
let d_key c = if c='q' || c='Q' then raise Fin;;
val d_init : demin_cf -> unit -> unit = <fun>
val d_end : unit -> unit = <fun>
val d_key : char -> unit = <fun>


Par contre la fonction de gestion des vnements souris ncessite l'utilisation de quelques fonctions auxiliaires :
  • mark_cell : rponse un clic sur une case en mode marquage actif.
  • ending : fin du jeu. On dcouvre tout le champ de mines, on affiche un message indiquant victoire ou dfaite et on attend un vnement souris ou clavier quelconque pour fermer l'application.
  • reveal : rponse un clic sur une case en mode dcouverte (i.e. mode marquage inactif)

# let mark_cell d i j =
if d.bd.(i).(j).flag
then ( d.nb_marked_cells <- d.nb_marked_cells -1;
d.bd.(i).(j).flag <- false )
else ( d.nb_marked_cells <- d.nb_marked_cells +1 ;
d.bd.(i).(j).flag <- true ) ;
draw_cell d.wcf d.bd i j ;
print_score d.wcf d.nb_hidden_cells d.nb_marked_cells;;
val mark_cell : demin_cf -> int -> int -> unit = <fun>

# let ending d str =
draw_field_end d.wcf d.bd ;
erase_box d.wcf.s_bcf ;
draw_string_in_box Center str d.wcf.s_bcf Graphics.black;
ignore(Graphics.wait_next_event
[Graphics.Button_down;Graphics.Key_pressed]);
raise Fin;;
val ending : demin_cf -> string -> 'a = <fun>
# let reveal d i j =
let reveal_cell (i,j) =
d.bd.(i).(j).seen <- true ;
draw_cell d.wcf d.bd i j ;
d.nb_hidden_cells <- d.nb_hidden_cells -1
in
List.iter reveal_cell (cells_to_see d.bd d.wcf.cf (i,j)) ;
print_score d.wcf d.nb_hidden_cells d.nb_marked_cells ;
if d.nb_hidden_cells = 0 then ending d "GAGNE";;
val reveal : demin_cf -> int -> int -> unit = <fun>


La fonction de traitement de l'vnement souris filtre une valeur de type clickon.

# let d_mouse d click = match click with
Cell (i,j) ->
if d.bd.(i).(j).seen then ()
else if d.flag_switch_on then mark_cell d i j
else if d.bd.(i).(j).flag then ()
else if d.bd.(i).(j).mined then ending d "PERDU"
else reveal d i j
| SelectBox ->
d.flag_switch_on <- not d.flag_switch_on;
draw_flag_switch d.wcf d.flag_switch_on
| Out -> () ;;
val d_mouse : demin_cf -> clickon -> unit = <fun>


La cration d'une configuration de jeu a besoin de trois paramtres : le nombre de lignes, le nombre de colonnes et le nombre de mines.

# let create_demin nb_c nb_r nb_m =
let nbc = max default_config.nbcols nb_c
and nbr = max default_config.nbrows nb_r in
let nbm = min (nbc*nbr) (max 1 nb_m) in
let cf = { nbcols=nbc ; nbrows=nbr ; nbmines=nbm } in
generate_seed () ;
let wcf = make_wcf cf in
{ wcf = wcf ;
bd = initialize_board wcf.cf;
nb_marked_cells = 0;
nb_hidden_cells = cf.nbrows*cf.nbcols-cf.nbmines;
flag_switch_on = false } ;;
val create_demin : int -> int -> int -> demin_cf = <fun>


La fonction de lancement cre une configuration partir du nombre de colonnes, de lignes et de mines puis dmarre la boucle principale de gestion des vnements.

# let go nbc nbr nbm =
let d = create_demin nbc nbr nbm in
loop d (d_init d) d_key (d_mouse d) (d_end);;
val go : int -> int -> int -> unit = <fun>
L'appel go 10 10 10 construit un jeu de la taille de celui de la figure 6.5.

Pour en faire plus

On peut faire de ce programme un excutable autonome. Le chapitre 7 explique comment le faire. Une fois cela ralis, il est pratique de pouvoir donner les tailles du jeu sur la ligne de commande. Le chapitre 8 dcrit ce passage d'arguments et l'applique au dmineur (voir page ??).

Un autre type de problme est de faire jouer la machine pour dcouvrir les mines. Pour cela il convient de savoir dterminer un coup sr et de le jouer en priorit, puis de calculer les probabilits de prsence d'une mine pour chaque case et de jouer la case de plus faible probabilit.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora024.gif0000644000000000000000000002367107073350152014277 0ustar GIF89a ww`[h`WWptwppwplw{wȻwww؀`[``W`WWWww𠤨蘠W[`WW`ЈЀhlphhphdp`dh``hȘwtwwpwpppplpphphhhhdhh`h`d````ȠPPPwtphhhd`{Ȼ!,  H*\ȰÇ#JHň*2jQ#CI$I*X\re /[h!8e)C=!cƌGm  l86ވpc`0vǎ gͪUcʵ@ A^_C2dCÆ d$8( 5ԘO>cbJ8%PF&`g0xFCiCvkl"F8m`{$$qDIx+DLY$r+=gf"mb%[Gl_J!mgomBopGrT0WMZ\P5 ]hf i1F |3~5 =@F/kBxNcx?4F!R͆)b$6(HnM($rфHZO/h8[kk K1&npmqeܟFwݡ"x1pޤA4@*ΩD]` `H> ƦAg]5)D[A#l{[9n2n w 7ޯW & LxPӘ)ad09|u6-:c:Gqt`z" f j3LTO%d V@, t ZM Jt4LhX:ֆ"a/DS8-6Z'"[6r+FÎz#xO9jH/E N G>R p"%>~KdN=>bÑӛd fc[#u ǐy; I(R0d;pv6]zw>~6 BPp yL5lh QL=5YF0M/0-G5##&1nMCɎ O B"CL?PL(@ PP{\8 q;PxU*1T dgWPgep:h!,aY AKރ4).ծ,khDEH"Vl3ky?!u69 WڄT{(=FaI (P@t|Xh  -dT$$ N :ey`R,>Y~hCU2vՊAh5`YHzR+6UZbM1[ntqFp'J4 hBpќ W&|K_:X֑l( Q>^lb{hc!;YKt|Ď M0= CS;P,T-;Ӏ<1 j!ZC,j6[@q4.8XH8M$0R="r|lB:p8{tӱ3)ID($)%ղ=2#-zP˝ @ YƖ+d+ZъWƌZE`C+nWT-mGU9 G0cM+71Ϳv=wC^:4M{D3Xh b;0eН$_U׼v mb#AkŎH3J@}uZOB p oF<)&Sa#m.ۨbkŦ8lլS=~HH/](yjuBO쮰a^20a8&&=g(Xqw9IA$ԑJA݁2B;V)ZTSbHwW8%P&_dX׶fCA'ҁI(&s:gb)"V';c a3du=SC6Ї D+DEƒ}є!Eh54f\w-B~fNUxxe?T7Dr7Ga^TRV%uz810@ &k&Á) cAD(/1#`(ug)RuJvC/5|uH [9u b? +nFi>"ƒ54"Ek=a6/267r6e>>p#!.ct/s@7'eqe}che30`0zU9UAű9|B~mYq:䁎.4uJ)H;:c;uh@#4HDSD+hLvu,P#EĂUE2jxc{G""7qW6?#]p3 ]ErOH`4I+ Qs!}!"j"3-|gf#>..CB7rT7 t@ Y @.`v`0i vpiypyw۩ y 扞VWyyp)}p~`~YП~]~  0;ڡBD@7GG,GТ2 /29 :@:J3Fz} 1|D0OJ5ڢ4P WZ]jO*_8J/U PeڢJ:T 8ڤoʥemGYGЦ_ gBڨ:Zz:Zz):ZzګZʪ*Úʺڬ zؚں:Z:Zj꺮ڮˊ:Z z[ 㚰K ;۪tJg;z tO[G( J-!˲ ;J%{1۳Ί}<+<4;@k?ˊ'K7;+N/WBZD*[M[ʲhg۶d fJxz۷=˷~ [ K۸ʸ [抸|۹;[[ۺ;[{+l+˻;[{k= ҋv z˫{k{ۋ꽂pKK뛫 K嫷{[r~{0y+Kk} x z̽n{+ ZL$w˴"˫ƪ_2u#|k;̾A8 lͪ6CH<3@\-\,Jņ{KZ )S\g,t+ Dž` wLǂ~ xĂckdž j{,KO- l=e~Nɘ *ɃLȱQ,ʁqls|mɰJd`<̕mkZ{Gl,K}̾˼ Ϳ<Ͷ<ɸ\ɺ| ,͹ۺjͪtשּׂ,<ϯjϴl| ]笭 M}}  L"]Ϯ<mѳֺ͑\]}Ҵ ԫ,Ѻ'mE={*6=]WL%lk 3]j ð }N=Psl;\h(fMqbd։$]nm}uЁ̓ ]Ȃz٤G؀}gל\ڝ]wyղ̴eۄh[I}뺓]Qʽa\ [ͺ} +M̈ݯmۮެ]mޒm]>v= Z >ΰԝ. $]M(*.0;4n6~:n<@^B>[F޸H䉻L^NR޷T^y{XNZm^^`W;df~jlM^=t^v~xZڭr~肞M.pz| n^.z$ͷNl>. ¡ٗΫ.Nnj'먽ǎ.Ŏߎ==>ҽN^֮~.^~.^ :2]o&* /NN*?o3O78_,o+/AOC)G;Q߬"?\Xe\i/ӷ`?$okuMWto[?\ E_jOo0Mc_s~_OoUrOIQDeQFE,fѢBED)dJ+Y.4RȌ5i2f΂ m "FuXqeІFu*)2FJfVb؈Cr(UΰlVmɯ[zk-Y =VĐn,'ܷt8f]} $bK%s0`?C<:5`djLگgիmNV5\#7HuK,,{"n%2k'>uջ"o 5LeҼuG: 1  #$-L+B/F gtFqG?-],?Kaz#ƘCÌ8%H|^iG]qosY$%9IJVҒd>Jmj@{(?9!F"9DQjO+vʫ򌨴I&Q|YQ%i `Qbݲe*.143ihJ_ lBD&IYw|7Nor '69TɓSfKqɀ!'Ѕ q|2~ -{ˍjU:yB|q=׳ i>6,4Dђ3 Jф=quQԗD{IS2U(]* [Ԧ:5HPݡTI:T(VVիz!(5HբPkkR mu+P:өvp'rQIcwt~\]}?x3>&(czϳo!m?Ww]k_GoGz[]s~O=藶租~ߓ_}K^ꗢ?ۗ:?ÿ3@3?9޻C KTC+33@) 0 ;[<355?A0 L8QsD4B@ t@'lB¦A$9sA̫밿31:3=4$~9D2<@,B-̾BC?+?#È<5 D:>6"+d=$;8CAF$@ݛDBC:BD\6#R4f@GdJI=LĀQ9ī7(E E\TxEQ^,DJ4 EXi3N@QF_LT\gi<lzF=,@@bMEhTtFt EśEp<92ߡǻ+G3Gx,TGGqDbst$˺԰GILv$L|ɫJ[DZ$GLJdu\KgTʰTK$̵lLlMn̢MZdG,L ML]aM,tdȎ|ʏ AJLA}+G$H<$5,;5TԚ Descripteurs de fichier Prcdent Index Suivant

Descripteurs de fichier

Au chapitre 3, nous avons vu les fonctions standard du module Pervasives, elles permettent d'accder aux fichiers via des canaux d'entres-sorties. Il existe un moyen de plus bas niveau d'accs aux fichiers en ayant recours leur descripteur.

Un descripteur de fichier est une valeur abstraite, de type Unix.file_descr, qui contient les informations ncessaires l'utilisation d'un fichier : un pointeur vers ce fichier, les droits d'accs ce fichier, les conditions d'accs au fichier (lecture ou criture), la position courante dans le fichier, etc.

Trois descripteurs sont prdfinis. Ils correspondent aux fichiers d'entre standard, de sortie standard et d'erreur standard.

# ( Unix.stdin , Unix.stdout , Unix.stderr ) ;;
- : Unix.file_descr * Unix.file_descr * Unix.file_descr =
<abstr>, <abstr>, <abstr>


Attention ne pas les confondre avec les canaux d'entres-sorties leur correspondant :

# ( Pervasives.stdin , Pervasives.stdout , Pervasives.stderr ) ;;
- : in_channel * out_channel * out_channel = <abstr>, <abstr>, <abstr>


Des fonctions de conversion entre canaux et descripteurs de fichier sont dcrites la page ??.

Droits d'accs aux fichiers
Sous Unix, des droits en lecture, en criture et en excution sont attachs chaque fichier. Ils concernent trois catgories d'utilisateurs : le propritaire du fichier, les membres du groupe1 du propritaire ou l'ensemble de tous les utilisateurs.

Les droits d'un fichier sont reprsents par 9 bits diviss en trois groupes de trois bits. Le premier groupe reprsente les droits du propritaire, le deuxime les droits des membres du groupe du propritaire et le dernier les droits des autres utilisateurs. Dans chaque groupe de trois bits, le premier reprsente le droit en lecture, le deuxime, le droit en criture et le dernier, le droit en excution. On a coutume de reprsenter ces droits respectifs par les lettres r, w et x , et l'absence de droit par un tiret (-). Par exemple, le droit en lecture pour tous et en criture pour le propritaire seul s'crit rw-r--r--. C'est, en fait, l'entier 420 (soit le binaire 0b110100100). On utilisera souvent la notation octale 0o644 qui est d'un emploi plus ais. Ces droits sur les fichiers ne sont pas utiliss sous Windows.

Manipulation des fichiers

Ouverture d'un fichier
Ouvrir un fichier, c'est rcuprer un descripteur de ce fichier. Suivant l'usage que l'on veut en faire, il existe plusieurs modes d'ouverture des fichiers. Chacun correspond une valeur du type open_flag dcrit figure 18.2.

O_RDONLY en lecture
O_WRONLY en criture
O_RDWR en lecture et en criture
O_NONBLOCK ouverture dans un mode non bloquant
O_APPEND en ajout la fin du fichier
O_CREAT cre le fichier s'il n'existe pas
O_TRUNC remet le fichier 0 s'il existe
O_EXCL choue si le fichier existe

Figure 18.2 : Valeurs de type open_flag


Ces modes peuvent tre combins, en consquence la fonction openfile prendra en argument une liste de valeurs de type open_flag.

# Unix.openfile ;;
- : string -> Unix.open_flag list -> Unix.file_perm -> Unix.file_descr =
<fun>
Le premier argument est le nom du fichier et le dernier est un entier2 codant les permissions donner au fichier en cas de cration.

Voici comment ouvrir en lecture un fichier, ou le crer avec les droits rw-r--r-- s'il n'existe pas :

# let fichier = Unix.openfile "essai.dat" [Unix.O_RDWR; Unix.O_CREAT] 0o644 ;;
val fichier : Unix.file_descr = <abstr>
Fermeture d'un fichier
La fonction Unix.close permet de fermer un fichier. On l'applique au descripteur du fichier que l'on dsire fermer.

# Unix.close ;;
- : Unix.file_descr -> unit = <fun>
# Unix.close fichier ;;
- : unit = ()
Redirection de fichiers
On peut attacher une mme entre-sortie plusieurs descripteurs. Si l'on dispose d'un seul descripteur et que l'on veut en obtenir un nouveau, on utilisera :

# Unix.dup ;;
- : Unix.file_descr -> Unix.file_descr = <fun>


Si l'on dispose de deux descripteurs et que l'on veut rassigner au second l'entre-sortie correspondant au premier, on utilisera la fonction :

# Unix.dup2 ;;
- : Unix.file_descr -> Unix.file_descr -> unit = <fun>


Par exemple, on redirige la sortie d'erreur sur un fichier :

# let sortie_erreur = Unix.openfile "err.log" [Unix.O_WRONLY;Unix.O_CREAT] 0o644 ;;
val sortie_erreur : Unix.file_descr = <abstr>
# Unix.dup2 Unix.stderr sortie_erreur ;;
- : unit = ()
Les critures sur la sortie erreur standard sont dtournes vers le fichier err.log.

Entres-sorties sur un fichier

Les fonctions de lecture et d'criture sur un fichier Unix.read et Unix.write utilisent une chane de caractres comme mdia entre le fichier et le programme Objective CAML.

# Unix.read ;;
- : Unix.file_descr -> string -> int -> int -> int = <fun>
# Unix.write ;;
- : Unix.file_descr -> string -> int -> int -> int = <fun>


Outre le descripteur de fichier et la chane, les fonctions prennent deux entiers en argument qui sont l'indice du premier caractre et le nombre souhait de caractres lire ou crire. L'entier retourn est le nombre de caractres effectivement lus ou crits.

# let mode = [Unix.O_WRONLY;Unix.O_CREAT;Unix.O_TRUNC] in
let fic = Unix.openfile "fichier" mode 0o644 in
let str = "012345678901234565789" in
let n = Unix.write fic str 4 5
in Printf.printf "On a crit %s dans le fichier\n" (String.sub str 4 n) ;
Unix.close fic ;;
On a crit 45678 dans le fichier
- : unit = ()


La lecture procde du mme principe :

# let fic = Unix.openfile "fichier" [Unix.O_RDONLY] 0o644 in
let str = String.make 20 '.' in
let n = Unix.read fic str 2 10 in
Printf.printf "On n'a lu que %d caractres" n;
Printf.printf " et on obtient la chane %s\n" str;
Unix.close fic ;;
On n'a lu que 5 caractres et on obtient la chane ..45678.............
- : unit = ()


Un accs un fichier se fait toujours la position courante contenue dans son descripteur. On peut cependant modifier cette position grce la fonction :

# Unix.lseek ;;
- : Unix.file_descr -> int -> Unix.seek_command -> int = <fun>


Le premier argument est le descripteur du fichier, le second est la taille, en caractres, du dplacement et le troisime argument, de type Unix.seek_command, indique l'origine. Ce dernier argument a trois valeurs possibles :
  • SEEK_SET : relativement au dbut du fichier,
  • SEEK_CUR : relativement la position courante,
  • SEEK_END : relativement la fin du fichier.
Une position errone provoque soit le dclenchement d'une exception, soit une valeur de retour gale 0 suivant les fonctions d'entres-sorties utilises.

Canal d'entre-sortie
Le module Unix fournit des fonctions de conversion entre les descripteurs de fichier et les canaux d'entres-sorties du module Pervasives :

# Unix.in_channel_of_descr ;;
- : Unix.file_descr -> in_channel = <fun>
# Unix.out_channel_of_descr ;;
- : Unix.file_descr -> out_channel = <fun>
# Unix.descr_of_in_channel ;;
- : in_channel -> Unix.file_descr = <fun>
# Unix.descr_of_out_channel ;;
- : out_channel -> Unix.file_descr = <fun>


Il est ncessaire de prciser si les canaux d'entres-sorties obtenus par conversion transfrent des donnes binaires ou des caractres :

# set_binary_mode_in ;;
- : in_channel -> bool -> unit = <fun>
# set_binary_mode_out ;;
- : out_channel -> bool -> unit = <fun>


Dans l'exemple suivant on cre un fichier en utilisant les fonctions du module Unix et on le lit en utilisant la fonction d'ouverture du module Unix et la fonction d'entre de plus haut niveau input_line.

# let mode = [Unix.O_WRONLY;Unix.O_CREAT;Unix.O_TRUNC] in
let f = Unix.openfile "fichier" mode 0o666 in
let s = "0123456789\n0123456789\n" in
let n = Unix.write f s 0 (String.length s)
in Unix.close f ;;
- : unit = ()
# let f = Unix.openfile "fichier" [Unix.O_RDONLY;Unix.O_NONBLOCK] 0 in
let c = Unix.in_channel_of_descr f in
let s = input_line c
in print_string s ;
close_in c ;;
0123456789- : unit = ()


Disponibilit
Un programme peut avoir grer une multiplicit d'entres-sorties. Or celles-ci ne sont pas toujours disponibles : d'autres programmes ont pu prcdemment en rclamer l'usage. La fonction suivante permet de connatre la liste des entres-sorties disponibles un moment donn parmi une liste donne :

# Unix.select ;;
- : Unix.file_descr list ->
Unix.file_descr list ->
Unix.file_descr list ->
float ->
Unix.file_descr list * Unix.file_descr list * Unix.file_descr list
= <fun>
Les trois premiers arguments reprsentent les listes respectivement des entres (lecture), des sorties (criture) et des sorties en erreur. Le dernier argument indique un dlai d'attente en secondes (une valeur ngative indique un dlai nul). Le rsultat est la liste des entres-sorties disponibles en lecture, criture et erreur.

Warning


select n'est pas implante pour Windows



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora021.html0000644000000000000000000000312207421273601014460 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a montr les principaux traits de la programmation fonctionnelle et du polymorphisme paramtrique qui sont deux traits essentiels du langage Objective CAML. La syntaxe des expressions du noyau fonctionnel du langage ainsi que celle des types ont t dcrites permettant la ralisation des premiers programmes. Par ailleurs, il a t soulign la diffrence profonde entre le type d'une fonction et son domaine de dfinition. L'introduction du mcanisme d'exceptions permet de rsoudre ce problme et introduit dj un nouveau style de programmation o l'on prcise le droulement des calculs.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora182.html0000644000000000000000000000471207421273602014477 0ustar Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

Les premiers besoins en algorithmes concurrents ont surgi de la programmation systme. Pour cela le modle impratif mmoire partage est le plus utilis. Par exemple, la relation d'exclusion mutuelle et les smaphores sont utiliss pour la gestion des ressources communes. Les diffrents mcanismes de bas niveau de gestion de processus accdant une mmoire partage sont dcrits dans [Ari86].

Nanmoins, la possibilit d'exprimer des algorithmes concurrents dans son langage de prdilection permet de s'intresser ce type d'algorithmique, comme prsent dans l'ouvrage [And91]. Il est noter que la conception de tels algorithmes peut simplifier la rsolution de certains problmes, mais la mise au point des programmes correspondants est assez ardue.

Le modle de la communication synchrone prsent par CML, et repris par le module Event, est bien dcrit dans [Rep92]. La version en ligne est l'adresse suivante :

Lien


http://cm.bell-labs.com/cm/cs/who/jhr/index.html
Un exemple intressant est la bibliothque graphique de processus lgers, EXene, implante en CML au dessus de X-Windows. Le lien prcdent contient un pointeur sur cette bibliothque.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora049.html0000644000000000000000000014721707421273601014510 0ustar Affichage graphique Prcdent Index Suivant

Affichage graphique

Les lments de base de l'affichage graphique sont : le repre et le contexte graphique, les couleurs, les tracs et les remplissages de formes fermes, les textes, et les bitmaps.

Repre et contexte graphique

La bibliothque Graphics gre une fentre principale et unique. Les coordonnes du repre de la fentre vont du point (0,0) en bas gauche au point suprieur droit de la fentre. Les principales fonctions sur cette fentre sont :
  • open_graph, de type string -> unit, qui ouvre une fentre;
  • close_graph, de type unit -> unit, qui la ferme;
  • clear_graph, de type unit -> unit, qui l'efface.
La taille de la fentre graphique est donne par les fonctions size_x et size_y.

La chane de caractres passe en argument de la fonction open_graph dpend du systme de fentrage de la machine sur laquelle est excut le programme et n'est donc pas indpendante de la plate-forme. Toutefois, la chane vide ouvre une fentre dont les caractristiques sont donnes par dfaut. Il est possible de spcifier la taille de la fentre : sous X-Windows, " 200x300" donnera une fentre de 200 pixels de large et de 300 pixels de haut. Attention, l'espace, premier caractre de la chane " 200x300" est indispensable.

Le contexte graphique contient un certain nombre de composants qui sont accessibles ou modifiables :
le point courant : current_point : unit -> int * int
  moveto : int -> int -> unit
la couleur courante : set_color : color -> unit
la largeur du trac de lignes : set_line_width : int -> unit
la police de caractres courante : set_font : string -> unit
la taille des caractres : set_text_size : int -> unit

Couleurs

Les couleurs sont reprsentes par trois octets : chacun est la valeur de l'intensit d'une couleur principale dans le modle RGB (rouge, vert, bleu) comprise entre un minimum 0 et un maximum 255. La fonction rgb (de type int -> int -> int -> color) permet de construire une nouvelle couleur partir de ses trois composantes. Si les trois composantes sont identiques, la couleur obtenue est un gris plus ou moins clair selon la valeur. Le noir correspond au minimum d'intensit pour chacune des composantes (0 0 0) et le blanc correspond au maximum (255 255 255). Certaines couleurs sont prdfinies : black , white, red, green, blue, yellow, cyan, magenta.

Les variables foreground et background correspondent respectivement la couleur courante et la couleur du fond. L'effacement de l'cran provoque son remplissage par la couleur du fond.

Une couleur (une valeur de type color) est en fait un entier qu'il est possible de manipuler pour, par exemple, la dcomposer selon ses trois composantes (from_rgb) ou lui appliquer une fonction d'inversion vido (inv_color).

(* couleur == R * 256 * 256 + G * 256 + B *)
# let from_rgb (c : Graphics.color) =
let r = c / 65536 and g = c / 256 mod 256 and b = c mod 256
in (r,g,b) ;;
val from_rgb : Graphics.color -> int * int * int = <fun>
# let inv_color (c : Graphics.color) =
let (r,g,b) = from_rgb c
in Graphics.rgb (255-r) (255-g) (255-b) ;;
val inv_color : Graphics.color -> Graphics.color = <fun>


La fonction point_color, de type int -> int -> color, retourne la couleur du point dont les coordonnes lui ont t passes en argument.

Trac et remplissage

Une fonction de trac dessine un trait l'cran. Le trait est de la largeur courante et de la couleur courante. Une fonction de remplissage colorie une forme ferme avec la couleur courante. Les diffrentes fonctions de trac et de remplissage sont donnes dans la figure 5.1.


trac remplissage type
plot   int -> int -> unit
lineto   int -> int -> unit
  fill_rect int -> int -> int -> int -> unit
  fill_poly ( int * int) array -> unit
draw_arc fill_arc int -> int -> int -> int -> int -> unit
draw_ellipse fill_ellipse int -> int -> int -> int -> unit
draw_circle fill_circle int -> int -> int -> unit

Figure 5.1 : Fonctions de trac et de remplissage


Attention, la fonction lineto a pour effet de bord de modifier la position du point courant.

Trac de polygones
En guise d'exemple, nous ajoutons les primitives de trac qui ne sont pas prdfinies. Un polygone est dcrit par un tableau contenant ses sommets.

# let draw_rect x0 y0 w h =
let (a,b) = Graphics.current_point()
and x1 = x0+w and y1 = y0+h
in
Graphics.moveto x0 y0;
Graphics.lineto x0 y1; Graphics.lineto x1 y1;
Graphics.lineto x1 y0; Graphics.lineto x0 y0;
Graphics.moveto a b ;;
val draw_rect : int -> int -> int -> int -> unit = <fun>

# let draw_poly r =
let (a,b) = Graphics.current_point () in
let (x0,y0) = r.(0) in Graphics.moveto x0 y0 ;
for i = 1 to (Array.length r)-1 do
let (x,y) = r.(i) in Graphics.lineto x y
done ;
Graphics.lineto x0 y0 ;
Graphics.moveto a b ;;
val draw_poly : (int * int) array -> unit = <fun>


Notez que ces fonctions prennent les mmes arguments que les fonctions prdfinies de remplissage de ces formes. Et que comme les autres fonctions de trac de formes, elles ne modifient pas le point courant.

Illustration du modle du peintre
Cet exemple construit l'illustration d'un rseau jeton en anneau (figure 5.2). Chaque machine est reprsente par un petit cercle. On rpartit l'ensemble des machines sur un grand cercle et l'on trace un trait entre les machines connectes. La position courante du jeton dans le rseau est matrialise par un petit disque noir.

La fonction ens_points construit les coordonnes des diffrentes machines du rseau. Les donnes rsultantes sont stockes dans un tableau.

# let pi = 3.1415927 ;;
val pi : float = 3.1415927
# let ens_points (x,y) l n =
let a = 2. *. pi /. (float n) in
let rec aux (xa,ya) i =
if i > n then []
else
let na = (float i) *. a in
let x1 = xa + (int_of_float ( cos(na) *. l))
and y1 = ya + (int_of_float ( sin(na) *. l)) in
let np = (x1,y1) in
np::(aux np (i+1))
in Array.of_list (aux (x,y) 1) ;;
val ens_points : int * int -> float -> int -> (int * int) array = <fun>


La fonction dessine affiche les connexions, les machines et le jeton.

# let dessine (x,y) l n sc st =
let r = ens_points (x,y) l n in
draw_poly r ;
let dessine_cercle (x,y) =
Graphics.set_color Graphics.background ;
Graphics.fill_circle x y sc ;
Graphics.set_color Graphics.foreground ;
Graphics.draw_circle x y sc
in
Array.iter dessine_cercle r ;
Graphics.fill_circle x y st ;;
val dessine : int * int -> float -> int -> int -> int -> unit = <fun>




L'appel suivant correspond au dessin gauche de la figure 5.2.

# dessine (140,20) 60.0 10 10 3;;
- : unit = ()

# save_screen "IMAGES/tokenring.caa";;
- : unit = ()




Figure 5.2 : Rseau en anneau


Notons que l'ordre de l'affichage a son importance. Nous traons d'abord les connexions puis les noeuds. Le dessin des noeuds du rseau efface une partie des traits de connexion. Il n'y a donc pas besoin de calculer le point d'intersection entre les segments de connexion et les cercles des sommets. L'illustration de droite de la figure 5.2 inverse cet ordre d'affichage. On voit donc le trac des segments apparatre l'intrieur des cercles reprsentant les noeuds.

Texte

Les fonctions d'affichage de texte sont des plus simples. Les deux fonctions draw_char (de type char -> unit) et draw_string (de type string -> unit) affichent respectivement un caractre et une chane de caractres au point courant. Aprs l'affichage, ce dernier est modifi. Ces affichages tiennent compte du choix de la police courante et de sa taille courante.

Remarque


Le rendu d'affichage sur les chanes peut diffrer selon les systmes graphiques.


La fonction text_size prend une chane en entre et retourne un couple d'entiers correspondant aux dimensions de cette chane affiche avec la police et la taille courante.

Affichage vertical de chanes
Cet exemple dcrit la fonction draw_string_v qui affiche une chane de caractres verticalement partir du point courant. Elle est utilise la figure 5.3. Chaque lettre est affiche sparment en changeant la coordonne verticale.

# let draw_string_v s =
let (xi,yi) = Graphics.current_point()
and l = String.length s
and (_,h) = Graphics.text_size s
in
Graphics.draw_char s.[0] ;
for i=1 to l-1 do
let (_,b) = Graphics.current_point()
in Graphics.moveto xi (b-h) ;
Graphics.draw_char s.[i]
done ;
let (a,_) = Graphics.current_point() in Graphics.moveto a yi ;;
val draw_string_v : string -> unit = <fun>
Cette fonction modifie le point courant. Aprs l'affichage celui-ci se retrouve la position initiale dcale de la largeur d'un caractre.

Le programme suivant permet d'afficher une lgende autour des axes (figure 5.3)

#
Graphics.moveto 0 150 ; Graphics.lineto 300 150 ;
Graphics.moveto 2 130 ; Graphics.draw_string "abscisses" ;
Graphics.moveto 150 0 ; Graphics.lineto 150 300 ;
Graphics.moveto 135 280 ; draw_string_v "ordonnes" ;;
- : unit = ()




Figure 5.3 : Lgende autour des axes


Si on souhaite raliser l'affichage vertical de texte, il faut tenir compte du fait que le point courant est modifi par la fonction draw_string_v. Pour cela, on dfinit la fonction draw_text_v qui prend pour paramtres l'espacement entre les colonnes et la liste de mots.

# let draw_text_v n l =
let f s = let (a,b) = Graphics.current_point()
in draw_string_v s ;
Graphics.moveto (a+n) b
in List.iter f l ;;
val draw_text_v : int -> string list -> unit = <fun>


Si l'on dsire effectuer d'autres transformations, comme une rotation, sur un texte, il devient ncessaire de prendre le bitmap de chaque lettre et d'effectuer la rotation sur ces ensembles de pixels.

Bitmaps

Un bitmap est reprsent soit par une matrice de couleur (color array array), soit par une valeur du type abstrait1 image de la bibliothque Graphics. Les noms et les types des fonctions de manipulation de bitmaps sont donns la figure 5.4.


fonction type
make_image color array array -> image
dump_image image -> color array array
draw_image image -> int -> int -> unit
get_image int -> int -> int -> int -> image
blit_image image -> int -> int -> unit
create_image int -> int -> image

Figure 5.4 : Fonctions de manipulation de bitmaps


Les fonctions make_image et dump_image sont des fonctions de conversion entre le type image et le type color array array. La fonction draw_image affiche un bitmap partir des coordonnes de son coin infrieur gauche.

De manire inverse, on peut capturer une partie rectangulaire de l'cran pour fabriquer une image par la fonction get_image en indiquant les coins infrieur gauche et suprieur droit de la zone capturer. La fonction blit_image modifie son premier paramtre (de type image) en capturant la rgion dont le coin infrieur gauche est le point pass en paramtre. La taille de la rgion capture est celle de l'image passe en argument. La fonction create_image permet d'initialiser des images en spcifiant leur taille pour ventuellement les utiliser avec blit_image.

La couleur prdfinie transp est utilise pour crer des points transparents dans une image. Cela permet d'afficher une image seulement sur une partie d'un rectangle ; les points transparents ne modifient pas l'cran initial.

Polarisation de Jussieu
Cet exemple inverse la couleur des points d'un bitmap. Pour ce faire on utilise la fonction d'inversion des couleurs prsente page ?? en l'appliquant chaque pixel d'un bitmap.

# let inv_image i =
let inv_vec = Array.map (fun c -> inv_color c) in
let inv_mat = Array.map inv_vec in
let matrice_inversee = inv_mat (Graphics.dump_image i) in
Graphics.make_image matrice_inversee ;;
val inv_image : Graphics.image -> Graphics.image = <fun>


Soit le bitmap jussieu affich sur la partie gauche de la figure 5.5, en utilisant la fonction inv_image on obtient un nouveau bitmap <<solaris>>, comme affich dans la partie droite de la mme figure.

# let f_jussieu2 () = inv_image jussieu1;; 
val f_jussieu2 : unit -> Graphics.image = <fun>




Figure 5.5 : Ngation de Jussieu


Exemple : trac de botes en relief

On se propose dans cet exemple de dfinir quelques outils d'affichage pour le rendu du relief de botes. Une bote est un objet gnrique qui servira dans de nombreux cas d'affichage. Une bote est inscrite dans un rectangle caractris par un point d'origine, une hauteur et une largeur.

Pour donner une impression de relief une bote, il suffit de dessiner autour deux trapzes de couleurs plus claires et deux autres de couleurs plus sombres.
  

En inversant les couleurs on peut donner l'impression que les botes sont pleines ou creuses.
 

Implantation
On ajoute aux caractristiques d'une bote, la largeur de sa bordure, son mode d'affichage (plein, creux ou plat), ainsi que les couleurs de ses bords et de son fond. Ces informations sont rassembles dans un enregistrement.

# type relief = Top | Bot | Flat ;;
# type box_config =
{ x:int; y:int; w:int; h:int; bw:int; mutable r:relief ;
b1_col : Graphics.color ;
b2_col : Graphics.color ;
b_col : Graphics.color} ;;
Seul le champ r peut tre modifi. On utilise la fonction draw_rect, dfinie page ??, qui dessine un rectangle.

Par commodit, on se donne une fonction d'affichage du contour d'une bote.

# let draw_box_outline bcf col =
Graphics.set_color col ;
draw_rect bcf.x bcf.y bcf.w bcf.h ;;
val draw_box_outline : box_config -> Graphics.color -> unit = <fun>


La fonction d'affichage d'une bote se dcompose en trois parties : le dessin du premier bord, le dessin du deuxime bord et le dessin de l'intrieur de la bote.

# let draw_box bcf =
let x1 = bcf.x and y1 = bcf.y in
let x2 = x1+bcf.w and y2 = y1+bcf.h in
let ix1 = x1+bcf.bw and ix2 = x2-bcf.bw
and iy1 = y1+bcf.bw and iy2 = y2-bcf.bw in
let border1 g =
Graphics.set_color g;
Graphics.fill_poly
[| (x1,y1);(ix1,iy1);(ix2,iy1);(ix2,iy2);(x2,y2);(x2,y1) |]
in
let border2 g =
Graphics.set_color g;
Graphics.fill_poly
[| (x1,y1);(ix1,iy1);(ix1,iy2);(ix2,iy2);(x2,y2);(x1,y2) |]
in
Graphics.set_color bcf.b_col;
( match bcf.r with
Top ->
Graphics.fill_rect ix1 iy1 (ix2-ix1) (iy2-iy1) ;
border1 bcf.b1_col ;
border2 bcf.b2_col
| Bot ->
Graphics.fill_rect ix1 iy1 (ix2-ix1) (iy2-iy1) ;
border1 bcf.b2_col ;
border2 bcf.b1_col
| Flat ->
Graphics.fill_rect x1 y1 bcf.w bcf.h ) ;
draw_box_outline bcf Graphics.black ;;
val draw_box : box_config -> unit = <fun>


Le contour des botes est surlign en noir. L'effacement d'une bote remplit sa zone d'affichage de la couleur du fond.

# let erase_box bcf =
Graphics.set_color bcf.b_col ;
Graphics.fill_rect (bcf.x+bcf.bw) (bcf.y+bcf.bw)
(bcf.w-(2*bcf.bw)) (bcf.h-(2*bcf.bw)) ;;
val erase_box : box_config -> unit = <fun>


On dfinit enfin une fonction d'affichage d'une chane de caractres positionne gauche, droite ou au centre de la bote. On utilise le type position pour dcrire le type de placement de la chane dans une bote.

# type position = Left | Center | Right ;;
type position = | Left | Center | Right
# let draw_string_in_box pos str bcf col =
let (w, h) = Graphics.text_size str in
let ty = bcf.y + (bcf.h-h)/2 in
( match pos with
Center -> Graphics.moveto (bcf.x + (bcf.w-w)/2) ty
| Right -> let tx = bcf.x + bcf.w - w - bcf.bw - 1 in
Graphics.moveto tx ty
| Left -> let tx = bcf.x + bcf.bw + 1 in Graphics.moveto tx ty ) ;
Graphics.set_color col ;
Graphics.draw_string str ;;
val draw_string_in_box :
position -> string -> box_config -> Graphics.color -> unit = <fun>


Exemple : trac d'un jeu
On illustre l'utilisation des botes en affichant la position d'un jeu de type << morpion >> comme la figure 5.6. Pour simplifier les constructions des botes, on se donne des couleurs prdfinies.

# let set_grey x = (Graphics.rgb x x x) ;;
val set_grey : int -> Graphics.color = <fun>
# let grey1= set_grey 100 and grey2= set_grey 170 and grey3= set_grey 240 ;;
val grey1 : Graphics.color = 6579300
val grey2 : Graphics.color = 11184810
val grey3 : Graphics.color = 15790320


On dfinit alors une fonction de construction d'une grille de botes de mme taille.

# let rec create_grid nb_col n sep b =
if n < 0 then []
else
let px = n mod nb_col and py = n / nb_col in
let nx = b.x +sep + px*(b.w+sep)
and ny = b.y +sep + py*(b.h+sep) in
let b1 = {b with x=nx; y=ny} in
b1::(create_grid nb_col (n-1) sep b) ;;
val create_grid : int -> int -> int -> box_config -> box_config list = <fun>


Et on construit le vecteur des botes :

# let vb =
let b = {x=0; y=0; w=20;h=20; bw=2;
b1_col=grey1; b2_col=grey3; b_col=grey2; r=Top} in
Array.of_list (create_grid 5 24 2 b) ;;
val vb : box_config array =
[|{x=90; y=90; w=20; h=20; bw=2; r=Top; b1_col=6579300; b2_col=15790320;
b_col=11184810};
{x=68; y=90; w=20; h=20; bw=2; r=Top; b1_col=6579300; b2_col=15790320;
b_col=...};
...|]


La figure 5.6 correspond aux appels suivants :

# Array.iter draw_box vb ;
draw_string_in_box Center "X" vb.(5) Graphics.black ;
draw_string_in_box Center "X" vb.(8) Graphics.black ;
draw_string_in_box Center "O" vb.(12) Graphics.yellow ;
draw_string_in_box Center "O" vb.(11) Graphics.yellow ;;
- : unit = ()




Figure 5.6 : Affichage de botes avec texte



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora051.gif0000644000000000000000000000275307073350152014275 0ustar GIF89ayyy!,*޼Hʶ jL ! flJKЩbS-C1355r'9.='8WhH)9IYiyi5 *:JZ*iں*;K +PKy ,n N~>l.؎~+ </ONp/\#x^=64(.[aUQ9Z1$]vR$D&A,%1O&T/4ٲdJ1]wTh;CU#Itd2U]̉H> Ψ/]"jצRvvTJSfzhޭmVWKx{g]{l֗b3VƊ>Nl^R lQΓ=_~*ZҞ1tuֈ?F&YuYۙA֣c) ͻ5Q5ڇ=L{gg7 !L[Nn|t%lL[ا~rņ 4f 6}%h*m!0tAHb:#bD<+c8'Hczcc>BˍD$$A&ɤK6 %/OFI%-S]ޑWr emm)J `F6)hηeI,sWNiz]⧂Uf:'jB * f]xYEh._&iQ鍤65J)vޥ *GRkq+:ˬڮgXT[mk˭RE.fzJWYk/޷*:خ;+0 Go^qj 02Rl9'/6er;o Kݚ1:8y Myi&9oxYK%ҩ<,_+o 7)kM)o׍%s|q.xۄ?U6xG/+|c] nb$rcl)X7 zo2cS=nZ/َ ngbc|BlO?+纻쯱2f0syt&̰V;/[+Wܷ/<_j汖l0Kג2 c;]4 ì@}+Jr; }B~( =XC g,M[?G &Q\RWsU4(2y2rX&3PgҼ5 hs7;ocaml-book-1.0/fr/html/next_motif.gif0000644000000000000000000000047507073350152014511 0ustar GIF89app!# Imported from XPM image: next.xpm!,@63333B! 0 A0 0 0  0 `0 `0 A @ `0 `00000000000000000000000000000000000000000000  000000 0000000000000000000000000000` ;ocaml-book-1.0/fr/html/book-ora059.gif0000644000000000000000000000737707073350152014314 0ustar GIF89aUUUrrr!,H0I8ͻ`(dIhlp,tmߵ:pTxȤl:%ǧt:F2*.n-%<<{"w]kFO _ |ÂbuZΌłٻ׷xsgJjbЦ@ $ A <<(8! x%$%1|cd `Ȃ9rqђ')na ^WÙA҄{YlB#ysH*Nj᮵uEX3”ԛm-X!/imMm=5ك.`8nUVv "$'Hbh+薋27_ Jb1x;gDiHbL6?VN e\&U^~YBjbkz8()5AAxXiɦ(x!*8)PX_y6s W%&PPqQJh-HJFVQ򔐬e=\F\It(Y_sOm*I*d' rJ+;nV*+ 5 fSJ]*Kh>N2Twuk{**Ma fTt{ĵ S*kwA%rڧg&5;~+-Q %MKNq# O-(Xc`8W׮) w=/u\[S͆FeZحƍUH!p:PFa *H$ߴȾ{+AfSĐV}~p줱QY}« M裲M7?{fO{{,͐ @"3J+Os->k=?+g+zӔ߂I7luSXxc Tdq sId@ґ $#!$LTX6/ y {bjF=iZP:xH{&1WBhE.. F"0x&-pbT [A ^q\>~ d$BrL"6XQ"9GB_ GER,P5Lf)I^' 􃥛h(X򓣻nJr쥍xIwWSg06arknHG'ΰʔn0Vov8:qY|)dS)..H)6PXyzm,px.Vg?.WSKG9̢^HcS\ %Ȱ^-1Ca!-CQ M>+Z=n"3Qw&wRR*!ySd:JKrHYF|˦grg!awe1ziՂ&ZK ~礏TeQ}(kMM)y恧iD7 ~Xjf6x$l n390N*8hDœmax86n/AP?eB)W\Ԍ itacqXsrpYr Cs؋|ٓx_9:cx[WYc|gdØV7H(X.$*hi]A-z[luoY/yi NvN9gT8 ڟ9AvXC)mci"9ZN Kɔ7SSy$mJۖƜvumcaINĈ9Džhى!zIЖࣣp.E:LB+pH/auYtSu9z9nwp~2"~ fxbE+{#wU\?  V2]&4~Y{TzFgZʪEz#tC|&y=ڤR_%U\(鲠 ?ʬ b Y6$$3:5Wҡ k%2٭H9$7A7n4nw§V* K-:`w31[u{ ٱ"! ;ocaml-book-1.0/fr/html/book-ora078.html0000644000000000000000000005650407421273601014510 0ustar Autres bibliothques de la distribution Prcdent Index Suivant

Autres bibliothques de la distribution

Les autres bibliothques fournies avec la distribution du langage Objective CAML portent sur les extensions suivantes :
  • graphisme, avec le module Graphics, portable, qui a t dcrit au chapitre 5 ;
  • arithmtique exacte, comportant plusieurs modules, et permettant d'effectuer des calculs exacts sur les entiers et les rationnels. La reprsentation des nombres utilise les entiers d'Objective CAML quand cela est possible ;
  • filtrage d'expressions rgulires, permettant des manipulations de chanes et de textes plus aises. Le module Str sera dcrit au chapitre 11 ;
  • appels systme Unix, avec le module Unix qui permet d'effectuer depuis Objective CAML des appels systme Unix. Une grande partie de cette bibliothque est nanmoins compatible avec Windows. Cette bibliothque sera utilise aux chapitres 18 et 20 ;
  • processus lgers, comportant plusieurs modules qui seront largement dcrits et utiliss au chapitre 19 ;
  • accs aux bases de donnes NDBD, ne fonctionnant que sous Unix et qui ne sera pas dcrit ;
  • chargement dynamique de code-octet, constitue du seul module Dynlink.
On dcrira par l'usage les bibliothques sur les grands nombres et le chargement dynamique.

Arithmtique exacte

La bibliothque des grands nombres offre une arithmtique exacte sur les entiers et les rationnels. Les valeurs de type int et float connaissent deux limitations : les calculs sur les entiers sont faits modulo le plus grand entier positif, ce qui peut provoquer des dbordements passant inaperus ; les rsultats de calculs sur les flottants sont arrondis, ce qui, par propagation peut conduire des erreurs. La bibliothque prsente ici pallie ces dfauts.

Cette bibliothque est crite en partie en C. Pour cette raison, il faudra construire une boucle d'interaction incluant ce code en utilisant la commande :
ocamlmktop -custom -o top nums.cma -cclib -lnums
Elle est constitue de plusieurs modules. Les deux plus importants sont Num pour toutes les oprations et Arith_status pour le contrle des options des calculs. Le type gnral num est un type somme regroupant trois types de base :

type num = Int of int
| Big_int of big_int
| Ratio of ratio
Les types big_int et ratio sont abstraits.

Les oprations sur les valeurs de type num sont suivies du symbole /. Par exemple l'addition de deux num s'crira +/ et sera de type num -> num -> num. Il en sera de mme pour les comparaisons. Voici un premier exemple qui calcule la factorielle :

# let rec fact_num n =
if Num.(<=/) n (Num.Int 0) then (Num.Int 1)
else Num.( */ ) n (fact_num ( Num.(-/) n (Num.Int 1)));;
val fact_num : Num.num -> Num.num = <fun>
# let r = fact_num (Num.Int 100);;
val r : Num.num = Num.Big_int <abstr>
# let n = Num.string_of_num r in (String.sub n 0 50) ^ "..." ;;
- : string = "93326215443944152681699238856266700490715968264381..."


L'ouverture du module Num rend le code de fact_num plus facile lire :

# open Num ;;
# let rec fact_num n =
if n <=/ (Int 0) then (Int 1)
else n */ (fact_num ( n -/ (Int 1))) ;;
val fact_num : Num.num -> Num.num = <fun>


Les calculs sur les rationnels sont eux aussi exacts. Si on cherche calculer le nombre e en suivant la dfinition suivante :
e = lim
 
m ->



1 +
1

m



m



 
On crira une fonction qui calcule cette limite jusqu' un certain m.

# let calc_e m =
let a = Num.(+/) (Num.Int 1) ( Num.(//) (Num.Int 1) m) in
Num.( **/ ) a m;;
val calc_e : Num.num -> Num.num = <fun>
# let r = calc_e (Num.Int 100);;
val r : Num.num = Ratio <abstr>
# let n = Num.string_of_num r in (String.sub n 0 50) ^ "..." ;;
- : string = "27048138294215260932671947108075308336779383827810..."


Le module Arith_status permet de contrler une partie des calculs comme la normalisation des rationnels, l'approximation pour affichage et le traitement des dnominateurs nuls. La fonction arith_status affiche l'tat de ces indicateurs.

# Arith_status.arith_status();;

Normalization during computation --> OFF
(returned by get_normalize_ratio ())
(modifiable with set_normalize_ratio <your choice>)

Normalization when printing --> ON
(returned by get_normalize_ratio_when_printing ())
(modifiable with set_normalize_ratio_when_printing <your choice>)

Floating point approximation when printing rational numbers --> OFF
(returned by get_approx_printing ())
(modifiable with set_approx_printing <your choice>)

Error when a rational denominator is null --> ON
(returned by get_error_when_null_denominator ())
(modifiable with set_error_when_null_denominator <your choice>)
- : unit = ()


Ils pourront tre modifis selon les besoins d'un calcul. Par exemple si on demande l'affichage de la valeur approche des rationnels, on obtiendra, pour le calcul prcdent :

# Arith_status.set_approx_printing true;;
- : unit = ()
# Num.string_of_num (calc_e (Num.Int 100));;
- : string = "0.270481382942e1"


Les calculs avec les grands nombres sont plus longs qu'avec les entiers et les valeurs occupent plus d'espace mmoire. Nanmoins, cette bibliothque essaie de conserver les reprsentations les plus conomiques. De toute faon, viter la propagation d'erreur d'arrondis et pouvoir calculer sur des grands nombres, justifient une perte d'efficacit.

Chargement dynamique de code

Le module Dynlink offre la possibilit de charger dynamiquement des programmes sous la forme de code-octet. Le chargement dynamique de code procure les avantages suivants :
  • rduire la taille en code d'un programme. Si certains modules ne sont pas utiliss, ils ne seront pas chargs.
  • pouvoir choisir l'excution le module charger. Selon certaines conditions d'excution on choisit un module plutt qu'un autre charger.
  • pouvoir modifier le comportement d'un module en cours d'excution. L aussi selon certaines conditions le programme peut charger un nouveau module et masquer le code du prcdent.
La boucle d'interaction d'Objective CAML utilise dj un tel mcanisme. Il est pratique que le programmeur puisse y avoir accs.

Lors du chargement d'un fichier objet (d'extension .cmo), les diffrentes expressions sont values. Le programme principal, qui a demand le chargement dynamique du code, n'a pas accs aux noms des dclarations. Donc c'est au module charg dynamiquement de mettre jour une table de fonctions utilises par le programme principal.

 

Warning


Le chargement dynamique de code ne fonctionne que pour les fichiers objets en code-octet.


Description du module

Le chargement dynamique d'un fichier de code-octet f.cmo ncessite d'une part de connatre les chemins de recherche o aller trouver ce fichier et d'autre part les noms des modules dont il se sert. Par dfaut, les fichiers de code-octet chargs dynamiquement n'ont pas accs aux chemins et aux modules des bibliothques de la distribution. Il faut alors ajouter ce chemin et le nom des modules ncessaires au chargement dynamique d'un module.

init : unit -> unit
    initialise le chargement dynamique
add_interfaces : string list -> string list -> unit
    ajoute des noms de modules et des chemins de recherche pour le chargement
loadfile : string -> unit
    charge un fichier code-octet
clear_avalaible_units : unit -> unit
    met vide les noms de modules chargeables et les chemins de recherche
add_avalaible_units : (string * Digest.t) list -> unit
    ajoute un nom de module et une empreinte pour le chargement sans avoir besoin du fichier interface
allow_unsafe_modules : bool -> unit
    accepte de charger des fichiers contenant des dclarations external
loadfile_private : string -> unit
    le module charg n'est pas accessible aux prochains modules chargs
L'empreinte d'une interface .cmi peut tre obtenue par la commande extract_crc qui se trouve dans le catalogue des bibliothques de la distribution.

Figure 8.10 : Fonctions du module Dynlink


De nombreuses erreurs peuvent survenir lors d'une demande de chargement d'un module. Non seulement le fichier doit exister avec la bonne interface dans un des chemins de recherche, mais de plus, le code-octet doit tre correct et chargeable. Ces erreurs sont regroupes dans un type error utilis comme argument de l'exception Error et de la fonction error de type error -> string qui permet de convertir une erreur en description claire.

Exemple

Pour crire un petit programme qui nous permettra d'illuster le chargement dynamique de code-octet, on se donne trois modules :
  • F qui contient la dfinition d'une rfrence sur une fonction f ;
  • Mod1 et Mod2 qui modifient de faon diffrente la fonction rfrence par F.f.
Le module F est dfinit par le fichier f.ml :

let g () =
print_string "Je suis la fonction 'f' par dfaut\n" ; flush stdout ;;
let f = ref g ;;


Le module Mod1 est dfinit dans le fichier mod1.ml :

print_string "Le module 'Mod1' modifie la valeur de 'F.f'\n" ; flush stdout ;;
let g () =
print_string "Je suis la fonction 'f' du module 'Mod1'\n" ;
flush stdout ;;
F.f := g ;;


Le module Mod2 est dfinit dans le fichier mod2.ml :

print_string "Le module 'Mod2' modifie la valeur de 'F.f'\n" ; flush stdout ;;
let g () =
print_string "Je suis la fonction 'f' du module 'Mod2'\n" ;
flush stdout ;;
F.f := g ;;


On dfinit enfin, dans le fichier main.ml, un programme principal qui appelle la fonction rfrence par F.f originale, charge le module Mod1 et rappelle F.f, puis charge le module Mod2 et appelle une dernire fois la fonction F.f :

let main () =
try
Dynlink.init () ;
Dynlink.add_interfaces [ "Pervasives"; "F" ; "Mod1" ; "Mod2" ]
[ Sys.getcwd() ; "/usr/local/lib/ocaml/" ] ;
!(F.f) () ;
Dynlink.loadfile "mod1.cmo" ; !(F.f) () ;
Dynlink.loadfile "mod2.cmo" ; !(F.f) ()
with
Dynlink.Error e -> print_endline (Dynlink.error_message e) ; exit 1 ;;

main () ;;
Le programme principal doit, outre initialiser le chargement dynamique, dclarer par un appel Dynlink.add_interfaces les interface utilises.

On compile l'ensemble de ces modules :
$ ocamlc -c f.ml
$ ocamlc -o main dynlink.cma f.cmo main.ml
$ ocamlc -c f.cmo mod1.ml
$ ocamlc -c f.cmo mod2.ml
Si l'on excute le programme main, on obtient :
$ main
Je suis la fonction 'f' par dfaut
Le module 'Mod1' modifie la valeur de 'F.f'
Je suis la fonction 'f' du module 'Mod1'
Le module 'Mod2' modifie la valeur de 'F.f'
Je suis la fonction 'f' du module 'Mod2'
Au chargement dynamique d'un module, son code est excut. C'est ce que manifeste, dans notre exemple, les affichages commenant par Le module .... Les ventuels effets de bord qu'il contient sont donc rpercuts au niveau du programme qui a effectu ce chargement. C'est pourquoi, les diffrents appels F.f appellent des fonctions diffrentes.

La bibliothque Dynlink offre le mcanisme de base pour le chargement dynamique de code-octet. Il reste au programmeur grer des tables pour que ce chargement ait vritablement un effet.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora152.html0000644000000000000000000000720107421273602014470 0ustar Introduction Prcdent Index Suivant

Introduction

Les chapitres 14 et 15 ont respectivement prsent deux modles d'organisation d'applications : le modle fonctionnel/modulaire et le modle objet. Ces deux modles rpondent, chacun leur manire, aux besoins du dveloppement d'une application :

  • organisation logique d'un programme : module ou classe
  • compilation spare : module simple
  • types abstraits de donnes : module (type abstrait) ou objet
  • rutilisation des composants : foncteurs/partage de types avec polymorphisme paramtrique ou hritage/sous-typage avec des classes paramtres
  • modifiabilit des composants : liaison tardive (objet)
Le dveloppement d'une application modulaire commence par son dcoupage en units logiques : les modules. Ensuite vient la ralisation de leur spcification en crivant leur signature, et en dernier l'implantation. Lors de la ralisation d'un module, il peut tre ncessaire de modifier sa signature ou celle de ses paramtres; il faut alors modifier les sources de celui-ci. Ceci est peu satisfaisant si ce mme module est dj utilis par une autre application. Nanmoins cette dmarche offre un cadre strict et rassurant pour le programmeur.

Dans le modle objet, l'analyse d'un problme aboutit la description de relations entre classes. Si par la suite une classe n'offre pas les services attendus, il est toujours possible de la sous-classer pour l'tendre. Cette dmarche permet de rcuprer de grandes hirarchies de classes sans modifier leurs sources, ne modifiant pas ainsi le comportement d'une autre application l'utilisant. Malheureusement cette technique est inflationniste et pose des difficults de duplication avec l'hritage multiple.

De nombreux problmes ncessitent des types de donnes rcursifs et des oprations qui manipulent des valeurs de ces types. Il arrive souvent que le problme volue, soit en cours de ralisation, soit en maintenance, impliquant une extension des types et des oprations. Aucun de ces deux modles ne permet l'extension dans les deux sens. En fonctionnel/modulaire, les types ne sont pas extensibles, par contre on peut crer de nouvelles fonctions (oprations) sur ces types. En objet, on peut tendre les donnes mais pas les traitements (en crant une nouvelle sous-classe d'une classe abstraite qui implante ses mthodes). En cela ces deux modles sont duaux.

L'avantage de runir ces deux modles dans le mme langage est de pouvoir choisir le modle le plus appropri pour la rsolution du problme pos et de les mixer dans le but de dpasser les limitations de chaque modle.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora009.html0000644000000000000000000002074507421273601014500 0ustar Installation Prcdent Index Suivant

Installation

L'installation d'Objective CAML ncessite environ 10Mo d'espace libre sur son disque dur. Le logiciel se dsinstalle facilement sans corrompre le systme.

Installation sous Windows

Le fichier de la distribution binaire s'appelle : ocaml-2.04-win.zip, indiquant le numro de version (ici 2.04) et le systme d'exploitation.

Warning


Objective CAML ne fonctionne que sur les versions rcentes de Windows : Windows 95, 98 et NT. Ne cherchez pas l'installer sur Windows 3.x OS2/Warp.


  1. Le fichier est en format compress (.zip); la premire opration effectuer est de le dcompresser. Pour cela, utilisez votre logiciel prfr de dcompression. Vous obtenez alors un catalogue dont la racine se nomme ocaml. Vous pouvez placer ce catalogue n'importe quel emplacement de votre disque dur. On le note <caml-dir> par la suite.
  2. Ce catalogue comprend :

    • deux sous-catalogues : bin pour les binaires et lib pour les bibliothques;
    • deux fichiers << textes >> : License.txt et Changes.txt contenant la licence d'utilisation du logiciel et les modifications par rapport aux versions antrieures;
    • une application : OCamlWin correspondant l'application principale;
    • un fichier de configuration : Ocamlwin.ini qu'il faudra modifier (voir le point suivant);
    • deux fichiers de notes sur la version : le premier, Readme.gen, pour cette version et le second, Readme.win, pour la version sous Windows.
  3. Si vous avez choisi un autre catalogue que c:\ocaml comme catalogue de votre racine, il est alors ncessaire de l'indiquer au fichier de configuration. ditez-le avec Wordpad et changez la ligne dfinissant CmdLine qui est de la forme :
    CmdLine=ocamlrun c:\ocaml\bin\ocaml.exe -I c:\ocaml\lib
    
    en
    CmdLine=ocamlrun <caml-dir>\bin\ocaml.exe -I <caml-dir>\lib
    
    Il faut remplacer les chemins de recherche des binaires et des bibliothques par le nom du catalogue racine d'Objective CAML. Si nous avons choisi C:\Lang\ocaml comme catalogue racine (<caml-dir>), la modification devient :
    CmdLine=ocamlrun C:\Lang\ocaml\bin\ocaml.exe -I C:\Lang\ocaml\lib
    
  4. Copier le fichier OCamlWin.ini dans le catalogue principal du systme, c'est--dire C:\windows ou C:\win95 ou C:\winnt selon l'installation de votre systme.
Il est alors temps de tester l'application OCamlWin en double-cliquant dessus. Vous obtiendrez la fentre de la figure 1.1.




Figure 1.1 : Fentre Objective CAML sous Windows


La configuration des commandes en ligne, lances partir d'une fentre DOS, s'effectue en modifiant le chemin d'accs aux commandes (PATH) et la variable d'accs aux bibliothques d'Objective CAML (CAMLLIB), de la manire suivante :
PATH=%PATH%;<caml-dir>\bin
set CAMLLIB=<caml-dir>\lib
o <caml-dir> est remplac par le chemin o Objective CAML est install.

Ces deux commandes peuvent tre incluses dans le fichier autoexec.bat que tout bon DOS possde. Pour tester les commandes en ligne, taper dans une fentre DOS, la commande ocaml. Celle-ci excute le fichier :
<caml-dir>/bin/ocaml.exe
correspondant la boucle d'interaction en mode texte d'Objective CAML. Pour sortir de cette commande, crire #quit;;.

L'installation d'Objective CAML partir des sources sous Windows n'est pas chose aise, car elle ncessite d'utiliser des logiciels payants, en particulier le compilateur C de Microsoft. Reportez-vous au fichier Readme.win de la distribution binaire pour avoir des prcisions.

Installation sous Linux

L'installation pour Linux possde aussi une distribution binaire facile installer sous forme paquetage rpm. L'installation partir des sources est dcrite dans la section 1. Le fichier tlcharger est : ocaml-2.04-2.i386.rpm que l'on emploiera de la manire suivante avec les droits de root :
rpm -i ocaml-2.04-2.i386.rpm
qui installe les commandes dans le catalogue /usr/bin et les bibliothques dans le catalogue /usr/lib/ocaml.

Pour tester l'installation, taper : ocamlc -v qui affiche la version d'Objective CAML installe sur la machine.
ocamlc -v
The Objective Caml compiler, version 2.04
Standard library directory: /usr/lib/ocaml
Vous pouvez aussi excuter la commande ocaml qui affiche l'entte de la boucle d'interaction.
        Objective Caml version 2.04

# 
Le caractre # est l'invite (prompt) de la boucle d'interaction. La sortie de cette boucle d'interaction s'effectue par la directive #quit;;, ou en tapant CTRL-D. Les deux points-virgule indiquent la fin d'une phrase Objective CAML.

Installation sous MacOS

La distribution pour MacOS existe aussi sous forme d'un binaire auto-extractible. Le fichier tlcharger est : ocaml-2.04-mac.sea.bin qui est compress. On utilisera son logiciel prfr pour le dcompresser. Ensuite il suffit de lancer l'archive auto-extractible pour l'installation et suivre les instructions affiches dans la fentre de dialogue pour le choix de l'emplacement de la distribution.

Pour la distribution MacOS X server, suivre l'installation sous Unix partir des sources.

Installation sous Unix partir des sources

L'installation d'Objective CAML pour les systmes de la famille Unix s'effectue partir de la distribution avec sources. En effet il sera ncessaire de compiler le systme Objective CAML. Pour ce faire, il faut avoir un compilateur C sur sa machine Unix, ce qui est gnralement le cas, ou d'en tlcharger un comme gcc qui fonctionne sur la plupart des systmes Unix. Le fichier de la distribution d'Objective CAML contenant les sources est : ocaml-2.04.tar.gz. Le fichier INSTALL dcrit, de manire fort claire, les diffrentes tapes de configuration, de construction puis d'installation des binaires.

Installation de la documentation HTML

La documentation en anglais d'Objective CAML existe aussi sous forme d'une arborescence de fichiers HTML que l'on retrouve dans le catalogue docs du cdrom.

Cette documentation est un manuel de rfrence. Il n'est pas de lecture facile pour le dbutant. Nanmoins il est fort utile pour la description du langage, de ses outils et de ses bibliothques. Il deviendra vite indispensable pour qui souhaite crire un programme de plus de dix lignes.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora084.html0000644000000000000000000000340507421273602014476 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

Ce chapitre prsente l'allocation dynamique de mmoire et les algorithmes de rcupration automatique, en particulier celui d'Objective CAML qui est une combinaison des diffrents algorithmes prsents. La premire section rappelle les diffrentes classes de mmoire et leurs caractristiques. La deuxime section dcrit l'allocation mmoire dans les programmes et compare les rcuprations implicites et explicites. La troisime section prsente les principaux algorithmes de rcupration automatique de mmoire. La quatrime section dtaille celui d'Objective CAML. La cinquime section utilise le module Gc pour le contrle du tas. La sixime section montre l'intrt des pointeurs faibles du module Weak pour la cration de caches mmoire.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora188.html0000644000000000000000000017455007421273602014515 0ustar Client-serveur Prcdent Index Suivant

Client-serveur

La communication entre processus sur une mme machine ou sur des machines diffrentes travers des sockets TCP/IP est un mode de communication point--point asynchrone. La fiabilit des transmissions est assure par le protocole TCP. Il est nanmoins possible de simuler une diffusion un ensemble de processus en effectuant des communications point--point sur tous les rcepteurs.

Le rle des diffrents processus entrant en jeu dans la communication d'une application est en rgle gnrale asymtrique. C'est le cas pour les architectures client-serveur. Un serveur est un processus (ou plusieurs) acceptant des requtes et tchant d'y rpondre. Le client, lui-mme un processus, envoie une requte au serveur en esprant une rponse.

Schma d'actions d'un client-serveur

Un serveur ouvrira un service sur un port donn et se mettra en attente de connexions de la part de futurs clients. La figure 20.1 montre le droulement des principales tches d'un serveur et d'un client.



Figure 20.1 : schma des actions d'un serveur et d'un client


Un client peut se connecter un service partir du moment o le serveur est la phase d'acceptation de connexions (accept). Pour cela il devra connatre le numro IP de la machine serveur et le numro de port du service. S'il ne connat pas le numro IP, il devra demander une rsolution nom/numro par la fonction gethostbyname. Une fois la connexion accepte par le serveur, chaque programme pourra communiquer via les canaux d'entres-sorties de la socket cre de part et d'autre.

Programmation d'un client-serveur

La mcanique de programmation d'un client-serveur va donc suivre le schma dcrit la figure 20.1. Ces tches sont raliser dans tous les cas. Pour cela nous crirons les fonctions gnriques d'agencement de ces tches, paramtres par les fonctions particulires un serveur donn. On illustrera ces programmes par un premier serveur qui accepte une connexion partir d'un client, attend sur cette prise de communication qu'une ligne soit passe, la convertit en MAJUSCULES et la renvoie convertie au client.

La figure 20.2 montre la communication entre ce service et diffrents clients.



Figure 20.2 : service MAJUSCULE et des clients


Certains tournent sur la mme machine que le serveur et d'autres se trouvent sur des machines distantes.

Dans la suite de ce paragraphe nous verrons
  1. Comment crire le code d'un << serveur gnrique >> pour l'instancier en notre service particulier de mise en majuscules.
  2. Comment tester ce serveur, sans avoir crire de client, en utilisant la commande telnet.
  3. Comment implanter deux types de clients :

    - le premier, squentiel, c'est dire qu'aprs l'envoi d'une requte, il attend la rponse ;

    - le second, parallle, qui spare les tches d'envoi et de rception. Il y aura donc deux processus pour ce client.

Code du serveur

Un serveur se dcoupe en deux parties : l'attente d'une connexion et le traitement suite une connexion.

Serveur gnrique

Le serveur gnrique establish_server dcrit ci-dessous est une fonction qui prend en premier argument la fonction de service (server_fun) charge de traiter les requtes et, en second, l'adresse de la socket dans le domaine Internet qui sera l'coute des requtes. Cette fonction utilise la fonction auxiliaire domain_of qui extrait le domaine d'une socket partir de son adresse.

En fait, la fonction establish_server fait partie des fonctions de haut niveau du module Unix. Nous en donnons l'implantation de la distribution.

# let establish_server server_fun sockaddr =
let domain = domain_of sockaddr in
let sock = Unix.socket domain Unix.SOCK_STREAM 0
in Unix.bind sock sockaddr ;
Unix.listen sock 3;
while true do
let (s, caller) = Unix.accept sock
in match Unix.fork() with
0 -> if Unix.fork() <> 0 then exit 0 ;
let inchan = Unix.in_channel_of_descr s
and outchan = Unix.out_channel_of_descr s
in server_fun inchan outchan ;
close_in inchan ;
close_out outchan ;
exit 0
| id -> Unix.close s; ignore(Unix.waitpid [] id)
done ;;
val establish_server :
(in_channel -> out_channel -> 'a) -> Unix.sockaddr -> unit = <fun>


Pour construire compltement un serveur en tant qu'excutable autonome paramtr par le numro de port, on crit la fonction main_serveur qui prend toujours en paramtre la fonction de service. Elle utilise le paramtre de la ligne de commande comme numro de port du service. On utilise la fonction auxiliaire get_my_addr qui retourne l'adresse de la machine locale.

# let get_my_addr () =
(Unix.gethostbyname(Unix.gethostname())).Unix.h_addr_list.(0) ;;
val get_my_addr : unit -> Unix.inet_addr = <fun>

# let main_serveur serv_fun =
if Array.length Sys.argv < 2 then Printf.eprintf "usage : serv_up port\n"
else try
let port = int_of_string Sys.argv.(1) in
let mon_adresse = get_my_addr()
in establish_server serv_fun (Unix.ADDR_INET(mon_adresse, port))
with
Failure("int_of_string") ->
Printf.eprintf "serv_up : bad port number\n" ;;
val main_serveur : (in_channel -> out_channel -> 'a) -> unit = <fun>


Code du service

La mcanique gnrale est en place. Pour l'illustrer, il reste dfinir le service. Celui-ci est un convertisseur de chanes en majuscules. Il attend une ligne sur le canal d'entre, la convertit et l'crit sur le canal de sortie en vidant le tampon.

# let uppercase_service ic oc =
try while true do
let s = input_line ic in
let r = String.uppercase s
in output_string oc (r^"\n") ; flush oc
done
with _ -> Printf.printf "Fin du traitement\n" ; flush stdout ; exit 0 ;;
val uppercase_service : in_channel -> out_channel -> unit = <fun>


Pour rcuprer correctement les exceptions provenant du module Unix, on encapsule l'appel au dmarrage du service dans la fonction ad hoc du module Unix :

# let go_uppercase_service () =
Unix.handle_unix_error main_serveur uppercase_service ;;
val go_uppercase_service : unit -> unit = <fun>


Compilation et test du service

On regroupe ces fonctions dans le fichier serv_up.ml auquel on ajoute un appel effectif la fonction go_uppercase_service. On compile ce fichier en prcisant l'utilisation du module Unix.
ocamlc -i -custom -o serv_up.exe unix.cma serv_up.ml -cclib -lunix
L'affichage de la compilation (option -i) donne :
val establish_server :
  (in_channel -> out_channel -> 'a) -> Unix.sockaddr -> unit
val main_serveur : (in_channel -> out_channel -> 'a) -> unit
val uppercase_service : in_channel -> out_channel -> unit
val go_uppercase_service : unit -> unit
On lance le serveur en crivant :
serv_up.exe 1400
Le port choisi est ici 1400. Maintenant la machine o a t lance cette commande accepte les connexions sur ce port.

Tester avec telnet

On peut d'ores et dj tester le serveur en utilisant un client existant d'envoi et de rception de lignes de caractres. L'utilitaire telnet, qui normalement est un client du service telnetd sur le port 23 et que l'on utilise alors comme commande de connexion distante peut tre dtourn de son rle si on lui passe en argument une machine et un autre numro de port. Cet utilitaire existe sur les diffrents systmes d'exploitation. Pour tester notre serveur, sous Unix, on tapera :
$ telnet boulmich 1400 
Trying 132.227.89.6...
Connected to boulmich.ufr-info-p6.jussieu.fr.
Escape character is '^]'.
L'adresse IP de boulmich est 132.227.89.6 et son nom complet, qui contient son nom de domaine, est boulmich.ufr-info-p6.jussieu.fr. C'est bien ce qu'affiche telnet. Le client attend une frappe au clavier et l'envoie au serveur que nous avons lanc sur boulmich avec le port 1400. Il attendra la rponse du serveur et l'affichera :
Le petit chat est mort.
LE PETIT CHAT EST MORT.
On obtient bien le rsultat escompt.
ON OBTIENT BIEN LE RSULTAT ESCOMPT.
Les phrases entres par l'utilisateur sont en minuscules et celles renvoyes par le serveur sont en majuscules. C'est justement le rle de ce mini-service que d'assurer cette conversion.

Pour sortir de ce client il sera ncessaire soit de fermer la fentre d'o il a t excut, soit d'utiliser la commande kill. La socket de communication du client sera alors ferme, ce qui provoquera du ct serveur la disparition de la socket de service. ce moment l le serveur affiche le message << Fin de traitement >>, puis le processus associ la fonction de service termine.

Code du client

Autant le serveur est naturellement parallle (on dsire traiter une requte tout en en acceptant d'autres, jusqu' une certaine limite), autant le client peut l'tre ou ne pas l'tre selon la nature de l'application dvelopper. Nous donnerons ci-dessous deux versions de client. Mais auparavant, nous prsentons deux fonctions utiles pour l'criture de ces clients.

La fonction open_connection du module Unix permet partir d'une socket INET d'obtenir un couple de canaux classiques d'entres-sorties sur cette socket.

Le code suivant est issu de la distribution du langage.

# let open_connection sockaddr =
let domain = domain_of sockaddr in
let sock = Unix.socket domain Unix.SOCK_STREAM 0
in try Unix.connect sock sockaddr ;
(Unix.in_channel_of_descr sock , Unix.out_channel_of_descr sock)
with exn -> Unix.close sock ; raise exn ;;
val open_connection : Unix.sockaddr -> in_channel * out_channel = <fun>


De mme, la fonction shutdown_connection effectue la fermeture en envoi de la socket.

# let shutdown_connection inchan =
Unix.shutdown (Unix.descr_of_in_channel inchan) Unix.SHUTDOWN_SEND ;;
val shutdown_connection : in_channel -> unit = <fun>


Client squentiel

partir de ces fonctions on peut crire la fonction principale du client prenant en argument la fonction d'envoi de requtes et de rception des rponses. Elle analyse les arguments de la liste de commande pour obtenir les paramtres de connexion avant de lancer le traitement.

# let main_client client_fun =
if Array.length Sys.argv < 3
then Printf.printf "usage : client serveur port\n"
else let serveur = Sys.argv.(1) in
let serveur_adr =
try Unix.inet_addr_of_string serveur
with Failure("inet_addr_of_string") ->
try (Unix.gethostbyname serveur).Unix.h_addr_list.(0)
with Not_found ->
Printf.eprintf "%s : serveur inconnu\n" serveur ;
exit 2
in try
let port = int_of_string (Sys.argv.(2)) in
let sockadr = Unix.ADDR_INET(serveur_adr,port) in
let ic,oc = open_connection sockadr
in client_fun ic oc ;
shutdown_connection ic
with Failure("int_of_string") -> Printf.eprintf "bad port number";
exit 2 ;;
val main_client : (in_channel -> out_channel -> 'a) -> unit = <fun>


Il ne reste plus qu' crire la fonction de traitement du client.

# let client_fun ic oc =
try
while true do
print_string "Requte : " ;
flush stdout ;
output_string oc ((input_line stdin)^"\n") ;
flush oc ;
let r = input_line ic
in Printf.printf "Rponse : %s\n\n" r;
if r = "FIN" then ( shutdown_connection ic ; raise Exit) ;
done
with
Exit -> exit 0
| exn -> shutdown_connection ic ; raise exn ;;
val client_fun : in_channel -> out_channel -> unit = <fun>
La fonction client_fun entre dans une boucle a priori sans fin qui lit le clavier, envoie la chane au serveur, rcupre la chane transforme en majuscules et l'affiche. Si la chane vaut "FIN" l'exception Exit est dclenche pour sortir de la boucle. Si une autre exception est dclenche, typiquement si le serveur disparat, la fonction interrompt son calcul.

Le programme client devient donc :

# let go_client () = main_client client_fun ;;
val go_client : unit -> unit = <fun>


On regroupe toutes ces fonctions dans un fichier nomm client_seq.ml en ajoutant l'appel la fonction go_client. On le compile ensuite avec la ligne de commande suivante :
ocamlc -i -custom -o client_seq.exe unix.cma client_seq.ml -cclib -lunix
L'excution du client est alors la suivante :
$ client_seq.exe boulmich 1400 
Requte : Le petit chat est mort.
Rponse : LE PETIT CHAT EST MORT.

Requte : On obtient le rsultat escompt.
Rponse : ON OBTIENT LE RSULTAT ESCOMPT.

Requte : fin
Rponse : FIN

Client parallle avec fork

Le client parallle propos ici rpartit sa tche sur deux processus : l'un d'mission et l'autre de rception. Ils partagent la mme socket. Les fonctions associes chacun des processus sont passes en paramtre.

Voici le texte du programme ainsi modifi.

# let main_client client_pere_fun client_fils_fun =
if Array.length Sys.argv < 3
then Printf.printf "usage : client serveur port\n"
else
let serveur = Sys.argv.(1) in
let serveur_adr =
try Unix.inet_addr_of_string serveur
with Failure("inet_addr_of_string")
-> try (Unix.gethostbyname serveur).Unix.h_addr_list.(0)
with Not_found ->
Printf.eprintf "%s : serveur inconnu\n" serveur ;
exit 2
in try
let port = int_of_string (Sys.argv.(2)) in
let sockadr = Unix.ADDR_INET(serveur_adr,port) in
let ic,oc = open_connection sockadr
in match Unix.fork () with
0 -> if Unix.fork() = 0 then client_fils_fun oc ;
exit 0
| id -> client_pere_fun ic ;
shutdown_connection ic ;
ignore (Unix.waitpid [] id)
with
Failure("int_of_string") -> Printf.eprintf "bad port number" ;
exit 2 ;;
val main_client : (in_channel -> 'a) -> (out_channel -> unit) -> unit = <fun>
Le comportement attendu des paramtres est : le (petit-)fils envoie la requte et le pre reoit la rponse.

Cette architecture prend du sens si le fils doit envoyer plusieurs requtes, le pre recevra les rponses des premires requtes au fur et mesure de leur traitement. On reprend donc l'exemple prcdent de conversion de chanes en majuscules mais en modifiant le ct client. Celui-ci lit le texte convertir dans un fichier et crit la rponse dans un autre fichier. Pour cela nous aurons besoin d'une fonction de copie d'un canal (ic) dans un autre (oc) respectant notre petit protocole (c'est dire reconnaissant la chane "FIN").

# let copie_canaux ic oc =
try while true do
let s = input_line ic
in if s = "FIN" then raise End_of_file
else (output_string oc (s^"\n"); flush oc)
done
with End_of_file -> () ;;
val copie_canaux : in_channel -> out_channel -> unit = <fun>


On crit les deux fonctions destines au fils et au pre du schma de client parallle :

# let fils_fun in_file out_sock =
copie_canaux in_file out_sock ;
output_string out_sock ("FIN\n") ;
flush out_sock ;;
val fils_fun : in_channel -> out_channel -> unit = <fun>
# let pere_fun out_file in_sock = copie_canaux in_sock out_file ;;
val pere_fun : out_channel -> in_channel -> unit = <fun>


Cela permet d'crire la fonction principale du client. Elle devra rcuprer sur la ligne de commande deux paramtres supplmentaires : le nom du fichier d'entre et le nom du fichier de sortie.

# let go_client () =
if Array.length Sys.argv < 5
then Printf.eprintf "usage : client_par serveur port filein fileout\n"
else let in_file = open_in Sys.argv.(3)
and out_file = open_out Sys.argv.(4)
in main_client (pere_fun out_file) (fils_fun in_file) ;
close_in in_file ;
close_out out_file ;;
val go_client : unit -> unit = <fun>


On runit tout notre matriel dans le fichier client_par.ml (sans oublier l'appel effectif go_client), on compile. On cre alors le fichier toto.txt contenant le texte convertir, disons :
Le petit chat est mort.
On obtient le rsultat escompt.
On peut alors tester en tapant :
client_par.exe boulmich 1400 toto.txt result.txt
Le fichier result.txt contient le texte :
$ more result.txt
LE PETIT CHAT EST MORT.
ON OBTIENT LE RSULTAT ESCOMPT.
Lorsque le client termine, le serveur affiche toujours le message "Fin de traitement".

Client-serveur avec processus lgers

La prsentation prcdente du code d'un serveur gnrique et d'un client parallle utilise la cration de nouveaux processus grce la primitive fork du module Unix. Cela fonctionne bien sous Unix et de nombreux services Unix sont mis en oeuvre par cette technique. Ce n'est cependant pas le cas avec Windows. Pour la portabilit, on crira de prfrence les client-serveur avec les processus lgers qui ont t prsents au chapitre 19. Il sera ncessaire de dterminer les interactions entre les diffrents processus du serveur.

Threads et bibliothque Unix

L'utilisation conjointe de la bibliothque de processus lgers et de la bibliothque Unix provoque le blocage de tous les threads actifs si un appel systme ne rpond pas immdiatement. En particulier, les lectures sur un descripteur de fichiers incluant donc ceux crs par socket, sont bloquantes.

Pour viter ce dsagrment, le module ThreadUnix rimplante la plupart des fonctions d'entres-sorties du module Unix. Les fonctions dfinies dans ce module ne bloqueront que le thread qui effectue l'appel systme. En consquence, les entres-sorties devront tre implantes avec les fonctions de plus bas niveau read et write offertes par le module ThreadUnix. Par exemple, on redfinit la fonction standard de lecture d'une chane de caractres, input_line, de faon ce qu'elle ne bloque pas les autres threads pendant la lecture d'une ligne.

# let my_input_line fd =
let s = " " and r = ref ""
in while (ThreadUnix.read fd s 0 1 > 0) && s.[0] <> '\n' do r := !r ^s done ;
!r ;;
val my_input_line : Unix.file_descr -> string = <fun>

Classes pour un serveur avec threads

Nous reprenons l'exemple du service MAJUSCULE pour en donner une version utilisant les processus lgers. Le passage aux threads ne pose pas de problme puisque notre petite application, aussi bien ct serveur que ct client, lance des processus fonctionnant indpendamment.

Nous avons prcdemment implant un serveur gnrique paramtr par une fonction de service. Nous avons ralis cette abstraction en utilisant le caractre fonctionnel du langage Objective CAML. Nous proposons d'utiliser l'extension objet du langage pour illustrer comment les objets permettent de raliser une abstraction analogue.

L'organisation du serveur repose sur deux classes : serv_socket et connexion. La premire correspond la mise en route du service, la seconde, la fonction de service. Nous avons introduit quelques impressions traant les principales tapes du service.

La classe serv_socket
possde deux variables d'instance : port et socket correspondant au numro de port du service et la socket d'coute. la construction d'un tel objet l'initialisateur effectue les oprations d'ouverture de service et cre cette socket. La mthode run se met en acceptation de connexions, et cre un nouvel objet connexion pour lancer le traitement de la requte. La classe serv_socket utilise la classe connexion prsente au paragraphe suivant. Cette dernire doit normalement tre dfinie avant la classe serv_socket.

# class serv_socket p =
object (self)
val port = p
val mutable sock = ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0

initializer
let mon_adresse = get_my_addr ()
in Unix.bind sock (Unix.ADDR_INET(mon_adresse,port)) ;
Unix.listen sock 3

method private client_addr = function
Unix.ADDR_INET(host,_) -> Unix.string_of_inet_addr host
| _ -> "Unexpected client"

method run () =
while(true) do
let (sd,sa) = ThreadUnix.accept sock in
let connexion = new connexion(sd,sa)
in Printf.printf "TRACE.serv: nouvelle connexion de %s\n\n"
(self#client_addr sa) ;
ignore (connexion#start ())
done
end ;;
class serv_socket :
int ->
object
val port : int
val mutable sock : Unix.file_descr
method private client_addr : Unix.sockaddr -> string
method run : unit -> unit
end


Il est toujours possible d'affiner le serveur en hritant de cette classe et en redfinissant la mthode run.

La classe connexion
Les variables d'instance de cette classe, s_descr et s_addr, seront initialises avec le descripteur et l'adresse de la socket de service crs par accept. Les mthodes sont start, run et stop. La mthode start cre un thread appelant les deux autres et retourne son identificateur qui pourrait tre manipul par l'instance appelante de serv_socket. C'est dans la mthode run que l'on retrouve le corps de la fonction de service. Nous avons un peu modifi la condition de fin de service : on sort sur une chane vide. La mthode stop se contente de fermer le descripteur de la socket de service.

chaque nouvelle connexion sera attribu un numro obtenu par appel la fonction auxiliaire gen_num lors de la cration d'une instance.

# let gen_num = let c = ref 0 in (fun () -> incr c; !c) ;;
val gen_num : unit -> int = <fun>
# exception Fin ;;
exception Fin
# class connexion (sd,sa) =
object (self)
val s_descr = sd
val s_addr = sa
val mutable numero = 0
initializer
numero <- gen_num();
Printf.printf "TRACE.connexion : objet traitant %d cr\n" numero ;
print_newline()

method start () = Thread.create (fun x -> self#run x ; self#stop x) ()

method stop() =
Printf.printf "TRACE.connexion : fin objet traitant %d\n" numero ;
print_newline () ;
Unix.close s_descr

method run () =
try
while true do
let ligne = my_input_line s_descr
in if (ligne = "") or (ligne = "\013") then raise Fin ;
let result = (String.uppercase ligne)^"\n"
in ignore (ThreadUnix.write s_descr result 0 (String.length result))
done
with
Fin -> ()
| exn -> print_string (Printexc.to_string exn) ; print_newline()
end ;;
class connexion :
Unix.file_descr * 'a ->
object
val mutable numero : int
val s_addr : 'a
val s_descr : Unix.file_descr
method run : unit -> unit
method start : unit -> Thread.t
method stop : unit -> unit
end


Ici encore, par hritage et redfinition de la mthode run, on peut dfinir un nouveau service.

On testera cette nouvelle version du serveur en excutant la fonction protect_serv.

# let go_serv () = let s = new serv_socket 1400 in s#run () ;;
# let protect_serv () = Unix.handle_unix_error go_serv () ;;


Client-serveur plusieurs niveaux

Bien que la relation client-serveur soit asymtrique, rien n'empche un serveur d'tre lui-mme client d'un autre service. On obtient ainsi une hirarchie dans la communication. Une application client-serveur classique comporte bien souvent :
  • un poste client muni d'une interface conviviale;
  • un programme de traitement suite une interaction de l'utilisateur;
  • une base de donnes accessible par le programme de traitement.
Un des buts des applications client-serveur est de dcharger les machines centrales d'une partie du traitement. La figure 20.3 montre deux architectures client-serveur 3 niveaux (tiers).



Figure 20.3 : diffrentes architectures de client-serveur


Chaque niveau peut tre implant sur des machines diffrentes. L'interface utilisateur s'excute sur la machine d'un utilisateur de l'application. La partie traitement est localise sur une machine commune un ensemble d'utilisateurs qui elle-mme envoie des requtes un serveur de base de donnes distant. Selon les caractristiques de l'application, une partie des traitements peut tre dporte soit sur le poste utilisateur, soit sur le serveur de base de donnes.

Remarques sur les client-serveur raliss

Nous avons, dans les sections prcdentes, labor un service simple : le service MAJUSCULE. Diffrentes solutions ont t exposes. Tout d'abord le serveur a utilis le mcanisme Unix des forks. Une fois ce premier serveur construit, il a t possible de le tester par le client telnet existant sous tous les systmes (Unix, Windows et MacOS). Ensuite un premier client simple a t crit. Nous avons pu alors tester l'ensemble de l'application client-serveur. Les clients eux-aussi peuvent avoir des tches grer entre les communications. Pour cela le client client_par.exe, qui spare la lecture de l'criture en utilisant aussi des forks, a t construit. Une nouvelle mouture du serveur a t ralise en utilisant des threads pour bien montrer l'indpendance relative entre le serveur et le client, et l'attention apporter aux entres-sorties dans ce cadre. Ce serveur a t organis sous forme de deux classes facilement rutilisables. On note que la programmation fonctionnelle et la programmation objet permettent de sparer la partie << mcanique >> rutilisable de la partie traitement spcialis.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora098.html0000644000000000000000000005436307421273602014514 0ustar Outils de mise au point Prcdent Index Suivant

Outils de mise au point

Il y a deux outils de mise au point. Le premier outil est un mcanisme de trace utilisable, sur les fonctions globales, dans la boucle d'interaction. Le second outil est un debug sur les programmes hors boucle d'interaction. Suite une premire excution, il permet de revenir sur des points d'arrt, d'explorer des valeurs ou de relancer certaines fonctions avec de nouveaux paramtres. Ce second outil n'est utilisable que sous Unix car il duplique le processus programme via un fork (voir page ??).

Trace

On appelle trace d'une fonction l'affichage de la valeur de ses paramtres d'appels dans le cours d'un programme ainsi que celui de son rsultat.

Les commandes de traage d'excution sont des directives de la boucle d'interaction. Elles permettent de tracer une fonction, d'arrter sa trace ou d'arrter toutes les traces poses. Ces trois directives sont donnes dans le tableau ci-dessous.
#trace nom trace la fonction nom
#untrace nom arrte de tracer la fonction nom
#untrace_all annule toutes les traces

Voici un premier exemple o on dfinit une fonction f :

# let f x = x + 1;;
val f : int -> int = <fun>
# f 4;;
- : int = 5


On indique maintenant que cette fonction est trace, c'est--dire que le passage d'un argument ou le retour d'un rsultat seront affichs.

# #trace f;;
f is now traced.
# f 4;;
f <-- 4
f --> 5
- : int = 5
Le passage de l'argument 4 f est affich, la fonction f effectue le calcul demand et le rsultat retourn est aussi affich. Les (valeurs) paramtres d'appels sont indiqus par une flche vers la gauche et la valeur de retour, par une flche vers la droite.

Fonctions plusieurs arguments

Les fonctions plusieurs arguments (ou retournant une fermeture) sont aussi traables. Chaque passage d'un argument est affich. Pour distinguer les diffrentes fermetures, on note avec une * le nombre d'arguments dj passs la fermeture. Soit la fonction verif_div qui prend 4 nombres (a, b, q, r) correspondant la division entire : a = bq + r.

# let verif_div a b q r =
a = b*q + r;;
val verif_div : int -> int -> int -> int -> bool = <fun>
# verif_div 11 5 2 1;;
- : bool = true


Sa trace montrera le passage des 4 arguments :

# #trace verif_div;;
verif_div is now traced.
# verif_div 11 5 2 1;;
verif_div <-- 11
verif_div --> <fun>
verif_div* <-- 5
verif_div* --> <fun>
verif_div** <-- 2
verif_div** --> <fun>
verif_div*** <-- 1
verif_div*** --> true
- : bool = true


Fonctions rcursives

La trace apporte de prcieuses informations sur les fonctions rcursives. Elle permet de dtecter aisment un mauvais critre d'arrt.

On dfinit la fonction appartient qui teste si un entier appartient une liste d'entiers de la manire suivante :

# let rec appartient (e : int) l = match l with
[] -> false
| t::q -> (e = t) || appartient e q ;;
val appartient : int -> int list -> bool = <fun>
# appartient 4 [3;5;7] ;;
- : bool = false
# appartient 4 [1; 2; 3; 4; 5; 6; 7; 8] ;;
- : bool = true


La trace de l'appel de appartient 4 [3;5;7] affichera les quatre appels de cette fonction et les rsultats retourns.

# #trace appartient ;;
appartient is now traced.
# appartient 4 [3;5;7] ;;
appartient <-- 4
appartient --> <fun>
appartient* <-- [3; 5; 7]
appartient <-- 4
appartient --> <fun>
appartient* <-- [5; 7]
appartient <-- 4
appartient --> <fun>
appartient* <-- [7]
appartient <-- 4
appartient --> <fun>
appartient* <-- []
appartient* --> false
appartient* --> false
appartient* --> false
appartient* --> false
- : bool = false


chaque appel de la fonction appartient l'argument 4 puis la liste dans laquelle vrifier sont passs. Quand la liste devient vide, alors la fonction retourne la valeur false qui est propage comme valeur de retour de chaque appel rcursif en attente.

L'exemple suivant montre le parcours partiel de la liste lorsque l'lment demand y apparat :

# appartient 4 [1; 2; 3; 4; 5; 6; 7; 8] ;;
appartient <-- 4
appartient --> <fun>
appartient* <-- [1; 2; 3; 4; 5; 6; 7; 8]
appartient <-- 4
appartient --> <fun>
appartient* <-- [2; 3; 4; 5; 6; 7; 8]
appartient <-- 4
appartient --> <fun>
appartient* <-- [3; 4; 5; 6; 7; 8]
appartient <-- 4
appartient --> <fun>
appartient* <-- [4; 5; 6; 7; 8]
appartient* --> true
appartient* --> true
appartient* --> true
appartient* --> true
- : bool = true
Ds que 4 est tte de liste, la fonction retourne la valeur true qui est propage par les appels rcursifs en attente.

Si la fonction appartient avait invers l'ordre du ||, elle retournerait toujours le bon rsultat, mais effectuerait dans tous les cas le parcours complet de la liste.

# let rec appartient (e : int) = function
[] -> false
| t::q -> appartient e q || (e = t) ;;
val appartient : int -> int list -> bool = <fun>
# #trace appartient ;;
appartient is now traced.
# appartient 3 [3;5;7] ;;
appartient <-- 3
appartient --> <fun>
appartient* <-- [3; 5; 7]
appartient <-- 3
appartient --> <fun>
appartient* <-- [5; 7]
appartient <-- 3
appartient --> <fun>
appartient* <-- [7]
appartient <-- 3
appartient --> <fun>
appartient* <-- []
appartient* --> false
appartient* --> false
appartient* --> false
appartient* --> true
- : bool = true
Bien que 3 soit le premier lment de la liste, celle-ci est entirement parcourue. Ainsi, le mcanisme de trace permet galement une analyse d'efficacit des fonctions rcursives.

Fonctions polymorphes

La trace d'arguments d'un type paramtr n'affichera pas la valeur correspondant au paramtre de type. Par exemple si la fonction appartient avait t crite sans contrainte explicite de type :

# let rec appartient e l = match l with
[] -> false
| t::q -> (e = t) || appartient e q ;;
val appartient : 'a -> 'a list -> bool = <fun>
La fonction appartient est maintenant d'un type polymorphe. Sa trace n'affiche plus la valeur des arguments, mais les remplace par l'indication (poly).

# #trace appartient ;;
appartient is now traced.
# appartient 3 [2;3;4] ;;
appartient <-- <poly>
appartient --> <fun>
appartient* <-- [<poly>; <poly>; <poly>]
appartient <-- <poly>
appartient --> <fun>
appartient* <-- [<poly>; <poly>]
appartient* --> true
appartient* --> true
- : bool = true


La boucle d'interaction d'Objective CAML ne sait afficher que les valeurs de type monomorphe. De plus, il ne conserve que le type infr des dclarations globales. Ainsi, aprs compilation de l'expression appartient 3 [2;3;4], la boucle d'interaction ne dispose plus, en fait d'information de type, que du type 'a -> 'a list -> bool de la fonction appartient. Les types (monomorphes) de 3 et [2;3;4] ont t perdus car les valeurs ne conservent pas d'information de type : c'est le typage statique. C'est pourquoi, le mcanisme de trace attribue aux arguments de la fonction appartient les types polymorphes 'a et 'a list dont il n'affiche pas les valeurs.

C'est l'absence d'information de type dans les valeurs qui entrane l'impossibilit de construire une fonction print gnrique de type 'a -> unit.

Fonctions locales

Les fonctions locales ne sont pas traables pour les mmes raisons lies au typage statique. Seuls les types des dclarations globales sont conservs dans l'environnement de la boucle d'interaction. Pourtant le style de programmation suivant est courant :

# let appartient e l =
let rec app_aux l = match l with
[] -> false
| t::q -> (e = t) || (app_aux q)
in
app_aux l;;
val appartient : 'a -> 'a list -> bool = <fun>
La fonction globale ne fait qu'appeler une fonction locale effectuant la partie intressante du travail.

Remarques sur la trace

Le mcanisme de trace est actuellement l'unique outil de mise au point multi-plate-formes. Ses deux faiblesses sont l'absence de trace des dclarations locales et le non affichage des paramtres polymorphes des fonctions. Cela restreint fortement son usage, principalement aux premiers pas avec le langage.

Debug

L'outil de mise au point des programmes, ocamldebug, est un dbogueur au sens usuel du terme. Il permet l'excution pas--pas, l'insertion de points d'arrt, l'exploration et la modification des valeurs de l'environnement.

Excuter pas--pas un programme suppose que l'on sache dterminer ce qu'est un pas de programme. En programmation imprative, cette notion est assez simple : grosso modo, un pas de programme est une instruction du langage. Mais cette dfinition n'a plus grand sens en programmation fonctionnelle; on parle plutt d'vnement (event) de programmes. Ceux-ci seront une application, l'entre dans une fonction, un filtrage, une alternative, une boucle, un lment de squence, etc.

Warning


Cet outil ne fonctionne que sous Unix.


Compilation en mode debug

L'option de compilation -g engendre un fichier .cmo permettant d'engendrer les instructions ncessaires au debug. Cette option n'est connue que du compilateur de code-octet. Il sera ncessaire d'indiquer cette option lors de la compilation des diffrents fichiers d'une application. Une fois un excutable produit, son excution en mode debug s'effectue par la commande ocamldebug de la manire suivante :



On prend l'exemple suivant du fichier fact.ml de calcul de la fonction factorielle :

let fact n =
let rec fact_aux p q n =
if n = 0 then p
else fact_aux (p+q) p (n-1)
in
fact_aux 1 1 n;;


Le programme principal du fichier main.ml part pour une longue rcursion suite l'appel de Fact.fact -1.

let x = ref 4;;
let go () =
x := -1;
Fact.fact !x;;
go();;


On compile les deux fichiers avec l'option -g :
$ ocamlc -g -i -o fact.exe fact.ml main.ml
val fact : int -> int
val x : int ref
val go : unit -> int

Lancement du debug

Une fois un excutable cre en mode debug, il est possible de dmarrer l'excution dans ce mode.
$ ocamldebug fact.exe
        Objective Caml Debugger version 2.04

(ocd) 

Suivi du contrle d'excution

Le contrle d'excution passe par les vnements du programme. On peut soit avancer ou reculer de n lments de programme, soit avancer ou reculer jusqu'au point d'arrt le plus proche (ou de n points d'arrt). On indique un point d'arrt sur une fonction ou sur un lment de programme. Le choix de l'lment de langage est donn par les coordonnes en ligne, colonne ou numro de caractres. Ces localisations peuvent tre relatives un module.

Dans l'exemple ci-dessous, on pose un point d'arrt sur la quatrime ligne du module Main :
(ocd) step 0
Loading program... done.
Time : 0
Beginning of program.
(ocd)  break @ Main 4
Breakpoint 1 at 4964 : file Main, line 4 column 3
Les initialisations du module se font avant le programme proprement dit. C'est pour cette raison que le point d'arrt la ligne 4 ne se rencontre qu'aprs 4964 instructions.

On avance ou recule dans l'excution soit par rapport aux lments de programme, soit par rapport aux points d'arrt. run et reverse excutent jusqu'au point d'arrt le plus proche, le premier dans le sens de l'excution, le second en marche arrire. Le commande step avance de 1 ou n lments de programme en entrant dans les fonctions, next n'entre pas dans les appels de fonctions. Respectivement backstep et previous effectuent les mmes oprations en marche arrire. Enfin finish termine l'appel de la fonction courante alors que start retourne l'lment de programme prcdant l'invocation de la fonction.

Pour suivre notre exemple, on avance jusqu'au point d'arrt puis on excute trois vnements de programme :
(ocd) run
Time : 6 - pc : 4964 - module Main
Breakpoint : 1
4   <|b|>Fact.fact !x;;
(ocd) step
Time : 7 - pc : 4860 - module Fact
2   <|b|>let rec fact_aux p q n = 
(ocd) step
Time : 8 - pc : 4876 - module Fact
6 <|b|>fact_aux 1 1 n;;
(ocd) step
Time : 9 - pc : 4788 - module Fact
3     <|b|>if n = 0 then p

Exploration de valeurs

Au niveau d'un arrt, il est possible d'afficher les valeurs associes aux variables du bloc d'activation. Les commandes print et display affichent selon des profondeurs diffrentes les valeurs associes une valeur.

Nous allons afficher la valeur de n, puis remonter de trois pas pour afficher le contenu de x :
(ocd) print n
n : int = -1
(ocd) backstep 3    
Time : 6 - pc : 4964 - module Main
Breakpoint : 1
4   <|b|>Fact.fact !x;;
(ocd) print x
x : int ref = {contents=-1}
Les accs aux champs d'un enregistrement ou via l'indice d'un tableau sont accepts par les commandes d'affichage.
(ocd) print x.contents
$1 : int = -1

Pile d'excution

La pile d'excution permet de visualiser l'imbrication des appels de fonctions. La commande backtrace ou bt montre l'empilement des appels. Les commandes up et down slectionnent le bloc d'activation suivant ou prcdant. Enfin la commande frame dcrit le bloc courant.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora068.html0000644000000000000000000000664107421273601014504 0ustar Excutables autonomes Prcdent Index Suivant

Excutables autonomes

Un excutable autonome (standalone) est un excutable indpendant de l'installation d'Objective CAML sur la machine utilise. D'une part, cela facilite la diffusion d'une application sous forme binaire. D'autre part si l'on installe une nouvelle version d'Objective CAML un emplacement diffrent, cet excutable continuera fonctionner.

Le compilateur natif produit automatiquement des excutables autonomes. En revanche, sans l'option -custom, le compilateur de code-octet produit un excutable qui a besoin de l'interprte ocamlrun. Soit le fichier exemple.ml suivant :
let f x = x + 1;;
print_int (f 18);;
print_newline();;
La commande suivante produit le fichier exemple.exe :
ocamlc -o exemple.exe t.ml
Ce fichier d'une taille de 8ko peut tre excut en le passant comme paramtre l'interprte de code-octet :
$ ocamlrun exemple.exe
19
L'interprte excute les instructions de la machine Zinc contenues dans le fichier exemple.exe et utilise le chemin d'accs standard aux bibliothques.

Sous Unix, la premire ligne du fichier exemple.exe contient la localisation de l'interprte de code-octet, par exemple :
#!/usr/local/bin/ocamlrun
Cette indication permet de lancer directement l'excution du fichier exemple.exe par l'interprte de commandes. Son excution lance la commande dcrite par cette premire ligne. Il faut donc bien trouver la commande ocamlrun l'emplacement indiqu, sinon le message d'erreur Command not found apparatra et l'excution sera arrte.

La mme compilation avec l'option -custom produit un excutable autonome de nom exauto.exe :
ocamlc -custom -o exaauto.exe t.ml
Sa taille est d'environ 85Ko car il contient, outre le code-octet du programme, l'interprte de ce code-octet. Ce fichier peut tre excut directement ou copi sur une autre machine (de mme architecture et de mme systme) pour tre excut.

Warning


La taille du fichier d'un excutable ne correspond pas la taille du processus de son excution. En effet le fichier ne tient pas compte de l'allocation dynamique de mmoire du programme (9).



Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora126.html0000644000000000000000000034004007421273602014472 0ustar Recherche de chemins de moindre cot Prcdent Index Suivant

Recherche de chemins de moindre cot

La recherche de chemin de moindre cot dans un graphe orient est utilise dans de nombreuses applications. On s'intresse ici la dtermination d'un itinraire de voyage. Pour cela on reprsente les diffrentes tapes et routes par un graphe orient, valu par le cot entre deux tapes. On utilise l'algorithme de Dijkstra pour la dtermination du chemin de moindre cot entre deux tapes. Cette application va tre l'occasion d'utiliser les bibliothques prsentes prcdemment. On peut citer dans l'ordre d'apparition, les modules Genlex et Printf pour les entres-sorties, le module Weak pour implanter un cache, le module Sys pour compter le temps gagn par le cache et la bibliothque Upi pour construire l'interface graphique. On rutilise le module Sys la construction d'une application autonome en rcuprant le nom du fichier de description du graphe comme argument de la ligne de commande.

Reprsentation des graphes

Un graphe orient valu est dfini par son ensemble de sommets, son ensemble d'arcs et l'ensemble des valeurs associes chaque arc. Il existe de nombreuses implantations du type de donnes graphe orient valu.
  • les matrices d'adjacence :
    o chaque case de la matrice (m(i,j)) reprsente un arc entre les sommets i et j, son contenu est la valeur associe l'arc ;
  • les listes d'adjacence :
    o chaque sommet i est li une liste [j1; ..; jn] de sommets et chaque couple (i, jk) est un arc du graphe ;
  • un triplet :
    liste de sommets, liste des d'arcs et fonction de calcul des valeurs des arcs.
La pertinence de ces reprsentations varie selon la taille du graphe et le nombre d'arcs. Comme le propos de cette application est en partie de montrer comment mmoriser certains calculs dj effectus sans remplir la mmoire, on utilise une matrice d'adjacence pour les graphes orients valus. Ainsi l'occupation mmoire ne sera pas perturbe par la manipulation de listes.

# type cout = Nan | Cout of float;;
# type mat_adj = cout array array;;
# type 'a graphe = { mutable ind : int;
taille : int;
sommets : 'a array;
m : mat_adj};;
Le champ taille contient le nombre maximal de sommets et le champs ind, le nombre courant de sommets.

On dfinit ensuite quelques fonctions permettant de crer pas pas un arc.

La fonction de cration d'un graphe prend en arguments un sommet et le nombre maximal de sommets.

# let cree_graphe s t =
{ ind = 0; taille = t; sommets = Array.create t s;
m = Array.create_matrix t t Nan } ;;
val cree_graphe : 'a -> int -> 'a graphe = <fun>


La fonction appartient vrifie si un sommet n est dans le graphe g.

# let appartient s g =
let rec aux i =
(i < g.taille) & ((g.sommets.(i) = s) or (aux (i+1)))
in aux 0;;
val appartient : 'a -> 'a graphe -> bool = <fun>


La fonction index retourne l'indice d'un sommet s dans un graphe g. Si ce sommet n'existe pas alors l'exception Not_found est dclenche.

# let index s g =
let rec aux i =
if i >= g.taille then raise Not_found
else if g.sommets.(i) = s then i
else aux (i+1)
in aux 0 ;;
val index : 'a -> 'a graphe -> int = <fun>


Les deux fonctions suivantes ajoutent respectivement un sommet et un arc entre deux sommets de cot c un graphe.

# let ajoute_sommet s g =
if g.ind = g.taille then failwith "le graphe est plein"
else if appartient s g then failwith "le sommet existe dj"
else (g.sommets.(g.ind) <- s; g.ind <- g.ind + 1) ;;
val ajoute_sommet : 'a -> 'a graphe -> unit = <fun>
# let ajoute_arc s1 s2 c g =
try
let x = index s1 g and y = index s2 g in
g.m.(x).(y) <- Cout c
with Not_found -> failwith "sommet inexistant" ;;
val ajoute_arc : 'a -> 'a -> float -> 'a graphe -> unit = <fun>


On peut alors facilement crer un graphe orient valu complet partir d'une liste de sommets et d'arcs. La fonction test_aho construit le graphe de la figure 13.8 :

# let test_aho () =
let g = cree_graphe "rien" 5 in
List.iter (fun x -> ajoute_sommet x g) ["A"; "B"; "C"; "D"; "E"];
List.iter (fun (a,b,c) -> ajoute_arc a b c g)
["A","B",10.;
"A","D",30.;
"A","E",100.0;
"B","C",50.;
"C","E",10.;
"D","C",20.;
"D","E",60.];
for i=0 to g.ind -1 do g.m.(i).(i) <- Cout 0.0 done;
g;;
val test_aho : unit -> string graphe = <fun>
# let a = test_aho();;
val a : string graphe =
{ind=5; taille=5; sommets=[|"A"; "B"; "C"; "D"; "E"|];
m=[|[|Cout 0; Cout 10; Nan; Cout 30; Cout ...|]; ...|]}




Figure 13.8 : Graphe de test


Construction de graphes

Il peut tre assez fastidieux de devoir construire les graphes directement dans un programme. Pour viter cela, nous dfinissons un format pour reprsenter les graphes sous une forme textuelle. On peut alors lire, partir d'un fichier texte, les informations d'un graphe et le construire.

Un fichier de description d'un graphe contient une suite de lignes. Chaque ligne suit un des formats suivants :
  • le nombre de noeuds : TAILLE nombre
  • le nom d'un sommet : SOMMET nom
  • le cot d'un arc : ARC nom1 nom2 cout
  • des commentaires : # phrase
Par exemple, le fichier aho.dat suivant dcrit le graphe de la figure 13.8 :
TAILLE 5
SOMMET A
SOMMET B
SOMMET C
SOMMET D
SOMMET E
ARC A B 10.0
ARC A D 30.0
ARC A E 100.0
ARC B C 50.
ARC C E 10.
ARC D C 20.
ARC D E 60.
Pour analyser de tels fichiers, on utilise le module Genlex d'analyse lexicale. L'analyseur lexicale est construit partir de la liste de mots cls keywords.

La fonction parser_line effectue les actions associes aux mots cls en modifiant une rfrence sur un graphe.

# let keywords = [ "TAILLE"; "SOMMET"; "ARC"; "#"];;
val keywords : string list = ["TAILLE"; "SOMMET"; "ARC"; "#"]
# let lexer_line l = Genlex.make_lexer keywords (Stream.of_string l);;
val lexer_line : string -> Genlex.token Stream.t = <fun>
# let parser_line g s = match s with parser
[< '(Genlex.Kwd "TAILLE"); '(Genlex.Int n) >] ->
g := cree_graphe "" n
| [< '(Genlex.Kwd "SOMMET"); '(Genlex.Ident nom) >] ->
ajoute_sommet nom !g
| [< '(Genlex.Kwd "ARC"); '(Genlex.Ident n1);
'(Genlex.Ident n2); '(Genlex.Float c) >] ->
ajoute_arc n1 n2 c !g
| [< '(Genlex.Kwd "#") >] -> ()
| [<>] -> () ;;
val parser_line : string graphe ref -> Genlex.token Stream.t -> unit = <fun>


On utilise cet analyseur pour dfinir la fonction de cration d'un graphe utilise partir des donnes d'un fichier :

# let create_graphe name =
let g = ref {ind=0; taille=0; sommets =[||]; m = [||]} in
let ic = open_in name in
try
print_string ("Loading "^name^": ");
while true do
print_string ".";
let l = input_line ic in parser_line g (lexer_line l)
done;
!g
with End_of_file -> print_newline(); close_in ic; !g ;;
val create_graphe : string -> string graphe = <fun>
L'appel suivant construit un graphe partir du fichier aho.dat.

# let b = create_graphe "PROGRAMMES/aho.dat" ;;
Loading PROGRAMMES/aho.dat: ..............
val b : string graphe =
{ind=5; taille=5; sommets=[|"A"; "B"; "C"; "D"; "E"|];
m=[|[|Nan; Cout 10; Nan; Cout 30; Cout 100|]; ...|]}


Algorithme de Dijkstra

L'algorithme de Dijkstra permet de trouver le chemin de cot minimal entre deux sommets. Le cot d'un chemin entre un sommet s1 et un sommet s2 est la somme des cots des arcs de ce chemin. Un cot est toujours positif, c'est--dire que l'on ne gagne pas de repasser par un sommet. C'est une condition de correction de l'algorithme.

L'algorithme de Dijkstra calcule en fait tous les chemins de moindre cot d'une source S1 vers l'ensemble sommets atteignables depuis cette source. L'ide est de supposer connus les chemins de moindre cot de S1 vers un ensemble de sommets et d'tendre cet ensemble en considrant les sommets accessibles par un arc partir de l'un des sommets dj connu en retenant ceux qui augmentent le moins le cot des chemins.

Pour conserver l'tat de la recherche, on dfinit le type etat_recherche puis une fonction de cration de l'tat initial :

# type etat_recherche = { chemins : int array;
deja_traites : bool array;
distances : cout array;
source :int;
nb : int};;
# let creer_etat () = { chemins = [||]; deja_traites = [||]; distances = [||];
nb = 0; source = 0};;
Le champ source contient le sommet de dpart ; le champ deja_traites, les sommets dont on connat dj le chemin optimal partir de la source et le champ nb, le nombre de sommets dj traits. Le vecteur distances tient jour les distances minimales entre la source et les autres sommets. Le vecteur chemin contient, pour chaque sommet, le sommet le prcdent dans le chemin de moindre cot. On peut ainsi reconstituer un chemin de la source vers n'importe quel autre sommet.

Fonctions sur les cots

On dfinit tout d'abord quatre fonctions utilitaires sur les cots : a_cout pour tester de l'existence d'un arc, float_of_cout pour la conversion en flottant, add_cout pour l'addition de deux cots et inf_cout vrifiant si un cot est infrieur un autre.

# let a_cout c = match c with Nan -> false | _-> true;;
val a_cout : cout -> bool = <fun>
# let float_of_cout c = match c with
Nan -> failwith "float_of_cout"
| Cout x -> x;;
val float_of_cout : cout -> float = <fun>
# let add_cout c1 c2 = match (c1,c2) with
Cout x, Cout y -> Cout (x+.y)
| Nan, Cout y -> c2
| Cout x, Nan -> c1
| Nan, Nan -> c1;;
val add_cout : cout -> cout -> cout = <fun>
# let inf_cout c1 c2 = match (c1,c2) with
Cout x, Cout y -> x < y
| Cout x, Nan -> true
| _, _ -> false;;
val inf_cout : cout -> cout -> bool = <fun>
La valeur Nan joue un rle particulier dans les calculs et la comparaison. Nous y reviendrons lorsque nous aurons donn la fonction principale (voir page ??).

Implantation de l'algorithme

La recherche d'un sommet servant tendre les chemins dj calculs est divise en deux fonctions : premier_non_traite qui slectionne un premier sommet non encore visit ; celui-ci sert de valeur initiale pour la fonction pp_non_traite qui retourne le sommet non encore trait dont l'ajout l'ensemble des chemin construit est minimal.

# exception Trouve of int;;
exception Trouve of int
# let premier_non_traite er =
try
for i=0 to er.nb-1 do
if not er.deja_traites.(i) then raise (Trouve i)
done;
raise Not_found;
0
with Trouve i -> i ;;
val premier_non_traite : etat_recherche -> int = <fun>
# let pp_non_traite p er =
let si = ref p
and sd = ref er.distances.(p) in
for i=p+1 to er.nb-1 do
if not er.deja_traites.(i) then
if inf_cout er.distances.(i) !sd then
( sd := er.distances.(i);
si := i )
done;
!si,!sd;;
val pp_non_traite : int -> etat_recherche -> int * cout = <fun>


La fonction une_etape slectionne un nouveau sommet, l'ajoute l'ensemble des sommets traits et met jour les chemins et distances des autres sommets non traits s'il y a lieu.

# exception No_way;;
exception No_way
# let une_etape er g =
let p = premier_non_traite er in
let np,nc = pp_non_traite p er in
if not(a_cout nc ) then raise No_way
else
begin
er.deja_traites.(np) <- true;
for i = 0 to er.nb -1 do
if not er.deja_traites.(i) then
if a_cout g.m.(np).(i) then
let ic = add_cout er.distances.(np) g.m.(np).(i) in
if inf_cout ic er.distances.(i) then (
er.chemins.(i) <- np;
er.distances.(i) <- ic
)
done;
er
end;;
val une_etape : etat_recherche -> 'a graphe -> etat_recherche = <fun>


Il ne reste plus qu' itrer la fonction prcdente pour implanter l'algorithme de Dijkstra. La fonction dij prend un sommet et un graphe puis retourne une valeur de type etat_recherche contenant les informations pour dduire le chemin de moindre cot de cette source vers n'importe quel sommet atteignable partir de celle-ci.

# let dij s g =
if appartient s g then
begin
let i = index s g in
let er = { chemins = Array.create g.ind (-1) ;
deja_traites = Array.create g.ind false;
distances = Array.create g.ind Nan;
nb = g.ind;
source = i} in
er.deja_traites.(i) <- true;
for j=0 to g.ind-1 do
let c = g.m.(i).(j) in
er.distances.(j) <- c;
if a_cout c then er.chemins.(j) <- i
done;
try
for k = 0 to er.nb-2 do
ignore(une_etape er g)
done;
er
with No_way -> er
end
else failwith "dij: sommet inconnu";;
val dij : 'a -> 'a graphe -> etat_recherche = <fun>


La valeur Nan est donne comme valeur initiale des distances. Elle reprsente une distance infinie, ce qui est cohrent avec la fonction de comparaison inf_cout. En revanche. Pour l'addition des cots (fonction add_cout, cette valeur est considre comme nulle. Ce qui permet une mise jour simple du tableau des distances.

On peut dont maintenant tester la recherche selon l'algorithme de Dijkstra.

# let a = test_aho ();;
# let r = dij "A" a;;


Les rsultats bruts sont les suivants :

# r.chemins;;
- : int array = [|0; 0; 3; 0; 2|]
# r.distances;;
- : cout array = [|Cout 0; Cout 10; Cout 50; Cout 30; Cout 60|]


Pour rendre ces rsultats plus lisibles, nous dfinissons ci-dessous une fonction d'affichage.

Affichage de la solution

En utilisant le tableau chemins de la valeur calcule par la fonction dij, on n'a que le dernier arc des chemins calculs. Il faut donc, pour afficher la totalit de ces chemins, remonter rcursivement jusqu' la source.

# let aff_etat f (g,et) dest =
if appartient dest g then
let d = index dest g in
let rec aux is =
if is = et.source then Printf.printf "%a" f g.sommets.(is)
else (
let old = et.chemins.(is) in
aux old;
Printf.printf " -> (%4.1f) %a" (float_of_cout g.m.(old).(is))
f g.sommets.(is)
)
in
if not(a_cout et.distances.(d)) then Printf.printf "no way\n"
else (
aux d;
Printf.printf " = %4.1f\n" (float_of_cout et.distances.(d)));;
val aff_etat :
(out_channel -> 'a -> unit) -> 'a graphe * etat_recherche -> 'a -> unit =
<fun>
Cette fonction rcursive utilise la pile d'appel pour afficher dans le bon ordre les sommets. On note l'utilisation d'un format "a" pour conserver le polymorphisme des graphes y compris pour l'affichage, d'o le paramtre fonctionnel f.

Le chemin optimal entre le sommet "A" (indice 0) et "E" (indice 4) est affich ainsi :

# aff_etat (fun x y -> Printf.printf "%s!" y) (a,r) "E";;
A! -> (30.0) D! -> (20.0) C! -> (10.0) E! = 60.0
- : unit = ()


On indique les diffrentes sommets du chemin ainsi que les cots de chaque tape.

Utilisation d'un cache

L'algorithme de Dijkstra calcule tous les chemins de moindre cot issus d'une source. On aimerait donc conserver ces valeurs en vue d'une prochaine recherche partir de la mme source. Cette mmorisation de rsultats risque nanmoins d'occuper une part importante de la mmoire. D'o l'ide d'utiliser les << pointeurs faibles >>. Si on conserve les rsultats des recherches partir d'une source dans un tableau de pointeurs faibles, il devient ensuite possible, pour une nouvelle recherche, de vrifier si le calcul a dj t effectu. Comme ce sont des pointeurs faibles, l'espace mmoire de ces tats peut tre libr en cas de besoin par le GC. Cela permet de ne pas perturber le reste du programme par une occupation mmoire trop importante. Au pire des cas le calcul peut tre refait s'il y a une demande.

Implantation du cache

On dfinit un nouveau type 'a recherche_graphe :

# type 'a recherche_graphe =
{ g : 'a graphe; w : etat_recherche Weak.t } ;;
Les champs g et w correspondent au graphe et au tableau de pointeurs faibles sur les tats de recherche pour chaque source possible.

On construit de telles valeurs par la fonction create_recherche.

# let create_rech_graphe g =
{ g = g;
w = Weak.create g.ind } ;;
val create_rech_graphe : 'a graphe -> 'a recherche_graphe = <fun>


La fonction dij_rapide vrifie si la recherche a dj t calcule, si oui, elle retourne le rsultat mmoris, sinon, elle effectue le calcul et l'enregistre dans le tableau de pointeurs faibles.

# let dij_rapide s rg =
let i = index s rg.g in
match Weak.get rg.w i with
None -> let er = dij s rg.g in
Weak.set rg.w i (Some er);
er
| Some er -> er;;
val dij_rapide : 'a -> 'a recherche_graphe -> etat_recherche = <fun>


La fonction d'affichage reste utilisable :

# let rg_a = create_rech_graphe a in
let r = dij_rapide "A" rg_a in
aff_etat (fun x y -> Printf.printf "%s!" y) (a,r) "E" ;;
A! -> (30.0) D! -> (20.0) C! -> (10.0) E! = 60.0
- : unit = ()


valuation des performances

Nous allons tester les performances des fonctions dij et dij_rapide en itrant chacune d'elle sur une liste de source tire au hasard. On simule ainsi une application dans laquelle il faudrait calculer souvent un chemin entre deux points d'un graphe (un itinraire ferroviaire, par exemple).

Pour obtenir les temps de calcul, nous dfinissons la fonction suivante :

# let exe_time f g ss =
let t0 = Sys.time() in
Printf.printf "Dbut (%5.2f)\n" t0;
List.iter (fun s -> ignore(f s g)) ss;
let t1 = Sys.time() in
Printf.printf "Fin (%5.2f)\n" t1;
Printf.printf "Dure = (%5.2f)\n" (t1 -. t0) ;;
val exe_time : ('a -> 'b -> 'c) -> 'b -> 'a list -> unit = <fun>


On tire donc au hasard une liste de 20000 sommets et on mesure les performances contenues sur le graphe a :

# let ss =
let ss0 = ref [] in
let i0 = int_of_char 'A' in
let new_s i = Char.escaped (char_of_int (i0+i)) in
for i=0 to 20000 do ss0 := (new_s (Random.int a.taille))::!ss0 done;
!ss0 ;;
val ss : string list =
["A"; "B"; "D"; "A"; "E"; "C"; "B"; "B"; "D"; "E"; "B"; "E"; "C"; "E"; "E";
"D"; "D"; "A"; "E"; ...]
# Printf.printf"Fonction dij :\n";
exe_time dij a ss ;;
Fonction dij :
Dbut ( 7.08)
Fin ( 8.02)
Dure = ( 0.94)
- : unit = ()
# Printf.printf"Fonction dij_rapide :\n";
exe_time dij_rapide (create_rech_graphe a) ss ;;
Fonction dij_rapide :
Dbut ( 8.02)
Fin ( 8.13)
Dure = ( 0.11)
- : unit = ()


Les rsultats obtenus sont cohrents. Il est clair que l'accs direct au rsultat contenu dans le cache est bien plus rapide que la reconstruction du rsultat.

Interface graphique

On se propose de construire l'interface graphique de visualisation des graphes en utilisant la bibliothque Upi. Cette interface permet de slectionner les sommets origine et destination du chemin rechercher. Une fois le chemin de moindre cot trouv, celui-ci s'affiche graphiquement. On dfinit le type 'a gg qui regroupe les champs de description de graphe et de la recherche, ainsi que les champs de l'interface graphique.

# #load "PROGRAMMES/upi.cmo";;

# type 'a gg = { mutable src : 'a * Upi.component;
mutable dest : 'a * Upi.component;
pos : (int * int) array;
rg : 'a recherche_graphe;
mutable etat : etat_recherche;
mutable main : Upi.component;
to_string : 'a -> string;
from_string : string -> 'a } ;;


Les champs src et dest sont des couples (sommet,composant) liant un sommet un composant. Le champ pos contient les positions de chaque composant. Le champ main est le conteneur principal de l'ensemble des composants. Les deux fonctions to_string et from_string sont les fonctions de conversion du type 'a avec les chanes. Les lments ncessaires pour la construction de telles valeurs sont les informations du graphe, le tableau de positions et les fonctions de conversion.

# let cree_gg rg vpos ts fs = 
{src = rg.g.sommets.(0),Upi.empty_component;
dest = rg.g.sommets.(0),Upi.empty_component;
pos = vpos;
rg = rg;
etat = creer_etat () ;
main = Upi.empty_component;
to_string = ts;
from_string = fs};;
val cree_gg :
'a recherche_graphe ->
(int * int) array -> ('a -> string) -> (string -> 'a) -> 'a gg = <fun>




Visualisation

Le dessin du graphe ncessite de dessiner les sommets et de tracer les arcs. Les sommets sont reprsents par des composants boutons de la bibliothque Upi. Par contre les arcs sont directement tracs dans la fentre principale. La fonction display_arc affiche les diffrents arcs. La fonction display_shortest_path affiche dans une autre couleur le chemin trouv.

Trac des arcs
Un arc relie deux sommets et possde une valeur associe. Le lien entre deux sommets peut tre reprsent par un trac de ligne. La difficult principale provient de l'affichage du sens de cette ligne. On choisit de le reprsenter par une flche. Pour tre bien oriente, celle-ci doit subir une rotation de l'angle que fait la ligne avec l'axe des abscisses. Enfin les cots doivent tre affichs ct de l'arc.

Pour le trac d'une flche d'un arc, on dfinit : les fonctions rotate et translate qui effectuent respectivement une rotation et une translation ; la fonction display_arrow dessine la flche dont l'extrmit est en fait un triangle.

# let rotate l a =
let ca = cos a and sa = sin a in
List.map (function (x,y) -> ( x*.ca +. -.y*.sa, x*.sa +. y*.ca)) l;;
val rotate : (float * float) list -> float -> (float * float) list = <fun>
# let translate l (tx,ty) =
List.map (function (x,y) -> (x +. tx, y +. ty)) l;;
val translate :
(float * float) list -> float * float -> (float * float) list = <fun>
# let display_arrow (mx,my) a =
let triangle = [(5.,0.); (-3.,3.); (1.,0.); (-3.,-3.); (5.,0.)] in
let tr = rotate triangle a in
let ttr = translate tr (mx,my) in
let tt = List.map (function (x,y) -> (int_of_float x, int_of_float y)) ttr
in
Graphics.fill_poly (Array.of_list tt);;
val display_arrow : float * float -> float -> unit = <fun>


L'affichage du cot d'un arc positionne le point o dmarre le texte en fonction de l'angle de l'arc.

# let display_label (mx,my) a lab =
let (sx,sy) = Graphics.text_size lab in
let pos = [ float(-sx/2),float(-sy) ] in
let pr = rotate pos a in
let pt = translate pr (mx,my) in
let px,py = List.hd pt in
let ox,oy = Graphics.current_point () in
Graphics.moveto ((int_of_float mx)-sx-6)
((int_of_float my) );
Graphics.draw_string lab;
Graphics.moveto ox oy;;
val display_label : float * float -> float -> string -> unit = <fun>


L'affichage d'un arc reprend les fonctions prcdentes. Ses paramtres sont le graphe interfac gg, les sommets i et j, et la couleur du trac (col).

# let display_arc gg col i j =
let g = gg.rg.g in
let x,y = gg.main.Upi.x,gg.main.Upi.y in
if a_cout g.m.(i).(j) then (
let (a1,b1) = gg.pos.(i)
and (a2,b2) = gg.pos.(j) in
let x0,y0 = x+a1,y+b1 and x1,y1 = x+a2,y+b2 in
let rxm = (float(x1-x0)) /. 2. and rym = (float(y1-y0)) /. 2. in
let xm = (float x0) +. rxm and ym = (float y0) +. rym in
Graphics.set_color col;
Graphics.moveto x0 y0;
Graphics.lineto x1 y1;
let a = atan2 rym rxm in
display_arrow (xm,ym) a;
display_label (xm,ym) a
(string_of_float(float_of_cout g.m.(i).(j))));;
val display_arc : 'a gg -> Graphics.color -> int -> int -> unit = <fun>


Affichage d'un chemin
L'affichage d'un chemin applique le trac des diffrents arcs par lesquels passe le chemin. Cet affichage du chemin vers une destination reprend la technique utilise pour l'affichage texte.

# let rec display_shortest_path gg col dest =
let g = gg.rg.g in
if appartient dest g then
let d = index dest g in
let rec aux is =
if is = gg.etat.source then ()
else (
let old = gg.etat.chemins.(is) in
display_arc gg col old is;
aux old )
in
if not(a_cout gg.etat.distances.(d)) then Printf.printf "no way\n"
else aux d;;
val display_shortest_path : 'a gg -> Graphics.color -> 'a -> unit = <fun>


Affichage du graphe
La fonction display_gg affiche un graphe complet. Dans le cas o le sommet destination est non vide, elle trace le chemin entre l'origine et la destination.

# let display_gg gg () =
Upi.display_rect gg.main ();
for i=0 to gg.rg.g.ind -1 do
for j=0 to gg.rg.g.ind -1 do
if i<> j then display_arc gg (Graphics.black) i j
done
done;
if snd gg.dest != Upi.empty_component then
display_shortest_path gg Graphics.red (fst gg.dest);;
val display_gg : 'a gg -> unit -> unit = <fun>


Il reste dessiner les sommets. Comme l'interaction avec l'utilisateur proviendra de la slection des sommets source et destination, nous dfinissons un composant pour les sommets.

Composant sommet

La principale action d'un utilisateur est de choisir les extrmits du chemin rechercher. Pour cela un sommet doit tre un composant qui ragit au clic souris et possder un tat lui indiquant si l'on vient de choisir la source ou la destination. On choisit le composant bouton qui effectue une action la suite d'un clic souris.

Action des sommets
Il est ncessaire de visualiser la slection d'un sommet. Pour cela on inverse la couleur du fond de celui-ci par la fonction inverse.

# let inverse b =
let gc = Upi.get_gc b in
let fcol = Upi.get_gc_fcol gc
and bcol = Upi.get_gc_bcol gc in
Upi.set_gc_bcol gc fcol;
Upi.set_gc_fcol gc bcol;;
val inverse : Upi.component -> unit = <fun>


La fonction action_b effectue cette slection. Elle est applique lors d'un clic souris sur un sommet. Elle prend comme paramtres le sommet associ au bouton ainsi que le graphe pour pouvoir modifier la source ou la destination de la recherche. Quand ces deux sommets sont dtermins, alors elle applique la fonction dij_rapide pour trouver le chemin de moindre cot.
            
# let action_clic som gg b bs =
let (s1,s) = gg.src
and (s2,d) = gg.dest in
if s == Upi.empty_component then (
gg.src <- (som,b); inverse b )
else
if d == Upi.empty_component then (
inverse b;
gg.dest <- (som,b);
gg.etat <- dij_rapide s1 gg.rg;
display_shortest_path gg (Graphics.red) som
)
else (inverse s; inverse d;
gg.dest <- (s2,Upi.empty_component);
gg.src <- som,b; inverse b);;
val action_clic : 'a -> 'a gg -> Upi.component -> 'b -> unit = <fun>


Cration de l'interface
La fonction principale de cration de l'interface prend un graphe interfac et une liste d'options et cre les diffrents composants puis les associe au graphe. Les paramtres sont le graphe (gg), ses dimensions (gw et gh), la liste d'options du graphes et des sommets (lopt) et la liste d'options pour les bords des sommets (lopt2).
  
# let maj_gg gg gw gh lopt lopt2 =
let gc = Upi.make_default_context () in
Upi.set_gc gc lopt;
(* calcul de la taille max des boutons *)
let vs = Array.map gg.to_string gg.rg.g.sommets in
let vsize = Array.map Graphics.text_size vs in
let w = Array.fold_right (fun (x,y) -> max x) vsize 0
and h = Array.fold_right (fun (x,y) -> max y) vsize 0 in
(* cration de main *)
gg.main <- Upi.create_panel true gw gh lopt;
gg.main.Upi.display <- display_gg gg;
(* et cration des boutons *)
let vb_bs =
Array.map (fun x -> x,Upi.create_button (" "^(gg.to_string x)^" ")
lopt)
gg.rg.g.sommets in
let f_act_b = Array.map (fun (x,(b,bs)) ->
let ac = action_clic x gg b
in Upi.set_bs_action bs ac) vb_bs in
let bb =
Array.map (function (_,(b,_)) -> Upi.create_border b lopt2) vb_bs
in
Array.iteri
(fun i (b) -> let x,y = gg.pos.(i) in
Upi.add_component gg.main b
["PosX",Upi.Iopt (x-w/2);
"PosY", Upi.Iopt (y-h/2)]) bb;
();;
val maj_gg :
'a gg ->
int ->
int -> (string * Upi.opt_val) list -> (string * Upi.opt_val) list -> unit =
<fun>
Les boutons sont crs automatiquement et sont bien ajouts la fentre principale.

Test de l'interface
Tout est prt alors pour la cration d'une interface. On utilise un graphe dont les sommets sont des chanes de caractres pour simplifier les fonctions de conversion. On construit le graphe gg de la manire suivante :

# let id x = x;;
# let pos = [| 200, 300; 80, 200 ; 100, 100; 200, 100; 260, 200 |];;
# let gg = cree_gg (create_rech_graphe (test_aho())) pos id id;;
# maj_gg gg 400 400 ["Background", Upi.Copt (Graphics.rgb 130 130 130);
"Foreground",Upi.Copt Graphics.green]
[ "Relief", Upi.Sopt "Top";"Border_size", Upi.Iopt 2];;


L'appel Upi.loop true false gg.main;; lance la boucle d'interaction de la bibliothque Upi.



Figure 13.9 : Slection des sommets d'une recherche


La figure 13.9 montre le chemin trouv entre le sommet "A" et "E". Les arcs par lesquels passe le chemin changent de couleur.

Application autonome

Nous combinons les diffrentes tapes rencontres pour la construction d'une application autonome, c'est--dire ne ncessitant pas la prsence de la distribution d'Objective CAML sur la machine o elle s'excute. Cet application prend en argument le nom du fichier de description du graphe.

Fichier de description du graphe

Ce fichier dcrit d'une part les informations du graphe ainsi que les informations utiles pour l'interface graphique. Pour celles-ci nous dfinissons un deuxime format. De cette description graphique, on construit une valeur de type g_info suivant.

# type g_info = {spos : (int * int) array;
mutable opt : Upi.lopt;
mutable g_w : int;
mutable g_h : int};;


Le format de description des informations graphiques est dcrit par les quatre mots cls de la liste key2.

# let key2 = ["HAUTEUR"; "LARGEUR"; "POSITION"; "COULEUR"];;
val key2 : string list = ["HAUTEUR"; "LARGEUR"; "POSITION"; "COULEUR"]
# let lex2 l = Genlex.make_lexer key2 (Stream.of_string l);;
val lex2 : string -> Genlex.token Stream.t = <fun>

# let pars2 g gi s = match s with parser
[< '(Genlex.Kwd "HAUTEUR"); '(Genlex.Int i) >] -> gi.g_h <- i
| [< '(Genlex.Kwd "LARGEUR"); '(Genlex.Int i) >] -> gi.g_w <- i
| [< '(Genlex.Kwd "POSITION"); '(Genlex.Ident s);
'(Genlex.Int i); '(Genlex.Int j) >] -> gi.spos.(index s g) <- (i,j)
| [< '(Genlex.Kwd "COULEUR"); '(Genlex.Ident s);
'(Genlex.Int r); '(Genlex.Int g); '(Genlex.Int b) >] ->
gi.opt <- (s, Upi.Copt (Graphics.rgb r g b))::gi.opt
| [<>] -> ();;
val pars2 : string graphe -> g_info -> Genlex.token Stream.t -> unit = <fun>


Cration de l'application

La fonction create_graphe prend en entre un nom de fichier et retourne un couple compos d'un graphe et des informations graphiques associes.

# let create_gg_graphe name =
let g = create_graphe name in
let gi = {spos = Array.create g.taille (0,0); opt=[]; g_w =0; g_h = 0;} in
let ic = open_in name in
try
print_string ("Loading (pass 2) " ^name ^" : ");
while true do
print_string ".";
let l = input_line ic in pars2 g gi (lex2 l)
done ;
g,gi
with End_of_file -> print_newline(); close_in ic; g,gi;;
val create_gg_graphe : string -> string graphe * g_info = <fun>


La fonction create_app construit l'interface d'un graphe.

# let create_app name =
let g,gi = create_gg_graphe name in
let size = (string_of_int gi.g_w) ^ "x" ^ (string_of_int gi.g_h) in
Graphics.open_graph (":0 "^size);
let gg = cree_gg (create_rech_graphe g) gi.spos id id in
maj_gg gg gi.g_w gi.g_h
[ "Background", Upi.Copt (Graphics.rgb 130 130 130) ;
"Foreground", Upi.Copt Graphics.green ]
[ "Relief", Upi.Sopt "Top" ; "Border_size", Upi.Iopt 2 ] ;
gg;;
val create_app : string -> string gg = <fun>


Enfin la fonction main rcupre le nom du fichier sur la ligne de commande, construit le graphe et son interface, puis lance la boucle d'interaction sur le composant principal de l'interface du graphe.

# let main () =
if (Array.length Sys.argv ) <> 2
then Printf.printf "Usage: dij.exe filename\n"
else
let gg = create_app Sys.argv.(1) in
Upi.loop true false gg.main;;
val main : unit -> unit = <fun>


La dernire expression de ce programme lance la fonction main.

Excutable autonome

L'intrt de fabriquer un excutable autonome est de faciliter sa diffusion. On regroupe l'ensemble des types et fonctions dcrits dans cette section dans le fichier dij.ml. On compile ensuite ce fichier en lui joignant les diffrentes bibliothques dont il se sert. Voici la ligne de compilation pour un systme Linux :
ocamlc -custom -o dij.exe graphics.cma upi.cmo graphes.ml \
    -cclib -lgraphics -cclib -L/usr/X11/lib -cclib -lX11
La description de la compilation d'un excutable autonome utilisant la bibliothque Graphics est donne aux chapitres 5 et 7.

Pour en faire plus

Le squelette de cette application est suffisamment gnraliste pour pouvoir tre remploy dans un autre cadre que la recherche d'un itinraire de voyage. En effet diffrents types de problmes peuvent se reprsenter sous forme d'un graphe valu. Par exemple la recherche d'un chemin dans un labyrinthe peut se coder sous forme d'un graphe o chaque carrefour est un sommet du graphe. Trouver une solution revient dterminer le plus court chemin entre le dpart et l'arrive.

Pour comparer les performances entre C et Objective CAML, on crit l'algorithme de Dijkstra en C. Le programme C utilisera les structures de donnes Objective CAML pour effectuer le calcul.

Pour amliorer l'interface graphique, on ajoute un textfield pour le nom de fichier et deux boutons pour le chargement ou la sauvegarde d'un graphe. L'utilisateur peut aussi modifier les emplacements des sommets la souris pour amliorer le rendu.

Une deuxime amlioration de l'interface graphique est de pouvoir choisir la forme des sommets. L'affichage d'un bouton appelle la fonction de trac d'un rectangle. Rien n'empche de spcialiser les fonctions d'affichage et d'appartenance par des polygones de description du sommet.






Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora201.html0000644000000000000000000002423707421273602014473 0ustar Autres outils de dveloppement Prcdent Index Suivant

Autres outils de dveloppement

Nous nous sommes cantonns jusqu' maintenant la distribution d'Objective CAML. Nanmoins la communaut des dveloppeurs dans ce langage est active, comme l'indique le nombre de transactions de la liste de diffusion caml-list@inria.fr. De nombreux outils, bibliothques et extensions sont utiliss pour faciliter le dveloppement. On dtaille par la suite l'utilisation d'outils d'dition, d'extension de syntaxe, d'interfaage avec d'autres langages et de programmation parallle. Nous mentionnons galement les nombreuses bibliothques d'interface graphique. On retrouve la plupart de ces contributions sur le site de la << bosse du chameau >>.

Outils d'dition

Il existe plusieurs modes reconnaissant la syntaxe Objective CAML pour l'diteur emacs. L'intrt de ces modes est d'indenter automatiquement le texte en cours d'criture, permettant ainsi une meilleure lisibilit de celui-ci. C'est une alternative la fentre d'interaction sous Windows. Puisqu'emacs fonctionne sous Windows, on peut lancer dans une de ses fentres la boucle d'interaction Objective CAML.

Extension de syntaxe

Les outils d'analyses lexicale et syntaxique proposs dans la distribution sont dj fort complets, mais ils ne permettent pas d'tendre la syntaxe du langage lui-mme. L'outil camlp4 (voir lien page ??) s'utilise en lieu et place de l'analyseur syntaxique d'Objective CAML. Les compilateurs de ce dernier n'ont plus qu' procder au typage et la gnration de code. Cet outil permet l'extension de la syntaxe du langage par l'utilisateur ou la modification de la syntaxe originale du langage. Il offre galement des fonctionnalits d'affichage (pretty-printing) des arbres de syntaxe engendrs. Il devient alors facile d'crire une nouvelle boucle d'interaction pour toute extension de la syntaxe d'Objective CAML, voire pour un autre langage implant en Objective CAML.

Interoprabilit avec d'autres langages

Le chapitre 12 a dtaill l'interfaage entre le langage Objective CAML et C. Une application multi-langages tire avantage des traits utiles de chacun d'eux tout en faisant vivre en harmonie des codes diffrents partageant un mme espace mmoire. Nanmoins l'encapsulation de fonctions C pour les rendre appelables partir d'Objective CAML ncessite un travail fastidieux. Pour le simplifier, l'outil camlIDL (voir lien page ??) fournit un gnrateur de capsules et les outils d'importation de composants COM (Windows) en Objective CAML. Les capsules sont engendres partir d'un fichier des descriptions de l'interface (IDL).

Interfaces graphiques

La bibliothque Graphics permet de raliser des dessins et des interactions simples, mais elle ne peut pas tre considre comme une interface graphique digne de ce nom. Le chapitre 13 a montr comment on pouvait tendre cette bibliothque pour construire des composants graphiques rpondant certaines interactions. Le fait de reposer sur Graphics permet de conserver la portabilit de l'interface sur diffrentes plate-formes (X-Windows, Windows, MacOS), mais limite son utilisation la couche basse des vnements et des contextes graphiques.

Plusieurs ralisations tentent de combler ce manque, malheureusement aucune n'arrive tre complte, portable, documente et simple d'emploi. Voici une liste (extraite de la << bosse du chameau >>) des principales ralisations :
  • OlibRt : sous ce doux nom, c'est une vritable bote outils, construite au dessus de X-Windows, mais non documente. Sa distribution contient des exemples complets en particulier de nombreux jeux.

    Lien


    http://cristal.inria.fr/~ddr/
  • camlTk est une interface complte du toolkit Tk bien documente. Son point faible est de dpendre des versions de Tcl/Tk ce qui la rend difficile installer. Elle a t utilise pour la construction du navigateur mmm [Rou96] crit en Objective CAML.

    Lien


    http://caml.inria.fr/~rouaix/camltk-readme.html
  • La bibliothque Xlib a t rcrite en Objective CAML. Efuns, mini-clone d'emacs est ralis en l'utilisant. Xlib n'est pas vraiment une bote outils, et n'est pas portable sur d'autres systmes graphiques que X-Windows.

  • mlGtk est une interface construite sur Gtk. Elle est en cours de ralisation et n'a pas de documentation. Son intrt est d'tre portable sous Unix et Windows (car Gtk l'est) de manire plus simple que Tk. D'autre part elle utilise la couche objet d'Objective CAML, ce qui n'est pas sans poser parfois quelques problmes.
  • LabTk est une interface avec Tcl/Tk, pour Unix, utilisant des extensions d'Objective CAML qui seront intgres dans la prochaine version (voir annexe B). Elle contient sa propre distribution de Tcl/Tk qui s'installe facilement.
Malgr l'effort de la communaut, il existe un rel manque d'outils de construction d'interfaces portables. On peut esprer que LabTk devienne portable sur diffrents systmes.

Programmation parallle et distribution

Les processus lgers et les prises de communication offrent dj les mcanismes de base pour la programmation concurrente et rpartie. L'interfaage avec le langage C permet d'utiliser les bibliothques classiques de programmation parallle. Seule manque une interface avec CORBA pour l'invocation de mthodes sur des objets distants. Par contre, il existe de nombreuses bibliothques et extensions du langage qui utilisent diffrents modles de paralllisme.

Bibliothques

Les deux principales bibliothques pour la programmation parallle, MPI (Message Passing Interface) et PVM (Parallel Virtual Machine), sont interfaces avec Objective CAML. On trouve sur le site

Lien


http://www.netlib.org
la documentation, les liens et les sources de ces bibliothques. La << bosse du chameau >> contient les diffrentes adresses HTTP d'o l'on peut tlcharger les versions interfaces avec Objective CAML.

Extensions

De nombreuses extensions parallles de Caml-Light ou Objective CAML ont t ralises :
  • Caml-Flight ([FC95]) est une extension SPMD (Simple Program Multiple Data) du langage Caml-Light. Un programme excute une copie de lui-mme sur un nombre fix de processus. Les communications sont explicites, il existe seulement une opration de communication get qui peut tre excute uniquement l'intrieur de l'opration de synchronisation sync.

    Lien


    http://www.univ-orleans.fr/SCIENCES/LIFO/Members/ghains/caml-flight.html


  • BSML [Lou98] est une extension par des oprations BSP. Le langage prserve la compositionnalit et permet des prvisions prcises de performances si le nombre de processeurs est fix.

    Lien


    http://www.univ-orleans.fr/SCIENCES/LIFO/Members/loulergu/bsml.html


  • OCAMLP3 [DDLP98] est un environnement de programmation parallle bas sur le modle des squelettes du langage P3L. Les diffrents squelettes prdfinis peuvent s'imbriquer. Les programmes peuvent se tester soit en mode squentiel soit en mode parallle permettant ainsi de rutiliser des outils propres Objective CAML.

    Lien


    http://www.di.unipi.it/~susanna/projects.html


  • JoCAML [CL99] repose sur le modle du join-calculus qui permet des oprations de haut niveau pour la concurrence, la communication et la synchronisation en prsence d'objets rpartis et de codes mobiles tout en conservant une gestion automatique de la mmoire.

    Lien


    http://pauillac.inria.fr/jocaml/


  • Lucid Synchrone ([CP95]) est un langage ddi l'implantation de systmes ractifs. Il combine la fonctionnalit d'Objective CAML et les traits des langages de flots synchrones.

    Lien


    http://www-spi.lip6.fr/~pouzet/lucid-synchrone/

Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora063.gif0000644000000000000000000000267107073350152014277 0ustar GIF89a UUU!, H0I8ͻ@(dihl~p,0@m|kpH]r32Њ3JVX4}l733^773.Ov">_M1 OnLSd9  cs "AZ!Z Q H8F6;GI=c hFE[ `m" Ӆp^ QP2]K9*$RYMz7aAsg2z1h0BK&= iN1Jի/juW]#سRѡ],%۷KݻBVݫ ߿A;Td+qSǐ-u2e/ͬy(>?)ztҦ[N=ծŽ-n6mW!΍i7A .+> kC?#}:R|7ʽ,O w5iGSz%ާ`75{ |.IeA"~`q($6a=Ĉ\(YH.RH  "ӍHԒ1xMY1b@Ib䒈X/BD`yHu) #F~G2BHM>`ߑ#16jZMuF\蒌$5,Eڀ^M,,PB5*>(rUL^'N9qå hdO96t,2!Z!'E!}DK4]bKjby蹊rZҜrhjD!GT~Gl"˕tN}j(V ʢ]A:Ph->z63%4wd(ʒ:GO%;H~ՊI4Q5MuY{UԽgՀl_E݆=k_ב- ݎ郵[5ߩ98$&8Ԯ!؅8ُ ΅p̀ޖpSQ:(Ϝ9#:ȥ4Πٵm!B|ȼJ|H?=_?w}~(я_?R~ٿ~яN^H@XB9H 60v! b΂w: = 6x0a`Q#F mQ{K`؈21@ٰ xf-Kڼ$,=UcPGb<cTBYE+lEW)J&]# ]6TW,_Cb)Ɛ `EF)_tf5'[uh:M#0 iirvb`(3hĿqD\*'ʽ%l[g;ocaml-book-1.0/fr/html/book-ora071.html0000644000000000000000000000267207421273601014476 0ustar Rsum Prcdent Index Suivant

Rsum

Ce chapitre a montr les diffrentes passes des compilateurs Objective CAML. Le compilateur de byte-code favorise la portabilit du code binaire et permet donc de distribuer des bibliothques sous ce format-l. Cette proprit est perdue dans le cas d'excutables autonomes. Le compilateur natif privilgie l'efficacit du code produit en perdant aussi la portabilit. Dans ce cas, la production d'excutables autonomes ne change pas la perte de portabilit.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora088.gif0000644000000000000000000000560407073350152014305 0ustar GIF89ak֖!,kH0I8ͻ`(di©lp,ϴF|pH,Ȥrl:ШtJZoSٰxL.zm]Wή~W}q<;;JtHptGI^:HpIpGFmǩ!^ @#JHŋaCંyTrH 3ʜI͛jOt?my#Œ!gԒ o=jʵ'K}+k &6 Vkfv+k覫0@k,0/' 7G,'|IWO q ,l!_LP(%,L8?o,0K, t4-2B.' @T+Nc}Ykuc2@,r4CM7[c=4-˽wS`-vz{sMj3ܑCn9ѕ;m ۓϽ29Wo9yc醓^q箣.{oϾ; N'ꎋ.{<Т[n=_} /wᣏ{S4~F[Bj0"4GDP<̢|Bc?0_O\LjF tU"(hBi.c+1'80쐯H R},a Ga{st-iqPD%ʆx;҈ +Y&q"sIAr~]6*r"" #IS&F(JnxK sӤ1ei-gf__i2u3b=yunX11M(Aω-np|hP*RkgECGtft i#)*Ғⓤ&M)CAҖ:L.i`*Ӛ6hӞ" >)NJTz2=*R]:ԥTNM)TZҦRSFՀ6"%(01*s`d ?ɯfԬdQ撥+>jW0vjǶ cCIXV/fy@e5ֆRk3q-nse#;P{K{ZzdVr$o XNR<o&yKݜ «k}ࡐݞq ݒЕk."1L^Coҥcx ξtEa(\rDn˹U5&mޮhGrW涶ӽZvo&jzsز38{u6~=^ly\8b[HP0V&[֍8ȁ Y ;fs#/kglG6R&S\9y>ꚛ5Z)\@3@8tY`O~`gm }[Ͼ_TO~NiyHSOϿCv/`TuVHUw7Ho Tx؀A%>8':hl?'xhR5EE50(R&V-R*,)f-5;P:hRo?1!>8aE_B~aDŽ7 '#ㄉGh\{g+=]8ǃ(dg[nXvl2tXvxxz|؇~~8XxHxy;"W,xxPT$8X؉  ؊8Xx 8XxȘP~H ;ocaml-book-1.0/fr/html/book-ora165.html0000644000000000000000000000374307421273602014503 0ustar Plan du chapitre Prcdent Index Suivant

Plan du chapitre

La premire section indique comment utiliser le module Unix. Nous y parlons principalement de la gestion des erreurs spcifiques ce module et de la portabilit sur Windows des appels systme.

La deuxime section prsente les descripteurs de fichier au sens Unix et leur utilisation pour des oprations d'entres-sorties de plus bas niveau que celles fournies par le module prcharg Pervasives.

Les processus sont introduits la troisime section. On y parle de leur cration, de leur disparition et de la manire dont tous les processus entretiennent un lien de filiation dans le modle Unix.

La quatrime section aborde les moyens de base de communication entre processus : les tubes de communication et les signaux.

Ces deux dernires sections se prolongent dans les chapitres 19 et 20 par la prsentation des processus lgers et des prises de communication.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora053.gif0000644000000000000000000000264407073350152014276 0ustar GIF89atyyy!,tH0I8=`(dihl뾤tmxnz',H1l:˧tJUجz.xLΉzn-Mu zz ||%r 1{ Üё#Ҕފ"֛ߑ!֦h o1cTnr)H[Āҡ*`k'5n$ĉ zJd2Ś.OV\ٰCUuyKGo-RtIm *;3!QMg颣_&F"۔iWiU{,$C UnXlj]GxťI8F>lnXܾ3iɡW,hFϞvs\bᢆLإ3ϮW*WCVhwvj#I޶KG՜p7q_%dwď]?Zn(d{swrXG|wѓ? kWtLH!-<nL}O" CbG&^w:&ϊB(L6A"DnYE&8)TBXXZTJYx RIIB= 6I/؉C0 zj rȧ袌6裐FSf馜fJi]騤j꣟Rj) ꫧ *Qj뭸**묅 Ǯ& Fk:fJ˭!;;Ŋn+i-<;mQV/sP+p7~KW<? /w ;P,;*k@C/k2 ߋx |Ɋ2 3m:dF L/@Ǭ0W/K=59gM4QLua@<uߞ-N[-F}B44l6-Aހ3>1l. |͗#n5ˏ9CRgN拫=ދ9{ߪsn{; `y'n77d3<Ѹ#?|iERߵzC:en.y +{5? Pour en savoir plus Prcdent Index Suivant

Pour en savoir plus

L'ouvrage de rfrence sur les analyses lexicale et syntaxique est connu sous le surnom du << dragon >> qui illustre sa couverture. Son vrai titre est Compilateur : principes, techniques et outils ([ASU89]). Il couvre toutes les tapes de la compilation. Il explique clairement la construction des automates correspondant une grammaire non contextuelle donne et les techniques pour le minimiser. Les outils lex et yacc sont largement dcrits dans diffrents ouvrages, une bonne rfrence est [LMB92]. L'intrt de ocamllex et ocamlyac par rapport leurs versions originales est l'intgration au langage Objective CAML et surtout de pouvoir construire des analyseurs typs. Sur les streams, le rapport de recherche de Michel Mauny et Daniel de Rauglaudre [MdR92] permet de bien comprendre la smantique oprationnelle de cette extension. D'autre part la lecture de [CM95] montre comment construire une telle extension. Pour une meilleure intgration des grammaires dans le langage Objective CAML, ou pour modifier la grammaire de ce dernier, on peut utiliser l'outil camlp4 que l'on trouve  :

Lien


http://caml.inria.fr/camlp4/







Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora086.html0000644000000000000000000002354707421273602014511 0ustar Allocation et rcupration mmoire Prcdent Index Suivant

Allocation et rcupration mmoire

La plupart des langages permettent d'allouer dynamiquement de la mmoire. On peut citer C, Pascal, Lisp, ML, SmallTalk, C++, Java, ADA.

Allocation explicite

On distingue deux types d'allocation :
  • une allocation simple qui rserve une zone mmoire d'une certaine taille sans se proccuper de son contenu ;
  • une allocation qui joint la rservation d'un espace mmoire une initialisation pour crer des valeurs.
Le premier cas est illustr par les fonction new de Pascal ou malloc de C. Elles retournent un pointeur sur une zone mmoire (i.e. son adresse), on peut alors accder cette zone pour consulter ou modifier la valeur qu'elle contient. Le deuxime cas correspond aux fonctions de construction de valeurs d'Objective CAML ou celle des langages objets. Les instances de classes des langages objets sont construites en passant un oprateur new, la fonction de construction particulire la classe qui, elle-mme attend, en gnral, un certains nombre de paramtres d'initialisation. Pour les langages fonctionnels, les fonctions de constructions sont immdiatement utilises ds que l'on dfinit une valeur structure (un couple, une liste, un enregistrement, un vecteur, une fermeture).

Examinons la construction de valeurs Objective CAML sur un exemple. Leur reprsentation mmoire est illustre par le schma de la figure 9.1.


Figure 9.1 : reprsentation mmoire de valeurs



# let u = let l = ['c'; 'a'; 'm'] in List.tl l ;;
val u : char list = ['a'; 'm']
# let v = let r = ( ['z'] , u )
in match r with p -> (fst p) @ (snd p) ;;
val v : char list = ['z'; 'a'; 'm']
On a reprsent un lment de liste par un doublet de deux mots, le premier contenant un caractre et le second un pointeur vers l'lment suivant de la liste. La reprsentation relle est lgrement diffrente. Elle est dtaille dans le chapitre sur l'interface avec C (voir page ??).

La premire phrase construit une valeur, nomme l, en allouant une cellule (constructeur ::) pour chaque lment de la liste ['c';'a';'m']. La dclaration globale u correspond la queue de l. Il y a ici un partage entre l et u, c'est--dire entre l'argument et le rsultat de l'application de List.tl. Seule la dclaration u est connue aprs l'valuation de cette premire phrase (l cesse d'exister).

La deuxime phrase construit une liste un seul lment, puis un couple nomm r contenant cette liste et la liste u. Ce couple est filtr et renomm p par le filtrage. Ensuite, le premier lment de p est concatn avec son second lment, ce qui cre une valeur ['z';'a';'m'] lie l'identificateur global v. On observe que le rsultat de snd (la liste ['a';'m']) est partag avec son argument p alors que le rsultat de fst (le caractre 'z') est copi.

Dans tous les cas l'allocation mmoire est explicite. C'est--dire qu'elle est demande par le programmeur (comme instruction ou expression du langage).

Remarque


L'allocation mmoire conserve une information sur la taille de l'objet allou dans le but de pouvoir le librer par la suite.


Rcupration explicite

Les langages dont la rcupration mmoire est explicite possdent un oprateur de libration (free en C ou dispose en Pascal) qui reoit l'adresse (un pointeur) de la zone dsallouer. En utilisant les informations conserves au moment de l'allocation, le programme libre cette zone et pourra la rutiliser par la suite.

L'allocation dynamique est en gnral utilise pour manipuler des donnes structures volutives telles les listes, les arbres, etc. Librer l'espace occup par de telles donnes ne se fait pas en << un coup de cuillre pot >> mais rclame une fonction de parcours de la donne. On appelle de telles fonctions des destructeurs.

Si la dfinition correcte des fonctions de destruction ne pose pas trop de difficult, leur usage est, en revanche, plus dlicat. En effet, pour librer l'espace occupe par une structure, il faut en parcourir les pointeurs et appliquer l'oprateur de libration mmoire du langage. Laisser la libration de la mmoire la charge du programmeur prsente l'avantage que ce dernier est sr des actions effectues. Nanmoins une mauvaise utilisation de ces oprateurs peut provoquer une erreur l'excution du programme. Les principaux dangers de la rcupration explicite sont :
  • les pointeurs << fantmes >> (dandling pointers) : Une zone mmoire a t libre alors qu'il subsiste des pointeurs qui rfrencent toujours cette zone. Si celle-ci est rutilise, l'accs cette zone par ces pointeurs risque d'tre incohrent.
  • les zones mmoire inaccessibles (cas de << fuite >> ou leak) : Une zone mmoire toujours alloue qui n'est plus rfrence par aucun pointeur. Il n'y a plus de possibilits de la librer. Il y a une perte sche de mmoire.
C'est toute la difficult de la rcupration explicite de mmoire que de connatre la dure de vie de l'ensemble des valeurs d'un programme.

Rcupration implicite

Les langages avec rcupration implicite ne possdent pas d'oprateurs de libration mmoire. Il n'est pas possible pour le programmeur de dcrter la disparition d'une valeur alloue. Nanmoins, un mcanisme de rcupration automatique est dclench quand une valeur n'est plus rfrence, ou lors d'un chec de l'allocation, c'est--dire quand le tas est plein.

Un algorithme de rcupration automatique de mmoire est en quelque sorte un destructeur global. Ce caractre rend sans doute plus difficile sa conception et son implantation que le seraient celles d'un destructeur ddi une structure de donnes particulire. Mais, une fois cette difficult surmonte, la fonction de rcupration obtenue accrot considrablement la sret de la gestion mmoire. En particulier le risque de pointeurs fantmes disparat.

D'autre part, une fonction de rcupration automatique de mmoire peut apporter de bonnes proprits sur le tas :
  • compaction : toute la mmoire rcupre tient en un seul bloc, vitant ainsi la granularit du tas et permettant d'allouer des objets de la taille du tas libre ;
  • localisation : les diffrents composants d'une mme valeur sont proches du point de vue adressage mmoire, ce qui permet lors de leur parcours de rester dans les mmes pages mmoire, vitant ainsi de sortir de la mmoire-cache.
Les choix de conception d'un GC doivent tenir compte d'un certain nombre de critres et de contraintes :
  • facteur de rcupration : quel pourcentage de la mmoire inutilise est disponible ?
  • fragmentation de la mmoire : peut-on allouer un bloc de la taille de la mmoire libre ;
  • ralentissement de l'allocation et de la rcupration ;
  • compilation : la reprsentation des valeurs est-elle totalement libre ?
En pratique, le critre de sret reste primordial, et les GC trouvent un compromis concernant les autres contraintes.


Prcdent Index Suivant ocaml-book-1.0/fr/html/book-ora174.html0000644000000000000000000000755107421273602014504 0ustar Introduction Prcdent Index Suivant

Introduction

On appelle concurrence l'indpendance causale entre plusieurs actions, comme l'excution de plusieurs instructions en << mme temps >>. C'est la dfinition que nous donnions du terme << parallle >> en introduction de cette quatrime partie. Les processus de la bibliothque Unix prsents au chapitre prcdent peuvent tre qualifis de concurrents dans la mesure o le systme Unix donne l'apparence de la simultanit de leur excution sur une machine mono-processeur. Mais la notion de processus et de concurrence ne s'applique pas qu' ceux obtenus par l'appel systme fork.

Le langage Objective CAML possde une bibliothque pour les processus lgers (threads). La principale diffrence entre un processus lger et un processus engendr par un fork vient du partage ou du non partage de la mmoire entre les diffrents processus issus du mme programme. Entre deux processus lgers, seul le contexte d'excution diffre; le code et la zone mmoire des donnes sont partags. Les processus lgers n'amliorent pas les temps d'excution d'une application. Leur principal intrt est de pouvoir exprimer dans un langage de programmation des algorithmes concurrents.

La nature du langage choisi, impratif ou fonctionnel, fait varier le modle de concurrence. Pour un programme impratif, comme chaque processus lger peut modifier la mmoire commune, on est dans un modle mmoire partage. La communication entre processus peut se faire par valeurs crites et lues dans cette mmoire. Pour un programme purement fonctionnel, c'est--dire sans effet de bord, et bien que la mmoire soit commune, les calculs qu'excute chaque processus n'agissent pas sur l'tat mmoire partag. Dans ce cas le modle utilis est celui mmoire distincte et l'interaction entre processus doit s'effectuer par la communication de valeurs via des canaux.

Le langage Objective CAML implante dans sa bibliothque sur les processus lgers les deux modles. Le module Thread permet de lancer de nouveaux processus correspondants l'appel d'une fonction avec son argument. Les modules Mutex et Condition apportent les outils de synchronisation que sont les verrous de zone d'exclusion mutuelle et les attentes sur condition. Le module Event implante un mode de communication de valeurs du langage par vnements. Ces valeurs peuvent elles-mmes tre fonctionnelles, permettant ainsi d'changer entre processus lgers des calculs effectuer. Comme souvent en Objective CAML il est possible de mlanger les deux modles.

Cette bibliothque est portable sur les diffrents systmes sur lesquels fonctionne Objective CAML. la diffrence du module Unix, la bibliothque Thread permet l'utilisation de processus sur des machines ne tournant pas sous Unix.


Prcdent Index Suivant ocaml-book-1.0/en/0000755000000000000000000000000007455136117010676 5ustar ocaml-book-1.0/en/ocaml-ora-book.pdf0000644000000000000000001275741607452266210014215 0ustar %PDF-1.2 7 0 obj << /Type/Encoding /Differences[0/Gamma/Delta/Theta/Lambda/Xi/Pi/Sigma/Upsilon/Phi/Psi/Omega/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/acute/caron/breve/macron/ring/cedilla/germandbls/ae/oe/oslash/AE/OE/Oslash/suppress/exclam/quotedblright/numbersign/sterling/percent/ampersand/quoteright/parenleft/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon/exclamdown/equal/questiondown/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/quotedblleft/bracketright/circumflex/dotaccent/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/endash/emdash/hungarumlaut/tilde/dieresis/Gamma/Delta/Theta/Lambda/Xi/Pi/Sigma/Upsilon/Phi/Psi/Omega/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/acute/caron/breve/macron/ring/cedilla/germandbls/ae/oe/oslash/AE/OE/Oslash/suppress/Gamma/Delta/Theta/Lambda/Xi/Pi/Sigma/Upsilon/Phi/Psi 173/Omega/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/acute/caron/breve/macron/ring/cedilla/germandbls/ae/oe/oslash/AE/OE/Oslash/suppress/dieresis 255/dieresis] >> endobj 10 0 obj << /Encoding 7 0 R /Type/Font /Subtype/Type1 /Name/F1 /FontDescriptor 9 0 R /BaseFont/XSVBDA+CMBXTI10 /FirstChar 33 /LastChar 196 /Widths[386.1 620.6 944.4 868.5 944.4 885.5 355.6 473.3 473.3 591.1 885.5 355.6 414.4 355.6 591.1 591.1 591.1 591.1 591.1 591.1 591.1 591.1 591.1 591.1 591.1 355.6 355.6 386.1 885.5 591.1 591.1 885.5 865.5 816.7 826.7 875.5 756.7 727.2 895.3 896.1 471.7 610.5 895 697.8 1072.8 896.1 855 787.2 855 859.4 650 796.1 880.8 865.5 1160 865.5 865.5 708.9 356.1 620.6 356.1 591.1 355.6 355.6 591.1 532.2 532.2 591.1 532.2 400 532.2 591.1 355.6 355.6 532.2 296.7 944.4 650 591.1 591.1 532.2 501.7 486.9 385 620.6 532.2 767.8 560.6 561.7 490.6 591.1 1182.2 591.1 591.1 591.1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 697.8 944.4 885.5 806.7 767.8 896.1 826.7 885.5 826.7 885.5 0 0 826.7 755.6 674.4 703.9 1044.7 1059.4 355.6 385 591.1 591.1 591.1 591.1 591.1 948.9 532.2 665 826.7 826.7 591.1 1022.8 1140.5 885.5 296.7 591.1] >> endobj 11 0 obj << /Type/Encoding /Differences[33/exclam/quotedblright/numbersign/dollar/percent/ampersand/quoteright/parenleft/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon/exclamdown/equal/questiondown/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/quotedblleft/bracketright/circumflex/dotaccent/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/endash/emdash/hungarumlaut/tilde/dieresis/Gamma/Delta/Theta/Lambda/Xi/Pi/Sigma/Upsilon/Phi/Psi/Omega/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/acute/caron/breve/macron/ring/cedilla/germandbls/ae/oe/oslash/AE/OE/Oslash/suppress/Gamma/Delta/Theta/Lambda/Xi/Pi/Sigma/Upsilon/Phi/Psi 173/Omega/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/acute/caron/breve/macron/ring/cedilla/germandbls/ae/oe/oslash/AE/OE/Oslash/suppress/dieresis 255/dieresis] >> endobj 14 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F2 /FontDescriptor 13 0 R /BaseFont/RROFPS+CMBX12 /FirstChar 33 /LastChar 196 /Widths[342.6 581 937.5 562.5 937.5 875 312.5 437.5 437.5 562.5 875 312.5 375 312.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 312.5 312.5 342.6 875 531.2 531.2 875 849.5 799.8 812.5 862.3 738.4 707.2 884.3 879.6 419 581 880.8 675.9 1067.1 879.6 844.9 768.5 844.9 839.1 625 782.4 864.6 849.5 1162 849.5 849.5 687.5 312.5 581 312.5 562.5 312.5 312.5 546.9 625 500 625 513.3 343.7 562.5 625 312.5 343.7 593.7 312.5 937.5 625 562.5 625 593.7 459.5 443.8 437.5 625 593.7 812.5 593.7 593.7 500 562.5 1125 562.5 562.5 562.5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 675.9 937.5 875 787 750 879.6 812.5 875 812.5 875 0 0 812.5 656.2 625 625 937.5 937.5 312.5 343.7 562.5 562.5 562.5 562.5 562.5 849.5 500 574.1 812.5 875 562.5 1018.5 1143.5 875 312.5 562.5] >> endobj 16 0 obj << /Filter[/FlateDecode] /Length 145 >> stream x% 0wɐ׼$5YZ% m$J 8W-YT DANSkҔ8paȰ])v+q{\ A떛6(kƢ[W[ޡ/qZOZb8R (o endstream endobj 18 0 obj << /F1 10 0 R /F2 14 0 R >> endobj 6 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 18 0 R >> endobj 21 0 obj << /Filter[/FlateDecode] /Length 20 >> stream xS030PHWS\  endstream endobj 20 0 obj << /ProcSet[/PDF/Text/ImageC] >> endobj 26 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F3 /FontDescriptor 25 0 R /BaseFont/NAFBSM+CMR12 /FirstChar 33 /LastChar 196 /Widths[272 489.6 816 489.6 816 761.6 272 380.8 380.8 489.6 761.6 272 326.4 272 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 272 272 272 761.6 462.4 462.4 761.6 734 693.4 707.2 747.8 666.2 639 768.3 734 353.2 503 761.2 611.8 897.2 734 761.6 666.2 761.6 720.6 544 707.2 734 734 1006 734 734 598.4 272 489.6 272 489.6 272 272 489.6 544 435.2 544 435.2 299.2 489.6 544 272 299.2 516.8 272 816 544 489.6 544 516.8 380.8 386.2 380.8 544 516.8 707.2 516.8 516.8 435.2 489.6 979.2 489.6 489.6 489.6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 611.8 816 761.6 679.6 652.8 734 707.2 761.6 707.2 761.6 0 0 707.2 571.2 544 544 816 816 272 299.2 489.6 489.6 489.6 489.6 489.6 734 435.2 489.6 707.2 761.6 489.6 883.8 992.6 761.6 272 489.6] >> endobj 29 0 obj << /Type/Font /Subtype/Type1 /Name/F4 /FontDescriptor 28 0 R /BaseFont/BOIFIX+CMCSC10 /FirstChar 0 /LastChar 127 /Widths[683.3 902.8 844.4 755.5 727.8 813.9 786.1 844.4 786.1 844.4 786.1 552.8 552.8 319.4 319.4 523.6 302.2 424.4 552.8 552.8 552.8 552.8 552.8 813.9 494.4 915.6 735.6 824.4 635.6 975 1091.7 844.4 319.4 319.4 552.8 902.8 552.8 902.8 844.4 319.4 436.1 436.1 552.8 844.4 319.4 377.8 319.4 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 319.4 319.4 844.4 844.4 844.4 523.6 844.4 813.9 770.8 786.1 829.2 741.7 712.5 851.4 813.9 405.6 566.7 843 683.3 988.9 813.9 844.4 741.7 844.4 800 611.1 786.1 813.9 813.9 1105.5 813.9 813.9 669.4 319.4 552.8 319.4 552.8 319.4 319.4 613.3 580 591.1 624.4 557.8 535.6 641.1 613.3 302.2 424.4 635.6 513.3 746.7 613.3 635.6 557.8 635.6 602.2 457.8 591.1 613.3 613.3 835.6 613.3 613.3 502.2 552.8 1105.5 552.8 552.8 552.8] >> endobj 32 0 obj << /Encoding 7 0 R /Type/Font /Subtype/Type1 /Name/F5 /FontDescriptor 31 0 R /BaseFont/UZAHIX+CMTI12 /FirstChar 33 /LastChar 196 /Widths[300 500 800 755.2 800 750 300 400 400 500 750 300 350 300 500 500 500 500 500 500 500 500 500 500 500 300 300 300 750 500 500 750 726.9 688.4 700 738.4 663.4 638.4 756.7 726.9 376.9 513.4 751.9 613.4 876.9 726.9 750 663.4 750 713.4 550 700 726.9 726.9 976.9 726.9 726.9 600 300 500 300 500 300 300 500 450 450 500 450 300 450 500 300 300 450 250 800 550 500 500 450 412.5 400 325 525 450 650 450 475 400 500 1000 500 500 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 613.4 800 750 676.9 650 726.9 700 750 700 750 0 0 700 600 550 575 862.5 875 300 325 500 500 500 500 500 814.8 450 525 700 700 500 863.4 963.4 750 250 500] >> endobj 35 0 obj << /Type/Font /Subtype/Type1 /Name/F6 /FontDescriptor 34 0 R /BaseFont/PHSMJV+CMSY10 /FirstChar 33 /LastChar 196 /Widths[1000 500 500 1000 1000 1000 777.8 1000 1000 611.1 611.1 1000 1000 1000 777.8 275 1000 666.7 666.7 888.9 888.9 0 0 555.6 555.6 666.7 500 722.2 722.2 777.8 777.8 611.1 798.5 656.8 526.5 771.4 527.8 718.7 594.9 844.5 544.5 677.8 762 689.7 1200.9 820.5 796.1 695.6 816.7 847.5 605.6 544.6 625.8 612.8 987.8 713.3 668.3 724.7 666.7 666.7 666.7 666.7 666.7 611.1 611.1 444.4 444.4 444.4 444.4 500 500 388.9 388.9 277.8 500 500 611.1 500 277.8 833.3 750 833.3 416.7 666.7 666.7 777.8 777.8 444.4 444.4 444.4 611.1 777.8 777.8 777.8 777.8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 777.8 277.8 777.8 500 777.8 500 777.8 777.8 777.8 777.8 0 0 777.8 777.8 777.8 1000 500 500 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 1000 1000 777.8 777.8 1000 777.8] >> endobj 38 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F7 /FontDescriptor 37 0 R /BaseFont/MCMCID+CMR10 /FirstChar 33 /LastChar 196 /Widths[277.8 500 833.3 500 833.3 777.8 277.8 388.9 388.9 500 777.8 277.8 333.3 277.8 500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 277.8 777.8 472.2 472.2 777.8 750 708.3 722.2 763.9 680.6 652.8 784.7 750 361.1 513.9 777.8 625 916.7 750 777.8 680.6 777.8 736.1 555.6 722.2 750 750 1027.8 750 750 611.1 277.8 500 277.8 500 277.8 277.8 500 555.6 444.4 555.6 444.4 305.6 500 555.6 277.8 305.6 527.8 277.8 833.3 555.6 500 555.6 527.8 391.7 394.4 388.9 555.6 527.8 722.2 527.8 527.8 444.4 500 1000 500 500 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 625 833.3 777.8 694.4 666.7 750 722.2 777.8 722.2 777.8 0 0 722.2 583.3 555.6 555.6 833.3 833.3 277.8 305.6 500 500 500 500 500 750 444.4 500 722.2 777.8 500 902.8 1013.9 777.8 277.8 500] >> endobj 41 0 obj << /Type/Font /Subtype/Type1 /Name/F8 /FontDescriptor 40 0 R /BaseFont/WVFCZA+CMTT10 /FirstChar 33 /LastChar 196 /Widths[525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 525 525 525 525 525 525 525 525 525 525 0 0 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525] >> endobj 42 0 obj << /Length 1105 /Filter/FlateDecode /Name/Im1 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF] >> >> stream xmVKn8 ZB?c`Lz40s<ʖ슃 H>RdJ%~swߟ[kKzmFVrʥRbJ ͅQi3x\{Yd&Fv[VxX0K^m"%uIȤAwKnLHYKhvDǝoNDfn-HI\rW_X" u_|V5>-ǔmd9-|c>ei"=jlb[8k+a \w oVdx{"Y<0wQKCWCRo8zwxW!*|M-u qy Is)uQԉjM@)HY+ʅ`i0(~S&?+S)i1 <2HD^ B r E'PAn6tMǔc"WtRRP~ j"Aroy{BARЖߞ2Rri=1}DD(>06JE-ְB='ҏNHQ?᷄-^['F\#71-O[rvZ~"w\:.KĆ߲:l×7 *zKZvG#퉄^ cE ؈ł']G+E & d藅K) :Va.etVF]S> endobj 46 0 obj << /Filter[/FlateDecode] /Length 1199 >> stream xڕWr6}W!Bt:udImRK3mM"k^T^=c+/2E-b]#.: z_m"3H6Y6F_-}m qiY0꓆ (48ÞߛESBX&%ޖM^HC mJc8D|L6u' !.l\'$?'ܺ:$=iuR~ˬߒ:(={[Eud3R|nìg1H>m,mϵ¼JZ7q00`[[ ("'Ap@zHP(gL5\7|4\1!iWTgÉopR'y%mj OJ8 8`}yIUX |Xi?ι$P.^ RסVSr0$ Tʪ gbFoByX."Jքk)²|6Cr'"MztD8)CÙGg,4m~p۝œGPڀpŒ9cPB(~* MU@o*IQicmi 鹓իQ+X4=u P[ ;%Qlv/%}Rx3ֳY:Őʛx4 t ѡ@B5P=zB! Bkẇ^0Ko}Dں QNw?M^{\WtgL]i wW7U*=KT 6gYG62}ju>qrc٣`gQ7J x0M@QV3 +qtF%x)ݛ녿]A0> endobj 48 0 obj << /Im1 42 0 R >> endobj 23 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 47 0 R /XObject 48 0 R >> endobj 53 0 obj << /Encoding 7 0 R /Type/Font /Subtype/Type1 /Name/F10 /FontDescriptor 52 0 R /BaseFont/WDZYLH+CMTI10 /FirstChar 33 /LastChar 196 /Widths[306.7 514.4 817.8 769.1 817.8 766.7 306.7 408.9 408.9 511.1 766.7 306.7 357.8 306.7 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 306.7 306.7 306.7 766.7 511.1 511.1 766.7 743.3 703.9 715.6 755 678.3 652.8 773.6 743.3 385.6 525 768.9 627.2 896.7 743.3 766.7 678.3 766.7 729.4 562.2 715.6 743.3 743.3 998.9 743.3 743.3 613.3 306.7 514.4 306.7 511.1 306.7 306.7 511.1 460 460 511.1 460 306.7 460 511.1 306.7 306.7 460 255.6 817.8 562.2 511.1 511.1 460 421.7 408.9 332.2 536.7 460 664.4 463.9 485.6 408.9 511.1 1022.2 511.1 511.1 511.1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 627.2 817.8 766.7 692.2 664.4 743.3 715.6 766.7 715.6 766.7 0 0 715.6 613.3 562.2 587.8 881.7 894.4 306.7 332.2 511.1 511.1 511.1 511.1 511.1 831.3 460 536.7 715.6 715.6 511.1 882.8 985 766.7 255.6 511.1] >> endobj 56 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F11 /FontDescriptor 55 0 R /BaseFont/WCPQOA+CMR9 /FirstChar 33 /LastChar 196 /Widths[285.5 513.9 856.5 513.9 856.5 799.4 285.5 399.7 399.7 513.9 799.4 285.5 342.6 285.5 513.9 513.9 513.9 513.9 513.9 513.9 513.9 513.9 513.9 513.9 513.9 285.5 285.5 285.5 799.4 485.3 485.3 799.4 770.7 727.9 742.3 785 699.4 670.8 806.5 770.7 371 528.1 799.2 642.3 942 770.7 799.4 699.4 799.4 756.5 571 742.3 770.7 770.7 1056.2 770.7 770.7 628.1 285.5 513.9 285.5 513.9 285.5 285.5 513.9 571 456.8 571 457.2 314 513.9 571 285.5 314 542.4 285.5 856.5 571 513.9 571 542.4 402 405.4 399.7 571 542.4 742.3 542.4 542.4 456.8 513.9 1027.8 513.9 513.9 513.9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 642.3 856.5 799.4 713.6 685.2 770.7 742.3 799.4 742.3 799.4 0 0 742.3 599.5 571 571 856.5 856.5 285.5 314 513.9 513.9 513.9 513.9 513.9 770.7 456.8 513.9 742.3 799.4 513.9 927.8 1042 799.4 285.5 513.9] >> endobj 59 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F12 /FontDescriptor 58 0 R /BaseFont/LAEAEM+CMR7 /FirstChar 33 /LastChar 196 /Widths[323.4 569.4 938.5 569.4 938.5 877 323.4 446.4 446.4 569.4 877 323.4 384.9 323.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 323.4 323.4 323.4 877 538.7 538.7 877 843.3 798.6 815.5 860.1 767.9 737.1 883.9 843.3 412.7 583.3 874 706.4 1027.8 843.3 877 767.9 877 829.4 631 815.5 843.3 843.3 1150.8 843.3 843.3 692.5 323.4 569.4 323.4 569.4 323.4 323.4 569.4 631 507.9 631 507.9 354.2 569.4 631 323.4 354.2 600.2 323.4 938.5 631 569.4 631 600.2 446.4 452.6 446.4 631 600.2 815.5 600.2 600.2 507.9 569.4 1138.9 569.4 569.4 569.4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 706.4 938.5 877 781.8 754 843.3 815.5 877 815.5 877 0 0 815.5 677.6 646.8 646.8 970.2 970.2 323.4 354.2 569.4 569.4 569.4 569.4 569.4 843.3 507.9 569.4 815.5 877 569.4 1013.9 1136.9 877 323.4 569.4] >> endobj 60 0 obj << /Filter[/FlateDecode] /Length 1486 >> stream xڝWKo6Wԥ(R\ƱFSFѢh[Vڈ6 ZkwKj873t4#}u]F5!*R^QR(.EtOvd$s6Nc7螾L۹n>NDFD;7?^$2Dw1];%^`iB7u\lCcEp9}FIZy?l~zwsĉ -.Z23%Lt_C|JB.TvoqU .]i *QARC\?ҧ^ XOW^\mO&dH^gNdBIyQzMBt}' qE:( h&-XVu LFIqUͩD𲈒XKs.%f"$0.$Ed\"Mӳl&Y.1I)y^Q=B!W%wx|kRv]7OOzf/(Q5f0p)աקFHu;$j1REu1T𼌒Iev8v|8cQ0/*!OH 4\8Otv?;ڴg&L`D;4.vJ`r:z:lu睢auUL<} B8J.Sd5c3XIҋع bfª4"hyXž5|ݱ~ AZѺSժ *u߭*с'dQDK32yEZ*II2]ˠ}恄q.P (T`>!ɴGjmmW l=\jj)G4yHozDA9}'<8OɕAG=(s䈤F%qڹǪX! Ŵ3^`v:AqV A+ hP没w>XX m+ '"<CO.}܌^I^yH y)'wu&Z3*q&w~4 gG d HXEm~0[_Rd d.Ortm}ƧH-6)yS=?[ȕc8OnᅆeDVZ zp1_v/p"i^~/j endstream endobj 61 0 obj << /F7 38 0 R /F10 53 0 R /F6 35 0 R /F4 29 0 R /F11 56 0 R /F12 59 0 R >> endobj 50 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 61 0 R >> endobj 66 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F13 /FontDescriptor 65 0 R /BaseFont/FDKLFJ+CMBXSL10 /FirstChar 33 /LastChar 196 /Widths[350 602.8 958.3 575 958.3 894.4 319.4 447.2 447.2 575 894.4 319.4 383.3 319.4 575 575 575 575 575 575 575 575 575 575 575 319.4 319.4 350 894.4 543.1 543.1 894.4 869.4 818.1 830.6 881.9 755.6 723.6 904.2 900 436.1 594.4 901.4 691.7 1091.7 900 863.9 786.1 863.9 862.5 638.9 800 884.7 869.4 1188.9 869.4 869.4 702.8 319.4 602.8 319.4 575 319.4 319.4 559 638.9 511.1 638.9 527.1 351.4 575 638.9 319.4 351.4 606.9 319.4 958.3 638.9 575 638.9 606.9 473.6 453.6 447.2 638.9 606.9 830.6 606.9 606.9 511.1 575 1150 575 575 575 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 691.7 958.3 894.4 805.6 766.7 900 830.6 894.4 830.6 894.4 0 0 830.6 670.8 638.9 638.9 958.3 958.3 319.4 351.4 575 575 575 575 575 925 511.1 597.2 830.6 894.4 575 1041.7 1169.4 894.4 319.4 575] >> endobj 67 0 obj << /Filter[/FlateDecode] /Length 1893 >> stream xuXI6 WV{cڥc:Y4Mc;RTd{<9  jn6>oV?:JVq~J(,:0V/ >:S[}O?.VUX(MX%uZqNY'ez4Nho{p K' nqt\lsd7_n@LTT25N{wv⍗juȉVfh@~KFZmqa`pvT # ?BoXV,4HM$TrprrN4bdd_gkivRy1nm⼮Ɗhsz/I=, w$dKh<::tS:E`1Y{ՙB'=XMj'T(s8[=Jeȵ;i8|9|9MQ$lsʷHܐ9DF1UuCS&xfdkTYST@P[t>i&>0l_q<&@A|MEE.D)H(Шټ{~o) qb#EL9X"y1VK0y0 {NchV\xDueiN8虺sftjY.,P3qI݋`Ĉ,pfڶUC{\UfyI2x{L"M{ JeP2flX }jkKe4{QDцo*D7l )C[\w-=iEl3 w M7{ڋ[/w,la^;=tf:{&ܥ.x;] j5Y)+ ? F mEu[O)R'uTuҦVf_PTi?ǑImOyxJ$x{g =R/Rwyڟ#oA#R#qy="g)?{o񶅚W `Y*^,Ojf5_f }Xpػ7 !<^w4YQqFjap'(,3tv u _n$..ᡱL<94PT-dBX| N#t4c`hŒE|"py~ GF'z\!"G endstream endobj 68 0 obj << /F13 66 0 R /F7 38 0 R /F4 29 0 R /F8 41 0 R >> endobj 63 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 68 0 R >> endobj 71 0 obj << /Filter[/FlateDecode] /Length 111 >> stream xS030PHWS N!n zf !i fz fzF !.e!^ &zf `"MSCԴd0 V4DfPB7k endstream endobj 72 0 obj << /F10 53 0 R >> endobj 70 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 72 0 R >> endobj 77 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F14 /FontDescriptor 76 0 R /BaseFont/NVLBUT+CMSL12 /FirstChar 33 /LastChar 196 /Widths[272 489.6 816 489.6 816 761.6 272 380.8 380.8 489.6 761.6 272 326.4 272 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 272 272 272 761.6 462.4 462.4 761.6 734 693.4 707.2 747.8 666.2 639 768.3 734 353.2 503 761.2 611.8 897.2 734 761.6 666.2 761.6 720.6 544 707.2 734 734 1006 734 734 598.4 272 489.6 272 489.6 272 272 489.6 544 435.2 544 435.2 299.2 489.6 544 272 299.2 516.8 272 816 544 489.6 544 516.8 380.8 386.2 380.8 544 516.8 707.2 516.8 516.8 435.2 489.6 979.2 489.6 489.6 489.6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 611.8 816 761.6 679.6 652.8 734 707.2 761.6 707.2 761.6 0 0 707.2 571.2 544 544 816 816 272 299.2 489.6 489.6 489.6 489.6 489.6 792.7 435.2 489.6 707.2 761.6 489.6 883.8 992.6 761.6 272 489.6] >> endobj 78 0 obj << /Filter[/FlateDecode] /Length 741 >> stream xWKs0+tgj,# eip`88vǏ4{v%74 coWO$bQDĉuz4LB9BHP͔%3=n@  8KIFや:fB8˶gy#HB#Ph U:RhQC4 EwU>NR@Ά/5.=Ő-'i"f#IS$CiVyy66[.]A*~.9UݢQxCY)W4$rl{F,\@سj(|̿ÿz6Rp~rǿ2o4ʟ 7?t} BE@UN\o?3 endstream endobj 79 0 obj << /F13 66 0 R /F14 77 0 R /F7 38 0 R /F4 29 0 R /F2 14 0 R >> endobj 74 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 79 0 R >> endobj 82 0 obj << /Filter[/FlateDecode] /Length 1295 >> stream xZKsFWp*#gnڭH&h,QCk0PIZ.FH2n,oy2w[߳khoC Sǎ{?D:n?‘WyVʬ,?Z7 }ed}jǭB!=O3n̑:- lTp02Jeg"bq~p>7Hz3 FE` |(Yڏӱ3~p_K:.d-KeݥqXt csqZoͳH7?:ѯu,!Mjw:h#7,異q@Fd0؆$īaAci c>;:'(P?eNHt ',h8Qcxͫ21c^p`$U6t< xvԉT*,ɅJ`#O(YĦ1fd#ʙ/RRju|( -%1|/@"}r02mQZǵHmj(˽f12x4y X@``ŎlϑܴF(Kے@%0v2'h?{vv̄:AG`HHJ4ehє&CY u'N;ЊMAQ+vMSАl(%H[<1f uNB[7>BľKLf`WJ!~ +k/;\iC/,5\R8"l|]& ᡳM}Z(RqE !W:,5}nc,E;Hh84{Jfϓ)\e{=tvw٦*UZ#a@iєe21u.ED,{3&[~1zd!:?īTyƛz4ȓ ]WyRy Q&sgX26MXׅ6ߌeW<e[suh0;֏FcYen;8{q9Ec$68zmgֶ!7EC;Zo A7O$} endstream endobj 83 0 obj << /F10 53 0 R /F7 38 0 R /F14 77 0 R >> endobj 81 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 83 0 R >> endobj 86 0 obj << /Filter[/FlateDecode] /Length 1362 >> stream xZrF+X6  &˞SH,,0nYTVx|}HX+q {ס}чw۷w"/b|0ߋ3̃ș_ n@|qw)b!E]F!}s5硛@Ϗ!xu3 ?Q5J=vgIŵ(0zi~\wxD{G B ^D5$Wk^&i%dkv4t].Esm ާ. +c|H>f",-xjSEt}xH-c)Z /W!xZSJ8 xӇfL&]9vc<.IHKDeU{#G N iee\ǝMR^Dy&+jvFQl38 u.3gN%bз8zuDNgLТ\L&M&Uԓg 񑭻08KODXвZ%E^]P!./=,ez85\̘z7!L6**_2CYN6ǾӡoTW2^-DtB9gE4$L\aa?PR5أv=w? M|R"AmGY[}_K+ӺbU,@BG65ރ|˭NlMD#jBg钡E"LZF=Bl`FP )m}d@@3ѹP G㋴j)}I,Av*oT5wk"6<~C",U\zMU kU 8fks/<6: sr{e>jӋB9AEF"gKv[Zou}m"fBxr0 ?iBVp2L3^+ gloKX8# endstream endobj 87 0 obj << /F10 53 0 R /F7 38 0 R /F14 77 0 R /F8 41 0 R >> endobj 85 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 87 0 R >> endobj 90 0 obj << /Filter[/FlateDecode] /Length 1247 >> stream xZr6+xdApo~Sfk692d3+ >EN%ݲD\e $N>p/WKy`x4(1 f*H?hfRM3,@gMmTm e0 IT,fd}]79sezU[w JQkY')(M %SP( "Y=Iɨ~NmȪ#ۣ=j?VАֳZ1$I 4CgnY5 9d K "DLW$_ Ԩ;LE+ʨY7?LB^HiDLJ q endstream endobj 91 0 obj << /F10 53 0 R /F7 38 0 R /F14 77 0 R /F2 14 0 R >> endobj 89 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 91 0 R >> endobj 94 0 obj << /Filter[/FlateDecode] /Length 1333 >> stream xZrF+XB?GvrʞqYUE lYB`qj I {sϽQzp}D9f D!G^H7;ݟM(</㲨dQ&d!9;b~s ˆ{1|ϼ}.}|,rce&N eQ'%'AE>` } yl TTiYp[}~y1ԯthN׫JDؐwVYYHLʤ8m/ߏ YRi, 1n%5X N@T>C[UYϛK F( AX7)ZK3V(#P?qw_gBŭ-Ht2lNîTyD*&!CW&[гueIwp8%"0ʥAͫ?:$ endstream endobj 95 0 obj << /F10 53 0 R /F7 38 0 R /F14 77 0 R /F8 41 0 R >> endobj 93 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 95 0 R >> endobj 98 0 obj << /Filter[/FlateDecode] /Length 1325 >> stream xZMO816; G{C&߲twЧW!oǙwr~0vH̑$Vޑ-][1Kg: we-$ oDZaAbeQ(o@\nPIPgOcƊqHpKخE/Otqw„u%#,:|UiPKD7K:g&VIjܝl5}vۦwa״]+Fh2_>"0&`jǼT*,&\նL 1B֦cg@2>e'J6ٔm m&p 瓒OL#;֎fson9! t,[: D/ڔvPgxp" 8;H(wɠ{0gT%2H$/BX .XZ-";5 3&QH ^wlMl))F)~6O^/A AF&)ڭnw<ŝ߀lVݻ쪲)<}~R8ʠGLE>N<}vPyp)JuɕRnC];3D-KLђT/g t{k*i9/2&t8j< ~ ]ϾKmvڋY?SQr7MS.v.~ӌ_cg$McGc(ꂖDQ^'FWLkDn䏶{t t]O$oVÇ/v.ŷ_ endstream endobj 99 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F14 77 0 R >> endobj 97 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 99 0 R >> endobj 102 0 obj << /Filter[/FlateDecode] /Length 1478 >> stream xZ[s6~0ӥys7vN<ĞCJ{,$\,w.NstWo7^)qn)raܼ˽ۜ{ [.RR5"(AON8)w\wONvLb}y0EnOC3Y]^V^${8֯d(DAGX[2pQSY1EH[?J/"Ⲫs݊\(} PkKkQlܥqղ* Ж"cJTڭ+}ӻܞY_rw.{3%>˹9$_^_$I`E#سdYdgHT6G{՚Kke_QkQ~fS (E? D2q0(V-VH 91A<03AoS(; ^@%+d_Q}b y;Ygш5;l&?mk(pRI"ze Z|%x1ңJNCƓۉ#Lll3JtBÌX2K_ApUMVi{E$"}3J?;R|Ȭv,R:s;rtmRC1'UQ2MkU^1lV}q9~y`} [vo_6g=mVi Q J9?D& ɥ=~p8}%-^\OdyմOg BG7e5<ߒ1{y+ B! eb#(vŵj*z7 :xʿsۆ~$D6ZUE yWm ݗOZV@Ӽ807' >-63]wet?c,A.32>v83 Fg+UTT^̄n;υG#thKx4~%m?(1O:tdYQ|NS1&b&Ft0ۢ`[$azq<$emղԬjY]vE'(J]{B51r_Iyo3;bU Ts܈R/ fg:&:ѴYVEԦ F<$(9>)یg|$9BMЮLDᩨ\cuR &?ړ*)9/ɔk67 ] _bn vKLAhOI$'_اdSYQ1ؙׅ7⼕ը|+PGΣ:mna j> endobj 101 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 103 0 R >> endobj 106 0 obj << /Filter[/FlateDecode] /Length 1353 >> stream xZIsFWpDU7<-D PlϯYHLF5P{z;K4fs/BaJF!&g}&1})'_:Yu fc?!J( Koo2}yU깧08FE&JjBb\d1? BGa1iJ4 0S8o*7xôՃ(JçU (uE]Ǭ4Idȫ2SLNGcڿC3>ﮈg?2Huy18ӮYgݺrc30ffaq1' F,|wEP"C RFf!nj5:؀{4[z*ďUz$)u4ҔW-V疫R.@m|⁓rK0SpEBW.eq*WU9㒦T <[X۷9ͲzmVw,t0MmrQ*Tk>E,9JurWBե6C1yTqv`v;@ 넲/d`TaH?OloZRnMV:yꟊFb_ŖҦ/F*'H`Y|*6)LHaZo:ӝz$qzh~p *7R4U_Lw烾\:Ma.*8y-olnZ6-RXcwYZ&e(e?=V˙ }1sSFcɐ\.'Uۉ* 2B"Ϣn7 ٶz値ш±2QҪW1,7y/ћ ̑j_W (2dgxdAhm8A=fJ eߏChPsHF{bdqҗs*[۹{zOy؟?e 0!KzZ- 8"n92-3N vzJtxqnb-7^ endstream endobj 107 0 obj << /F10 53 0 R /F7 38 0 R /F2 14 0 R /F14 77 0 R /F8 41 0 R >> endobj 105 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 107 0 R >> endobj 112 0 obj << /Type/Font /Subtype/Type1 /Name/F15 /FontDescriptor 111 0 R /BaseFont/BSWAQJ+CMMI10 /FirstChar 33 /LastChar 196 /Widths[622.5 466.3 591.4 828.1 517 362.8 654.2 1000 1000 1000 1000 277.8 277.8 500 500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 777.8 500 777.8 500 530.9 750 758.5 714.7 827.9 738.2 643.1 786.2 831.3 439.6 554.5 849.3 680.6 970.1 803.5 762.8 642 790.6 759.3 613.2 584.4 682.8 583.3 944.4 828.5 580.6 682.6 388.9 388.9 388.9 1000 1000 416.7 528.6 429.2 432.8 520.5 465.6 489.6 477 576.2 344.5 411.8 520.6 298.4 878 600.2 484.7 503.1 446.4 451.2 468.7 361.1 572.5 484.7 715.9 571.5 490.3 465 322.5 384 636.5 500 277.8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 615.3 833.3 762.8 694.4 742.4 831.3 779.9 583.3 666.7 612.2 0 0 772.4 639.7 565.6 517.7 444.4 405.9 437.5 496.5 469.4 353.9 576.2 583.3 602.5 494 437.5 570 517 571.4 437.2 540.3 595.8 625.7 651.4 277.8] >> endobj 113 0 obj << /Filter[/FlateDecode] /Length 1337 >> stream xZn6+Z#).R$H{vA+tVW'N|)lN&J=+ JHEi PGdV7Kiz ZË:6_Mpk~;TUwjheֈEf+U(saEjo#颀M U;*FGIm؋CW,g.D`xeUfZ]2{j(\% |r!zw~¶lu'"ہ(= 9xRJV]'GFܶFpɋQe*2DgUӑXGJJ P@G*THi1g QUHMjAd*jsGXeԯ-ڮx kwРG5iXڊV[_Pp:3[m+俈^ 8`o9+x=fҖ-YSVJF?ښ` vKi^mZyvF܂1=kJ*TA]n לU7e< @Ro:W(F$H"Rb[J2_JԺ^(rQ:#q/7vzʳhX!j{)VCLD-wМi&~2u !%Pˑ_Z$߆C I'Ɣc*q4Uk_nOyta!`v-rDGq#MRg8d:GMtYpa}pFǭcDc)O,?05܀#i0.`:n8c sM"]FZוxl)>F-.q[ʶĶ N}׹6ie,+Hg7%OH@x}e?\^.Mz.dD5H8oe`6BYڝFMy"pYŻe<A]PT}$^ވ22~eG3D1+dj (k%:Xkq֟^k=TLP׬_yt endstream endobj 114 0 obj << /F10 53 0 R /F7 38 0 R /F14 77 0 R /F15 112 0 R >> endobj 109 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 114 0 R >> endobj 117 0 obj << /Filter[/FlateDecode] /Length 1307 >> stream xZr6+8]Q3M?tbEE/ʔDM2'h0 828{yM.n@H h  ?Oh 9$LF鬐1",*kY_u'C;Q,e0l`jfu)1̺zRf^faX5j4hLxPHH ˴J>ٗN'ĠrT:J28l`a#܅G 28~fƬJ#1L<.[wٺViV $pCsm]i >DPC'$&-zNrj Guh,·2w*]-!c_Y4:7lfB]5br*Nxgi.;TKY]nWmnm C~ښ,>,Cs-Xm so4Tik-o#ڗP9vToem>4Sʲ( z~|enA9uӱ9"-Ki9MkvO,7Y}v~,X!>r_Pz"ԛO" HG"C_B(bܿ5ic8N1yw:]Gj-LW/23-\WnD$uqm>1ap8pzii6FFA^V:hE`ߊ gL WaR: ^/j&BivDWs9ǽtʋ~[,jշ_G㞒Q2D"ѶC6j^3z׻cSfƬ*:|`>MU?YKb1iڏ>L&+OHn:_d endstream endobj 118 0 obj << /F10 53 0 R /F7 38 0 R /F2 14 0 R /F14 77 0 R /F8 41 0 R >> endobj 116 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 118 0 R >> endobj 121 0 obj << /Filter[/FlateDecode] /Length 1417 >> stream xZv6+bε8O Z%tHC}q-K!&q,0/߽q#Ne@㈣`h`0>; ӋL аԯIx\(z0 qx{-oX팃v' y0l?g9YݘcJzwjfAl,.1h0$q2/_{[BcЂ1 a*Y)!7(k^6,f6}Ltf}a֣=υI0,D0#}Cb I_36#!QJ]|}$#-GFwu#rE y%rzuR#u7 p*eVb.ډzaZ2dcY9sMAlVCfO4ǧ Ic8hhŠ*en3 va'k_yZY2Ay3Ve&Ȟ؊&dQC@QrbNצK[s)Q7<7%$`)zKu0`[?XU!7Nf;+:%#6{(d#|͠V&znMyw7Eh:Dm.H\my#J5+GzZ>̓zRE,BeO%\]O⡧qu(Tr,5;k7!YZT׺%(*?*W܏=}t> endobj 120 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 122 0 R >> endobj 125 0 obj << /Filter[/FlateDecode] /Length 1261 >> stream xZM6Wp 6ݭԎrHrQJxv#†1I&?愍^~0^{~'yӅ '^@G(8BS?aH̴'\c.ˢESM?ySœ{G Шv,Wّ/% jY=Qde$viJ;+Tg?1G?4!ŸEP/xl,4)J]soPZ>He[j F'5X<@pEl:Pg+K"Ճ+އj+wRTH粲IuҨ@BSkL? |ՑBd6p|Aզ{n5|]Q*3DE]V P#fvlv/S(ME_kY7ƸzS'}A{ Ĭ6*:{ZmLAg:XL=L'F P(iAf#m$I]QnvWͣČ2#w5 \~YpŮev!2\k;;RC6̤ BQpBIABމb6 b>m`g YwjVJEm.im鉙*c9eu`.~px̙q +; YF]h5[֐S?'sIs(}qA ύ,jHPI|t:r LiسəۅvͰ+tddX=Sa_!;'cH@iS ^Uy\MfkBCa:).w0 n n,)xDTp1X#}/fmpiqu6כҫ1#єt4>^|KyNq C]RK- [;sg-LL0?<pLy8Zj#AYu\=ݮ=l|V+S]A 0$|G:nHqD&嫬S@>Vv_J~|vm%;n֕icxdX ~5>+wWdz !i={a?G$ endstream endobj 126 0 obj << /F10 53 0 R /F7 38 0 R /F14 77 0 R >> endobj 124 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 126 0 R >> endobj 129 0 obj << /Filter[/FlateDecode] /Length 622 >> stream xWKo@+>xz5 P"aqAM m9i{fv` if78'+{2_x-ɯ+*˄$ۯ4Oż.TKkͶl$]-H.s~҂qOD+ʹk9D(5膁$RӋͲZPxxlÀyMfJ#'t,XE߀\~QW rQ=AHaGHwaqGG62$5`ᜁūn[M$:;s#v'aD' "TQŹ17d$|-04Vw(|Le0b]+ŸgI0+m*c\ l<1;s |V̱*ҲK)TJAQG-v]6XXEGT#"hܵ!wx Ιjh{%K}UVPtܟt1vPL8-0cY菳j3=O_%dWIW!lEo>I/5HGl'j^W/0C܃rYeGhEa H :кw]>IsyO endstream endobj 130 0 obj << /F10 53 0 R /F2 14 0 R /F14 77 0 R /F7 38 0 R /F8 41 0 R >> endobj 128 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 130 0 R >> endobj 133 0 obj << /Filter[/FlateDecode] /Length 20 >> stream xS030PHWS\  endstream endobj 132 0 obj << /ProcSet[/PDF/Text/ImageC] >> endobj 138 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F16 /FontDescriptor 137 0 R /BaseFont/JOLAEB+CMBX10 /FirstChar 33 /LastChar 196 /Widths[350 602.8 958.3 575 958.3 894.4 319.4 447.2 447.2 575 894.4 319.4 383.3 319.4 575 575 575 575 575 575 575 575 575 575 575 319.4 319.4 350 894.4 543.1 543.1 894.4 869.4 818.1 830.6 881.9 755.6 723.6 904.2 900 436.1 594.4 901.4 691.7 1091.7 900 863.9 786.1 863.9 862.5 638.9 800 884.7 869.4 1188.9 869.4 869.4 702.8 319.4 602.8 319.4 575 319.4 319.4 559 638.9 511.1 638.9 527.1 351.4 575 638.9 319.4 351.4 606.9 319.4 958.3 638.9 575 638.9 606.9 473.6 453.6 447.2 638.9 606.9 830.6 606.9 606.9 511.1 575 1150 575 575 575 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 691.7 958.3 894.4 805.6 766.7 900 830.6 894.4 830.6 894.4 0 0 830.6 670.8 638.9 638.9 958.3 958.3 319.4 351.4 575 575 575 575 575 869.4 511.1 597.2 830.6 894.4 575 1041.7 1169.4 894.4 319.4 575] >> endobj 139 0 obj << /Filter[/FlateDecode] /Length 1773 >> stream xW;6+4b\wy]6WmI@ϼdiׇ AT$gpWx]W4z$[y\իaVU%Mq[#`Y770&;.ޕq&eիM^iI?>7e}ҰM^Uxu^gqxKH0T{dB:GyқiVQe)'et!ꕩWI ڰ2$wiD󳜔˲"Zdyni^Yخ2: g:+񁞇䢋 '٨/L,0QRf>(K-\1PEG a`1Ux*kL~و(}0a*6?][&BOZ!1ۂVN9!T0(kRmށҰJ3Σ >~IY*HF+.hTxx}!lM`8B47nVwN.;0EIP_} !2r2dLlyX]L'(̸p_B wӓoMvYA=>iM hhվuH? &>6beQ&́b;xUЙYhZIn0aT%U)iFÊ#[ʪdA-7HrT AιL/K:AysUIūjq!>H&KM$?>+cɕIy_G9I9RQK Y/KӚ endstream endobj 140 0 obj << /F13 66 0 R /F16 138 0 R /F7 38 0 R /F10 53 0 R /F4 29 0 R >> endobj 135 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 140 0 R >> endobj 145 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F17 /FontDescriptor 144 0 R /BaseFont/CSDHUI+CMR8 /FirstChar 33 /LastChar 196 /Widths[295.1 531.3 885.4 531.3 885.4 826.4 295.1 413.2 413.2 531.3 826.4 295.1 354.2 295.1 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 295.1 295.1 295.1 826.4 501.7 501.7 826.4 795.8 752.1 767.4 811.1 722.6 693.1 833.5 795.8 382.6 545.5 825.4 663.6 972.9 795.8 826.4 722.6 826.4 781.6 590.3 767.4 795.8 795.8 1091 795.8 795.8 649.3 295.1 531.3 295.1 531.3 295.1 295.1 531.3 590.3 472.2 590.3 472.2 324.7 531.3 590.3 295.1 324.7 560.8 295.1 885.4 590.3 531.3 590.3 560.8 414.1 419.1 413.2 590.3 560.8 767.4 560.8 560.8 472.2 531.3 1062.5 531.3 531.3 531.3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 663.6 885.4 826.4 736.8 708.3 795.8 767.4 826.4 767.4 826.4 0 0 767.4 619.8 590.3 590.3 885.4 885.4 295.1 324.7 531.3 531.3 531.3 531.3 531.3 795.8 472.2 531.3 767.4 826.4 531.3 958.7 1076.8 826.4 295.1 531.3] >> endobj 146 0 obj << /Filter[/FlateDecode] /Length 2332 >> stream x}ɒ`jp_|s8թةrbPrf_b99H/$MKD_?=,רNEUÿ_9iݡJc{ K^fIEcTeRa=D Ok̞']dj&6SopS.|dj175WT?kglzYɋMl㫃.h,Dyb6Yt,`:g8[;U?x Gu5w&yI\g|.46$(/;!o8/0AXOf3_yƲIoaF$I&r9>v5һa{X dž1"৿3Ufx;y q::a?q\*.zN BEy;y:~&3Yf ˷81.D0pwA=_T:H8$iF{ghQ⫒4hђ> AY@I!@^x8qv\ysBcN !O7ew)jVAw0Oj|x[ ̦ pY:(Y% ~Vb;=Ot]p?4X98 F 8dr Z(9~˫aAM @><'!:k/T#qe6B'n4v).^8(4|0Fa}9#`Ǚ\ g&E#f!gd:(Ar|YtJ'^.I k@YtIPȑ&91z-O&՚"yww9qu}R^ȳ{T!3M p@'K%#<"VNv&H7%vZ IpY{SrTxd&7t3ϑe‡аqK/n;/햜b,R_I%GŊ\,jdOE7uZUgӪ 3~a= f]T#WM/MrS5:Of0,m̑"TT6mV\"sAb)HsΊ8ԡ`;"r]f)9 @I)L`6nY,k*Pi5(4fn0NvSPaܖ Z {:SqV+}6W"2oSᴢ CL 闼zCП^_:G:"G"{W$q Fy.` Q4L0s{Oop{^Q>{[Co2g>@ !ϱݿY_0Y7D 4*OLJY4i;)IZN{sTÖ%?##-;q 3ƒZ߿!YؽOU%iPI7kF)]; i)M Κ_ Ɉ`.Cqi6.rA n6/ _kCRdǣRZoW§ hrHDRm endstream endobj 147 0 obj << /F10 53 0 R /F7 38 0 R /F12 59 0 R /F13 66 0 R /F16 138 0 R /F17 145 0 R >> endobj 142 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 147 0 R >> endobj 150 0 obj << /Filter[/FlateDecode] /Length 2009 >> stream xڝXɒ6+tF4wR%RNUCt䀡0b%_@Qq\4F$qlN4}9o$]I9wܶLs;$+6Oa~m,/:~}\U"vU&08h"Z9=:u&%x{M~r4/Ze4Lqݡp~$p4=vQ';$]{6Y{*<M⹲))_<>/"ӑ4H| ubzP5U_G ̏r yD!͹/ʚ Gj˫ ~f<[%)xcLhԃoDOF_q WU) 9%4\'4NhЎ _@;_N%(qG]}>hTO G_tZ4&nױ!LO\̝L-OQ"¡` E x(Q1dxY^R o^{|]EN~R7ȿcşCTU kB!'qdPi'X endstream endobj 151 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R >> endobj 149 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 151 0 R >> endobj 156 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F18 /FontDescriptor 155 0 R /BaseFont/TZLDMS+CMSL10 /FirstChar 33 /LastChar 196 /Widths[277.8 500 833.3 500 833.3 777.8 277.8 388.9 388.9 500 777.8 277.8 333.3 277.8 500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 277.8 777.8 472.2 472.2 777.8 750 708.3 722.2 763.9 680.6 652.8 784.7 750 361.1 513.9 777.8 625 916.7 750 777.8 680.6 777.8 736.1 555.6 722.2 750 750 1027.8 750 750 611.1 277.8 500 277.8 500 277.8 277.8 500 555.6 444.4 555.6 444.4 305.6 500 555.6 277.8 305.6 527.8 277.8 833.3 555.6 500 555.6 527.8 391.7 394.4 388.9 555.6 527.8 722.2 527.8 527.8 444.4 500 1000 500 500 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 625 833.3 777.8 694.4 666.7 750 722.2 777.8 722.2 777.8 0 0 722.2 583.3 555.6 555.6 833.3 833.3 277.8 305.6 500 500 500 500 500 808.6 444.4 500 722.2 777.8 500 902.8 1013.9 777.8 277.8 500] >> endobj 157 0 obj << /Filter[/FlateDecode] /Length 2361 >> stream xڍYK Wx,*]Yf=:#݄;sHUc5\Ԛ hO,Lb\9WZ]nZTz\a<0[KH'ugrq-Mľsmv!&Aܦdڻ2zmOv @4u%?."|ee8`. VvoPU0mIni\eug DcSJB3dz,ֈ/ΒO-.F^XYWC氝mݯdP. e_ fGw[DKX0- < NZe](hл$|EQȱXgE]xh uD&90\ܶ+ ,:KLC`\. kcRʝ+7e=g'ύ@VXysFQֵ>[мXZ_:~Qe,{薋rvei2D7-YaY ƥ/G; Iu(Nݘ${ͱ0etgq&Ipc)FjS1$TXYIQ9IJaND6= $er H怘Ta{4Mk$<$jƒ*&yCI%G>7w|!WkW73ZiJ$MIa3- VƊ6a2-(8eWB­$1}qzm'u=X1)`EuXѴKSlMf/K=,x$ݩA٣n5//cMcD}}e^-w;߅!C)?i9#1UBǶQIW2["ÓSgr չL;)1*)VPpvPZ DIz̖qH 1{ucm9dwLjV)#vvdƪ%v:ݠpO}\(/@w6] {U>h;*|VB׶b߫d4_ޛIM/-b ?'P*M$CNnvNT: ^S%% л ʇ6@Věɢwdf)%p؃H qU߭KEVEGLw̪ oe Ў7 h]`"s]ej5l0X픙VˬY^+nxOäk\QJe5( Ҁ Jԩl|}_j$C'k9Ga_4v{<±xL=l_ȍ]rD|ץb*Ir5Zaճ@֤1B@M82 [-նVd1٦цmZ1 L1ѕWX౫T(n$䉌m΃%WW"-foK%'/Y>zY:1>&V+(g7Z\SY"Ǔ? @>y=zWukyɴRPHxJc$JW 4CtM?\k}:awbY2=WXGW)į? 3% endstream endobj 158 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F16 138 0 R /F18 156 0 R >> endobj 153 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 158 0 R >> endobj 161 0 obj << /Filter[/FlateDecode] /Length 2488 >> stream xڝYK60$mE5LvY`  KliY2Dg~}ًIVbK~l~|?`SEyz٤_ě]AyޗvIuS_tvYzo=k'6iA9mT|fܖ^&/;jeޱl=mw*Nå˖X<#Q$}wio(s3Xݼ`i+hڪk;Sov(-O 60 Ȧ|tOˆcP7c!!ZWG7wvrDɕrw56@gkqۋ;:R}_nQRj 3? Yu\a!\?^ƴL:wh]Ev}YcD(}DEDvޙ F^Kȩd%\}|ߗb [ztxxHg;$sзa9 zdS;S6 %Wb?T"ӽaY.@2~;Tl'IxiVo̅S#EIRZoN.x_.}4϶_)Zg>{t6sk>FAev]*4% PЗO?wفH,T'.w{+~,.yӹ!'rw@tP8 ؗTw>g!FtӢhQvO,##qYZFJn.~nh?c@D*ii`ѱ0f9aD'S޿"N3ŝZfZ[T#ΒTt_LD!3Kq|7`l(eϗ^}#z+{(u+Qk#XBQ8K;Lo1`9^rҊi<+< ײY=e%D8ܙk)T| SْOs)d/kK"̫ˡ$XeGI0R+Aj>$\7@[-eF΍+=D[@ݼbVҫ.{!PJ1߶qHugSITΑqi:~]B.4!FaJ16;ZF\չ`Cov\!FZ&{-GO[g+Eݳ3  NkENXVJ|'盏$_*J Řh2UYͮs֑y"7Wn݉޹”vH+^rI)8}ThpHys:v.EM9K%h;b;>FU{f87X M1΅#/ +0r3*:JZn=et\zP0H&J82*INQJq1*dM*cTKC aA^5WܭJiF%2럗3Ce] Xky^Rϕ= kRd!ڣX̻Kn2f=˾7+X %QnffuII6[9$S"  -NH)tfZ*עh7w (b%.&|XZ]hvq7!vURs9QqJ (;`iKRU| EcXIW/sdij%.E261vN=/ c1z]f~/؁uMTbA89/hK1k]P2ƺE%F9\‡ @1a&a񍣼ccɞ=15*#|;锼!| D Fq{%'wc%YR_!#;\@Q{Эu}̄ؾeCIr5ɡt?OVv6 Lc6+HNriZ|H x<;֍Ԫ|]sϾMc~4#05b\a\9 \v(ahFJ޴Ll![TTS$ٜ8f7x<_$rH/1ںrFgyvc  !>ЋS~_Lٵԥ dַwPvjZ$G"rPŵג_/9-(U\-Ju|.">'=S8Dލu 3ђG̏|EE;D܎"'t٘9O]@k90J PiFTj3pQ!_ZM)Zuf"1*F2 ݯ} q.3AN6 yK}3yF;,H8|ț<ߊt; endstream endobj 162 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F16 138 0 R /F8 41 0 R >> endobj 160 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 162 0 R >> endobj 165 0 obj << /Filter[/FlateDecode] /Length 1270 >> stream xڍVK4+\\?X ȍJ"Ə ٙoZvd$-/ {vaG^VyyVeeǿ|R~FK?j?g1kzTCq{yFy =:z!.򜽨v˄==nyGrꠓC#qE%o,Z, d_%5X87 /ݭla _@!`*f"pJnaH+2eIr/>dV9`)pArĝ%VloҌ7Ҭj 8%d;;Lkɜ:Xmo&2H0lbR iCw&%ѓf zp{B_I/3o~VIĝZG ?+it$adѪ%g" '26@FxD'1ӣF߉s%u\/HS==js6F ihOc]6μW@"\$> `HG ûI 96n?KF,b> K`!IGAڡ=+ؙf)wg>nvCR8#5tCՊMMapBWYx}>ׯ~H$O3²We,xl*`e/hzs)Ӆ [Kŕ Ί?d4] 5[qMcz+Jܜ 5M1Y>pZ;eٹ"uxyVc\#4189ⱰuBV4;(`/mPତi){}L~~K{nj*G=[)+x<,{:~ѿ 14 endstream endobj 166 0 obj << /F10 53 0 R /F7 38 0 R /F4 29 0 R /F16 138 0 R /F8 41 0 R >> endobj 164 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 166 0 R >> endobj 171 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F19 /FontDescriptor 170 0 R /BaseFont/OZPACJ+CMR17 /FirstChar 33 /LastChar 196 /Widths[249.6 458.6 772.1 458.6 772.1 719.8 249.6 354.1 354.1 458.6 719.8 249.6 301.9 249.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 249.6 249.6 249.6 719.8 432.5 432.5 719.8 693.3 654.3 667.6 706.6 628.2 602.1 726.3 693.3 327.6 471.5 719.4 576 850 693.3 719.8 628.2 719.8 680.5 510.9 667.6 693.3 693.3 954.5 693.3 693.3 563.1 249.6 458.6 249.6 458.6 249.6 249.6 458.6 510.9 406.4 510.9 406.4 275.8 458.6 510.9 249.6 275.8 484.7 249.6 772.1 510.9 458.6 510.9 484.7 354.1 359.4 354.1 510.9 484.7 667.6 484.7 484.7 406.4 458.6 917.2 458.6 458.6 458.6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 576 772.1 719.8 641.1 615.3 693.3 667.6 719.8 667.6 719.8 0 0 667.6 525.4 499.3 499.3 748.9 748.9 249.6 275.8 458.6 458.6 458.6 458.6 458.6 693.3 406.4 458.6 667.6 719.8 458.6 837.2 941.7 719.8 249.6 458.6] >> endobj 172 0 obj << /Filter[/FlateDecode] /Length 1635 >> stream xڥW͎6)DlF)Q"&i u{%:VW R^g߾3RiAOC7(aI}廴zd2Vi +N&eV)LJ ^/x+^fcnp$gGbeN|,VQ=aEDʨbUAe89Kz"qm:}D8{'U- |xu~d="(uC?VoEV oXYTJ",ϙgEvnմH$Ɲ2d,k:UOiJP 񛷫?OQ7@\ _;Z 7x#ZCqd]1m:Ox{ [B]e` *JȞ`W2\[-NKoq}EtexzZ/6>gI{ Rλ6^cz ja }i1;\}U鬲??1 v?r`o)컦@(1Z_W+_{sMy:PSEl5HAR egl;;ijߊܤV˽Sgcz]ZQ5yX\d2 "K+N20I(i'3ɢAyAGKa1Zyv^6'0$$j.˘ň>󗲖azw.ü1Hqt 8_UU[q2땘g/۰0 >F0a&AfI'TztAp GUCo+ 20 7j_{$G3%Oihq%1=\ev05v?-.FA(c7?Iߌ[~]KD\!R, d rb{ U0".@3xիkǰӉiwc[sAZ3v*sMV$XW9n K yd;9>a̾{(1L7SçOw (+9f*Lǩ5:=ûY4Ttz*۴KCoL;vPUai9YAV,H 7H.1mƠ N;mGxr8TV `sTp> O5aw= [Ҳ9=p_+9?ycCEJ%rghsp Y`R endstream endobj 173 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F12 59 0 R /F4 29 0 R /F8 41 0 R /F6 35 0 R /F16 138 0 R /F15 112 0 R /F17 145 0 R >> endobj 168 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 173 0 R >> endobj 176 0 obj << /Filter[/FlateDecode] /Length 2454 >> stream xڥYYs~ϯPe* .$;G֩֎\[QhHv~}Iګ"\ b5F/erRbPR1%¸Y|^|,2ŋ@ΙwrœT]~U\I)#NrI]Is"*l^Dw\8wU+VD67mqOz1[styYOD=)SīF5"Hi]F%JZyv=m=rK۝?kAn,ʘyΒ1nNR u}~zU4W:8a2t DHPUKq#?&>vȼ궯s_^EuY hȒG֐N.&OtRMAEo\TfW nEԵ4+B'mIu4y>ںk -?v=F5*kqڪi*cBx?Sco/9~y:NDu-\9 9˴۟iܟ(KKiǐБwl%W4ոPK4DfK$*uLV#f- \sk܃ӧ Y1pAW[c&E_`j״1ͧۥ`:Bc}:*:J6]_yZjl74_Q~_ث8uA"$oW'1TAɌZd3NkoW?u-vԂ1qh-7~\JЦ͛,f{c_ɦ+CyQt}cqQ`Dh]U|%>;\NTzJGrނyW9 -<kw4O]H#YC^P_8uS/jBe`slB3ݣf>*7uz![Lʳ ҤC`TvRib" _3_+].EQ1^ЍbbB<~Q@C !`9^ŌJZz`fj PWt0<.h]u66!}Qk0){,l01yk3\-;\kN_9sh\+u8YB.lX-M\)[;_.]OU6_֭ڣj;R]|.1+2psY U5((_ ~7p7J`^k$9&h'%Vb+>R;9 %R~?d2Y="#lg&v5:zw7bX HB JBz!A(y}kg7DXғ}K| oC1vOSar#r'8pY'q4 v<U=,`׉Q"+㇤jfUqHٗYBMMaBD;sUz ^+CNזCsVIj's?PhtO>`t$3cx~a !1̀b&BդE:v8zm傠۱*q0N$Dބ3i=GoJvmԏ!oxŁvE'0Z$?R:YO\}ŞYi_ qK "+;D|S\ -gI5 \_cV UMgLa aGr-<M32hIKdBoۋXE# /;x0:X.|vxu!bסWk} -7Ƹ]UTV~ӷCKx'rN#9'~\!^w}yw 1ѷ Ӄ#US>^\!- s endstream endobj 177 0 obj << /F7 38 0 R /F13 66 0 R /F16 138 0 R /F4 29 0 R /F8 41 0 R /F6 35 0 R >> endobj 175 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 177 0 R >> endobj 182 0 obj << /Type/Font /Subtype/Type1 /Name/F20 /FontDescriptor 181 0 R /BaseFont/TDBRFU+CMTT9 /FirstChar 33 /LastChar 196 /Widths[525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 525 525 525 525 525 525 525 525 525 525 0 0 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525] >> endobj 183 0 obj << /Length 44 /Filter/FlateDecode /Name/Im2 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 184 0 R >> >> >> stream x33T0A(U`QSKK~K>W % endstream endobj 184 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 15678 /ColorSpace/DeviceRGB /Width 837 /Height 599 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 837 /Colors 3 >> >> stream x_a1A 8pdH ȲMIJrsc\"&B`lG!*sBl\ǶBH 6Dzp!pz9;gvfvgoW=3;=}˓p}g>?^_^k?JY0333_^swfVfَVE Pl|(N=vuh*k&x;+jÖfy<=diNfڞJ]~D5ֺ2fۡNC=VžmiOiiwڽ8vwn>nX?x\{=QB-Nmw{apk_\^v~`L+XٍxׂM|ofMޗݬu[0N.%}G]o|Cslץ]>\tNk/ֽȮZxºwKaKp&z@q2m7O1`%5f:~t/@/okr>~vjp?.L=ka||,=z~ƅk{뱁,sLK>W|3ɞW_}韲!ݏߛ>:}n>s-kO+AA>{>o4 />^[uC=Nqҥz.tH_xM:[r]6@+eZ8FX~}]Ko o dx;#ݪ]ٓ'Of.ҥ>wܧ>ڇ_W}}NZ֞>-7V}GǏ뮻!ݭޚ7'|2}O.d~ǟ__ܽfLl/e/WΧ S>蕾}.'N?m?W|6{3Oie;M]yneN6珝n`_A:>>sᛡ>y;sI]K} is٣w_y![o^Hy_])|sϥ+n* R>3{I<W|w%M}|03gAp;)4ay <j.wesïgvv;9-7>s\Ӡs\f犍-wm/̕_;~C=tw}ʕO~!]A|,54ʡ[ń;\+vg>W,s{=l^ԲVwuq7R+]_OmWŻs.&ޅ3]}rᚽ<7徺aŸbwy?__Q5gkd>rй9}.inS)^r\13>|8ҥ7x^zd^w>7/mjek\^ s\-oԌӴ_~gl_Sc[aWJ?sIa<ϕ^>Ns>s7qZϕ3vm-}\)dC7ͼegΥœjGWMs_R-b˦0+\or>W%WG`glN5[C-Vs-|`s\fչ9}.inQwY s'[9я~?$>֍;Usûdɼ59wy 93{ϟ+Cv̹dGaܳs쪃j+]MY羳tv6llQ\%Ms/\9}N 1+K7ِ.e={~eŋw\O?z[ I r-~4IS9=.>v?.א&SR+9ԴU?W!Z/fs\fչ9}.inC\[Vl>Kkz\ܛoyҥ[n}o:K>j8%sI'՞3p.GT\VWzkzrosy.lkucK?$t*\}>υo}is-kO}M|o:!G>O8y>[8}[7dž yQs5u,omyZNs>sS  ޳>k UPƳ0n)яvfpx\v;[V}N[|69}fЦ5?m76۟wEVK½l>sOW˴>7p\J &sF:KXkp1xs\ݳٱ\#jzaGy _{isˮ,ffff5}k>̼֛U)[/8p<3ٚ|=033o{w\{25oߙ[귳 fR;o1@ =\m})UUKRr>4^plTWǾ0^ι9ffq>^_VXrVc3]FMLﰥVmJyOi2}.|MfHh-2lTyu=T~waffY=F%Ssw4ZnUt|Vu o[BV\G(}nsњnUVr\jytuf>| Y?\t1Ϊdff&/{\SɮYl0U{MqɠiIjb}a>tvl+5#ΦUV65_.aEiz^9ff=JKV-e콦%{wطV4Mڴ/aK)lZWxx䘙thkzw4}UavYBskU89ff)9D>̼}y]^{l1ؑ#}.t5@7?>5sm}` s˟QwK/,?og W2ox0}33J>?ȝ2_<,~嗤a.PW>}.{2ctyقH0,$-}.%dff^^e;}B|N s{hr~K Ȱ>ײ2굄MY]W魚4|2}>ԋkjNӇ?uReZϥ~c䗫fS…i=ҲjיMv}|xeq sl:tZ{+>5LJ뼺X]Źs+Xϝ˓>?ҢJo>gx4MLGx|K֯bF]H.}skJis2V{ylZW]z5+dH˛SS_)gKl|Ktu܅ 狷-;jʋW^KkӾ}Lk+ͧ54֪43G.nG133}.AgRʏc}3#yYy]K.y>9}5챧sfųZ\mOY+^YtZKZi\J}lewkc{˧-t.\x*t-}}L=u9W{bsn?˧u\s9ffz>Wu$>{[VjGw_MI5]ݯYk?.v7獭Z?ϕQ9VȲ}O9ϥKOWJgF}..tiC\e+o,2\[Uws+=>-wޙZ\msOe.2ϕfYy sW:?/5Źut\X \6k)˰>$eϟ+9wSݢV$>WbY\lȬtH}VEcff^}o^e8flsUioHW;U.a^Fs\uJ /L\*}^Rw\9FcHKvpMcjwsAEZ$o>u֧υ<ܬ[UVV133C\te.;4}Vt_SKVJ\[+Gzku7,BVcjiz_dϟ;vHw,a^U2:Vgqmt.?:UYt33 = _ ~kvVYӭj ;Nkg%8>|ܔKYB$[j G}6o>>>@l7%uVt.\xjg{d wg쒴Hpg{9~:|K2*0@/3333G>h瘙5}_ffff쪃h$q}CffffG=G:%efff:{>̼瘙333z{HKǀ,933ߜ>siY> sp{6;̼B_xq9dpǀ>2efɎ;́:ܲ}.Bw@}1%[[/5\Β֎FvCC'ɡuodu4}&PW] _ 5۞ytniI-m{͖jW># ڴ(=U7?[Q$fqv)/_|7>u,noUx ~oỷ^xkt^ |/>'Iu^*),CyE&KgG͖ao?7{axP* ~WIӳC[C۩wwW_=Hw/uvyG޸~Žtੲ27D8l8xW7Zvۣ;ߓw/~vWpݼ$w,;5:.>//|݇MP9 +mK *L#4̿eѲ]<Ը~|u.T>ota\NZ6sq]J;/ݢi?#Oàůtg>s/NlNקec-9ak Ut}7NNG8]'wsq~s>ϝ>td!8}e7̧^d;~n!w׮}W惹rܱ#G;#So]{GUE}t"_~Bv>׹LΟ bk]j8dֽ,xūţD㠢,>sQK}ek~|jU>NKt'Nd7,ȗ痯Go Jvoy}q{/n~u}N+4,^0kșso՞?,- 9}N9}HANcw/_:{sRK׮]ϟKvv5ٽ2GUŝmw}gz7#s;Ks;ˤ}nd[nI*OΟ'ϬbKO1^Qg>YѧN?l~6\~>0 @tTeڟ}\^Ԋ.^r\XT~СY{s󿷖Ο s{|9}g鱿w7n+;n/Ns_yow^Nn>׷?Wҕsp}.ra/_rU.FfIaV0+OQwuxN9}>daO_.gx/mm|'^y%yrڵw8^>{s\vܩ ?̵֍q5oJMR3Yج ge}KZwsu<z_|]ﲁ]jkGl>{).r\8(`qPAkvus?!2}.)n^O_L\wո7~u-?wy~Ͻq},e`ns\UϽ+w5yxrDr"r={oxa5J0nxMzO9}Nd쯓>pw}}WL:o mz, *ϥø/򞔗dg>7_\L綿/'/3z1yxkNo,>W^nL[>UsuMj߅W-gK3\oUfٳgǝW{ e*]m˧)BOs4;gvZ}n\l>Kk]/Zyyt[C%s3s>}_^u[9~W/<#z;3=;r{/x}x\:zKlwqG6[7NA, zu,+Nt0}Gɛ\]|,^}?>s܆}?[7={kF q'orE/G+z܍x}>c}4un7C$g*o/zO<{\wj|y]x5:^=\uB}Nk;">snǝ$9+'fQ>0Hy︖۟ӽ=.AVˀ>owϙkZ/X+ m#sG!dlB^9}N+/>H6dZ\s=`0( bfQ,\ϕ^?+m}n66sÆtm >eT5 R9}8t;u,Wv7Us}>c}na=D|9V*2'133g>u;!>ub[Mv tg) +s "j?=~_9C hy_Xq2} Xz/O2;:9oHcGŠ\ƘfuOe9X_9 6}c><ύK:NF[ &z}iɆn~Gu;tfyƘf>x[ren #9p/^=Z_PAe㹱 KTސM*:vHqb}Nu)}CqsG[Mϭ~F8uq.m?7sjo(}Cs~bus=KI1d,ݤz}mki▙7eV4>:ew89H7esp}C rSG)U[Sz^m[ir6o|ܓףMݴ{-zc^ W\g2ou:Jg_)]8\~M/z.Ӻ]2-Js@Qҍf=\|#{5zU-Ϳ ] yE U[߬ϵ,g϶2ې噦k|]`SpsdK=-wki}z_BWh_ꙋo-s>S vN7:Xx@!ƕ^߷#N0y}SA5wacIw8 P7ӅϳUTS;.g폷~ݨ ߐovʼn}C.9r[O^z!Czx3wI}nڻl}{CZ6}.ys1s#R= 6}Gcf1s)p9ffx>}Gcf1s)p9ffx>}Gcf1s)p9ffx>}Gcf1s)p9ffx>}Gcf1s)p9ffx>}Gcf1s)psH3.ߟ7xt}./6}'>.ViAW̼Q_9s@8瘙7x>}'>9f ><@‰͜?̼_|?` 9 x33o9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSs9}0>1ZcoSsϜ}!^ʹ>gΎ#!QfqA733Rok>;s18Nv簱s9^>}Gcf]}ju{.s1Rsbkkk6e~_>s.skϟ+ݩSOvC~>Ǜl}#MϥNjbs[ߚ;k9psV }cϟcsp9ffYKx]`6ܱGb23Zt,}.2pDb}H,}.-334xK`s(9ff F9>x[[[ٌyy1a?0|o#s>PW<+}>">/?@H. cyz6K9u88>GDvxc13sp9@s瘙9F7xqѣ> GHmK&{GG}B >1J$Tt6}#>ȼs瘙9FW\zZG}>oe>1sí13s\=-sy=>k}c{y_ސUfb=׫7G}733h}GĈ>Ǽ}. `x"@c瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 F9f}>}Ycf>bDc瘙ys9ff^os@8 Fs}qWp>1=/;ϧG}7>wc8'>O\.Ǜl}ctϥGl~!̹DSs)9M&ҝ:d39dW\:ud;T9ffMϥNjbԞ?WGy,Nz}0-o͏5oΟKb}ᴼ5s9D9p{+33h}b$sXMvt}asX>Wz-z >w!`ϥ7Iٞmm׿:`t<9%33GGBxϝ>~_'99} zd.\x*c?d 'C̶tu})) ^cf^#g}nDžs9esYl>t2}.9`J\&'瘙ozWD!Yu>:}vns8Td>wfwM`ӨsُJn}.ĻS>_K}S0#Kl/m13o\>*tGyt>gm?sϥg:M> sYKoLÁs>wcⷧjs<s|s2Wvb;{>71|?D]Dn{D{+;}MJZ&:ލvޯVFG{N!pCثFcf^#{8<mmmel>;ݒӭ4|,27s;fffe@)c 33q9Ыs>JefɎ;́J5hs495}6(!>\UͳQt>asZvۯY=eyMy=A;{ .Dr\: 疏O7M]he>W:}.]f|= sgy,jkc|[]Ϡ-S̥cSSli̛i\>K@`_ۢsYKW\~ϕoM3UuL󚖧:M5sd<}PSQqb[oSϫϷkZy&//>\)ܬn4犣l\-{\7d&?V'^ϕYl8ӿTn>W*s˞?ڱp1>2Mcly̼ɞ$uo1s2=LsGM:Yii2f:ZOk*s=Ο;@~j]mKcffז~ 9} 82tyi33s<5-e.u0>lfV`#γٸh叢 #>!֛>"_2333soV`)?.m endstream endobj 185 0 obj << /Filter[/FlateDecode] /Length 1111 >> stream xڍVKo6WX3m &M"mZEQ KrV73Jv#ۋ9=߈<ǏpDlgar<xf\flywC^/(|Ц]&GVZk:*T<̗!\5nKSMZ-C;_ֺ]3=[:j{0_@"U-|D6_{Zԫ3KmU5O,#^#)t_H](y@DWx&oKMV8js endstream endobj 186 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F20 182 0 R >> endobj 187 0 obj << /Im2 183 0 R >> endobj 179 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 186 0 R /XObject 187 0 R >> endobj 190 0 obj << /Filter[/FlateDecode] /Length 1755 >> stream xڍXK6WEV\Q3)zhE@Kl)˲p">' < 4qT"M >߬A%Zk}ccQ )pdIv̨^GEXn=1 OYd<]9]VVJ9ؙϦ=™U2dEzzYhq`g>ʬxy7JJ$yI) `*LhУFJҟgAO޴fsnGo_#KKwˮv[!)EUAދ'{h ]E/فaz7"`Z*DB2B_o n|O&ݡ8.v;lBkNhcBp{瑩}G[ݩBwlݾmd98OfD/_}K;| IXj&n[P/ 5t6L $P18t_gT׹ipR)ȏUUae-nL!;%lQOyO;Lb0mkzÏfKœ<,SS.y[3KSn/( !04"d6v{9s)٧3.¯1Aae*TEyD vrquښwJ(;E4}o˫Dn ցD:MPl Fsa-,12$EcLoB/M뾜Yv)tplj_B_)^#U$Q ~0ĕKS^"Ҕa`}c.Qո9sb;`s|C_Ӈ gV|v9;\q@;JcU[֐ [5Ě`}+ Sz`oROvT)yx#V=PM*9+*n%iYfF*B*dKFT2vFL I<ߴ+]r-sjp]x\6s'fm%o ^y%" a\Aԋ8;fq`U6I G1?ɝFuvZIL]w;LSP|S<׃фf/Dk~m/a|`Fi.< Y8nfC^g06"t]%<oM sEVQ j7z) 3*yU?Ӌ g5yVf aEWv?}  endstream endobj 191 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F4 29 0 R >> endobj 189 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 191 0 R >> endobj 194 0 obj << /Filter[/FlateDecode] /Length 2226 >> stream xڍX[۸~0N*NJ$Jyj6d4.P`<2m+K(_ߙHY胡!g8$ы(~!'[6L[p]W;2BDacݼ:w N!\q$98M"2 c$ }kI5_1/'s8{쳾>,+l$ B|PGfd2jG;`djڻ= 4#^!fI,n8SukPOjfoZӓk_}e-jFRwhNsLeGXLg S(׸ rlʞ[ % icdtk }/gearrZ+ f.<=BDķeRObJXĤ|H-g4~-Ǟ \c]4; `0Sߩrdmz^x)+V;>gc** HUTU\aR/x8\ ~jMm%m4#i^F6a nlA9TKzT_TMq3Y!{lF4u#:Gluo^-:!9b;Е2 a޺9HM̀d;x؛M+fZ 0ᗵS[O6}RoqL+AIISMDz0 GkvcZ;O y#4L4ݏN  6q^4SKTv)  o B &j O2%U^DFߋd=89TnXr>_v]T\jT<*J]`;:(- 9J/0dO%7I2{P~}^2jgm'xԃ;c[:C;Yr }#p 7##fy9w~\01q ¼j8lG╉Yxs0w1BI YG&YA_⺏0{L~y'w^b Fq#/@M= ͝\|ϧ[k/w~k3oȂ_[i@<۷"ZE^pni$,[{@1kXF+(L~;.*NBFoC nˈ,g08wWF3Y9Ѻ]$/ wM0x~+&aob%:706U~I;|љr9> endobj 193 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 195 0 R >> endobj 198 0 obj << /Filter[/FlateDecode] /Length 152 >> stream x=;0_qƯCk (`7bL#_> endobj 197 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 199 0 R >> endobj 202 0 obj << /Filter[/FlateDecode] /Length 129 >> stream x%ͫ1PW܊^<A bdOU3bNAÈ3ve{rpQ^p)Gh#YrxLwT^tQr$N4w6bY 2qR/ڳ HX63$ endstream endobj 203 0 obj << /F2 14 0 R /F7 38 0 R >> endobj 201 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 203 0 R >> endobj 206 0 obj << /Filter[/FlateDecode] /Length 20 >> stream xS030PHWS\  endstream endobj 205 0 obj << /ProcSet[/PDF/Text/ImageC] >> endobj 209 0 obj << /Filter[/FlateDecode] /Length 2358 >> stream x}XK۸W趜WARͻY0$1CHL~}D_?.6 }>'ۼn]\ڴXo6_7~Tlʸ7F2ƓX_O:mM}%qbZ7v8i'siQߙ 6v2="N)X uɪ}X݆ή7ŸqE0Ǔ#KgmIw6O۴Htќ3v@_3| P:uJ0BԌ:/QGLJeF/u{rv<0tAiTї'S vRt[Xݮl4# Ʊ QZ&Umlw.1wX h2"ګTkM%œieE^3 O)42I2X1s %p* T9%$.-)-cx Wr!+SECG[ W#乖!P=rUe0M[P]HMEUE5h1}43DD辰nQT7`g*mˆ <5lIT~XLQF)|X_ꗚrJhw+c@ؘ0kI >@hܹ?d`ۏ޳z)ZZ#G Q3F tn:\BEK>q2r0HTde/BeqYyŕ'?BWμg;mR*=b<Z_FshdL^m,A^.HJY`غi{W`KՔہ[Nݝ}e V(2ҜPe8N"%)He'KBCԁe.9~o7qg30#V|{nq- -_-M}Yn.E ,z12EQQ 'Kd"6KTBxI7QQ. x[0ǔۊ",LUS `yu\w2İiχb(g .NoS&{O#.r}Yz,EA?z8DZ9 >~;=?R&MBu$ٴ;^٥ b:йMI H]$&wLy#?GR3[&Qp$#6T > endobj 208 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 210 0 R >> endobj 215 0 obj << /Type/Font /Subtype/Type1 /Name/F21 /FontDescriptor 214 0 R /BaseFont/DPIQRP+TeX-cmbtt10 /FirstChar 0 /LastChar 127 /Widths[525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525] >> endobj 218 0 obj << /Type/Font /Subtype/Type1 /Name/F22 /FontDescriptor 217 0 R /BaseFont/SUHVBD+CMITT10 /FirstChar 0 /LastChar 127 /Widths[525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525] >> endobj 221 0 obj << /Type/Font /Subtype/Type1 /Name/F23 /FontDescriptor 220 0 R /BaseFont/DWCQAR+TeX-cmbtt9 /FirstChar 0 /LastChar 127 /Widths[525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525] >> endobj 222 0 obj << /Filter[/FlateDecode] /Length 2141 >> stream xڵYKW*L<ڵ]N8T29p(HbB ;3>hIvʕj4&da9mLgh&Y&7;Pl.%S7?9d,7HΙ6?#~3Y!Q ` gTp(/JN9RJ 1CVa>6-p" I}*뺬O8[veV<(i(lBp 4GDb$d8Ty}f4L͵mNm~AmPkPD͓,Q0]Fd0ۼ ʚlqcح\+V}J#ϟ*vTC9 fqBApʦ~6aqh3QBÔz^_L©]@6hke}z5NniUuEЄĦ )w%ПB(8ܘ1YAP}_-WP!i_"c~gNN2=}qY͓D|n¡YPJc#9NhUҞǀ"#BG c:_MĄk{.FBl2[9zb vUԝsGAM]ǜ>!QC״e^@0,|Px`8-`RA4[Br^+,KZ1NVq׏KXIԎôfߤN"ƕpsD\.Qrt# #=@ zZpqSN:lvy)@9SATS#OW^ +V[);zK< d̊ , X8,_0Ӕ~XL|OwpX MbQ܌ś6͟{~[K@RJHŏP:r&#_٥Z|),*.̃A81!H@ۈ /ř%cY׫Zr]`kSDɤSϒɊJ:<4l'hC| 3Zw1{,  U[%f(sRd9HFmO54*\HS*.m 7GLhشD}Ȼ3MkH=Jy@c־4csMd்QBx8*`M{Z '^[H/@hx`]՜~7!F<>re>݂;>riK endstream endobj 223 0 obj << /F7 38 0 R /F13 66 0 R /F21 215 0 R /F8 41 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F10 53 0 R >> endobj 212 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 223 0 R >> endobj 226 0 obj << /Filter[/FlateDecode] /Length 1994 >> stream xڕXK6WdWH<3u]袧 G'Ʊn$'NIbo6B>,פ^u\.vmX' Qs+e4S"ޔ6 D_u]3S-X2Γ:VN;vQǵ]2HpPz,zOu67!oUwA͸,HuZEZYVy&C`=LR] NX%7Tq]r8+S'LX)ߊ^ʢVu+OMgi j0-.+E[54'W m 7NМ\XBOtvEӶ,#q `~<£bTŗ[VC{]ViB:^=kt /֣?{|'mM>fPDxU(eWV z_bwaw=.d!bt ")'9yRݗ^!%{ `Dx,x?^f[}(T8ڔnMkKu5m Y`/EZGl4Tgc>^lWKDޟf#){ J\+qmQӟ߲ ݀8|FI)1:VX0ڒʎ,;P`'o]sp)>|x'%x| ~œAݴ\&nf%\FӁ-"Sv $4Pq՘Iib'*Tt#~ndJOR>Rvy;e:/*GSج_sN@d<8sR.r$'1\x:0(|, 2`bޙ-~ŝwWpL -[ +_ыSl8M+{O΢^flGZ(|/ T^+$ڏMW?=?؆81\Rg ]QP9r_}4Pm` ̋[{@,G| vbpBؔb;:vGOL0zIQ0@HL(SnafY$\ôf>n'ͳ~ Yo*nofl2Fܕ6zeM3ܞ/DNAxl :^UBӁD$b9]dQ~+T<_fixCͭCsuh4-,Xfi; Fy5za{J&L,t_'BZ4{c ) /& ;A{$4 ''oG+Ҧ4/ܴ5'3x灴Τ! 0@aPʊihHjXgy=r?>ctރ"~K_P+/.N;~%5O)u(2! ,teMxSq}^.iB,b7'|rk޶=f-qsu /ap }EGsVE),Ќ4@"˫ɍ(-L[K}}W/33U9Ir`+n|XB〸}2%K4|K,'C^h4ko =s+- J3q=:SI-d! r /Ĉ! h}sT*|nQil Ύg#asZ,ƻs0nY`x๚:;ԁeuSIgOu'o)cu h cK=6*ˆ k67 endstream endobj 227 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F15 112 0 R /F10 53 0 R /F6 35 0 R >> endobj 225 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 227 0 R >> endobj 230 0 obj << /Filter[/FlateDecode] /Length 2162 >> stream x}XK6m*[(YfRJU{67z({G">f}~i7yǹ f_O},7Xl'4VRݫ$ɲmu~(JEmY*t駍fO4g4jtwvz肞l+(?ށ`; 8lKD4D/[UDHi; ,TiOFvlid"FZ)v=]dEKvk4^Y"IH7v@zX7nAO]'Dy^~ e3/Ávzl1zuf>`;: c;c1m<WЌFI[[[!UYt '˞v\3X,aE x]:% i4YO$hm={BO4dQ;s?7d´ w-ݞՐǬ5oAUC5V@s[岬`<1 ǭ< V zj{; &C΁:3+2f 0+I  jb]PS6YQjU578K.q+J%JWe-Ve| Dȭ`l8.x6FWty+4;Ud1N&gͦg+'t:f+RX::z,ejJ>M5/ $wC&unLzsyE(, uhI !wUهD,bc/xPIuHDQZIwƅdΑDسQGiD}Xjfu^]Hl@sJUH153LWU`V.v4AI7Bx׃ 1Z u:ϫ4$7.˛X錆{,UQ&1 n^Neh2/QԻ<%a(ƚ$D@*?*Mв;Ch>ubS`BV!9f@X Aq ԕZ iPXʅ͉Q=kWi/i0ݎ~#18Ynj(BÀIkcQ~X ? 9XwL¬Tl*J񹕪D[%W}Cꐭc9YNZRsD2ߧik%zu!e K0t'+T*KeSF&pG Mn.ngB,W&!!'ng减DZ?(٦oJ 7_j,뛩Y}Śc\a?X\nB#bێ ,q+Pd*eAwp#HvgℴΧi!,yvL9iZ|B#}XsTbsA0FQ~OS/U.~W*?q? endstream endobj 231 0 obj << /F7 38 0 R /F6 35 0 R /F10 53 0 R /F13 66 0 R >> endobj 229 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 231 0 R >> endobj 236 0 obj << /Type/Font /Subtype/Type1 /Name/F24 /FontDescriptor 235 0 R /BaseFont/IQSYQX+CMSY8 /FirstChar 33 /LastChar 196 /Widths[1062.5 531.3 531.3 1062.5 1062.5 1062.5 826.4 1062.5 1062.5 649.3 649.3 1062.5 1062.5 1062.5 826.4 288.2 1062.5 708.3 708.3 944.5 944.5 0 0 590.3 590.3 708.3 531.3 767.4 767.4 826.4 826.4 649.3 849.5 694.7 562.6 821.7 560.8 758.3 631 904.2 585.5 720.1 807.4 730.7 1264.5 869.1 841.6 743.3 867.7 906.9 643.4 586.3 662.8 656.2 1054.6 756.4 705.8 763.6 708.3 708.3 708.3 708.3 708.3 649.3 649.3 472.2 472.2 472.2 472.2 531.3 531.3 413.2 413.2 295.1 531.3 531.3 649.3 531.3 295.1 885.4 795.8 885.4 443.6 708.3 708.3 826.4 826.4 472.2 472.2 472.2 649.3 826.4 826.4 826.4 826.4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 826.4 295.1 826.4 531.3 826.4 531.3 826.4 826.4 826.4 826.4 0 0 826.4 826.4 826.4 1062.5 531.3 531.3 826.4 826.4 826.4 826.4 826.4 826.4 826.4 826.4 826.4 826.4 826.4 826.4 1062.5 1062.5 826.4 826.4 1062.5 826.4] >> endobj 239 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F25 /FontDescriptor 238 0 R /BaseFont/JLCNDI+CMR6 /FirstChar 33 /LastChar 196 /Widths[351.8 611.1 1000 611.1 1000 935.2 351.8 481.5 481.5 611.1 935.2 351.8 416.7 351.8 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 351.8 351.8 351.8 935.2 578.7 578.7 935.2 896.3 850.9 870.4 915.7 818.5 786.1 941.7 896.3 442.6 624.1 928.7 753.7 1090.7 896.3 935.2 818.5 935.2 883.3 675.9 870.4 896.3 896.3 1220.4 896.3 896.3 740.7 351.8 611.1 351.8 611.1 351.8 351.8 611.1 675.9 546.3 675.9 546.3 384.3 611.1 675.9 351.8 384.3 643.5 351.8 1000 675.9 611.1 675.9 643.5 481.5 488 481.5 675.9 643.5 870.4 643.5 643.5 546.3 611.1 1222.2 611.1 611.1 611.1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 753.7 1000 935.2 831.5 805.5 896.3 870.4 935.2 870.4 935.2 0 0 870.4 736.1 703.7 703.7 1055.5 1055.5 351.8 384.3 611.1 611.1 611.1 611.1 611.1 896.3 546.3 611.1 870.4 935.2 611.1 1077.8 1207.4 935.2 351.8 611.1] >> endobj 242 0 obj << /Type/Font /Subtype/Type1 /Name/F26 /FontDescriptor 241 0 R /BaseFont/HIZPYQ+CMMI8 /FirstChar 33 /LastChar 196 /Widths[660.7 490.6 632.1 882.1 544.1 388.9 692.4 1062.5 1062.5 1062.5 1062.5 295.1 295.1 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 295.1 295.1 826.4 531.3 826.4 531.3 559.7 795.8 801.4 757.3 871.7 778.7 672.4 827.9 872.8 460.7 580.4 896 722.6 1020.4 843.3 806.2 673.6 835.7 800.2 646.2 618.6 718.8 618.8 1002.4 873.9 615.8 720 413.2 413.2 413.2 1062.5 1062.5 434 564.4 454.5 460.2 546.7 492.9 510.4 505.6 612.3 361.7 429.7 553.2 317.1 939.8 644.7 513.5 534.8 474.4 479.5 491.3 383.7 615.2 517.4 762.5 598.1 525.2 494.2 349.5 400.2 673.4 531.3 295.1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 642.9 885.4 806.2 736.8 783.4 872.8 823.4 619.8 708.3 654.8 0 0 816.7 682.4 596.2 547.3 470.1 429.5 467 533.2 495.7 376.2 612.3 619.8 639.2 522.3 467 610.1 544.1 607.2 471.5 576.4 631.6 659.7 694.5 295.1] >> endobj 245 0 obj << /Type/Font /Subtype/Type1 /Name/F27 /FontDescriptor 244 0 R /BaseFont/EQKNMN+CMMI6 /FirstChar 33 /LastChar 196 /Widths[779.9 586.7 750.7 1021.9 639 487.8 811.6 1222.2 1222.2 1222.2 1222.2 379.6 379.6 638.9 638.9 638.9 638.9 638.9 638.9 638.9 638.9 638.9 638.9 638.9 638.9 379.6 379.6 963 638.9 963 638.9 658.7 924.1 926.6 883.7 998.3 899.8 775 952.9 999.5 547.7 681.6 1025.7 846.3 1161.6 967.1 934.1 780 966.5 922.1 756.7 731.1 838.1 729.6 1150.9 1001.4 726.4 837.7 509.3 509.3 509.3 1222.2 1222.2 518.5 674.9 547.7 559.1 642.5 589 600.7 607.7 725.7 445.6 511.6 660.9 401.6 1093.7 769.7 612.5 642.5 570.7 579.9 584.5 476.8 737.3 625 893.2 697.9 633.1 596.1 445.6 479.2 787.2 638.9 379.6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 742.6 1027.8 934.1 859.3 907.4 999.5 951.6 736.1 833.3 781.2 0 0 946 804.5 698 652 566.2 523.3 571.8 644 590.3 466.4 725.7 736.1 750 621.5 571.8 726.7 639 716.5 582.1 689.8 742.1 767.4 819.4 379.6] >> endobj 246 0 obj << /Filter[/FlateDecode] /Length 2526 >> stream xڽZs_>T0AL_u4%Z-(;ﻋ@%]nH⷟X"eix\-^:e\TR-TL {ۛ<]Ћۇ@ϙ(?|^:9r\L'e&~Ƒ<iqȟk"m_nq9H-Y&]Kr9+2$X9P Ƒ_R²Ôg Ăn=/R @i hZ_XHh\#4v\,4+r\r!洧tq ~=I'O}#bp(5m? &.ۊ8 veߴ+dk >٠-vD`yX9]o ܛB)a KOqڄ3Y,F;A5[[ovYxHӧOg _Tra徚EpK6ELeYQDjy}}[TbIUw+. YM@xrK/}XKt?o-M6$$:riHJ[ lBhA/U}X{1`iw"D)8=~SM҇V8n ҆5 otXɒΖt`^07E˦Yr4ڳ|p܂{#͘:Bj$hHiiOq];ЩL(U`&Ǟ^&iiՔ1 롯w9 h&CoZ?Lup?(E}u@B(Rʉp a|bbG)c :jb4(RS향Tjd} t [a6Wt#DXIEBY 8屐a`*G,S@g28_|⮾}xb6+"Zq guxK[9װ,ؖW~,/EZV'm.CTj)N PjVsX⼯w%Tz &t'L>|ङ@2|d'ld(DY>KR`)mKsrv1PΛL6)uPEIPk_$UN,(wCx1x 2WRy  4@Nq(0KpS+٨`XA.#=x/| JBz+aP*8}=X c!/4Ei|5*OL_C*;H=vn@i'|G|vS>-cy T` ,ʕ:Kg\2(!iPJT&C d"J <>:6GJ\DAr6w A {ȷčK%{_c3N͸) Ϯ6kдoF;GxX9쑦 wӒ;E?3nS>i]t)&W:!, R{R6m.GzT^FI&-?܉L]^ n" vkl 4`̧% _Ƈ]<8bҾ_{Gu)}&߾"=?vI[I_ W9|GL)Yh=g2eY52v['kJm u)Iu{*زDʪ~4[8ݱ #YG tۼ0ֱ=/Q !M{4Lb4C X߮q5^,+q]m@R9e4Nf5O5Z$ø͋ a-8XSNa4px10`e^2kI8$u^9uT3+Q~MD }ERLJ~Sq|V/7ctd(ʦt9}gymnuTIv<v<"B@0ENNl)̀ k~;YK'MK q«908O_k_Qǘ.o30 RPxG׽$] d+ȕlpqgXyP+\!XV}(@,c θK3 X?c ]K IozzߕkDlӴ6{}r+OˡdOt9tUEצf !5SPb[ΪfUŅ 9]5{ՠ]86*t:;.H ڒq֘@m(`sl֬S5kEgLv%9!=b$(UBP]5 ~aqf7ؓ;̇ R4hE̽%;#?ڧwg endstream endobj 247 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F12 59 0 R /F22 218 0 R /F8 41 0 R /F11 56 0 R /F20 182 0 R /F23 221 0 R /F16 138 0 R /F17 145 0 R /F24 236 0 R /F25 239 0 R /F26 242 0 R /F27 245 0 R >> endobj 233 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 247 0 R >> endobj 250 0 obj << /Filter[/FlateDecode] /Length 1558 >> stream xڵXK6WP T-z!iS95a.-΁hllͯOlfv*HHVu$I d 1<$ }_VT`ISON 3귿C*% ᯻*9|0>Dq.,<&/lXxmyY%$A!bɄc% rphEp3D{c¤an:;sTEL_#wnUTҡ2'oe㚥I 6vdxCm*c^M_OUyY+kphԠU pG S:DA)#\hp`VL;!QLU%X$$e(qS^4g^s;K$&xaᒟDES+y]=B$M䖆1^[m6I?FaЬv߷_űe|=ډ'?=VTG= f VԠh * H*W9Tr2K'An?ś0GfRTniSœwR6*vKn5sȓD|Un=8V rd/{lz밶m]Pv)2.߸ wVԸ")b5r61e۱HFe]Z^4 h7?GRdu+F ߺT. bǏӥ˩Z.I2q9s}D1毹}ij %FubzPҸAqV;~EJgCRi?K|[N8ᣭ7Uwq-K=!%Y.}J&pKPpň\2PJG @aXJ}YP%XV>]/ t1_8I@Moˢ> endobj 249 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 251 0 R >> endobj 254 0 obj << /Filter[/FlateDecode] /Length 2214 >> stream xڵYKW`GF\)MCfl-j[ݭ-u$y: Oh\,X,_3Γ? <%\%[mCfZ[ǯ'9mr}; &/HnMO8nt1`sZDYC诫cYij.dd2,w8F3gŬHM,v n;r2[ qM/.B1`轗 8υܰЯU< zpers$'y)4/"Ԑ#a:%A1Qup\,Ɠ94#]r8sEF~900Q Mf6^،w,V)~sE"ږ22&BLD bnMؑLp5 &(fR(s OI;5hs;(aK֐ 3A15HZAfѶ`  r!5+ @|SG,>8G|@?=/2ٔ H_";%,?dS6q>Dy> 9?, pl قʳgO0 ۟qi%TQgʋ.(GW2IQ rIa&(IS EF%mel[qɶA mڷY2 !y1=VW ְ,.ܲzX- /\G / gV5.J)$hRRD *Egn!sl,!ld LdliWTV3}Kyb!+a*d(SdAG]Wa6B#/T~[X.A|sϱ{_  [ar.dG[d\beɱLe=$ā 7<˄h`#x{s9uVцMss!2^MBAoϒs#l}iݜ+l8C)FŗG 1/:bá\DxhNǰXܕs#.vq}12}Ƙ$Xĉsb "|jq37Mst'>9^pweO뫁L#_74 yS}^}{* BTrmqӣ:ܔ $޾ZJYVWYnVbyVN'Mr>`!w q6L_ )kkګk{.< $8Fe82 M@N\neVbKŁjfkp9u>G s!nnKB:̺Zc|rjCa!dzߛZqGb^Xj s2@4pf>GDX5_0ty;,MC,k*BF,.ϠɨUkMY޹ 9Ed0ـV6Bk &!?Qs  ~~'ĩR<6JYm+kQiʘ>/ endstream endobj 255 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F22 218 0 R /F8 41 0 R /F12 59 0 R /F6 35 0 R /F11 56 0 R /F20 182 0 R /F23 221 0 R >> endobj 253 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 255 0 R >> endobj 258 0 obj << /Filter[/FlateDecode] /Length 2039 >> stream xڽ]B9X1"=\!̵Ȓs w3hEpP?/^"8^"V~ݧ+DzO/3o_@ai&C&Hɕ?XwmO}n, _985O@åڍJq0Xl/5AfoHOG g3$4doԪyjwQ#M?q eA?4<%3O<Ჯf?]׸ Ugz}=jf0R./]$^@) `P(gКNW2C'8"c @? D@$ oK2t@Fȍ۵(%bBOGw*zD^6A -HJd^2ԕ oNרz_Cﳢ(X-hS.n^?J2g˻YB&TΥw{<|%_U'Fn;#/>_ky@}~@ɀKד 'X y#&}S3H|٣X [Uw<}=&yo(y$2HQ'^{e%)*6wz*39K_ 6+r6uY*+Zha0t;.`8<(4GIS{mϸ,tPeEv =d2O`s史UO`G h,B Vn #nOdn~0GPp&g3<@ f  Vʜ ,(/Qj> Qo;塡yp) N(2WKR#.6Pdu}` rcAuI(er%nI_PmJ߻Xn_9NÝ&:g&Jܔ +AMӐ8B q  b1ܮpxK(u\55M,`VlRRqVΝБ$yv:8Yr_ꚰ|MscЦ'frS`r XJJ3OE5 HCtS)SQ@ag@!}ie^7s鈯>?ٯ+ Pec~2;p V6-FHH>WL%]may@Ƕdu0)"OwxE⥨Bg'Vxט5A㏫SC$C7EP 㺥Rt.l޿?\-5#ݾirZN {g&{)ȖI8s<38-@ Q M\"I^'L@4C iXecWUd {~nbcAWg+8vǸ3nsMA &†K1S<#n/tJ՝ؙqJ(.*~cWjߏ> endobj 257 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 259 0 R >> endobj 262 0 obj << /Filter[/FlateDecode] /Length 1977 >> stream xXo_!- IIti$^Т=hD&IHADVQܰ[ Xml]ik86SD}EKTKVBw7V碃?VYxl5|u]i鶼щ)ڢhq ,;D0r?ϷUd˖ش.Z[y%n ^E> ?nd|e}sOfD䦶߻d[;ۀ(oG a7M-D4aee\(ʹ޺[]o= ũK̂4'l@c[BQ*Q:&:Ln zz!!p&$j>Jۮ*h^3/ן\$90f/|oa-,oB8^|qK3fC&y١}D#XҾXZd>4$Ro6 2#a"!!Y4^]~E %,hJ|}IJ9Ol8|K^C7Rx)?ōqh!#bWޟKPJ^tWV M-sQOۧaxE=,EΧWkϴ) p8wvG6Qﺧ 艋%|jPXBφ{c|'A~miJ$k6Wm`F14ƟEQZ _a42O=SPpVs]bF1J]_4 Kw H.ZAH闯28G|C"7)LjK90> K1lbf>3]cuɴΘ7;l~{t{bPz> P+4vjhӂ_:q "^3Y&-i Vrpϲl2+8 8k9nۖ 9: 8l5=/+Am#ɼ`ο^ , J-&8{׊ Hi\ʤ.$L.ߝ9B.ϝ 8g =YUZێHrQ~Q24tΝX(!ښΝ9rW5ղUteczIk"]߽Чptc[q:6ֆh G,-JRglp> endobj 261 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 263 0 R >> endobj 268 0 obj << /Type/Font /Subtype/Type1 /Name/F28 /FontDescriptor 267 0 R /BaseFont/QRKVIM+CMTT8 /FirstChar 33 /LastChar 196 /Widths[531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 0 0 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3] >> endobj 269 0 obj << /Filter[/FlateDecode] /Length 1964 >> stream xYK6WCefkE)RSvZk %W)ɖ8hH5fM۞v z=dK%. ғ zL-}ujn)T8s[ E{2e|tIhjqw:5szsp_~/v]\R߬=w<6q*Bx'c&.[Yl~ܥB$. `Oa挧TW3U>x7f~iX.|X^5ywᑳ)r;GCJ"oNw1λJKZ`p\z#f ?ULEtJ+ eK*>'t54 = "ҢXU?.h4=з4 o CۋJp$4LvySM޼ Ec"6]CdL4wvMXo BV橣|.90>ڶu^d x6/Byk~Vk8fJqE 9@3`̌B(/:7X{cemCkG> endobj 265 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 270 0 R >> endobj 273 0 obj << /Filter[/FlateDecode] /Length 1767 >> stream xڵXM6WP*FF -'v9\3%a`30ޝn1&qN@%u_n$,I># ,a bD@IŔCի< Vdϙ( F: ͶڦXY$lQCß7ߔ:Wo$KexA" r7;X(ƥՊ8*Gvj5=^:Is+|0Ίvaͅrza7by, 4ugs&k]F1"aDs+7fё$gL}4t ?>e@6^*voNЙz{E?E X$[@mn$۞>q[)UXoIV6]Aqi|RׇwZ"ܛoBI3A-E5`'9 v*.L(5P7(p#],K?QNt%~al'?< hPM4(8<5, (@"t x 4GœiÂP G%HA-NuZDx1f BvHíE.lXo]%}ل×6uQ-;7tH{mG7uă!"*#A ׸dZsMc!xB l[@ k! ѥD#tID:Tlphp13^H E=! O @#N=,U0CK]OF!CSyL{jAj(4}N;c^;q兇Q0txPG< /Qj9#,Dq`O.t -v+< (`8VI`S57hʝ7l;H Tjo~x nXt 0X]`v  6.`zˉrnS3&[\n;}NZaN a >"{"J1_) Êb<˟Mk. >kJ m.C%JrTt*9~9@oY_`n3m@KpGCBT 8Xb(v];} {CCWO#LXrnt =mC1!H 1EمCQ~ny%tHMO_>-w/l3Or$" wů@C C4)ʯ6UyS+ϒ!w,,F )Sg %r=:}F9 VZj-anZa>\l]o Dc: r B61L{j]b& gS{Zmφ&nr3ƕO2g*)h $﫱#~C>C} o U|O3,hE_Fߙ p2Vş>5y+"I3xZ3RX EN0@ݓ;'K<'w4,,9Y)_2 LBws>o?!Ŝ_h5tL` r wP_ &=d endstream endobj 274 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F16 138 0 R /F21 215 0 R /F12 59 0 R /F22 218 0 R /F13 66 0 R >> endobj 272 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 274 0 R >> endobj 279 0 obj << /Type/Font /Subtype/Type1 /Name/F29 /FontDescriptor 278 0 R /BaseFont/OSCGST+CMMI7 /FirstChar 33 /LastChar 196 /Widths[719.7 539.7 689.9 950 592.7 439.2 751.4 1138.9 1138.9 1138.9 1138.9 339.3 339.3 585.3 585.3 585.3 585.3 585.3 585.3 585.3 585.3 585.3 585.3 585.3 585.3 339.3 339.3 892.9 585.3 892.9 585.3 610.1 859.1 863.2 819.4 934.1 838.7 724.5 889.4 935.6 506.3 632 959.9 783.7 1089.4 904.9 868.9 727.3 899.7 860.6 701.5 674.8 778.2 674.6 1074.4 936.9 671.5 778.4 462.3 462.3 462.3 1138.9 1138.9 478.2 619.7 502.4 510.5 594.7 542 557.1 557.3 668.8 404.2 472.7 607.3 361.3 1013.7 706.2 563.9 588.9 523.6 530.4 539.2 431.6 675.4 571.4 826.4 647.8 579.4 545.8 398.6 442 730.1 585.3 339.3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 693.8 954.4 868.9 797.6 844.5 935.6 886.3 677.6 769.8 716.9 0 0 880 742.7 647.8 600.1 519.2 476.1 519.8 588.6 544.1 422.8 668.8 677.6 694.6 572.8 519.8 668 592.7 662 526.8 632.9 686.9 713.8 756 339.3] >> endobj 280 0 obj << /Filter[/FlateDecode] /Length 1773 >> stream xrF𞯠CXیRSIfVX`z==rrb~wO8>: e2fn%Sgoߛ cY}9Yp_Wc)ßMWlWAU$&|JE]y n6)g}e94H(;Cyٵp>U\XZ,U͑|%ÕʘDFZ]~OxdҰT2b 0 t@G>"B)!x9o3}, Jc N^t"MG.rl|;*6Oy PĔ6H7soڅT\6;9?_bc&1\ T,@@H ֨+}2lk?uf)@|KwYMST@\-`|~j c(h\14r g[յ-qmxPi^c+ Za$>YY_Svnj&c XOXϐ?%<1_V1'EP{}D,cL綠L{\kL4e~i"z^2W:~zI$eی\/]/\oFS1"M3r ]!҇}BcIvj&=,䰲wO@o}FN(,H,CRLgݑHEjl/Ҿb-nK`tl:nP +,ohyh1 t[Ѕi,]evnĀ̺X(~$KͭD1vjΈy9ڪ9iN'V9|/- endstream endobj 281 0 obj << /F7 38 0 R /F13 66 0 R /F16 138 0 R /F21 215 0 R /F10 53 0 R /F12 59 0 R /F29 279 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R >> endobj 276 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 281 0 R >> endobj 286 0 obj << /Type/Font /Subtype/Type1 /Name/F30 /FontDescriptor 285 0 R /BaseFont/FCEIRJ+CMSY9 /FirstChar 33 /LastChar 196 /Widths[1027.8 513.9 513.9 1027.8 1027.8 1027.8 799.4 1027.8 1027.8 628.1 628.1 1027.8 1027.8 1027.8 799.4 279.3 1027.8 685.2 685.2 913.6 913.6 0 0 571 571 685.2 513.9 742.3 742.3 799.4 799.4 628.1 821.1 673.6 542.6 793.8 542.4 736.3 610.9 871 562.7 696.6 782.2 707.9 1229.2 842.1 816.3 716.8 839.3 873.9 622.4 563.2 642.3 632.1 1017.5 732.4 685 742 685.2 685.2 685.2 685.2 685.2 628.1 628.1 456.8 456.8 456.8 456.8 513.9 513.9 399.7 399.7 285.5 513.9 513.9 628.1 513.9 285.5 856.5 770.7 856.5 428.2 685.2 685.2 799.4 799.4 456.8 456.8 456.8 628.1 799.4 799.4 799.4 799.4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 799.4 285.5 799.4 513.9 799.4 513.9 799.4 799.4 799.4 799.4 0 0 799.4 799.4 799.4 1027.8 513.9 513.9 799.4 799.4 799.4 799.4 799.4 799.4 799.4 799.4 799.4 799.4 799.4 799.4 1027.8 1027.8 799.4 799.4 1027.8 799.4] >> endobj 287 0 obj << /Filter[/FlateDecode] /Length 1771 >> stream xڽXK6WEN"OQ44)P!SӃdic]w$ZNbZ3#΃JY~EEQJI.D*ByDE\>}h<.yFx]Wfx_MS"*W EfP,.,~X"?VU_T%"j4pfٜF'N 0a>re|/첩;]݋a/%.?9Ub`#/sTe|g$- ͉dܚ'B wB:T.s< ^w9e>[$LSX83H 3MG]>/wr=蜤$;'YJ {HH㪁5W\\UŮ2ڮͮl[G\zuo-yi/-kߖWNqo5uΒn]ڋ b g>X~v#u":z ؚȂ p-*-XSg$AAۗ6~µH6UTmc Iv5 VuE]68R_8Pz!xwA$bK( ь0 Fsk5s"f( ZCA-c( 2~⚤uu-Rk# 0Fm5ih?TdD)-9 Ґdd+Ҟ/ovwW4zOLLZ%yPr`'N`/ 6u"#ZF=Hx8غ37<5.P/Y=;.,&53<53)C~->AoH9@Z,8cyyD<=a]O.=:LH>7ISc95N199sjҺ|`.>f! pcS0_i%Nj uՕZKv72LY/=B9ɾ.V<!ʲP媿Pxθ۞ E׎ xT OgDk퓹Z݉ovA@Lo(ȦPon \OV]Km 7p.i=ŰxxB)cS,+)oCQ2Yt6:)J,ٗڤMi~߾ $ \7q_uV()%HfghCv0A2 /#:38\-ԃ+S0 1PH*a !RK#&EI14G9Imw: T&dcǽFi/še  > ѧI@Rzu(#TGc V/ \u2S>er8nue}1;#? _ ^1Aԛw_7r@WgȋGO]먎BSi{x@n>}ە[=گ/P;|\iKb h'P:85*hJ!;*G{|HOT @/Y]ٺf*C/3]S`ٶA{$h˘02w]=,"OjssyN"i7n6s777DI2ftZ½"#R </5#VWTU]\Y ݵI7C3_dRߣ }e?́trvc3W5b\ۘJK:ASp JC~7'zAjLQ!W5DmQ"<|Xܕn8/b# endstream endobj 288 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F16 138 0 R /F21 215 0 R /F12 59 0 R /F29 279 0 R /F13 66 0 R /F15 112 0 R /F30 286 0 R >> endobj 283 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 288 0 R >> endobj 291 0 obj << /Filter[/FlateDecode] /Length 1806 >> stream xYK6 W8衞dG^~Mmr=t{zfS?vvKۻ rdQ"))9[4?B{,Ri (x^=8|gi9wWRxwX%b)a yX~Y­uY)|[ćCm~~F,b\4c|:Yڥ i.@0tOH[L'r7yq@11!b"Ғ"9urM3,r}|3/.!ɥ4DF,/X@YP񺪍%tڃ=X@ \..ȴFG'XB}ϑ0qZ flTҪd󬬊x}6fc݊FH  0h-Ŝv.=p4dR0=B,Pu } c)d2 &[uN f];a*`(t`IpdS4\0ٜSsKCFو)n'إ$ {ł0??stsodmo}+ϓolbүKph;@y,}W[.Z(`-fu%5 @L.!xKʤ$ Y^Y]Erh6غH+!o?Wc{?OCk!gûPjem !O&GYDŽ;A4fv˺4'wФj iW&HP$͎tZ(OXUIu EC*&EI_h.4mJDU6Uj)n@jfoj6]iظ&8ӹݲZ"o$wmӛ30_5R{{̀i&NieQQ'SUH} 0 Z#CLИf: Z:Tc`1ڍ) Ga+7f 5Rvulh+7>Oax)jY01P. uxBN@( @|A=BE\Р.-TB pglDnq' ) fnӸH_JFm[Ô,W>GGE: %b';AgKLƀ<[v#kk#>jrS^CvӒĖ1D|?qPv+쓍]4HN2yl.ЩQ61\li>q`|U cs/R+qǴ'xxsr)o$+{K{6 X$#D`y *NvWlv8 dg66̛8^|0mIKd qq+sjnWOώ98GxX莑yUP>M<jx>mڷ*i,{YJQtN]r&!JA:+,&ݚWwzQ?do)*'[gu:}U_|,g#ڤl?dCnCSx (K Ֆ:PJYަ,̷I5ʨB/`ewg&h匉fJܗ֣jmӿ V_ 31 endstream endobj 292 0 obj << /F7 38 0 R /F22 218 0 R /F8 41 0 R /F10 53 0 R /F11 56 0 R /F23 221 0 R /F30 286 0 R /F20 182 0 R /F21 215 0 R /F6 35 0 R >> endobj 290 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 292 0 R >> endobj 295 0 obj << /Filter[/FlateDecode] /Length 2138 >> stream xr`*PxM*Im.{Xr˰YTGO7 AQ82@7Ѝ~%,I!,a*e)us7y̢ۇ 3QF{~g솪mt4w4nJ8Ow PoqoI($0La0B1XG?vհ">$0j8B$G'f#xeENꉀ(L L=o0(uOh o-c2ůjؿxBR/-f5#/Z:3JUB l ԒWR[%ffK *=:kC[?5Aktf%R p9S q" |zJE~&ALCg{*w < 9VO3&y%е+9[Kx( Neb+XܕW df^D ?r?[B˖-z>?GKYߺY~f_ՠX`"h/a9\|v=~_!j(}ACU $JUAO EUU.Wk4ͮ]{2TqLƩPZwu:U8RxT= &Kjz\3WUPLX¡T_ؙEd4.,_8_tc{"4i]DOvX@[XԼiW(tSXzgkP1> ~оyD|i;KSNYl/ɦ?TOUZfg5֭3y>~po9SH:7\u/.=Pty쟎ms0r W) endstream endobj 296 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F15 112 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F22 218 0 R /F30 286 0 R /F16 138 0 R /F21 215 0 R /F12 59 0 R /F29 279 0 R >> endobj 294 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 296 0 R >> endobj 299 0 obj << /Filter[/FlateDecode] /Length 1957 >> stream xYYs4~Wx@FXe{ak)x%U@xpf| ')`.llkj`s0irϗ,ӥz! ^f|8wͺ7̣Go%'SmڐsԖnPՠQAInR0Ak 'twFN9p |@MXP\uƠ$SSe牬++`iG v=R=^pc Qp!vsj,]K*&-C[`THj uv|wh]Om_TSZ>#rr{^hL(.͋or^G~UȈZX-7Qz2+kH"-(}D6mҒ1m`D ' @) $lz5¢^آr \~K'ޖ~nN'>`qxB:pxbtӪK"Hدݴ>?PT2_S;Z Os}C B}YN58Yv1_+bJ(Idypl pM4aEYUXӋqP"@V%/%($%$7pdVY|$WϺP1Sz _x >ٱx6[8m"B1O.T?ۼwHOs'.a͙W80ࢡtc),L eծ#&ڀ.}?WWBQ)F0=ČU ''b:&)S,Da8p4>шQ[1:,;fId[Tb`fgĢȀ8`mD>[nY^"8[U:_/put_/ghxtj>|)z`u-_>@5e<_#'_۫O3_a\1|ۙ| X^ <~1BԿp<3LțR7hDݓ,3Rlo4U9ͳrc݀9<"ᘊ&yqH.?g[3i=bGq=#/@hAճY6Ycoy 4xA{tV0D ~1Kf! endstream endobj 300 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F10 53 0 R /F13 66 0 R /F21 215 0 R /F16 138 0 R /F12 59 0 R /F29 279 0 R /F15 112 0 R /F8 41 0 R >> endobj 298 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 300 0 R >> endobj 303 0 obj << /Filter[/FlateDecode] /Length 1797 >> stream xڽXKs6W U[+mrhtKLuML$QCRﻋH,"Bb_x o_ {/ X* Y bJ;x)K#o9xnGae6 #?_ճ~oX~*pȿD&nf-~.HPz BZ! ŸT_Px(G>宥`}~_vI睌s1*nhͺX ֤{#&,I$LJ)nHf! bo99|FgC&Sᚘ7"Ќ {g mVmMC(Z e`M]oݚƎPP둆ae_\ƣ|0ʐNJ~c&2YNe-/'n-4tPdB 7IT(G d?d85; 7- d5\HKܣ?6"# bMQc-*r_gJc  T8\ۂzrC4T`,zMarU[F5nJC'w9l(mI.Q<.Q|N9 #8fr;n n2.L[{lABxǢ^7iʥN^jR^`vWӠ-Jc DmM$S6Fꥊ  }V֍}|(QwMW_gР08'5y..yⴍÍJ9䦺&ruz̮Ok dďwN8c ,3"7Xkt:_M^^º2.7Cu݉7˜iM1]4qsA*'VȠnQX endstream endobj 304 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F8 41 0 R /F30 286 0 R /F13 66 0 R /F16 138 0 R >> endobj 302 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 304 0 R >> endobj 307 0 obj << /Filter[/FlateDecode] /Length 2117 >> stream xɖ۸`U(e%s `m AĢ(T$eDbX*5 gyl{rDc(ZDϻf!hxZ'I~Xe"<6]Y Q"<6}S~oARc)#fZǑ Ga,@" ;}ѹiX-o+ ~~%65-Wԇ cpr:F])UK"eS؞6+%$ńS>ճ}fv Ù ?h,w49M*݁iQ7xWޗ]ky i?V,ñwPA[!Pu*/HU",Ќd*CG+ڎ;KМ*hJ*7@+b[WU&ؾjP$˅UX '!3#L`fdY3UmERm3) f)+d 9KRC~g5Sntdl!XD'N3r0|[۩38 ϘvYHBSæiŊ 4;_v5X콚ʇ!Ѩo^;od"uuyVC Uk4^ r)\.b'~XJ w]A~\۪KbZ|u8vqͯfcq-\/\A+Iϻ̼Cйf`jmѵǿXIȗ_Ք^nۢjQhu$7$YL 7*Ჸ_F1V=ǩqRv4-?z$+ &{y:Bق&xwg:q폼9JhjaK~yGIebٙw<>i%PHnj]9UECFK7{8*\Trq7k~7D9 endstream endobj 308 0 obj << /F7 38 0 R /F10 53 0 R /F8 41 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F16 138 0 R >> endobj 306 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 308 0 R >> endobj 311 0 obj << /Filter[/FlateDecode] /Length 2235 >> stream xɎ:9)sHJt\*R,9ZjGʔdW/ s"m|83$x gyq8Y,wny'x\x^0'|JU8۾lZDUæ]%"Ը,pAC߼KG,X`)nk1٩@t] {ҽݖ&vqSp+fqs& Ӥ+뭞J8锎%+;R7J62X7+ǚ>8OVwj$,n+Mdy`yb(bSW4]ef>*Kan^׭OE5hA&`֋C No*0 ?#}hE]3E7|Y,rfś?։LWn˧W b'Wk {ʶ7OdkPRemzGg YY^Wbh@rC'tPH ^mY}K> 鋥7@d KCdIAch01<\\DZ~9*Ee%jhasoz(ۦ>\:pqPxIo^{cE`XZ`APmtCi8\m8\[0od@F?9Cc(VbB!-X,w|rxGt 3x}M[.x:vw:FF`&=4|zH, LGA|u?13DwHxZЋp r0b<9%/K7͗P* -V%o?O+Yx)qY5yu~5HK'% z;P_e/UpNa_bh"rޖH ZΔ*[ /`s8[i8׋,TM: ͈*K\,-;iY~bܹۢ%W9IVy 0xE-Oq9) 4転bn0ݖdyBtoN$LzFE+2y !(1Q|K3 *3,˕Ks!3E4lb(d/.3]蒨33Or+(fvkL>f6P@y84]o*I}z%er=6M)gY(7@D?!n;9&Y'ݙ2AJ)3frR<%eyb1Gtq)iW EG'g0- DBVԈʝwr6}\ 99/6[#,۲*{mxe;T2` hOaŃcWܙ&mLWn=Z|V[db+xh06|fq#ԹL/{}yxjG@ex,=ɴv\vd DMNMJ/E,})AXڣpMîu ;jIۺ{&}*=LCöw|0{ ijMPc xG,΃?&PlNFxRLqN|l$O^1ƸWCcBtPHӤ{] ]ޖ(Tȶ=ƅw3l"1ZprH%O'emBlo#SVydNi UVfzFF*6~'β1ؖ}YY|彁7> endobj 310 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 312 0 R >> endobj 315 0 obj << /Filter[/FlateDecode] /Length 2327 >> stream xڭYݏ_!\DFNH^CP ۢZk%WwpHdeSAߌqQsƬHH*D2)\~ x,(XwtD7$ t(O6QAw(M&U۔5Ե<f~<.q xbv- 1s&h_폥3D΄3xρÿDf#Pّ~PޗC4BpAa.e&No]og"O%ò>.nd Vu;c @,.Ǩ{w4m6e]#puѹ}5v%ғ>^S̑P#XlLvljg^vTى6Y0wJgߐ[,_s"cq+`teD<=Kͼ;c8ّGON1me R9FYJ;UJǭvz߾tPϿoXħ6JvphѺu4\5[ %uTޢhЙ9Zv7gGYdx*TJo;#{=хH|348&,;'F0SˍC՘8  kIs;EE^(g ;oc<5z'qxf#-[j4@ݝ*3W5}y-& uMےsCjnbq6,UƬ$7掣".íJ)K居wyZkMStu})ģgxUS<6C r¨ab ^"$&r ˆ}D-X~ʱ[a(3t ݑb`z'OxXwӶr^}̘y9OX_>*ڋI{Wjo|2^焩/9\Jp@"iW jţ}hzCcl8.p'n./^.{KeqxSǵ஡^j%cw ZO zD,0]P3B4j6p )۲y.ˆF-P9<3= u+B[ }0kKͪ]5Ѝ (q%{I@]lˮ v'i2p#NyџP>BEV mK ǼB|Yn`a6*@/937>FRf6ro*~bǕ}j.JΖX'3bGR731dr$X327IF<b"͋7%ظ@<;~Ƅ$0\D4ADH$SJI]*d1UvGQDa=W* C{w4iB0Ԡ!|<%xm8::3PÛ@%J Iw PSP5e#IbVyq 2EQi2S׺l)Z"'k =! %NҜoL|l 2S2ưIq#h"Q@|"DZ¯"@d*+6Jn1w֞8ޞ(πT!ݔqĉT#.DqߠSmrN"舉sSYuzZX? t׃=W PU-qc+bXk7*[ endstream endobj 316 0 obj << /F7 38 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F23 221 0 R /F21 215 0 R /F10 53 0 R /F8 41 0 R /F16 138 0 R /F13 66 0 R >> endobj 314 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 316 0 R >> endobj 319 0 obj << /Filter[/FlateDecode] /Length 2282 >> stream xڵYKW0a(CjÉ}p  5"G~})$_EQ؟Ï ^$b6R@ $*5q,Oݰ?f<*MS˦j#TnW* vL6;_3۟ m>TpHS" <`&7\XؕuiR%þ_{C[כyf͙^m4)M, g?Q"PsDލ'c岓_g_=Q'hqjDd "AFh5 8co5l5S.(}uG~E*C%>:;^[#u:O#{H,J`6>OMw?ܑ85,C4/4~UfUU /-=IF>G/{LҴޅkp}YSҹr6Gnmí*_`¦z;4q_niG_H\5up UZe]̏u 7mG{mꥭ-FR;L (8gQX7=xq;zٖ66pCB7$uoA+kZڷmhR.k{Z`I ̕ey0 8AIp,+KF]]t4 Bz2i;:evG-^wc @,ф!CwfM\<L3}jKXnM 3ɽbv.s}ʹ{` 2f"ʧ %- */ͭco; ǐE~aÜezf=_=}:2&2ũ>r\A 4,{ $ՠ}kZRVw~X'=y=z!۱ I ~XcI' #%DDJ-"[;u"mQXG,p2/4-hʹ:Hlo؞r,vV9Rn1MT4UoNO{l NǯbK`9R&<g/<|aL.g6%GZ IRq!֐p4$eU,p(o]B2(ԇ@ab@gY'I;ag/}Fmct|I'9ET:zwms/Lǯ2F8+ Ғ #=0v2&D*%s&^6Mkl횦[)Y m[R(sinR=vKq Jbf1~ f f8,hh/ @냃;턀yږ޾] yi@%_^9>30~$F36U"\ԝW :$ R8q&&'b;*QhɌp*_M֓S2PX,\|ƣs5 |mrr[sLs01oqAHf2{>Q|RJRzxȬı6ѪX8g^>"C/ӧ8:XKs,˥}ظvm;j_1UVu<"0) [9b-(-Uo :Nld˅ ځNa : vucTz37+{·{C ܣYb*Kst?Lٹ=8N=8pvo9Ŭ[BبE!xfͶS,0tg) .o[ueTUO{%Xt%1 nbs(& u?I*@{ UF۟|U =V"+'ؤ8RQw`KKSiV塤^Zbp_ڦN@&o»& endstream endobj 320 0 obj << /F10 53 0 R /F7 38 0 R /F12 59 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F13 66 0 R /F30 286 0 R /F17 145 0 R >> endobj 318 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 320 0 R >> endobj 323 0 obj << /Filter[/FlateDecode] /Length 2312 >> stream xڵXKܶW*qb LHڑr%IZ83Y&p‡v׿>hڸ gяD,K`~?OX{!Yă:`~|x4Y^}Nv{.p=es-*m.mQt \81T4~>OVoMgL%а+4<+ ;]c)߀ Rs:~D/jcY&F]1)'&iEXue "NtOqFa 7ed<^D&"Fa бzPi\ H+}Ք n~mIʔCڊoo)H̷_c^0&,$_!1SzO@0I0^'|_wq:q=K}X{~B6=cKEHku?WI %YJգሿ*BlyƏ-5K>mMNhsyĨ(غbD?O ѝ׋zj7 F*, A1wȀ{\U[QCYbtgbw]f"Qm<@\梙Vt"RQ~ZF)ٚMV.w5 ;AV&q49H,鶓 hMv S)(բQ&a*6UH!yI`%p$byF  t2 lsfPk ~ZPfqL7  ъ"*4 va]8?//V5TOwADva M1A,|V&9(i XX<`)>e yE F lb_m#Ƴ٣wTs}tde'#ӝY,|w, UCCvVВƜMcnYaH챬*vtsc38j<-9WfYBm,ca]jk#3'\+Y`#3i~.P /XT_@CyZp;t,4Cߕ'D˓9 .{ԿĮ| LYqF &gg*oiO2i':@*" F07rRX4u${|qzX!~g(\cUrWZmNe $ԖtZ {ʛ+؈S4P5//=N*rtRKUfQNF'Kd8Ʉ.AKmjwH0Iq\XK0?4)PLQb 0azPX? 3u{hzDSn =I1qBg(*h.QOyKk`<$"؃W&hIxN/%G lIlXec /YMSnoG09cՎAb@Év?dr3,Yds(\@ ^(QRLzAoYfz݇Xj endstream endobj 324 0 obj << /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F10 53 0 R /F12 59 0 R /F13 66 0 R /F16 138 0 R /F21 215 0 R /F6 35 0 R /F17 145 0 R /F28 268 0 R >> endobj 322 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 324 0 R >> endobj 329 0 obj << /Encoding 7 0 R /Type/Font /Subtype/Type1 /Name/F31 /FontDescriptor 328 0 R /BaseFont/SYTRPS+CMTI9 /FirstChar 33 /LastChar 196 /Widths[314.8 527.8 839.5 786.1 839.5 787 314.8 419.8 419.8 524.7 787 314.8 367.3 314.8 524.7 524.7 524.7 524.7 524.7 524.7 524.7 524.7 524.7 524.7 524.7 314.8 314.8 314.8 787 524.7 524.7 787 763 722.5 734.6 775 696.3 670.1 794.1 763 395.7 538.9 789.2 643.8 920.4 763 787 696.3 787 748.8 577.2 734.6 763 763 1025.3 763 763 629.6 314.8 527.8 314.8 524.7 314.8 314.8 524.7 472.2 472.2 524.7 472.2 314.8 472.2 524.7 314.8 314.8 472.2 262.3 839.5 577.2 524.7 524.7 472.2 432.9 419.8 341.1 550.9 472.2 682.1 473.8 498.5 419.8 524.7 1049.4 524.7 524.7 524.7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 643.8 839.5 787 710.5 682.1 763 734.6 787 734.6 787 0 0 734.6 629.6 577.2 603.4 905.1 918.2 314.8 341.1 524.7 524.7 524.7 524.7 524.7 850.9 472.2 550.9 734.6 734.6 524.7 906.2 1011.1 787 262.3 524.7] >> endobj 330 0 obj << /Filter[/FlateDecode] /Length 2126 >> stream xYݏ۸_+1OЇsk}.Mۺ%CP2mN6CD3!j8'w$ITb1NTL_N=7oyDH/H)>ExC?w^gL9A;]4g $=t$zAs'㊅_]40Hѧ`L?6W;=Q_Sϟv1CLv:d"/wC z K@D'tG D.ίHBJꩽ9SoG0z?tdsÝ|r_|ϟlԟvGXg +0n5%$ܩv8w#ik!A%7lF{ؙkn{F'lL'icn-`)]\ |K)t&.ۺف ["l=NE)0! " LmSn qSEԑ]n_]K.?N NFq/lEEWQٙQ)')?/Ɩf_Uyd5[F8=܉$٧4Y`rȪLؙcE(Kg:vXjȢs Jl@\Iډ8Mxi PشLƪ.=:Iowj!NzfC6&d8a=\8.Z ."tc1O9(BB #{>f1 v A#ԕ~$NJ ><)c@.Ѻ3ryqsͥ+ϡ8 |=l2=xyy?/QGGv ؀hC+Ry$&!Bp'( g0(Z/e%$ idc o ,P zOFIyTCÅ 6(QT\≴ޣD7$svE5⦅]`ǰ{?Œ^T' B2 (❩.}SʘK  mo<rj 6>o9Oh9CtڗÄ=Ižp.h|zj|r|WDXEfkxYr3jab#\ WC=ca@fXr^)z'btRWV endstream endobj 331 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F31 329 0 R /F8 41 0 R /F13 66 0 R >> endobj 326 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 331 0 R >> endobj 334 0 obj << /Filter[/FlateDecode] /Length 2454 >> stream xڵZKsWah[RO@݃:l_V>&6DKD+z5_z@BmI=A'Q,ϓ^<'&gL2Y.mbJߛ/7|,U&Ys&oR)Vs)ӿ>Շ92 ?\2ciw߷]G"=zmw7ȓү'PK¯*ݝ6N#[|uv˾w׻޿y}},I®K{^ 6xӇVb8' 9UfJYvywﷇS?]Bf?9~duFm(ʙ"a?zVHOە(;m&dYUK{42ە_sXJۯ. >=6E1( f45 MHR*]\f83Ԯ8,BKťH!ncY ؏tE&O€N_ +O7oY?nWԊ߮2^p̹d\K^~ow? EZNBQ,h;Ь} S9Ēfd@˜V#O)>x'iK#x*YԸ'_lv5ɜ%5\~]bzo"|ق%IS7Ɉ*uuZoN>A鱄O}@_ʨþn7g/B:" $geieJچyϨtH];4x 3L_a7;~(Jw[6YEUθ3 m}_^o'=:`ߑjvtq%gٍ6j^SCD ?NR08.rQ駴8֪ Zw5 H/jBDe# egIg|W,&I<[C~Z5'90TOCe(jeFY}]=4[rL\rE y$g,C:e59fV՘v1!,:2rWx+f?ĬĐHm|נKkGLj3(7ŀtm | oţ+ktwYE128(RBX!@ӟ'/xC:V6v 3}a@AS݂Ԑ idvg, I-eI#_¢:ا*yNxqP%[ (n04a5^ΏSɄ8U{s)':Ґ`a粬v 5\gKu*m "(=U`濬sbC<y(?Ox qI[`WtpB=B1F*FY!-8\5eEyqH; NA?޿T;JT֏^>[rf=Fװ-b«gEťԟ]ҳO' iU=Y =ܓz@聛{D$=%;M%*JU&ƌYIi3֌zVv*( kw D O=I_:l) ~qsAfR +vb)G]EV$Tf9$#"EpF/9'JLZA7<6pOeęP)x8$QO( m%Δvf4=n'eI9g1XIR(C{2^P9`A=4FYLͅҞZ>*`倾x ;$rU#3P}Ss HƬճQl%ل1dw<&1&TҬjwAUyӱ\l HҰ@*@r(rE!LHgBf~ P ĔҝslT'TqeZ_ՈP_^92,ѧ{R%`!+P8VYs %Kg5rDXD4\3R_7JGܶZO&eĜ$.L"Sw'.GH##Ql0gE@Ҕ!ꗁ*w,/x0N.:j1>2>2˓n endstream endobj 335 0 obj << /F7 38 0 R /F20 182 0 R /F8 41 0 R /F10 53 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F13 66 0 R /F15 112 0 R /F30 286 0 R >> endobj 333 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 335 0 R >> endobj 338 0 obj << /Filter[/FlateDecode] /Length 2035 >> stream xڵYIs6W0!T!AM]e:C<ӃzhrPGK>lD&'?0.3_!Ĩdٜ i8Sd/$8+Q)e7 Zf?_/gJv_mڪ͙b&pkMQfEZSnfo.xRu0ɐ`6;0f) 7>w#̬l.(Cvo_ν=K}aw" ߋ '8_C:ah7]($wz8AHl^*%cUoߘ(esh=F#F#F¼cB((JQcOM!FܹmwVS{2|+\(8O_7WYvu;*Vjϛbo*[e] Ǟ2/],(AlEpcgB|ݬ"l [x~"◶GAv' .a&}سng}S"h2o + sT* 5ԺxE""Q!?+ΑLYET*6:7D#kZmHʔ)uls5F~ :UPë:k7OħEpU䑧+@EA8Cmy4џjFҚ*lR픰oRe 㰝6fWOuDS"cHy(AFϓ /$z(H}B[T|-#JrL uKĎl0 2xPK}JY 6qvT&%!C& <^G'O%^7Zux2W!r wJ?_jf_1@WյͶ }_4n R[U@ՍU`=8SXuMK{tTAVX*P)_ŜB9ETېT/~Rxʄ e!ʼnNShyi@Q&"ꘒHT0R9C V<Teߍ8nT6 :n)P R DʓrpB)AWjd $= UFעjsWi3 rt9;%"&k#C,57K r9ncm 3"BiXYEv)k#[_o\[ok[ѫuHT{**o [ۇ@29)Ss][%T 49qx+'и$.:X (G0BADg6YBHu:ßP,c-x:՝4C<((wGf="?7/PW&?!BfdRQ:jח+v݊%RS|;.=4  : xYlbo?n+*TrmP dxulCq0m;U627jT6U[kWQP%.ԽEip̥\lY ~ 3XQe_FoبF8m|[7q7 LN a 8 #1iaz9vuVZ$쾝#"TyI` ߶de ՛ϼ)A`#`YW[ڻi/z]I v/`s%+??{Ov_TNBP2DYO%y8% YḹDN9Xޭ^Ӵ`m:/u1ha3IqLr$Mq[4TqWjT!fX& |R9{tfX҉\_LǙcJ"7?:s?z> endstream endobj 339 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F22 218 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F13 66 0 R /F15 112 0 R >> endobj 337 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 339 0 R >> endobj 342 0 obj << /Filter[/FlateDecode] /Length 2578 >> stream xڝɎm䠊#.3N:H09lUYiYr$+m^0||$߾лXyG?'ٽXvwyS΄yefBiD?#noǓ e01aYvm|K&tsy߿v3e4^M+zm:*7wѱ<Ծx^ȃ~8aiebȕc6é>~~'i(7^+{dM 74D:qC0ujSɃ>tW C'>FvpkMO]{륦`b㨮c`۔ l(CˠG&'.ˁE6 7.sHnUboNBL*8Ƹ]_E2"%Z%]1ʂ $d+׮g Б\i,~K{6܂xE>$B9rFbAt6 pO@GvDuA\_@mhF*́P;0V[(c]@kQ1$!5[\z! 4^Ǖ$J =dU09GHOA$`e ~}qC=t@`>V@weX9ܵ&9Ɠ*S2a(vZI#R%YUU,W :zHB8 OGe a}|b"?/ٟU-y`| :~+goRлAeyuݢB^P ʾ15b\08 DWKԬ{AO<ᘎeFBH(82vAWIIGIDH+(E'+Ypv΃-LfZ7FUQrx9 oRq KH^Nq$IdA"n#0#t;! Ryny-ȏG+"!ìX;~̢_(G-h:48T~嬊5tEb;ιبChrf"σP_Tz d؎!]p%PgF#;=#p`oBW%iZܿc;, (_BkU8ϳkia>˗Z" JeC\J8qppW x7|Y-M$rOmwfTu5BJ#gpQ^z"[<_#W$OtGx{&e!K5xTU{ 慄fD/ov& & "'_ b6-[Pt-JgB2P+t cy)p1jALxUx5* (5dÉ|HɹJ!E1ȨAuxћ+leCf O5&}z/5"l~ib,d7Kws]Xb.dudhzn#`ƕf-,L+j 䅙Id,jLV{>n fЗQQDP48X2ƶ%W= mNl_q\Ⓠb~vrXU'RI'iҭUIMbYe<C*&ТX"܏QO $Wd"zlxl.'_58dcs 7>E& RGPUO9M쌺%}Mz%°[\~d, Q|1tj55Kʒ") iXJU/y*mέ?6u=Oɀq Ry'wPmQLH7@w1w<K9 C 4zL$ֻ }ׅ߼oa7^괍O%Śby)瑖W%h٣#帰2^_|Lct\n4+ ZQ~l_z6b{n!fMGpup)9ϻ\=7[U75UJ]zIknqiB<;ۜG3Sw~s{\|$=tw]JY.!LE{Czpo7q endstream endobj 343 0 obj << /F7 38 0 R /F13 66 0 R /F22 218 0 R /F10 53 0 R /F16 138 0 R /F21 215 0 R /F12 59 0 R /F15 112 0 R /F29 279 0 R /F8 41 0 R >> endobj 341 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 343 0 R >> endobj 346 0 obj << /Filter[/FlateDecode] /Length 1925 >> stream xY[~ϯP9o\-lPȴE.(.;!ez>O&C΅7h/`Ag"?oWi⟎r Ue](,յGRhw#j 4PV=~\, c6V!9Q0_8@ǕH}kpݞϦ]^5tYZٮD揍AjZkjWVu,kg#ЏCeOp7ֻ^fp(_Ɲ?'%.X袩`ygW!T,̊U,p 8Klߡj4B'hh: Z*#va’a/E#'0&T\$mGCERH A^w( c o$atһ}0ZL;SneѹJ(uk꺺F*T[8_,VE٪UM^ ŭ@OPosJRβ%Iջ5 b7E›ќwԜ~_=p3~FoŌj Oa^ʒ+ ji'׫qUp=o/{k1 f>JJT~RmpE<2R~e1!3@fM#Yes͎p:pvt5#irfRU˷I NG攃TItA554WCv4~ѩɪ 344D߅Wxp#(V0?$Sɝנ=}EG=/zyF?ҀL=e{mep endstream endobj 347 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R >> endobj 345 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 347 0 R >> endobj 350 0 obj << /Filter[/FlateDecode] /Length 1995 >> stream x]BMVVHsh\{t \^ %C׻pH rp8'q09bV JR$2ap:x}ի<(Y݈Ι(?l82vVneYZ"<5jU]ЩájV?  PeoE7[EI+ϴ{Tg7jجD> hWͶք;D73T&^hDx}J+΃>4_Ҷ6L5Ŵ_B}.`pST8aedu>P CkWQ*@!xN D^0[M~(pǛ?W~q9vƴ+c^Q^<,uxa3T?xc6=i`w7a"uucA5Wcգf>FBj1u8֚FfGt{5pKoHė9#J;K$ohoۚf_ݩt>EBl/xlނpǎF8g8!"GB&Lܞ ]ꢀ@@B6hmݾqVۖP t<ӝgq˨3HCKékl {^*)Ͻ m4DEPtp9"Hw1#dV, H~P(m5|q}E:|hH,tDfv۩3L:ҡۻVjණ*2IiK꟩Lܛfvtu㮭kjz Ń U4Ȝ.K<8f|2OMγ S"9LL/)O\A# 1y" ceH)KxB:eE+X.22-AA Z:!ˣ(_ౙ43_10)flLS_xW/ĦVE_%z~c~Dl~`0^Kp1~Ι 2PЎ%6 o%^fI۷t̀s~,XXncy6NඩZ@b6$O$rJ"JԨ) f Be J[} .GIhH{pR􍚄'[.՟"-l? Q 4!Wj*6D\,<%؞7Om~o uqnTu5[i~Ykd?Ě(cǶO0?4 endstream endobj 351 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F13 66 0 R /F16 138 0 R /F10 53 0 R /F12 59 0 R /F21 215 0 R /F29 279 0 R >> endobj 349 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 351 0 R >> endobj 356 0 obj << /Encoding 7 0 R /Type/Font /Subtype/Type1 /Name/F32 /FontDescriptor 355 0 R /BaseFont/JWIULW+CMTI8 /FirstChar 33 /LastChar 196 /Widths[329.2 550 877.8 816 877.8 822.9 329.2 438.9 438.9 548.6 822.9 329.2 384 329.2 548.6 548.6 548.6 548.6 548.6 548.6 548.6 548.6 548.6 548.6 548.6 329.2 329.2 329.2 822.9 548.6 548.6 822.9 796.5 754.9 768.1 809.7 727.4 700 830 796.5 412.5 562.8 824 672.6 961.1 796.5 822.9 727.4 822.9 782.3 603.5 768.1 796.5 796.5 1070.8 796.5 796.5 658.3 329.2 550 329.2 548.6 329.2 329.2 548.6 493.8 493.8 548.6 493.8 329.2 493.8 548.6 329.2 329.2 493.8 274.3 877.8 603.5 548.6 548.6 493.8 452.6 438.9 356.6 576 493.8 713.2 494.8 521.2 438.9 548.6 1097.2 548.6 548.6 548.6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 672.6 877.8 822.9 741.7 713.2 796.5 768.1 822.9 768.1 822.9 0 0 768.1 658.3 603.5 630.9 946.4 960.1 329.2 356.6 548.6 548.6 548.6 548.6 548.6 884.5 493.8 576 768.1 768.1 548.6 946.9 1056.6 822.9 274.3 548.6] >> endobj 359 0 obj << /Encoding 11 0 R /Type/Font /Subtype/Type1 /Name/F33 /FontDescriptor 358 0 R /BaseFont/JCOTZY+CMBX8 /FirstChar 33 /LastChar 196 /Widths[372.9 636.1 1020.8 612.5 1020.8 952.8 340.3 476.4 476.4 612.5 952.8 340.3 408.3 340.3 612.5 612.5 612.5 612.5 612.5 612.5 612.5 612.5 612.5 612.5 612.5 340.3 340.3 372.9 952.8 578.5 578.5 952.8 922.2 869.5 884.7 937.5 802.8 768.8 962.2 954.9 459 631.3 956.3 734.7 1159 954.9 920.1 835.4 920.1 915.3 680.6 852.1 938.5 922.2 1262.5 922.2 922.2 748.6 340.3 636.1 340.3 612.5 340.3 340.3 595.5 680.6 544.4 680.6 561.1 374.3 612.5 680.6 340.3 374.3 646.5 340.3 1020.8 680.6 612.5 680.6 646.5 506.3 483.2 476.4 680.6 646.5 884.7 646.5 646.5 544.4 612.5 1225 612.5 612.5 612.5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 734.7 1020.8 952.8 854.2 816.7 954.9 884.7 952.8 884.7 952.8 0 0 884.7 714.6 680.6 680.6 1020.8 1020.8 340.3 374.3 612.5 612.5 612.5 612.5 612.5 922.2 544.4 637.8 884.7 952.8 612.5 1107.6 1243.8 952.8 340.3 612.5] >> endobj 360 0 obj << /Filter[/FlateDecode] /Length 2283 >> stream x]۶񽿂>r0Iƙd\z}IkTItw ;_j;N_pX` fqv$f "Y,]bJ*)xw9˓fL^$|د4*: 7fp]-lWҰ78=b}oښ~-/{M:'ɴ b{Li sCb\Z?LfFr%L"#Q@uIO` Q9H䊉9H5GSEEƄgEb-Xx0'e1*8g*BquT4{CC>AY 6H~ @tGP\aMQu^2p۬8JQ3i%yh- "lnhA-v6 HWfD`.@JDlhV4n4]gjԗEEY@TnV6JvPJ$~ϺL~eefn-{KY(ր\a d4ŀJ)NkZ[u^RD9}|ŭ*STUԏQ{Ƀ#V$RԈʉLKKzi~(JX?oE}qp)<O-)\'{`\B'ȴ}10@,χ0 1=<v,$qBwݻU)B ?34n=ʁIH] n Q"ISdgeOQ 疿9@ 3N1y^ |ʉqz <"Qf,UYNrOM -H Pfi:5>xJ繖H.ѵa|CbZkIh)d[6I$yCD@vC#X=i BnSU޻7_/hZ,!вW9qMSo/w<s+oX::rfpn ",3uF4c6 $i@~?eSOg-x@ʒl~l7{6ѨC?7U !!j8Ď9.:7.!G Z(S)9hEq\6!JѶ,+h/#.jBU%) 1#^r/ʊDz+wLٹpƲ~}6Zͭ+/sIzXsT8Z| =YȰ_mi7`lMTo@c{8"ljZnP49RsŢaCvd%SĘKLz,?O\)t|V~v Mwc Kú-u K;]c~kSWm 7l -nzvrO}EbQ>dERoyqƺjiqW៏\ٽߑpH禁*¶n<*Wp+蕎9Tp_9Q+s)(W A!`Dd6oC,xOΑCkv>rDevE8 Fy m'AQRT Q[poklmG*Nm&'/{tŮf?7enxBircሀvhw@u)mcBƻ  IgI,0}x/(xRU=`( O f@={#SVerj`$maM$Aً{%wGDF~m}mvΦ&F;z)uupR?8m'뇫 endstream endobj 361 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F21 215 0 R /F16 138 0 R /F12 59 0 R /F15 112 0 R /F29 279 0 R /F8 41 0 R /F23 221 0 R /F30 286 0 R /F17 145 0 R /F32 356 0 R /F33 359 0 R /F26 242 0 R >> endobj 353 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 361 0 R >> endobj 364 0 obj << /Filter[/FlateDecode] /Length 2375 >> stream xڵn_/#R7MC6y( @zy YI]6IQۤ;$ΌEQ/0sE}Hu K}xCȂ3Qw3n/(?ˠ>>NplC6DxǮy "aʱ'Bw8iYn K؄g5u1K&c?߁4J{Y0QŠ&SXh4V]kDYjD}*50 j%=9 KR{L^uligz?ʮҮT5mvU 'VsSמ+{/gE?HSyp*+5_!_3E||Ҷ(n Ơܿm4潱L{,ʚM`xe00P] yECpO\f'[r\ =iطZ{0iih ֔Ȼڎ lF!@7 #w'|ng_ p1ڮLk+<eAD<wjq_j,T9j%qCjقCЃ2s!k"߃|(?/Ys&2I &3A2e 4q! ,iIG$r$KSeEBQ_Z+,>L Z(),*|%,";[d1pATCQ4i:4΃cIwƃ`i/'BZbِKsa$OGC W87\voͬd؆ݛNvd̜cx#cAx[ UVc)Rrf"V o/ l5#<pD 0>X⥬:LOFLzF}gY;N׫tWd5zNh "kά4GZ;ڕ =3UǶr3rAya2 ':,K? kPUnQcI~E_q,$c8Kebz56R# Mqw3{+d}g L\Ǽ!#&),D >&O rqc,1ΙŰ63:._=je-x{ \:IbEzL^~Ng|U j~e-x\st;s^?/V/Gz]H}CR O.ZbKW\ =r"rb O 5HvPa.z̥iҵ!w\<l,5zlc:>T8ݱ >Ԕm|AŇ31غ!+A65@[agԚ ڲ.Ap (XY=!\}ƺ7⢍[X^s]FTMD1&FL_f؁7ףW +tOK#WI"1X̱ H!iA.^Mx@4cČGl#Z.huUoz]d(Ů Gj{F{jk H$@]6"t](V>ͮXRBi,&w;V\wR,'' )g_`:1Z%DNYl]4>vہ+9b2w: v٥~Ф:C쑁X+:O&sdchx0!<ⒽFn<]0ya{JWXaM[^ؙtFL1oSp6 q]24o 6~0}\ ^I#~7~C%1}R375&TJ/h*7Jo7" D+an endstream endobj 365 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F21 215 0 R /F16 138 0 R /F10 53 0 R /F8 41 0 R /F30 286 0 R /F29 279 0 R /F15 112 0 R >> endobj 363 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 365 0 R >> endobj 368 0 obj << /Filter[/FlateDecode] /Length 2256 >> stream xڵYs_L,e,Ht{y%^ĝ{;,6. !V}sbAǕyy,m@P,n|XxJYnw3nvMzQ.& !kue]8YUK~BDuh1}Y=n}oߩ%*0dJ"X%v~+CZmEȸ4;_wk29.#vׯr ^Ϲ%Yدm$B(f՝t*b|~\,JѹhN}D„Kpowj\置<ɉf邏k4n|w8dڐ9(%%[SE/B\`ƾ=6^ԝo>ͥKvޛUx WE2 |r'J F^ Y@+$t%Ƌ!Es4+njz4`*cӥP9ܤc,җW3ƞTS#ʘ(R &%~MWAOg,S?G0KaB5=$g@4A %C0kUl83 r 2Nr dAd?Mk>dκQ8bvMu$AU15!z_(Zzez6U$Sl ą0a[@3)j 2x@A> `}FWi+p)=Y`ڍ.(9`[@)lm`ݴ)Ͽ1^dS&q/`S퍥([eޗOi.F(x7Ət~ 8 endstream endobj 369 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F30 286 0 R /F23 221 0 R /F22 218 0 R /F11 56 0 R /F16 138 0 R /F13 66 0 R /F8 41 0 R /F12 59 0 R /F29 279 0 R >> endobj 367 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 369 0 R >> endobj 372 0 obj << /Filter[/FlateDecode] /Length 2011 >> stream xڽَ6}B;yhi3"91ٗ,`Y2tLw["%h RaIoV@h&|2&n2A:a>w֜$P&'oI90#`O8ϙ dY}z̊WD2cKD76,$ u迫4W1?X"Cl[QW!> 0`a2Ã(p?0 : ?Ǭ~E{8.bKqm>IwwǴϛ]YmFBЀƦ4|uot `mnsbҽ7݌&ef|ժޣԯ=UwnEc?5CoEr1R&re[r~?lW{ M#>kF7HM/yO dO)('+W"r\ P#cC-qg1m_288Rwgp.)mw+ݣ aț\&ZFH1' dYyKS"o֜%}hCt"ķY=Y}:H3ѰeZIR+^-{vA#A9P0{hu\H]xn* 8pu:p"j|BMp@ )#"c+:K *tF2}Mbb# -W&L7DL3 q}lڙP stJ3@CZ墨&L;Ll)){yi C6Pk90Vh$cM[Fyi^0 e *r ;.Ǟ16Ev4Ni kҳGKUfNUʠ~ׇAuy,'{U#h۲Hȳ̅'j?a^٨6X^@ BS&1>8SŚ~',ԣ4څO^/U='kfHឞGVv'g2fN`M\m)j-l{x-iڙeAIPU'k.SR%A-$PIUeٟ*TW@r~^-sJ2\`5`(+hשdO;u)x=[>k$4Q)B4e r;O0MG4.O[$SJi#F*[`F :q#IìG=Y Ac=А#^a!KA8u$/d)>b1K $Be XC/9#q/H:$F}ԟo0y"Ly5 ±e±ek $] eq`vcRs&O)ƈ1dRzPg=%@>`wUK_N @r[xk[w"/X  ̊a:ʙuJc endstream endobj 373 0 obj << /F7 38 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F13 66 0 R /F6 35 0 R /F21 215 0 R /F8 41 0 R /F23 221 0 R /F22 218 0 R >> endobj 371 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 373 0 R >> endobj 376 0 obj << /Filter[/FlateDecode] /Length 1595 >> stream xXK6WCdd͈[M@u"JZ,z#3JV^L Ǻ uov=koesWwܱ"fs&"kͥYxXyNWkv<ǎsUjتHOhC5MZ$WMˊ~9KIK±BC_pN嫵'\Ͼ,s Mզ9g?GJ|7 &ɌVٓWCv^ن,zCL&@x / ^q`xNʊr"!NZ1[D4BLэ/Wkp&h-u<#O)ƬhFQ)80vZ.4z¾[yLiM̌ )v[1 {[VDK=wBIEiňJMA@O [U{TGvꌕݔ4OnpյUN94\ n>M;1?2d620d>b A&e0d ,n(P9, %`ne;`8*ƚ.ɑ&QjM]qgV@1mg22UqON &H}*VG##IJiqJ ?'/Z Hv9S6[' r}PtTP07 q_%,2D8es<A.wG0_ Dv_ z~WU f侁? U P" X}Io'7bjS@k" wn}٩ڮ4z0:Lނ㢤K~\NK  C[6o]]LqߊXIΤOVey[Wٙ9!;jV,WTbS&ܯ}&gE]d )ky*t{Twi2ZڰT%|XН..uD0(~;v&!"{2&~L_tu: 6hѿ3 i}]GC2_сD}0/zzYwF|g,ݻ oP7z 돫Z%=;W5;~٧+y4;FŸm[X\uPUZp.xBGֵC'&[aztqBdTP]DnR*1Ձ k  ݥ@ ܈bG %fTe+ؿK+!Ud)"P6R%fLg]|D"f3iyoR٤B s81-XIb9,PBЫ$P[|' endstream endobj 377 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F8 41 0 R /F13 66 0 R >> endobj 375 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 377 0 R >> endobj 380 0 obj << /Filter[/FlateDecode] /Length 2214 >> stream xڽYKoWb~Isn2Ar@ŀ(D $5S/6_ <]U_ڛ$iy; Ow<emwxroǠ \ D`Zȵӫa+5;pl7`7Tp/Aq@{K@ B9e;Y ֣3dFew:RbBIHQ<`oEXYN䛛ŨGJVղ IvJLqN(5BcR01zx7ߊpeD ,*a 8MC;^RR g"dL&T+j\\B3QFH)O&iFep=OFW f/NA=k7OsOkCl!uAJam׷hsgNe]3EyPkwx#efy.4'"hD'Y&rIM#,dT8ȐeSա0YV[fުD8m\X$Y?Xz ĄG&4 !lt[? +Mnu7R]y[1i] 6B14-[AUp6VaZ};6Y舷K<XR#2O4m7'~X5גOU>:R eRMSP *%8$ B#[:h|ka Bg>!-)/?j9c~@&U^PSy#&vAҰY:nq\Ac@Yԓxmm5pMd/.pfJ]`W[ R150X?Urt6?TgUzS~.Χn!׸ghps}AaAv,}zXz\MFE/ ]i^d[`C74%@O&ɕHW;(%z | d7y!HF<' c`55wYFM\ Y[wg[nXζ&b:7e*_{+j%cP:Y¼Z @)rBFkK0˥LaX`Pu&&Ό&2KPPUFQ<\O6$aXۢ-f{If9 $FD endstream endobj 381 0 obj << /F7 38 0 R /F21 215 0 R /F16 138 0 R /F10 53 0 R /F12 59 0 R /F29 279 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F31 329 0 R /F20 182 0 R >> endobj 379 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 381 0 R >> endobj 386 0 obj << /Type/Font /Subtype/Type1 /Name/F34 /FontDescriptor 385 0 R /BaseFont/FNQIVL+CMR5 /FirstChar 33 /LastChar 196 /Widths[402.8 680.6 1097.2 680.6 1097.2 1027.8 402.8 541.7 541.7 680.6 1027.8 402.8 472.2 402.8 680.6 680.6 680.6 680.6 680.6 680.6 680.6 680.6 680.6 680.6 680.6 402.8 402.8 1027.8 1027.8 1027.8 645.8 1027.8 980.6 934.7 958.3 1004.2 900 865.3 1033.4 980.6 494.5 691.7 1015.3 830.6 1188.9 980.6 1027.8 900 1027.8 969.5 750 958.3 980.6 980.6 1327.8 980.6 980.6 819.5 402.8 680.6 402.8 680.6 402.8 402.8 680.6 750 611.1 750 611.1 437.5 680.6 750 402.8 437.5 715.3 402.8 1097.2 750 680.6 750 715.3 541.7 548.6 541.7 750 715.3 958.3 715.3 715.3 611.1 680.6 1361.1 680.6 680.6 680.6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 830.6 1097.2 1027.8 911.1 888.9 980.6 958.3 1027.8 958.3 1027.8 0 0 958.3 680.6 680.6 402.8 402.8 645.8 402.8 437.5 680.6 680.6 680.6 680.6 680.6 980.6 611.1 680.6 958.3 1027.8 680.6 1177.8 1316.7 1027.8 402.8 680.6] >> endobj 389 0 obj << /Type/Font /Subtype/Type1 /Name/F35 /FontDescriptor 388 0 R /BaseFont/DBNJLE+CMMI5 /FirstChar 33 /LastChar 196 /Widths[886.4 674.7 855.3 1144.8 726 578.1 918.1 1361.1 1361.1 1361.1 1361.1 458.3 458.3 736.1 736.1 736.1 736.1 736.1 736.1 736.1 736.1 736.1 736.1 736.1 736.1 458.3 458.3 1083.3 736.1 1083.3 736.1 749 1036.1 1037 996 1109.9 1007 867.4 1064 1110.4 626.7 772.9 1138.9 955.6 1284 1075.7 1047.5 875.4 1082.2 1030 856.3 832.3 943.9 827.8 1279.2 1112.9 824.3 943.1 597.2 597.2 597.2 1361.1 1361.1 597.2 774.4 633.3 649.4 739.7 677 684 700.6 827.6 533.6 588.2 758.1 480.3 1228 880.8 702.8 739.7 658.9 671.3 670.1 563.7 846.1 722.2 1009 791.7 730.6 688.7 533.6 553.5 889.2 736.1 458.3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 832.6 1152.8 1047.5 966.7 1017.7 1110.4 1065.3 840.3 944.5 893.5 0 0 1060.6 913.3 790.6 746.9 654.2 613.5 666.7 743.8 677.1 549.8 827.6 840.3 849.8 712 666.7 831.1 726 815.2 681.6 791.7 841.7 864.6 930.6 458.3] >> endobj 390 0 obj << /Filter[/FlateDecode] /Length 2221 >> stream xYKWE\H},vOA29mu[[2$yTHzkvŒbXX^,W+ab&IY,VU"}XyXV7w==g"[fUtZ<*hW8󮬫gtW;|1 )+n/o~JTbU"iOL`(6aJYۺٵSVj#&F$gQҝOGE] iþn "$R]<*?;7TPw[d -Ẽ dΜNRcK0%\Rw3 1+2mò2 mmct"݋اL'~jjh\)z8qɄWՂzFgPW=,pMӁފWWcEwWWN<(=)>3EsmM-gӄL܂_>FOtMq4YT#TDbGL9hd}S짹`{zO LM@P80GϘM1cC>4g{nvPjɋ&}fV3_Aj 7p3̌ IE28D*RP}MqI/V+z/ ~Y(`j we1փ:MϖV@P4Nu6ܗ,<tP'f7eEH]4֋ *ZO0L-"fCEYj EJ9H Tdw` tgR0# P/AKxjfsk~3x3D)/DsXW˻ `Sl?0$ R Y|dD| "cc& h: `T0-7I87.ǛLh>kHTߗUnPr+QQ) fNdz  P/|+38d8.r12ג}yH8 t P.bsS\ #GFF{QKFySt-&~MK,A L!8Kad9/V=V2߻J}a_4. %X-0fwPcWEP*TSc"-v-w0OJ،s(WV˙b& U>omg/GԗA5r֖HpoCKb-/jWܕZv23{}%XT|-ֶ`v ot5 K˝;v#NU*K٭Xۯ0WY"G%☐v[4]^VcWg=]TN W 2x=K/9?\~U+bj>&T?0/SpLE7RθSL*mA+YK p\M %DS4lQʔM,-j&2+=CoؖU#{ rWE9*ڒZ!}̉GTNn2Y*lD޼Eoԏw!#t6 7CζᑽtהgW:jvTpqRS}3gV7Xo v|vǵeeh#J+4BÐ=95oE9&)Ч|wZhkܓf0$c%Uu~C2 <NDMy1C7?3Q rqAںk}91 V/Hi~]w=XM]9=mj7PX[:P15A?@)_H塯U2?Ekx_Τϴ;sĨk endstream endobj 391 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F16 138 0 R /F21 215 0 R /F6 35 0 R /F12 59 0 R /F22 218 0 R /F29 279 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F30 286 0 R /F34 386 0 R /F35 389 0 R /F8 41 0 R >> endobj 383 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 391 0 R >> endobj 394 0 obj << /Filter[/FlateDecode] /Length 2000 >> stream xZI6W0 琥S5}Kh*6O5=`KjNBg[.r]kmwփ(%</ۏ78nWrHly./YkjP!&N$"X4yY$>E]Ud͋?-r0f)[JK>D-~6r)vCp0T8EmUEZi ?L7Kkܯ3Ց/ˆ}`.&lLa!7-9ǩq[~B CCsdd1S&#uԳwI^6iS+1"K@{XBh~6rtYݻF1B.SJ o/lmf)c,f1^,gW=.}1pPM2 [i{Y\{fO!"̷W(&b;ǥD/[():8ĭ隳}'=Iǜ.C#`? Hea4;u;G!ceݭ¹+Y N}ٳU~S"+*{;΃7|0! "YT~;B6E#r3阽 l'"\qg??MMOL71Q7C}PNf vc5Gÿ1 _J cXңQ ӯC c@^ۋn=HcϺ2VcWq?߭rʺaF6 \EsR( RB6eO?Q/B?w[L]LP9}'#j%y $3t 8o#u&)n_bqm@ScΘ;*y?[Ԕi,uāĸJҡ9~{=))8* tKN9ez !zjoT/}ns|u;0Za ƃEL=B^h5֌59}SY_ 1Rsc^zNv@aH7˔$crM(R'uŒF i { vmED[@t<!ݴZ.w)2OgoZ.)Go o*?9 ʠh-@ƠwcYXa'lVWT粳qBӐhb&YamO 1P]\(TR 1N/Q^O)7V.=,]rTۓ*&}^D I^j_~۠0! endstream endobj 395 0 obj << /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F6 35 0 R >> endobj 393 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 395 0 R >> endobj 398 0 obj << /Filter[/FlateDecode] /Length 2213 >> stream xڭ˒PUR#x 5?<XU>dsHD*$3Sq=hI2FUtuX_ᓭ^VyJڨbu^)itU2_m>g\mg]=MOnJCr$\K8V؋ؖYS5}1rnvKt77tӂaW~[.D+1m"3k-p?z *O~RG?h*_Gyppp W.L:y_3}cEL%t?[H_a3LB˺wv*LR%/Ǫs7mrJދLWJ(1s8ĵƣoC Vfb˂ tA!gZY2?<1]KyjQo1rh05bf%3S!"9xn#+*d6y^Qs͹a VUS-49dZIj-(lNX3[J vET(H0a[,&%@UPTxx*֒cOϝ"H(@lvL? DR& Q^vmrIWXU=/ p=5D U_wk-ܵ64`o_6KAd%5+'=5'@3z6tHj 5kLJq>yFz5gRT"8uJי~I99X7ԃ0#:$wWF-電 ն}XY*Bf[%|_ CHќ4i=l.fT[r="j:֚+`ZZY-(t1ɵN\QPV潄)s;8,.`NchT‚dve6q˱dKD 6a̾(F8unJksؚ˩@g7z#l,iydoKUzl 7 n?j2dHȣPJU' j <]g츮b"u~HKTA2,Sڇ] ;^NZ3^=8.UE[ " Ib> Sz@bh[ P'] s%PS臋XκMfC 4l'a>@dxTP>q}q#!)O0_͍ߜ3{T:o*l_TFYͲqlV=,IwN] z!n~' @я ;Ugcn$ P4OKfn֕C" u}\/{W~k Τ8`py wub,+758b8r|SJ2 13)`7CСTܨL4zYXK\M,1z0AMGf TBRƊ"@<ѝu.L|B844hBeDQL]Feo4BJ}nsl[wҶat:ٙ ˾Zf>ZWӠD<6m(<6zB+@+4ɡqlf+νDCh0Xק d:>Oj\{x_èe-n]@ h;go-&yQq>kf#<.GOx̾_/0Ҹ>5_~PazFY~Cnsҳ(XT @!B[2ʕ~QTǶ]TH|6e (XB°_gX>ǚ endstream endobj 399 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F6 35 0 R /F21 215 0 R /F29 279 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F13 66 0 R >> endobj 397 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 399 0 R >> endobj 402 0 obj << /Filter[/FlateDecode] /Length 2222 >> stream xYݓ _AB ^Ul֕& ]@d0 fR'@eI~L8<)1y,8 "Y9e~S`PUNYE>yܮ")*cte]=ԏ:qiӛiehIl Ej*H!0M9#>@[J=[6%],|T!izilR[v= ǟ ;KҒ~ٖw,Ec-G\ z AS]|o6"÷5}{[֋MGtW։7ː^&8Fey4 lhnmˍ(ǽ}ʈ8,ܪNFa)M:QͶ5<yTPv(Ķ땎aIT;2`Jܑ)d̈́ XsM #Tvao&츪pE*^f@[ kz 0fB5 2IDaiP+(PFxf鞏E8I,n<'a/G-= Lmij?PW)Z~o'ffaoWM)< q0B'}N𙸤qkl "b*Y?O Dl BE!J*wD ߡ+S$P_)QVuk(^a [k/ .yO0׈j3;IYZknNkdV;E35ǚ`[" ,5 p \&ߧǗUjs8O3t+` R9ٍvM}zܹ+:X^ [bRvͣWNiuS[ul 7)#!hDfq^#pd=^A)eP2#Y!Sm ֧ޙNjۢ ef#O{T1Fcbt5.g5 9D 'Prc C Ah_J]ekm_|Jj^eF'}+45WDžt ?C J_)ԴNĒ)ln6Iͪ~ "< P_Հ#X\>9e$+]Xk_yC| cm%F%{Xsu =o"H]SΜjSl&S"!8A7Qfı$%4;+SoUMXx)tEr?ˎK 4اm}\!ʾ5lNڡ*jAC8GdQ_y>雷3KK 0r-hfXfx :Dg؁PPڋi`F28.Ԧ8o%(^ {ļ'@9 _s4>.dz,_ 6_3"Z[OĄ45goYT_5BO1Ù®X8X;S*™2/!Aqz32%|h8Q8ZbmܳZ>ճ+veoVs ְ̲:/UIľrj`~M8/2= Osn2YB$Ku7#NY o[@|W/-؄hK?k2|3p{'1BNxZ#lĨK&T˭oEjcm8omр zwd Xohiޭ^Ւk'r8E?odRlhL8 G/M!?nE8AF;( U{baUw- {/im&iCA.*rjb~&W^ZfޜqZRnpj~V};(JQuq~{- B n&t8Z${kq^]Xe^Aaeڠ뎮ɐP)V6YȜ7&?=zTԇ7ZC?> endobj 401 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 403 0 R >> endobj 406 0 obj << /Filter[/FlateDecode] /Length 2383 >> stream xڵɖ۸P 5!.!v&y32}K~hX"5\||\Qw'EP \E$V+5dJ#U'$b*1}߯~ZiI|JX~z:AybDf/ʮd #ΐ]W4^sfWV\ێ$| ?B YL(7DԈ* v˾$/8;Ӡ[ a(+Czq 7RY Dޙw~_#G<^|iSW:G )%yNvM<)aDP4-0Pjcٙன Ιs»:'i2hB0n}`滖ŸP,f{uWkĸOV<&ͯ!HB™~Q-73+a"'" م9 8Ep|FO`1 31Qe`qpec0e$c }9™g{ofIlykrv0}?TMV1ЪogPNrfjWZ?x7g?x ])u}[cNAz)dӽh/|^uH3 QH9X}uŇD%k= ;FB m'4 W P7\ɚ5 GTDL_n)iX) dT$d@FXw>E)80M0Pi\؅m,QvϾ(s gYXH.+4ʖ#7F5,3'$Cߢ gQ3xhe0zɃ2bu_SF~ pe ˚g |brPhVrf%'4CrP݃gFqI/(qTy%~3.+q+rZVyة<jA~T}.pju ~6j=)hrdR}Yʰ{ZpDg9\RJb8,*iL6xP5ҕx芭~(qGt+c6 mP odvL.l%;7Q1YG {1j6 &ZznSҗMaDXɃegw=Z6AwP H$wvyj겝>bXfdŪ6+xMl*A :d5ΣFZ5grrl8БwY2 3xnE a>r_Nv_[!(0 ;yNb~Z;[4LkOS'iJo/>  Y_uMZVu1<eap#Ƒ.ӂE8C]n5h'ս`? TH{x pp`ԃǫ+ a[ESVvzz=%z~u˕(Z U7;oc\GHx b5`~;tHqH:ἑKU9叻r̻91Ѕ`ƶaIZ4]?Eٗ\`tmy65&ޛJtwh0s^S'S;-gsm]VĻ.r4^ o&1_Kn6EkünPm\z8|9Hgڹ˭ : l1@gZWj9Ea{6-OEwW4d|B`wנ+=(:uvPS?ĿMߴܳYhsB sx@LYU A*TC QjqL> endobj 405 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 407 0 R >> endobj 410 0 obj << /Filter[/FlateDecode] /Length 2224 >> stream xZo_C,1OWl[y%V9ds?3En<$G ~G2{1+lLIŔ׳.. V;73Q.SRıчRA2M"ѶMSqHDO-~gl)RstX&I @)-(=uכufj旿JdQIH6˟J]11<[8gEoK&Qoڊ^r/o[Gzܣ:4.q/GTaiV {"l]sO!%=PVv{o&mZ>Ok c}vdg}Q*fYhAOZl"m YܰOpO> =ZsVm޵ڢb};%ruUbF4&q}v,ڙPAr`@8@\18EUdզq(;DxɣNV/DTd򨆗v6@<@?ˌ,z[0.Yh }P a=ȵs GpԄqMV|Hn+CTaۇxc0U CN:h$pCdbrTj_P";Dz#TỾ*vHSs%iC޴טخ #YfwwMnJf4gپ%gT 1큄J9 ԫ.$M"d<5kPJgPH.1"Xﵪ͔.JMe)ג-Gt҄xkp0L16Kp5} =h?,]yƛyȵ"Wv)󛉢C;ϐ+~&>υurI• 㒏6t5k[Ԥ ~pVD-;|C#TWДxUK z'J.8UahwϨ\>7hk6vx Ȍ/$ endstream endobj 411 0 obj << /F7 38 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F13 66 0 R /F31 329 0 R /F30 286 0 R >> endobj 409 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 411 0 R >> endobj 414 0 obj << /Filter[/FlateDecode] /Length 2092 >> stream xڽXKs6PjCLxYVRĵ{XoM2dB*俧 RIiMNFыEqa?^> Q$[7S+\tSwMLd)++yl-q1벴?ד΍=7}N2Zx%KgOٛb;~"eB9_/-/ˉmqϝBI\E6=o+}HW2='^t9ǛKW-o. d~ Da}l;ͅ5"}칿I^6ljOufo.Zd/oLwdķOX7[PF|wp:ح$eW߾.5o˙(wd^Tt><ʼS~x=g=ĈVBy}T8yᅡ܅a+B%Xc C3=7ѱbqȥq0OAd/˽SHV,'Kԧ@~89VOsۊ7m+]8SlICLg/%*(FOG ~;w^*9\s4!Kb_q_CbNfF! e4ԁ~zցԕ@ʶAF`).>p3 -2b 8>R YQ]j+9tUZOAei+,Ƴcek@  ~xeKStѥt"yri &Oރ߃Yw_/`|`lz=l5< g7 ^O Y%LAk Z)q;oo1n #LpE,ϛtJ 2)W5R-,,ON Ϛ-?:dxRMr> endobj 413 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 415 0 R >> endobj 418 0 obj << /Filter[/FlateDecode] /Length 2113 >> stream xڭX_o6O1{8Ok-[\{9}Np&ΌQ== _RdyflERԏWn_Z=rƊEbuX2fonUdus&~ TEI{_[~a:U5Dp]WU[v2J E̸+ Or*,𭂺|K? Ǐְ ÿ95Sƍ36kj*՚E`"֊\5[4I`k>=]xW۵H=`[w,0ESOu1HHʌfD]4S+"Q$؀sŷmi;ЦZN<AG{&+x2A0J\tH̀kbrDM6LjCCa_v]_j"NqΙ'm"4-1,7n\_dRK}_5E|y?S]RKsCW2QpgVL32xܷ44K_ QL=1!;ŦBGCR }*%jB.P$:"1CiEO s.! rXQ\4n*s[{Ԝ.6<&`2OC!z־;9g.&r4_a2?~\vR,.rG. ]LQNgNmQđ"zO6ԙ/*G8EIβt 2OS?ry2ӯ-Gu5 !mgR 0 zeѹ"x}7H7Ÿ(A9 a_7PH 9`ʾ0 ϦxQF4@]> 5шHCzR֏+a" pa;\C3∁ igEO'd+́q1xSN<3$Lmb1bMK2Bu?oA< #ܕZ^ ЦSrNHތݡ \Y0rC+6bb^,oi3?f*$ۣ迦&iKpjO OԞXoWsm "mnk2`4ilMnpEjmWoO ԶDw;e.~reCX%/E+8t PPu*h M$h· >ߴݽ‹^}aj֞\c>m]$ s%oډ+#ݰTb&BUSG ֣$8G|")e.ɣI/n>an> endobj 417 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 419 0 R >> endobj 422 0 obj << /Length 2526 /Filter/FlateDecode /Name/Im3 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 171 184] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 423 0 R >> >> >> stream xZˮ+Lf~ld7#@w1sbhQؽF˧X%փEieVMڿŬ?L.çoBw璭UVZu[hPLR>7'e|ZY<ڤUM$(֛F;D]Q:*N;q@BQA;W\yY)oQ[kwUlu&r^WE26ϹHG?׿~,G\Aw+~t1u*~|i]纪ၞ8F;{1alʯ8ĠbIsc4CjbR9Ghǂ2ش%gh^bL 2&g $ "QYL`&𣶘."4ELR7*m\GhĤ ##lHuw*e`a5UG0j,2&#t1m/h;7c"}Z TylN|*$w>q|.XKsDBZem<<+9,rs[Y9k1xpc $J'U&^z6#E{F{OXA='h{#!118Pӄ?K "3pv%}!]FVL̙A=opfM {CUf](8 H=%3 Gz_Kv"_r]Bs'KHfg]j6_3X` </)!#(&2U&CYl(Ɉ&K_M&4&RP?gh(``?2rB]hw@ IY:~ 2VYF{51@tnBcl@;hJt@F:! Dedo9P̗N֐gAyqs6TiF{"Cl!U5RF4Y'5{ެh74F(5 uV"hNΡ!Y0@:X&Ym[|)EK&>Ȫ`Mc'Gr3 Ӡ:3ܷ`uE8@vZ{ȼlMlNԥSdu[G&v7Ļo?m &Hv94qr>4ggF{r|3H4zzpAi{ؾx~ a/zI/2\ ~&PoRPm=FՂFܙuÚbڝkMJgL7;}U"maqt}mWh*{4oQaIڎD z۵ p_H-Ltn/-6bvŽ6`KiQ.-'hoٴ7iF6Ņ4HV& LkO./eU]DGz%zKmƗJE6%MSna%)ǁ#r<` ZZUK547x7PmUcE1$n> endobj 424 0 obj << /Filter[/FlateDecode] /Length 1619 >> stream xڭ]6}j}[^Ca/\ 8Nfv=`?~ԗ؎}DER$(AI=DGOHђqh1#~^FGoW/?$RHh1"*ZW )Â8_,)}I.Z#W^qVA4Eֶy]Y]֮EmGEFيc_EJ)I2g)E'D .zqGw/8HS(hOX(12?VҘ ~eGo*MVIy"KLČ10vHit6+*+#b:ԋX#hvqt-rn-Zk#kVP2uvalƠ/vY-ch^ J$#;s/2(ף :Ij>[:D R y$HCGm{zqnj q!LA'،'`29S9m>й[j.8FRQ<82p\`%Ԟ>/YU{Nw7UE^-nPĒ߬Jʂ_3 oP>k ^C3,P oj ɾY0RUf_,:@WIUxjaSW_5aX-igG -Ў}GӾK/nxFu\T&AYͩ]Ӥ{Dd GCˇ)EXva)3$F5C F5`$㪹v=!Z1ᰳ4#F D5u;RՈS8םUU/v$qZ%Q(.'p.J` {dR!"BO~3y1!&'a )<*iaD24ִ Ca-r Ak LSjwa# *") J4RwE8wX6Twi 6l$(2>))  PMW{)B)ALg#@ :mtT PJ7 2;2yKNtEd"t \ƺcbcEIW6 nFӑGr2s,G:9aOf牜 @@{HҜ:vcIgE>+62`")0dد0d,%z>䕈*8 9HgUY^̉/L.soU:}z_L],X ¾} q: .n 1i~+xd eVe Tm{#6VtLJ60~=enW7k'bk:,Tq?*E`V-xPIV? }v'ƍZep׋߽5W.fJZ0 fu-VP!Q,i[t0ȵ96 Z;@Y,K0wƞvr^v!h1_^s y,+U?| endstream endobj 425 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F16 138 0 R /F6 35 0 R >> endobj 426 0 obj << /Im3 422 0 R >> endobj 421 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 425 0 R /XObject 426 0 R >> endobj 429 0 obj << /Filter[/FlateDecode] /Length 2382 >> stream xYKoW"7W$E=L ķ ۴[ZH1*)Q1IKⳊ>71Bm.4fb9nDznm Vq8g)Rbq,eCytIT\owiE~ڦKD}joc:nov"a\ D*h@>Fa~`mc6nAR2z ԋ˔}^~#茫q&,n<Vdс>ʲRDVul/[F} A " j+c&B[5m%nM$X p ehS&Қ;cW,lQ~p*CH7~S]?vgG-ֺlv8ǧLZ;ۘ[t֢=ui+sD$wB5q;2Em6iR$DYz|t&Lr{Cw+f͸U:ݟUz8ymq/"H.Ú\1n}&%ùkB $uCRƕh+>dP(vY<-fI7 f$^\MQ}0=z˽./J, 5.U0#+b t,ﻲcn8h\f@ į3DM(]vRjH.o-DQI2 NـLfdIT=Ծ*$,@QFGh:fVGHz`ɧ@%z yK23(7nusdd($ʝoJ;_Th KK\~8`RUθit/RA`-Wm0-S"!/͔8G7K0UqJb9OcpW*I &f >3ZJDc-Bs3N2f{Z q.PI ǃR <АYmHn_'My_aЯU4,!et OxXa5Mu:ץfT+kĮƙSAzkɚ&MZs"JUt6O= 1Yey$ao`3)y:ttӌyx r>fbEU,. dB: ;R0S C ty UT'JK]1~m T-ˆ[O{xMU9tvX35Q F4䪀+%=rx ԆXf415MyX (`xܥDyi?I>< 'lVN|w!9O|.l> 4Ւj%QZR⢥uaGk"JQSܳQfxpb> Y=1zfH9<_'.MNL>aQ9.wQ0=a%<))t3=BcYxsy:q]By퀃J.U1j˪l|Z;Z黬rg`>Ba=>'`LהߎN=m[Sa!H4ք{Dx2Cm0l K;p[:b:ҡcj1=TA vPP#CϓqIҐd eoIY?8QjS?r4!IՈ2E,G0\?߻N->oi_: SGxb4t& }^>_07$t=]{wZMbfނ$R*UzĭAj[\k)j,ڋpo&{_M۷K엁qj{9wT/g"V"3ckŵ>:g 0{,O$ ar+Ӏ%ê^B.QX4K`nvޟ $ ? f\egѳ(T[|";f;YȌ uٕ"mK7;dz3iD.=-e@Ho;{Sݢ۟k|[Ưr(CXJrweZ\{` ^uF9ٛ$yr/v4Gibb . endstream endobj 430 0 obj << /F7 38 0 R /F6 35 0 R /F8 41 0 R /F10 53 0 R /F11 56 0 R /F23 221 0 R /F31 329 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R >> endobj 428 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 430 0 R >> endobj 433 0 obj << /Filter[/FlateDecode] /Length 2088 >> stream xڵYQ ~6:Kly{v+ aI4M(9dSd(H1QB$NQGTE()IџO E?"~ga%hVk.xV"7uib9Tm<.9aP]kUէ}>'GJp4v~m,%#c#eK+j &:)Rv9wpPneq=cd깬m{;9v=aEFH0[w ǮrYe] +Nʘ ݻ?{]p3oHxPF$MbGaOH2ޯVvGݮL?ڤx1-!Pm|;_~5 *y< 6"6QN*K?:/D"Vj@u,л;G,P: G3ӲQ Σ{Y3+K3/+r[0`F B$Iư4Ѕf;p ϳ| PS:*3ړ|94%,马tIQ#{tsQN\(У1`JsTʄ&سʴSE9 Qjk"`:ZI&80wT"<"m?QO$3SUȇ jZ'ؕJc{D,qZgF89 1FvFAם*/JC_ը-UDs78:5n0M#3x:6S3|K8`ثs/dPqLͣ?'H4sz߫fc%4ո*|ZA&z8zt.30~ΫZa86n* BhFR/M{DTw(vuɴ;6oIMp備Է{ 哒}崵U~1iouXF‰{C0KN)xrDs R/A$a|RJ2OIFR'iMh~L/M휈y{-y>jf-{b Ox֖KJMau C^++|~4>)儦>?54tjaE,~P"GF<>uvL!첖Z,I4P]6=͞b̞an<ug7!q% BK"D:jS٬(!itdi$@$G&TO:7wzy#;͐l6,eF d> endobj 432 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 434 0 R >> endobj 437 0 obj << /Filter[/FlateDecode] /Length 2314 >> stream xڥXKW(RU#, 'S7Y_q0$1ˇfv/IK.īu7WqE_W/"Vud[ei2_?Zj{<[otidΓכ(ѥMЛtth_?T:cզHU]5Vi:2go""32Ӊbf< *' *vGZpDHPt\2䙼`C*.wyrv>qfZ𭊗~?6'\6K<289.pX0M DzFa ߈d,Dp ]',h9F{8: ,AZídvtt6Ρ<'q}yh{;5O?Oxi;j{iš{PGPf^D5ӵƍĹC“)&i<<"i_"0K;9idޓkWkB qd˩pgh>'ؑY#ޅN|V>8)@Q<"\U![졶ljsU.\lzΌ8x^F"!?gdw_XH'# TTf@8A㴧|1pL 1Uwa3N 7CRvAش,8/e !&@0-8[ ,*Z>fc " \Y,7lzx0\> qZ^x ?ZW?sP8H-9#\t!A;sز܌~80X͠ zMV- |i$ԡQ/b_f2sxU-6L|#13('\3p(`7^Hedu Id14;߳i"ͳV z^,i68[ qX]|waڗDr>k+,}pgs|i&vd s4y qlɀڦ߁n0zj-9Us/֜M:s-R"[o$YjxY8tG#MA9UhlL~mbTi{~NF3%v}vmyH(gHDaFNNa$B"]bkU*jw*+1Ò:?7Mv 2X5'ssL<>wSA?H $^5y߰, a=)hlt} %A|įO \'nXofi#0sV%{(ޫDԅ*zrDYH,&ɗ De|b_Z{Ndd^@*l?&lb@?8N鞘 νQ 'd='J!6\mXs1@ <ĊKI'ȳm5CQ70daMiowns@(E&D? Pj!كS\7\$odC~6t~)/R}s]A0htrNNzeů 9򡜛/ (/("AIfhLzT'MÃ=V4)S<{3(]ԡ>\vDI'c쒕/3uLY0̅n~;[ ?кy4@En_gNLq0K/۹g,"H;9kWm} 7V"8 endstream endobj 438 0 obj << /F7 38 0 R /F13 66 0 R /F22 218 0 R /F8 41 0 R /F10 53 0 R /F11 56 0 R /F20 182 0 R /F23 221 0 R /F30 286 0 R >> endobj 436 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 438 0 R >> endobj 441 0 obj << /Filter[/FlateDecode] /Length 1954 >> stream xXKW0LHpNT}Rra$Έ6EHjgUn4]f} @(aI=E;4:z҄2Z+"%S"ox,O㰞3G?ƛUcY?ݮR9زƾGjwŽY]eS;o+∓??ˆd*Q'bv3E}AcD&=z>YΔ[ol]Jpš(-;OeGĩĩӉ% ޹Pw 3HUF^ermKCoC/-< 31LwHܜڅx05Cl-#w-jW]Ʒ70 Y1_.}fF)539<dgNK9*bN؟)Teߐc'.H_voak0Ǻ!-oU,DKZbM ^> ~YeKU?Ԣ^j>%5~u`` b?yT^wyƗ^3ˊ# VF_HGpt }b.@y J E| FG$+(0' W$B2S3ڧiE0[fZ$LwYDS(م`e."»mBfG5mwq;r|_Ƙ`$$?\L\vTPAJ H 3/]p.~DJ>k»^mk,8^ QOQl[dbu"ڏS@\ kuU!7¡=F'ޜ:0RvG8T䪴z(ͤp:6ch8=hC+Zߋ4)vO/K[?BdLCqԊE,qry ^ؑg@%7+]~C m#gW!*8 {W2|hD0`KP  `8qXX$_%wQf̴np >t!u.|/5s)u >`n` j}š endstream endobj 442 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F23 221 0 R /F30 286 0 R /F13 66 0 R /F12 59 0 R /F16 138 0 R /F21 215 0 R /F17 145 0 R /F28 268 0 R >> endobj 440 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 442 0 R >> endobj 445 0 obj << /Filter[/FlateDecode] /Length 1772 >> stream xڵXY6~Z6@H@nQpeVáx;á(n}583am=|M4 V ^Q|W;ۛo2oRfcYWͫ?$],yFr'TeNbfshlQ*Qe]y7,2 _1R;$  RD|\r9 2 'Xfinv,foD-;F~{#wǦ$#j%^p֧1yef✛,OMD${,zC8ةۦ[ߕ50<\[4Ӵ=uvb4o>q't֒@Xk~,*+tgDzȑ)unI-R;1Az&L&x^>q4; +Tu: :ky˓)O5ݘK'Pwe:#/6({^RIiPVvRR l5m[w84B?GuF;~eqvhOؖQdzlWłs+ g##OU@ّ2M 9wl-'+Rv4|ۛu17;Lې,gt1 r@";rVlh[!{1c9%av"@<'@pvgYp8<.5]AyPgI~MWmw)k]?~YmQalNE~<iN{4 ؅}SaMZ t=ץ.?x~ŭc(>C FyǙrfy2ȵ 6efyd쓵($;\_b_9G?/@ '@OB[r;m_ 㲈b'Y<+*IS}K2Ut ,M0? ĺ $a痢ykqy1 ɾr}!-*Xt^RA%ص#x >ʦK=`\t TH+¼YiiA[i@6:8bR絫;aMG-~1 3_EtfO49]E:v6mR@(;Hun)%l&.5N90ۚ1\M( Qt:pjSTp|h <5֒uXz’ܒgn7ޮ(uf:ᛎ~PT}kLQKjԬZh*ٳ*us}'?D9k;PPanjV3P&\G.=έeW NR9V;iEPm:?nzy#?Onb1Q1LuGjtSqHExtٿ5ԙ]2fi}~t xؘ1ݛ&y0RtWvSkvm~ endstream endobj 446 0 obj << /F7 38 0 R /F16 138 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F31 329 0 R /F13 66 0 R /F8 41 0 R >> endobj 444 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 446 0 R >> endobj 449 0 obj << /Filter[/FlateDecode] /Length 2643 >> stream xڭYK۸Wp7H) &'vU6ɤ6=r].F0HIyFt$hy<'ݍ(fqG71JcVh(%2a&9 it{s&/7,\Nu{j*cYX7}}EMe{B*N9l?MT2%XRùPzϗv+f,2'f7쐀x32 8 L$84\9 roڔ͹#Kp`jwv'M5ЗP 4X4(`q[Jfj5L=-9g):[,)lm`.#cOX#2G+kW9Y-~ij|[0 r9PmƟqvGXVuFƎGt0~t5 z\ fBGn"^ 0R)`s.Uv9:xГi i5]fɁ4FORE= ;hž$T<ݜTޗ#: }QEQXhJWY; dԅmKh9{퀙ڟiZuGG`R.aG}yDOyL]iJ N-8bVo v9B<cW,̏SS뱹lsl4VӔ4Ro&BA־|۝f.aZ+,"ێãfa7 nHwY2L38a>K1f 55af8T͝PgkrG<}EuH P1 A"g L>-% 1U X6wb<>sS}# ß0G\䌰~̀ƻPDU#0xOgfH9i j&M&txE?UVeP bB--{d)h|ҊSpv)oX#d.$`@ rvA1 ڂ4V|N*b4LIz&X QMy€^ IP=/iW/nL@gIջsG wf5^u6PO\%ԣ0e)@ȷ:K,oʺ9&= cxg) (䬳HlHIM) #aX! oGU|b|FRU)0!CHo9^r3;~߬ǘ GY{{4e,Hlj1'N Yk1lU! M0?a Fz`L,[Eɻ`w so:@(OɫT7N3? $_op?d=\=^֩lOd{:#ץ&P*Onq`OQ&89,4hN+`Ok0-[Ř2G2l :+B"tuk-i 4$L`%~D?VI؇ddD#Qv +Kc" ;&{Tσ_:Ӣٵs<: /O]/dť0m_cּi!GR#bN׾znł&#I&2s0tgT֩.~Ac&Z[;kmDWO%R+\I?D;˭UתZH00ڦ7jIZ24ІOSG 9EJ?A ,U}CaxOBw@s{C?6K =Dq05{t|F7_lo [!s6^|Z͛pmO^ IG^b'>KtmnZ @ Ȱo%ۿQћ/"VryOGn`89>vT4 B;? kLߏ/f*aLɝOCX |ﺾ0 lr9ۘkeתO̻`(zs5K|%f1]-Яm;UAo^`ޮ5ÿ}S)׎SJ)R)5~2~! wGP+cY<'{ |B8H![(\S7e6Z x׬'{:2%3}\3ӏna%,I endstream endobj 450 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F21 215 0 R /F12 59 0 R /F15 112 0 R /F29 279 0 R /F6 35 0 R /F8 41 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F13 66 0 R /F30 286 0 R >> endobj 448 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 450 0 R >> endobj 453 0 obj << /Filter[/FlateDecode] /Length 1907 >> stream xڽXK6Wa%`ňi M&=@IPp-zD QM}g8$[N@/k8 aI6z;+f+X`pINtŬ V[xy^׬ CseC2 >噡n UЃH΅Zyj X+vaQ9^1bM .vPiШ4 rm5@ /IvhG%&|2  *`8խybUuMqT"ezұaW-b^rh%/GD[Ȧ╤6~z:v(DLJey0 ortbG_+Cmmh{'2XHD"tJ1Ndd@G^N7'"]Z^q쒉q~'x!vo}|n)Ie `xf++%K7 ;n|W`{F*C=,亱 MaDу0jPq 4Q;:P+束STFZ[Q{* GȝlFvBkLʎQ@䇀ϣ'.S]fXyiQot2VN} 28%^)Yp hqa/EZKa xxzFI ]gso_76AH}p50e:ǽڐ^I9l a !X rlODθJBfNؙ Q$IB\]ώp%a{Y2!+uhHK-DSbXD#ۻQ9yuTJ4"$d<8* O}RR rnsў k6]$( EPAy(}~UpVG(Ȃ? P]B,,C ѵҀ {<.;]aI6lta [%.@7zXkw >?]X29 ]M.ٴy*B/mYRaI Ro}碐8 xO޼(mEJ2ff(h0wQɘP]IULLۉTe=z‹+'.ov}6|EqW_\%?e%çY%ϟoe띅'Iw08ՙl TrU"7o ؿN_t.8[kK`M{5ɺ'*1ZɆ %0a4$}űKCW{B{?zq \=o?6U endstream endobj 454 0 obj << /F7 38 0 R /F20 182 0 R /F15 112 0 R /F21 215 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F8 41 0 R >> endobj 452 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 454 0 R >> endobj 457 0 obj << /Filter[/FlateDecode] /Length 2568 >> stream xYKW(aʈ&>'Y&v9Wyr3<ǧݠ@{/9hh" pqߡ$ rXGq~(Wy'0_2_|et}sXU.XiWtXj5qk&eeôRj]ߵIp-wMd|&aRɳĘgRƁd:r n5e!H ?j$d2ޢ2=Ԛ yVhija~GDbEj-R9k&2tELYjS)=5o&<3FQh]ew"ýW~we}G\/T.JXX},>.Ud:MZ<";;HL@7;'J%eSw4 ;u_aO,˚g e;6ǎfF'̛YYF@$? xT 5.ӜA`E< QtCpipQ=uTպG 'q _xv@#/v& (@e%^[WLߖ}geNSP֑H!Q0Мmza\\c QE(Ļ [o-5ZLbcڰ?4 ~,x.)p;\L#FNYHqN]_,\5`W\Be1d8}D;]4 z& x`V+H@u Dj C+LA7LxzGjSY[Á) 6W!e?)L(Km45وGѴMaY L:,еrܣ*r Mzmtۖ7Ǟ:1tͱ}\1}+dn&g% !ߕ5g"iG 9)\BJޛ"s@o ;DNsG#@GYt! ۔dXyEu7.;VhX뽩i`s>@Jу )y,WE[]_u<ЙDcxC}RByo.5s& -TiKYsA,)`]LM>|! vpК) ف<_LT(lU Ht=NcU1}oڽwaIkkM0@UwJrO$v1WaMPF%#pDr ׆fAϡ!RrvؾVӃ=p@{ vMXGgC.GsMGQlRP}5F3ՉI=|$ *9%45$\Ie*W-FqAqd^ 0; mڑr8t*Fց'6HFixoqAc1YńZ ](/*D(AWe1c-`OԗMz8ZzrMӂ#;T@FgkIH-f.&U?ٌ{Ge7ՏWQiI% b驋=JnϤ ,:G!E=^KL/YA}CIZۚvT8'lOE|Nh놜?w endstream endobj 458 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F8 41 0 R /F30 286 0 R /F13 66 0 R >> endobj 456 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 458 0 R >> endobj 461 0 obj << /Filter[/FlateDecode] /Length 2540 >> stream xZY~ϯ"Z[Mku yGhDfV@~|桁V0 ,bNj~ xY$1b4bPR1%|?EƲdq-LdOxq,ec~lzy\%>/S붬Dtmab%R{,`Ϣ/Bi#i#ʆmE6.<4Hr&z. ;)?HyX$j ?ޝ6ņiTHee &aCNAl.mG?8XaC8㒂#vTx:BrWx  h^d pPKfSn˖B$+w;|ٕA#-[#+ B)KQx,J3шyX|߉ J1EA"eafwNƬ*ţxwùdp_)eft0~Y.WZ;G אCZi1`9*T3-hWҀe}ʃ>v@c? :I?,F8`!,Z} os}bog:}RgϪlCMk˶!~z{6٠rSbD,ќ:o~0>BQjI^HBҵ s(8/G^P\K6OX n𒅄~V8_fOBb5:Ө) pe氃)~ jk i0+jlpw@Bw@2sB`D:耖s'\t! 4tgsBf.e*:iܝi'zEnrk 0xCemEѶĬRÊ ueM.r}y4/5W6{P5A/%o;Rٰ.δ7w8t ORc"BkЫ3^|aZ=YSQYץ+bㇺdzO[U :ŋhE/O0I/2P ɻ +s.R)* a :*JS'k-iIeJ7ljf fAHvX$+IDXNu23Ne %f&L:@OzĮ1\K3fuȭv;D8ÈJu/0:=^ Al 6_;{}!H^ԉC܉?ICUIR)2 ŗ> endobj 460 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 462 0 R >> endobj 465 0 obj << /Filter[/FlateDecode] /Length 2552 >> stream xn_/⊤n7&A d>E!ȶj44RHEQO~0<8dHPx;/WOo硗,ַ~D?Pt}_R%y9Tyߴd8'cɔB}&f2RXEzMU5+ONDJ~A(/C˦&LF1y_Ly/7==8*:i%RFh ym`˩y\)!/8g)h2_lnWAt3ߛ!Z4)"d2\e]wxw@a1h5^\s0mؐθ-C$Խݰ TEwӴp [ #E#nX UW4UYs/ m FS B%5])>E"{.qQ}G*zaW\sy!FNRӼ )Lois/5"&]k^.L U )KT%"#6 i̓MDŠgg,!Ug/A瀃Vq~~>"`69B).=_ї,`~aůgG 8z/?!W/C.wg ssχZ y2zei0H?rwe?Us6/+G"0d/]K2nm_LenC3NWk2uOhҗww༾Z}pI @MO*0X#!D[iEwtccz)h꧆ʞeG㜆Z; n(hki qhKCgwQ@=,MP5FcJ#1Dr^GrKAg4DFWa `V牆- ДscM&ΡM*&PC7x+u#-$& ))KV0m}o(2@lGoqjTW&GruLؘM}B팠_[ɤ`(b `B ,3(QC⪧G +B7fN` ٢ Pk 5kڤנPJ!Y ko(74>Ged>pϒ`SX'KȬQ4իDS +\p@l>$8*{@qpqX5Ўu4aQ4@=2ѩ;z_+KYx3r/(.@84; E8v')U#Oı;Dpgtm4QLCOSǞiSJ. {;S*`5554옚P:+׾& 'KdCz {[L21&KHb7Z/)K;O7,8vn'_p;g{l; .2v5&7ڪC֖Nɬڥv1nksNN6Ae+PhFW i*VaSlMN3ŔоwDKH`:=-Dz.NvC.0M2S^蛸czq_ON~Ug\b -P \(19V.Y=5 Leڒt_MbNx/9~ՠ^(8K(N_xфۗTM)q6)sesvlEJ>0̱.eFvU%>b/o~N endstream endobj 466 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F30 286 0 R /F15 112 0 R /F6 35 0 R /F16 138 0 R >> endobj 464 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 466 0 R >> endobj 469 0 obj << /Filter[/FlateDecode] /Length 1904 >> stream xY[6 ~߯9Zra[ X=E(NmC-NN%$b"?J$(U_I(A`0# ~?zx0_v1"Y02d84yZY" bdz4|1$ܗ|HUoEaADTsH :/BPDWM4SӾo͍i_˧q;bD J4hFVzkw3Nv#%( E>D2܂=Fvԡ vol4Ky7"c(>)'G8 "hD<3jk&h(-4JΌ8ǩt@]-?ޮn#09ݴ2od)HP;_-9$A8S~X rrNٰ87f@Yc95S[Y܎(cpMNq`ܼ'I |ӌxd B윝էwO&T֓YS)D$Èe𼳀|^g$n'61p&4M1ޤE$R2vԚ=NgPNlJQ IAI7L|BڳbU'f#`9`U@0.=z}8q<{PwޠkPIRDLzrTF""twuѶvinmUK3EѸ_Tπƽ7*9nsoPG\XN`]~X̦eӰ*o EU\AD7U1{WIțF؂B^[Po)qXA&G<|~ ZٳM3BjQ+56㈋^62uK DƬdri%:,J:)JIFT?y=7n(*^笳a3IBg}xwt1?7KvF :EtTE9wؑpcpKσr(/8~n1Uʌx.p'7*:RCLTU {plPnyxeBOI!{+(̡„{xQnu Qn&'wY(h(+Pݒh}7C_HS6HWFr4<@PYmnAyhX.8$ʒڂnd? ՘dОݺXzm[̍(w|ʬ)}/W9J MU*X04\ LvX2Pê/\Ij[ࣖUЏ-︩oD_Zđ6/0)7,43ݒW$I릝t8WO G&@W~ŅC\CӁ92bۙ^~fGLhty la>ө؅,̀@ٗ)qVOK {VV!Úe.J4n޽=<kYbBe*ڗO';z3 (;8߷U=2<29KpT,J1w&=?B  '-U ̘;K|-SSikЎ1|vM}ȥY5אPAsK ڂȓ=іͻrHH}De?T endstream endobj 470 0 obj << /F7 38 0 R /F20 182 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F30 286 0 R /F13 66 0 R /F8 41 0 R /F6 35 0 R >> endobj 468 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 470 0 R >> endobj 473 0 obj << /Length 1233 /Filter/FlateDecode /Name/Im4 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -2 -6] /Resources<< /ProcSet[/PDF/ImageB/Text] /Font<< /A 474 0 R /R6 475 0 R >> >> >> stream xXK7_pGϒttxw713SI]=!f1k}RWJ]U_=nѿ>jlJvֻـs<妖}65qi“@`iɖFC9d˞Mqqqd `}kFҽL|2 }@rzp<G'#%x\K 4{8|l=rlPl]-j 5+%pT:U8~fPjՋ{x#{p_rZ|F7@ԽŮ{M:ahD3ha=urF;m̜"9T|e4:&Y GFU,՚q &V:eA#>Wj=9(%R]Oe'*u Pɸo,6C\N/^)+K#r@l8!߈4q@#:niwՀͽƷRBj%P n(`Ѷw:pud7> %rʞ&9/Ve<~ w&%..FȰp1 g_ۣ endstream endobj 475 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 474 0 obj << /Type/Font /Name/A /Subtype/Type3 /Encoding 476 0 R /FirstChar 0 /LastChar 4 /CharProcs<< /a4 477 0 R /a3 478 0 R /a2 479 0 R /a1 480 0 R /a0 481 0 R >> /FontBBox[0 0 72 69] /FontMatrix[1 0 0 1 0 0] /Widths[0 0 0 0 0] >> endobj 481 0 obj << /Length 199 >> stream 0 0 0 0 72 67 d1 72 0 0 67 0 0 cm BI /IM true/W 72/H 67/BPC 1/F/CCF/DP<
> ID &!Y ޿޽wnw޻]z.tK^׺~~ EI endstream endobj 480 0 obj << /Length 215 >> stream 0 0 0 0 50 69 d1 50 0 0 69 0 0 cm BI /IM true/W 50/H 69/BPC 1/F/CCF/DP<> ID & t>!WA|&o_Z_XK XKAa-Aip b:~w^p]]d#2@^/ EI endstream endobj 479 0 obj << /Length 193 >> stream 0 0 0 0 61 66 d1 61 0 0 66 0 0 cm BI /IM true/W 61/H 66/BPC 1/F/CCF/DP<> ID &%=jx}GC,?2__ 6|?_/u^t<.rO EI endstream endobj 478 0 obj << /Length 212 >> stream 0 0 0 0 67 66 d1 67 0 0 66 0 0 cm BI /IM true/W 67/H 66/BPC 1/F/CCF/DP<> ID &H?@ _[Iz^ץzz^z^ׯ}z^HdM<~7_z^ VaQȠf EI endstream endobj 477 0 obj << /Length 207 >> stream 0 0 0 0 70 66 d1 70 0 0 66 0 0 cm BI /IM true/W 70/H 66/BPC 1/F/CCF/DP<> ID &T!M ? ~߷~߿~z^/K^Z]!aUȠ5 EI endstream endobj 476 0 obj << /Type/Encoding /Differences[0/a0/a1/a2/a3/a4/a5/a6/a7/a8/a9/a10/a11/a12/a13/a14/a15/a16/a17/a18/a19/a20/a21/a22/a23/a24/a25/a26/a27/a28/a29/a30/a31/a32/a33/a34/a35/a36/a37/a38/a39/a40/a41/a42/a43/a44/a45/a46/a47/a48/a49/a50/a51/a52/a53/a54/a55/a56/a57/a58/a59/a60/a61/a62/a63/a64/a65/a66/a67/a68/a69/a70/a71/a72/a73/a74/a75/a76/a77/a78/a79/a80/a81/a82/a83/a84/a85/a86/a87/a88/a89/a90/a91/a92/a93/a94/a95/a96/a97/a98/a99/a100/a101/a102/a103/a104/a105/a106/a107/a108/a109/a110/a111/a112/a113/a114/a115/a116/a117/a118/a119/a120/a121/a122/a123/a124/a125/a126/a127/a128/a129/a130/a131/a132/a133/a134/a135/a136/a137/a138/a139/a140/a141/a142/a143/a144/a145/a146/a147/a148/a149/a150/a151/a152/a153/a154/a155/a156/a157/a158/a159/a160/a161/a162/a163/a164/a165/a166/a167/a168/a169/a170/a171/a172/a173/a174/a175/a176/a177/a178/a179/a180/a181/a182/a183/a184/a185/a186/a187/a188/a189/a190/a191/a192/a193/a194/a195/a196/a197/a198/a199/a200/a201/a202/a203/a204/a205/a206/a207/a208/a209/a210/a211/a212/a213/a214/a215/a216/a217/a218/a219/a220/a221/a222/a223/a224/a225/a226/a227/a228/a229/a230/a231/a232/a233/a234/a235/a236/a237/a238/a239/a240/a241/a242/a243/a244/a245/a246/a247/a248/a249/a250/a251/a252/a253/a254/a255] >> endobj 482 0 obj << /Filter[/FlateDecode] /Length 1681 >> stream xڭXK6W.֌=HRȥ=t{PlzW,%wCҔ5ڠ'Fe9>s/01+rfdQ";dJ*DXϷ/<3>s&Lv/vXk6Me!Y( onb\^ c]LvQjĪt`fsVi-KW6ӃÁfF|~|wxT7]ݓ`wBݍ/Ow; glaN wOY7F©sCӺñ\`e7-Rm4%UT[o]''4,Hot6=$/M&zLԚEξROqUt+Vj﹦mIvwK=XrL's4"6#+UʒT-QWbJA)Y[b8L&T a"yu0PL^B;T ԋҏ$+ T߰ Umud\PS3r;] 6 jP4vV詊4xrrfBW 4؁gg1 N;2pddh#`sk`x} w@K@:BT`|= ɮ«~xy!`;;MvX(#2%0<}~ 3 Q%+չOь 3bU:_Z@VyV=vH_٥ J) q7 Bls'odyPa ې#q8j*&/ȳ1˫4nb =U-*@< @) z$h7^6{#Gja ab(>05ӱn=BǨԬ(# 7!zM6-ݥL*=xNګd,EFھ=(`|!Mxvo--s_E,ʹ}߶= HU!N ƘR`;V yh sCACsf]^BŽ?9t s$sY0%=d7Yե4)p%Mx윖iRJMAiRJbmpAmH.3Jden9>SrW~ٌ'g h4/ 4F_߃G跷SK)T ~;RvYD%\G{ʹ';oy endstream endobj 483 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F8 41 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F21 215 0 R >> endobj 484 0 obj << /Im4 473 0 R >> endobj 472 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 483 0 R /XObject 484 0 R >> endobj 487 0 obj << /Filter[/FlateDecode] /Length 2489 >> stream xڍXKW*SM|n*d8e29`HDQ~}Y"FяEa;|O{ݥQX!NH]0n~OٮtP HI'3 &?4͂O\sWNudp۶?OBDv@#>|M AAFE[Wx}eTI&Y3^ H$I0.@ v$2 ՙ`+!-`΃MY0&{̟n6齃b2F87A.[| $LA0ψU+%POg #8x ȒER"G@B"T)q!"6ĘD}ޮuUq)U\$7m]ŃLYa0jyX' !v%0K{&-@d!fTI4i&â6͖gS"P0ؚhn5'U5/ q3#6Յ'yЏ# = B/ F-LWԢ_'E7ր(Y1J$t~k_+gY;,Na(Q|0eax¤Aq-̧Za`+l+^}U x-^Ѿ\kĵXW2M=m,ۺ˼^NIy7'}g!=!geyY5bv&+a> #w@+)aFwYMx#vŶxo<,r%yrCVŅɉ e?_4b_I{9tGrnn2SqofGgM1r-b&ˬv MgfWх-C4 ,t'UGŮiIK?cD⬪A|ji= d +/b]䠘:,D>-9sRm/zsq+*O؆X-ԝB&?P*\rQS; [iΫXH ՘} b̍#ύAd35(8uɔm 6 #HL9d fD3{DgeߞIO6_MuAscH3ԇGpyUV,u@B;7x^:4Yˢ'EyˇR7M _lNS{xҷ_/Y+<¦[oFfQ`!Ɔ T;|T}ŕS%90?Z9Hhy`R$Px g#'(# 7`#<5wLt :>C]~/HsTue]٢$ j.-3arU(@جGS,758G\Sd |0P,ˆ1pVynpkpVCݎK T^gƚ:`,0;jKP _ spzɐc(` fy#>>xw> endobj 486 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 488 0 R >> endobj 491 0 obj << /Filter[/FlateDecode] /Length 1221 >> stream xڕV9s6+8i1!v&lFNcu0I\-Pd{} D8H?IE9ĉH8Oϛ0pJQff"*/38FyiJ3w^[y_7>q4v>KGc짮e3rk^VQA7<(+L_0ֲI+wi^(sU%pgƣɇP)Yj=?I׌:ϝj$j?1t &~?{E>0!Mm=7/M\LŴ|ШVuHg5Y0iuow@6qB&#>\#7 zW$o1^ԝOmV7/Rsh_P`gջΊk3X;Wb҅p}E 1aGR̜L2 Wm+$)aljub0(mTjHA0 Izշ4^Z^`36L;[O鲞$QS*ЖNTS3l_xD0^~j(3,) ')hmx)q T7UU& 7B m ce @_~HSLBe [NFCqUeE˓_^.2$L y\'G1 U+#Xz%_R3zj42h6m[_B8=5K[y> endobj 490 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 492 0 R >> endobj 495 0 obj << /Filter[/FlateDecode] /Length 138 >> stream x=ʻ 1ާ2)'٬"ƅV|}WE&B‡D'iol-0Jx_hۓpN*&2FlqY*3YI\)*d.cwyO,ܧ۶<va&& endstream endobj 496 0 obj << /F7 38 0 R >> endobj 494 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 496 0 R >> endobj 499 0 obj << /Filter[/FlateDecode] /Length 1577 >> stream xڥWK7 W13iR@Pt{Ўͣf"5?IEQ&lV~_ӻtnjʛ&U@oG?)_eER7o7IQ:MLqi$pu3<6i {#N{\_g,Wq'b~߃Ժڡ_gU䌰(n^uj&x@xP- 9i0|I 'U.a'ILxDs1O`~|͖lKykgkr&NC ; E+ÀqD : >X-7x(c=+.oU_p*R:j`9ȏ2uzO(##G#$y%hC8x$3QV2C'hnzFWrjN-e;q98-G5ͺ[k^N&#νGϢ# -gf΀"O xI֙E]eJ&ٖеU]0!=_ ѷ ZYs^ZgI^Z&ٷ2@ZW6/]<-a(Fz>2krtzSi?0 s\ ̓w&Eرj~\ ҧ<'t񙔢MOYYbO"w=+΢^F{?~ivD[W)<MuҒ>/2=ǘa2uގ@ RϚ) = R63MV fJZ:'C,]Vi'Xz!DU>q\ i.Ԡ5Bm44?2h= ˑ $/_N.v`99G^?S"Vs"^uB]`1Ϛ '~J CN! q%X(?‡B;sac1&n__{mLŷ"k)FP9vC-7$f"M`2[9.}F'|z?cgy-NWkj]8y;e  Lȷ#&HC`] M!{\]zgpX|v"-b?pXd;W (bF SS`[Υ\$#2 "6I};5ilh X-MҢ ƲjcoK ɕCBxF}%jVw; ?)f endstream endobj 500 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F10 53 0 R /F6 35 0 R >> endobj 498 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 500 0 R >> endobj 503 0 obj << /Filter[/FlateDecode] /Length 2387 >> stream xڍ]h^dY>&)^,yʴ͞,{ ܏|Q-ߵO$g|]ۭ+CzYeTmxu^%* دկ޼WeXfHqzcmT"/qR*P<]o,~>_۸ӽ}^y`lχvm"6LMm[/x gYqHyq{St 8 - 7+<0%ƩvK^( ˔nU :5 1N3h89:{ĴzllmIW{hB'aOCOκ#m*{l< Zv1*CC.h8ƁnD^n>`:0JE[(Q Sh^v||/x8̓n;ӾDlRÇybghqL\t|J7B}2Me75jꙜ%G! cq)T1ƌZ| Z0367 sb+/d~:σHkF =dpĮmjW L>gdg`j{z3Iw<ڳؠ`ɱ`^ }x9Uz^*rIi\NtLg+].R[B娑 g^נM{5[`2TCorxZff$-97xR'+6T~TSpa߼aaUr&V d5k?exLEpv8qHN(>#TiP=Y(VU&' )$a]jq p"3ǁL*F$ Hlۡ㕄*^x# B۳l>UC'3"2n 8#XgP#~-K! #aٺ^1c+ޜb8PTAI},`_Iȫ(CpGӘ1Ztgsn)tLD8E]Df$É)N)rqyEmK0 %O,ꯘtC,3nS#o"]@ZVBjT9``;J4u(wcBZcI!X[Bvc~.24Ů*vпio" 8Sx0'[xOfL|T7M Z&"BM,|R6o %:Zc󋐎^J,AXRBtHחہt\[9&gOd8s阈kХ#]\ʍ:DPI`5XŸ!]~Q"lgFw8rPvX<=W\ sv4-"*gS' 4׾vBF<>vlPV#m@&*{F贼œ8 :? |ݧs[eҘoǺmF{$R +BjgRT^yG=~{Jfϙ sy]-xYpuҐZݛɖ9ݕ] I0o@b"=MU_!/ wIc EI=ߑbx7]X"=MÔ8`myDGg+ U WH*NJW*v2]:$I >]DNAa4?f} e/ >Ix4LBuz 1/7ϬHS \fDZNSzhdeU@Q݆:\mXIm;18k?bYZSQ wx`Uf38@&<` LxH( ߤGFfn::$j / 33wL&CU$O",eD7 c0J%p3^0.Q R,,l#h̶Q2!z/Cb'SW~/w;sE(bO4Xpxe*(!3dt榨+RE?#C+M) *1F9VYJvžFK{?{#vS;_ OL endstream endobj 504 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 502 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 504 0 R >> endobj 507 0 obj << /Filter[/FlateDecode] /Length 2145 >> stream x]BH"#kH3{H\qڗsۇ8(x6*K>"?>3R%o6ɋIqF39XY K\4b q"X, ټzˣ`El#>g6|Y%<ܗ[dCWkd7W};]%Qzd*Y"ȐJ sk3. ֟ I &sKҽ BZ[ūUlj4aAz%#j+@5U{@9#Uo_Bk;WHU=,.cyl`hn bgy #d<0%bʄe1b,kՔ~xL&&HMe% K Ĝ*afQPvjZ=Ur9ц\ȆyVaaTI_FZ'! v==J 6"!@^y;UG['o{z'Ǒ4\,e7}4+MUlbRCG)u hU,Rm3Ua#WFAVo([g*U*n64flr*W@?PK5u6#޺ 75ӯaC ZC\vrۙ/ o(^愧ń4+>zyP/jaD62qC#Nf5TF(bq3]3E8;mҡ eO ]ѣvg1jҪnU҈!{{7wYp3QW5w!#c7 I>Y$qN ǞqGܿ߆t{\hxcs4-p;VQnGQojMH-{ڟU'zz7Xw|9D(aἫ5% ;ݪi3\TD i6m,<tFu-Ũ|ij}ffy25O%N}.#4Cl+RG7oY0f1@/t~}Ԙ6}ѕKgu W@\#fh{yR/nx|ŧ#/f~` ybn'.:u^=Y b eo2>?8֛imN:p`, }7B!ea:AD>ÅMQFf}wna^upVu3^޼ݕ^`سeB. |Jmx9E♙L*-(%qkOrF)J>-f]3i͆r-)B;-:t<܋CJ.Q.\SR|2KWy+B^]H辻L8_&ا._\> endobj 506 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 508 0 R >> endobj 511 0 obj << /Length 407 /Filter/FlateDecode /Name/Im5 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 512 0 R >> >> >> stream xTN0 )|vH<0@VqN-hTU~c;~֬d=e91lsJwCj\x0!0{x2&L7"%I\I>ls`=}-':o&v\D; wk~L-9?ЧME:b[ЊgͶY#WJZzYKvKN ;fLw:UtzSяt|0¬@!"MJ~ɪ̋O;n+\q[pӛ뻠V gS`e~ 2W!ɞa5bllk=$Vi]p-%vxs:Y`U#y^EQ]KݏUY#%9g ]2h#` Ϧ3_9R endstream endobj 512 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Courier >> endobj 513 0 obj << /Length 411 /Filter/FlateDecode /Name/Im6 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 514 0 R >> >> >> stream xTKN@ )E=FA* >Ai(jb?/Pvp{Su'Q;\Sɽ:gND ,Gx{pWD&6\};D3s[D2_6B]^(^B0yV}NQ=J'I9q-iK#fo(L"(aJTz"?I'5vIFX HL@Ŧo=ˑc+{BwfݿKs2趺v]&]7@#.Z^/QY,.7=* ^;'U9Ā=93[,0)j SW)cQ9%+R-2B.C3\l dP>: endstream endobj 514 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Courier >> endobj 515 0 obj << /Filter[/FlateDecode] /Length 1405 >> stream xڕWM6 Whڋ<1$EQRҤ3SO/q\kveMt.ϰd}9+(Q2j#*~\?G%+u= &h}U"ʂ9֭4M㔖D<~V,bX߭d[wiۺۭ>yV[(osgUIdI0Ι+ZKO5iE,2)fϿD%`6z͢7}ǣ .pT2Ig}[&gCxAүl}GVC TZǶ{GaTlۆP'KǺ'iƔ)ЭGo궾} k qeYnpi&$|sU%em(}]a'6,""Gi=N#+XVwTTL̻J4hL<*j桚 Efzr?|K,tǠw .|֒#>yLPY1:D} +Of!YZ\o1ي"& MA,|s(rIV"\'7p-lbn|0&*M3^~>/4/4rP)ց&s&Ҕ*X?Air@)OX~xAϱ]`bZ.XthyY@?rU5*o @StJFN]OէooΦV=LMD:) UW38W0 @{`cвynpR) O p;͝E$/Tbp?żȎM8PDhxemN߳S Cm`à`IO^ɧ~${|a0pTzA 9`mچ$WQNqoeL.@pGlWֹi_F :^"5MY7Oq 7~z|}cM_p󡱡%iüi BO|E aPrDrïa> endobj 517 0 obj << /Im5 511 0 R /Im6 513 0 R >> endobj 510 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 516 0 R /XObject 517 0 R >> endobj 520 0 obj << /Filter[/FlateDecode] /Length 1721 >> stream xڽYKs6WpS!nivҙt:9902eq츓],("%َvp[ X8׎~A9NT8T,NH!út9?]5 sܞOܷB*> >Y͢ߴ ܼ>^x+ND)ZTv(0d9sɀhj|gZ&۬o89 "e|F ĽxЦ%r;Wmьu:ϯH}COAv:75l@Ī4Lf 7@wrtmS,2[ >Թ,i_m22}GBo~EL< Tö~Qޢy}ݯԔU> cw7]ܔ8Ow@+ 5GZWI n@V"̈beXMY6xVn<6^O{!|%yJHޢ;Ѝ%6aj Fw+ё)DBXx9K¸#8Ĭ[dwf(sf>Iv_63n?a{ݔl|ťv?| |(ǭTp>XDFY3>sN̐͘zU8|&eddG.OGiz_e|ѣ9~g|]8ވz| kѬrج' FI8ےnir:kn2}JƱNC@S{iho۞|6#>U|VMkإ+.P,d"n'w$/}$Ѵe."BI(j]g"8lġQK0M)4*lɯ6&y9a+̯ ̾{3Q4 Yy*~C10Pw9'b#=՘#5ͱth0aL8C6US ;Kf\E)`fr!мWP1>8F,Oj{+{[Oc{z(yH)ecUY.D@9~VHEh*3uAB xI‹6 >]5iylS$TdXt&<#mpF`׿8y!FU}m뼾Jj3 N&P@+״Ѯ?VMvGA'eX11S_.ꇯ endstream endobj 521 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F15 112 0 R >> endobj 519 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 521 0 R >> endobj 524 0 obj << /Filter[/FlateDecode] /Length 1375 >> stream xXKo6WCd f}l}iV )u%Ӗ$= y|r8K| Ҙ2Tb,%SN~A489ypy$yg3$Rw(Muf,4MUwCtM\V췷DdNUDB1.h%BD$ah󁖔.s_Ia/d iVLsGda|1!Y8֏MSY٘3#)g\;FIF#y2N :eWRቴۓQpnWvt.B.LqNN6sx6:~Tv2OO_q48dx<)۷GY3!t#Cx2*ZaːAS<6}$")R0X)&Ӱda :1mm$B$nRP_lPD_w$LvL t7ITX,n 13"]bZQl'N^1;ycD{C;R$iJ80ij5ԖETiJnG&M%6+b_6q(&zW^yì^(W*c( d2< \]IOC_+Bae qfVP1ִGY:@'KʮnP"J)ic\\h"eeV(YYx1IYmBBFB9IRPi& t0r ;4m>60ji#QmfSy´cT$ͮvkfjp^EK=e)ɚD_߹ []J:> @arie?Uoy!@Юdp78U8_) xӡ!۳ 8պ,7_oSfLa_`䛧ѓ܉I=bLw"I;ߎ)rJjHA>IBNJVm6?nvRUY,k7|`fkJh2’d,3 DSWDs!ӜSѧT 9v1is8:> endobj 523 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 525 0 R >> endobj 528 0 obj << /Filter[/FlateDecode] /Length 1968 >> stream xXo6_!`/2"Ϥ)>aj`RfɍnU8&ds!Cic}D#gK`]#ζ#C{u%È1/Pe#b(ՆHIPo_kp F3f hWЖ·Ղtԝ XHq@Vஔqbo{yݖ'u{a#={|\iFrkJӄZ/϶n??Z"3A%֢E!ztDy 1)+%YvhwI+h𾬵)Z#QUaqF;[(%C Q$aYo_Q`,q1 fQxXV-X<% tR̎$¥VEZDBsbnnB-#0Li!dU34>aI$Ht ű^l1pX&G+HnϊcAd`˩~fC&l_RfqN 97P&+nx4th>(WAi.aDtW#0('6ci\:LN',N.4qIO+y1 AN:kfU^k/JsDIq4@ԟ>ކMh/If{/4 9h:hf3WL &pHr\'fbf)AwrQI V r\6s\ifjT/P9EǛnO0%j w~mSQ_Tu;,!-\&CbtHp| 5Bڷ'vG7D9 ~|G˶ B{v[Ş9G(K'6Xmj:k?C_mij'HբevLW*gʣQIˬUtitlK&EB'bjJz7$~eGF5>-.؝CE.嵨h7pl~[ߙ$a; endstream endobj 529 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F21 215 0 R /F16 138 0 R /F6 35 0 R /F29 279 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F12 59 0 R >> endobj 527 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 529 0 R >> endobj 532 0 obj << /Filter[/FlateDecode] /Length 1538 >> stream xXK4+`[_5GA)8dsP<=e˻;Pte=3J8Pd[~wbKB o!croI!ü~\={z9ou=GW~*Ega?ߨmB`$v,xV6੯msӪo׫"/sGނK {̧D|$G ڞlΙL#HB8,x[Dc˺1ucа"Z]e\ 8IԝASSgƻ|3UW]54huĕfC_x]^8Uo8;mUo o8D5F LQ\; \};L۾0} "Ʉ(׺u3ރhb.Gն]mfIc~SM۔dMx4#&!jQ~ ߡH>O1P~*nYu#4ak`yiE%^8xqA,}UeML֟ɑ?A'-\¦jnnVʱƝ,}1pحPCaY x#v0))_ 6<>'׌VWk^OvB݁`@Pyʎ$5TJ΍4uoTLm]ƛ(#GwSE mչmoHtMmtm\C6h-q֮&E ݞ\AnӴ #XDI1'h4&TVO*T} 4S#%#fIȌ$H,'!t v{ eѽ6i6W*N+Ut( 1񑝍zOšX!bx )(LS2z0RaƲt4byj%\r΄'HReŔԯ/DJHZbj4Mw` e1‹8G!,#*6%}ZwI C#4^:K\8c2P0P8E3'i'Sa'0L3-$ PJGWoG5[Pu&O1wncFM'`J;nhuqK}+X4}.*Sʄ1!-ƫ4^:}/SW endstream endobj 533 0 obj << /F7 38 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F8 41 0 R /F10 53 0 R /F21 215 0 R /F23 221 0 R >> endobj 531 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 533 0 R >> endobj 536 0 obj << /Filter[/FlateDecode] /Length 2436 >> stream xYKWxCˀIz $b9L9LjYn++KInc*)Qx3CDgzZW+DA|:@raosD|DzroW<0J|a}v)bCלԬC~>ꗓJP|Tjk:"`\C^ru!: )zո_5YEߦ[L 3W}He-blayE~(`Jnມh;l^[ vGѵB/-O9&#?ӱo$.R rΒ RUS;z(P:Y]p1Pv拢;8jl;`@b,hLx <늗PzNm/Hwe*~8a)$I"dl .i61*gagw-8mݸ/ uOZDsTHg),%aNW$҃ˍ63D,`&> Z(ڨWHA3X-;:^Jc31~RT\kut^tҢ36I~GuFвBc@+nP[2R[s&6{X˳ gQ#¦nQ̢dY@rŒt+ 0z#K/Z9 F+^s#V3v3`#^ZB/A喀 ȩ*^Pe9eR.khyHmlnL * $.#AWVPTpO A$ hLCeuUs'ʉlC}z:5\iF c$%h ;䊬dwzQ?Z㷙I)uS&X@M۶ ].\ ` K8VI"a(]!DrG--_HGo.sGSX:K8a#rg}3c{]\=P| h2<< n89z+(%,{=LLEh!A2ߘh_ׄnW g;;}NˎX`JKstVvXvoBߗ `HRSM1+Җ. 8p} ] w KBR](EaǚMFA ;N$v-"FV!!3p.{ "S]SdKn Tb푆 "k×IJMyZ"EJrg:`;/` D ҭY:nIuu=MyQu YikTTALF"N:%ʂt՚w袟t6m!tI5W.ᘃԭ9x~ a.@1E/\nWn@+H#9S2JJ26u$gC1-9p~Xx|{>X(\^qS?ad5+r/,+`vҶC8n3( P~ui#w35ELDKfSaxxxqXze8fV@ b,`RAsC1R15K`Vrf3J6۱^35f'Op%ZMws-ZI|E>.w8&&Bd}]x Txʙ, 31)3mΌS*  ȄID&%@X2f"='hJ~I>LiŭҾnL8; f3ԍ.5Dc WN GÑX83vk:`ޅ endstream endobj 537 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F30 286 0 R /F21 215 0 R /F31 329 0 R /F16 138 0 R >> endobj 535 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 537 0 R >> endobj 540 0 obj << /Filter[/FlateDecode] /Length 2277 >> stream xڵٮ_6H'M9EPA@zt}Z[ yg_lg8/D͉} #svB}w,v#zxt8je_Wj,qPS~NN v<5pxt9Xpsy&NtEWenޤ* J2PM^O[tG)5&aA6IIQ…~{*&P,?5T/dmrD$G*#/Fw8k`GqK"7[@V;IN\!*]OpC/'4Z;KpGV,d|IXs6q@z G=LWBy^x"ᣇb$H- o|F0!zHqZ(NC^EW:LNtn(ծT՝bMM4!dNC,z9R2*)idM+x3ݹMHbt"TtJ¢ݺ{ "f X+R]@ʼnC6xlKF{i! $V }suj5U.Ԇ J.pB:BAaD D0p0\6M3 + 8JAKytmPc οu9GXb*[1/+$0Y0fс)]J<XVp47hOVh1mW?›<`\36+WIDA%>dd3ڕ5xXpK!N3 CU 홾V_z3[l7Vx\6r-'ORZ|+S(>nf XR } IUu>dBZ~ڥ5,:“2s*l r BVtmEW1aEaeZ*L+T^ui;| *MKCC}?yPabK^M=ڀ_ J~ 0B´Ww٤O|>mh gY0M rh*=\_0wZzK` .d.F&X`aKQ^:9d|:D>M,#OWL3l+qj:2!8=$Nil]Q4{ܺ?y~ [lP]PPpPdV(BLrW̨TRPG5?U"FR mG-9|V:CmjOOW>;97[DD,=|:?4Q_d>`9Q-cg-% oy ɚs9 cH %dwHԒjڨrPp("ĢQ"bQ4WO4E/[M_ fB7Ix-/0S# Izy[/J#djÜC=0ꝟiQJBHOP:U h6X,zlZ,#:Ŀ;?ʖhI({Pƅ­s~uo0kD嬓C \l]t!'6|3ÅTql Tl6S͑ӯpb0L6c+2<&D2=s"_~[p endstream endobj 541 0 obj << /F7 38 0 R /F11 56 0 R /F20 182 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F8 41 0 R /F21 215 0 R /F6 35 0 R /F13 66 0 R /F10 53 0 R >> endobj 539 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 541 0 R >> endobj 544 0 obj << /Filter[/FlateDecode] /Length 1946 >> stream xڵYK60 h+z1c^4C&lN^3qwǝGdPvӹ;nhh9D䂩 L‚jr*}@"f]TUN K7Y|r-KH}F#~w,t ڳ*2@ift"}uW&k+G R:Vy+on؇6ñas[i@ڝmt}4n:R[x@+|yϗogcL!P Tf4wF Z[H@埻DdO{ F0Kouɷ4v _= UD+U1!b@l eR0{N1ic{\% 2D)nr@,gtR,. :t *@k*"niEMP]ʊ" 4R,%e5cxf qc;`?ՃڗuHBh]z?{3~XNs YyJ{!թ{LȔ 3ŷ$8T3ƔVXјޘ3K, F0"R:5ZԆLzVGZ0&ZFKofOXDu64ո 9}ouѿ[fXI(cI! ~ϭ+2D;?26ʆFWףq ǙA)V6"_K(HIQ,--q)MJ&c>:_ m;op d\ի3G\^ jC!0cL#,8DY2Fb+!?oBrE5jZ\NJ^QGJ ^Z6qߪ#AnmuWG\9_@%zt ~kvHæ)t-Scu8k u4" 2{l.Ȧ ^>LNv$]5`j,驩 j̲3#S3]6#7 So==xkݹWGfI;`9jqse,获F-.;*R}ӂTȆ9p"tDyX<x3ib4Zܩt‴kMSq6[x87g,חe}eo#g2.[lnpJeB4PMTӪb}x q…-E3\O |Oڍ$!p<BXAz0}!XU^ endstream endobj 545 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F22 218 0 R /F11 56 0 R /F20 182 0 R /F8 41 0 R /F12 59 0 R /F23 221 0 R /F6 35 0 R /F17 145 0 R >> endobj 543 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 545 0 R >> endobj 548 0 obj << /Filter[/FlateDecode] /Length 2107 >> stream xYQ~6/2扤HI\H.ڇl6*K$pH{=R73t$6mFEH'2U,.JeRߢo^}E+tt8NL|E~0b)%5KnX<6]9T" ǮtnW5?%RK*m]jڦǟiܙrvzP6[ӌhǮ xר٫o9rh9+ b&U OOB,Zf](Ip2E13%dJ];84J2NZq\,HbP-: 2PE 0A!c)R8$aBD(դoH?F&HG#< X&C4H0EԘEQ84 ',:-ðENH/`z-4ݾ64g@7llhzD'gάێޭ׬͚ w{k< FA@QM8:n꿥Vo?c68Xp0k*+%;ztٕˮ\]>C]2Zu8hPCxmVuK 9ڒvym?^@V? BKPTPͿYaa/%E`@t }gޝV{h6v;R>犣GqtfޝDWi 1ROW- UNqZEL{Zy҄ön|@`oggIiYqƪpZv5 헌 n\Zg>$Ç/G7ںOU3GN]-qY/" uQtcvBAc'PY4Ng1{JhتYD@CtOL#-^ 391٫3GD*tKiәBxfd0ua4O/<9ٕCpH `C$I.%<;KTD6s^O-H*?,.0}6 `kט|<8 P& n,bz6fvrL s/ƻO٩Dq#>l+pf󢛾m-eX yN\ &;X <1<{G %5a`:G s&0gN} L?` p)I`^ t_}Ed~d#' 9"N੐koX~I+fXz v(7Hu1>}_*O 7χEXX`Hur>+]6T䌟U#4ɗ..йH0u.ⷩ_0?`w6xko~ LR endstream endobj 549 0 obj << /F7 38 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F16 138 0 R /F8 41 0 R /F12 59 0 R /F23 221 0 R /F13 66 0 R >> endobj 547 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 549 0 R >> endobj 552 0 obj << /Filter[/FlateDecode] /Length 1654 >> stream xXߏ4~⋝{Iwq [CuiRg{+ΌNܦ{!^jg<g> Yzn&^"8av^,w˗yHfL_/Fum$T7Зw/g(Xy4,"/7(91֪"5[52cڿ^;*6b-Xྫmz Ā  ҄ 9˄&P\!zngDYCu|<,msΊDVmOf,I ,dQaߓ0ʴFꪑ3#`1q:/]ek;3ŐLA4(Td?" 'FpG / (Vu|5WgS4"eiJ+Zɢ Bvh*#yMysiB/W|ȯzjˆZk=m.VƯQmP%i ٙM; Чꩆx>S-g,?ZX8 +c:dyز:]6h,+猦}e5vrE7: [$_փIߡ^$I_e aV*Wg4mUGݡ7Je_ɎFoB?_h3| o endstream endobj 553 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F11 56 0 R /F20 182 0 R /F22 218 0 R /F8 41 0 R /F13 66 0 R /F21 215 0 R /F16 138 0 R /F12 59 0 R /F29 279 0 R >> endobj 551 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 553 0 R >> endobj 556 0 obj << /Filter[/FlateDecode] /Length 1740 >> stream xYKs6WTk":3m1ig` Hʇm !bǞ@-bhϛpv;<37fY$`ff,!I4S’8ܛ4w^Ů}Y(˸#KQg7s;R3HKfy:篙7z)o沀P]ݐιjBfM{M>#,h55We+KFrYWg(Tq%Jm4U#68bYU]6:+r8(;F"h/)m/g2sz7`#_Py>juz0WifNt"9ƜCoލ>׵Ꜿx?3'd31aSUp/<(3wja(4RdEi~"qXady:b蟻R.J%PMTr- X%ʹUV-E3:AW{%JGIDwޮeamtQ˫6/R+={D$bJM~bq @@91+=p$*ẹCG UX@,!=G+ FjMqb?6@c@N"B@(XB!4pi=ƴQ:ZUmQulV7NC']8Od<]9˔3OPle ,vH+ ZTI]Nu&B{$,͋RS!Y:xXF1AhQ]a)M}'saOџo!۲$ FƘ`E˕^c++k l [)LSMl^5V^#KL 8id+;bU5;9!G?{PQ2:v3JnF[fz@7Gl}ya~EF7p/GD~t{l/>{wʹBW|哜;5kKhv5 J1Dwi2SD/mf:!يDUtEiU'o~j6Γd氮 ҹkzȥ3q=InBEFx@qs68qS7Bw174R(%+DyQRkYehi%ndZ]]F×~!f:!!z6Ι;h@{ml&xg=N}VG[%9ĺgBP 8F(q?W#Ф%Z#{Ok̺V U6׍$4LWݏ4z-)%6-Z/2ިH?7c] Gpq8T+D8 TdY.I8+cyAuWG{-w,3޲w!ܶ<}}VWTyt Vz[f~n?eZ-1z/Zk7s}U_jDݔ*J5GZ2-@&2_gb>YЍJ7{/"q7 6㵪˱{m0<,tț,u6!'v_ |R?!<R}hfD#Nh ;q%Ml(gznjxeS>K$ endstream endobj 557 0 obj << /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F8 41 0 R /F16 138 0 R /F21 215 0 R /F10 53 0 R >> endobj 555 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 557 0 R >> endobj 560 0 obj << /Filter[/FlateDecode] /Length 2268 >> stream xYo_\_ȉDM$iӦgnWJ I{PkaJCof6X AT,!PR1%2x|Տ< Rð3w懺7uyJm6N@6y{dZ64-dP* BvUVaI6)l4̦Lq7SoZ4j?6E/wLF0ǚ &7ƴ'mD8 wj;q!,7r_ߛҸ,R0r?6YycQ|}jHmPib1zE{TK5q7z֜حpȸdθO}q3s}|·q`:\/&B.M^'%b81܇Z1I;v凬Sw- J~K*~8U:>$:TxF*a< JTEZl.N&7li[W7"tMw@h]T&krP5==4S;Uw+mWUVOrh]ȷ@H&@wؼnɏlrz|K2PL&jQ};z8duJoۼjs8U%pC p۬Zmuܣ2bḰ8Bm իe*?b;iPt97nrZՎcoU9uw4hQ0%(V3oB& 7 ~O/o1m 7?d򖖠a|pk!Cq0s{S竒Lk- ۑhrޓ]S%$P&*kҰ?S 8e7&y)HcNa_$Cd'G-v<~CR)H %8)~#6p]-pћx蓌o4~vex77 nl_ͼk1R|#.Fs?S@dmVE]qY(fz@`OfFlh|vZ.9fĸxGߍԦr段x+P =BxTݠd.6,酾(`[FK$1K@@@2 # ]P*x*`$FZ+(xڣto{0t"7.)tAbL |Xy3vggr?rي@D%1#?8*Sgpv ܋0F,2܏L"f敹*LR"7<\͆Z s/qo[ 3!rlVa+T$JPxPJ$0v$$q]]o݆{3m}8r۱uwZ3GgnTw?Xf^E>jS&4@(/vG lM>uxLLV+}O"{TA;aL)Ɖpv&.u|X<3^aSS0n1r U:`J?xXk*X^}WItnb+rT_N3v9T?k=9?3:$)*$i>&X\.thE|l$B'^!\n%;"": YHZA0QH9z4PcO {[xS++6> endobj 559 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 561 0 R >> endobj 564 0 obj << /Filter[/FlateDecode] /Length 2669 >> stream xZݏ۸_aO$w{WmC6(^%Ww)і%1 g~3EĢh0O$EB.8aXY,wxEt9bA.!/(~ةCe( $50Ma<Эǥ"G_҂Q\7[K}&8b!aGbw?(@3|ِ'ҿrXta>G4ɕE4.,삻 Nڲ6/^c0mLd0/GD&0x‰ap&" t_Mp9qh)p̗p 2o13;j'|EĬ<,sۦջ3rB|ǧ6h&iv7U3"eؼo0`Q꾝x)]IqDBDpNDzH4hXMT|6^w]j.[@ˏ}$>AlIjbjjRj2jrj j}s%3n/_An* \"#pcw-*Rvm 嶝arTzv!0dv'fq .W$n }3D"仨T)tֽ M"Lf-@lPŐZwͱP5!:M*q (?G\b_i=ߜL'>LP]Fz0Nq6e%LN;5=UE-ұN+0s Lzݴv׺*kW9@r E!mi[B$t MgrwF f-wݶZSJӕpjcA5Vـ2;9 CX g)3:!lzF%cfkwxOK r6mg@]mX(h$:ȍ2X1ȇt *D oy0͘L< _ǘJ5:(f|&<12 ^u#֚ETb$1_11qGIh@1r9TUMGݶ4us~HevDf4w{\G.ȫàiS.! [<H,?e)hWoPfNlb'2|~bg`kH{i0c~eq i1S,!NIWFDtSO hzQi(`讀ڗk~~I&3[{;$ d6AkP4qSxnW8# e!ʃRV{rnPqU*nh:ϵ:vv3h4m*sgP{Cۘ)} aYÕdи[_XᵩOL#LJ :G`x]"OF>ޞa+ͽ%XVLiof&ZlDyb5cCF$0pTR{W}|uc 4[K{0d8јlؐ, 0%N &) L ւ-JJF/2ם_ Q.s{t9F^[ ʞzH/+ 9 50pf\Ae?Ok25>1"W 7JMnD7J _Ц^Z D Y 0mDl'b%g /!FVj{-FWwzS_ʭ*;Mf+eI"0 'Àh/=. 4tO|U<0< Df{Mu.o/em]WUu]~9p E endstream endobj 565 0 obj << /F7 38 0 R /F21 215 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F8 41 0 R /F10 53 0 R /F12 59 0 R /F13 66 0 R /F31 329 0 R /F30 286 0 R >> endobj 563 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 565 0 R >> endobj 568 0 obj << /Filter[/FlateDecode] /Length 1635 >> stream xYIsFWPCPz쑫Mxj [&#!dzCl^nqpH(A`0#{_z G9.xH\6aQ7Ql$ ϛ@[ѻ^IS s3Id?"rVN xIPuS\PYkLqWD J._jQh-2-lt?g(3,7[%dv\:1X.f0 10I]Xw"g*tr֬r6ϟ9RRr5e/XJD }'WukV]ʢ)5xO#41&;u0DZ1&3(XDʈ/G.:-F9[k({HSēpH޳׃C] رMVE??]@NeSI%?or6iMͧu 1MC :2Uȳq. 7BŊf@t'mѹߍI 6,Iv² v:ps9fHn"j߭> rm yi2Ro|a\wEn}1HcC׹?rR=?nJ<#>^4ћgirzN]@/! endstream endobj 569 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F8 41 0 R /F31 329 0 R /F30 286 0 R >> endobj 567 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 569 0 R >> endobj 572 0 obj << /Filter[/FlateDecode] /Length 2133 >> stream xڽYo6޿B@?")ꑭ no(POBk+dHr9Ld(3̓ n=( )PywޏW/^^JȻSR_oIimfs˥Eu_,YM.Y8[ԷMVEu|{͊$=r}~[Tz맥J^JI*Ԭ3B{#jz0u&$afbY?ɘù6.c8^.};مq"9²npOQ,MNjF#z PRKyL{5aJ"{?Mw?2lCksGG <3;AnG~K,ْE Dy"PGG:kڔ-nMez9CؿkgkWg`ԇNDTd-E6ǿFʼnpQHɰiLFYH\@.=N/=K>`ehM!~뀊2 ]޹Bފ$0#ϣtB8H&)=cn, K 5p*G})IRTW4!޹x*xPwd^yxxI&Y&:Y>5Ͱp[ 6lȬFLȐ9 # ( ,jc~ lT!(-Ū4c'LĔ xy"]w٤.$NOYT,bdXL\T;u~&`a ( r;bBdk([ThǗVQSv|g{.ð4\Vuvm-1I&:= &|(Hܗ+~,x}dylMՌTBk9rܗ$xȔiYؖ,=@:IƦGB>e]A0  ǾvrxwO!z2^7Ug^'R!au@ju񱶜PklL䙾[{Zm?ftײX_`C:`'TDL_LV e!Y8'""sdgi9~g*:ihup =? ΈeOU$ K̇Le=M0况|%=Q|{ܘpAgڌVݲ쿺՛7浾o'?鄞$&)gTg#,"äM&1(nI,ƚINЌؽ4TnS%> endobj 571 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 573 0 R >> endobj 578 0 obj << /Type/Font /Subtype/Type1 /Name/F36 /FontDescriptor 577 0 R /BaseFont/VDEOUV+CMEX10 /FirstChar 33 /LastChar 196 /Widths[791.7 583.3 583.3 638.9 638.9 638.9 638.9 805.6 805.6 805.6 805.6 1277.8 1277.8 811.1 811.1 875 875 666.7 666.7 666.7 666.7 666.7 666.7 888.9 888.9 888.9 888.9 888.9 888.9 888.9 666.7 875 875 875 875 611.1 611.1 833.3 1111.1 472.2 555.6 1111.1 1511.1 1111.1 1511.1 1111.1 1511.1 1055.6 944.4 472.2 833.3 833.3 833.3 833.3 833.3 1444.4 1277.8 555.6 1111.1 1111.1 1111.1 1111.1 1111.1 944.4 1277.8 555.6 1000 1444.4 555.6 1000 1444.4 472.2 472.2 527.8 527.8 527.8 527.8 666.7 666.7 1000 1000 1000 1000 1055.6 1055.6 1055.6 777.8 666.7 666.7 450 450 450 450 777.8 777.8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 458.3 458.3 416.7 416.7 472.2 472.2 472.2 472.2 583.3 583.3 0 0 472.2 472.2 333.3 555.6 577.8 577.8 597.2 597.2 736.1 736.1 527.8 527.8 583.3 583.3 583.3 583.3 750 750 750 750 1044.4 1044.4 791.7 777.8] >> endobj 579 0 obj << /Filter[/FlateDecode] /Length 2087 >> stream xZ_۸][4m@ ܾe@kem+˻ 7CR2%ʲw{i>3Ù!9DQp.#`*$X D{h$v>%, &|]M$ y5r%I6[Xی儉P/ڭNޞ㇟T+ǜHDy"L B/r`yݭLGK80,\euUr+VčHBdʹ8%<4bkLu1QԮhjCf…Xj1IZJ\ 1CrK#߳cێE;ʳd"s';?3J=V o'QFxbuXֺ Iڈ^Yph, R&w+bb2LXQ+CVz S"hO+7C"$SD~yսII#yiA v},FofVY\&HG8>I;Є3:1.œ~Gm?=Mؖ ~緿QAy? !nVpѣBG?#QF-<>Scb#Bm2 9 ^yGEI ͌ks4u:g @xifBϦ'HCT;̼pl|%o'\C`\vabvrq 殾=LK.=n9m$?|={^~5R~;#2OM,+=wT{!w2zfO77)JtL|YG#V:XQJFPSN~v'jdY%h@ҥ'@JK팦R+= GѝfH8鱂Ex -q‰ɘı`T ~B`!`&N~w ܉ȃ TXzQ yB: @XF u߷}u;y0KJ>4Y0)$CbUg Yyˮ3Ě6iۉbLЖfׁVCPC=g$̷fjM|R畾Ng"EՆִڴ+s^\0fBAOWok;¨WnpQ--̲lrZRkEUj'QXk<5^~6ɗfxUs]2G?|4e~7\kjUpE6UQ=f|hόD3g2֕|P)< < R޻:N}4RC)/ Yd?nPMZ݁N}ݡ&_AC +`?q1 endstream endobj 580 0 obj << /F10 53 0 R /F7 38 0 R /F15 112 0 R /F29 279 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F36 578 0 R /F12 59 0 R /F35 389 0 R /F13 66 0 R >> endobj 575 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 580 0 R >> endobj 583 0 obj << /Filter[/FlateDecode] /Length 2548 >> stream xڥYK۸Wh 2rm9;SV9pHHbǧ_ )^D4\庫>Oz\EJZWҾW}RFM?S~N]4qf;\ pZGQ~&/W~zd}y^z* h?Þ&g/֡BSfL?Pa 3W?5<P*m0&rϱ)݄S(GPI@Nm/UEW0UUACOshׁDQx)FQO{1?MU|'ڂkOW0؃ϔXX4W49ԷfotwW%lni rJNgt8?5`u'w%HJX͓isXBMaj\ɪSW &29"!1""!p=@a!ծZb+{{:[ߛTisujg}yXZ>*..GtpNY^,xcm.n,; ^xܳ; >/V:dͷ߇fK!֡<ĢmE`yi*[q 6'Mw3#yI'+?hf8]f8` #䵡_|OzZġg`+BYw_^ <=.]yUe1cU1D0M=S#j>+b\[%q+-}D{sߟ@ N~;X0a!z8:ro<;By3GY$;;D~= =מT);UCqgڸPttI1VyBWvbo?޼#ޭsy}0JLO,0p endstream endobj 584 0 obj << /F7 38 0 R /F11 56 0 R /F20 182 0 R /F22 218 0 R /F23 221 0 R /F30 286 0 R /F13 66 0 R /F16 138 0 R /F8 41 0 R >> endobj 582 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 584 0 R >> endobj 587 0 obj << /Filter[/FlateDecode] /Length 1686 >> stream xYIoFWAg9ԉR4z F6T(ʮpićBr{c֪?ó,Y!L2gGV"ߚ̺Ț˾dUQiu'B=ɯWAw?\& uPVAnD(bas'*-fv~>ҩcx0CT'v(+s qg1MB~;$!.EGݖ л$& %hhڰ=B#!4J4j4Dmv$CbhA^63flԩ5m+#G>"vv I:;?~Rǐzeg5j#Ҏ:D2q0b c~b$β=rp\?f{HE#-zx-xAC¬zmZi I!~wqb%=ƪ@r#57T3Hz 49Mwj eϜ^H)!5Dlmm' "`j% 䮙%FWb@ȧ _\*Y@>fQ).c6/6 0rcuǛ+F f m0/*4Ee RVweZªDX4r$9MbFg;{IyIySkOGԴ[Dv6IA]x1m9s17A?i endstream endobj 588 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F16 138 0 R >> endobj 586 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 588 0 R >> endobj 591 0 obj << /Filter[/FlateDecode] /Length 1999 >> stream xɎp&Wklɋ<i th\.cTt|#Eɖ5=d&"Y^л# !˅ xYw}sYz7=b8wX]Y/1n/!dѬB?DˆOIU\*W4 ] mLm55 _kD GB?б+}y˓_?$FtL' 9KlD!DX+T[bVX.}UOFj8{. [v^b-BMt=,Kcz0>^O2kG][}[Sn j?^O?ҲGYLĎFe{n/v5{^( @nw pI+ьd%$CBzCYiWLKZN4vmt~!Ƌ3{ b5}h Y?M{5 DB+mq>ܚ&EלўSIii7ʥo1$ woz@ }Tn}؈c.hXnd=iee X k]Vڼ2݉o}*l,Q47c? n6b﯆)+\&xʵpnϯsyU^ q"^BI*-&қj&}Ozv>d TukA\?}׳?»J4~1̄)rw4wA΃Sf^f'1 !f5 ~ #2~s(u4wݪa4#V{"Y' GW SC5H7V$u`3 #0#ap4 h'5 n9DRr]OUo:q[͕Ē?V=1R mYTYr`l@E|^AdNbiPZÓDa.N $O'2IzO_CS3SdFD ,lހQ\:zI3' {F7׎~4WvCR#}FW@uo{$fe@}ypl!d͌|h5 endstream endobj 592 0 obj << /F7 38 0 R /F20 182 0 R /F30 286 0 R /F22 218 0 R /F23 221 0 R /F11 56 0 R /F8 41 0 R /F21 215 0 R >> endobj 590 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 592 0 R >> endobj 595 0 obj << /Filter[/FlateDecode] /Length 2166 >> stream xXKoW\Oilm lAnɶj#q~}X$n3="'Q$_}%IOQsh+KxLro_n&Qr0.\sa ,;ڢU}}>@SMP54&j"}ևcS4L۵ˮw/ a쏻-jj)˕Usxq,kTM9en+}=b޹s,8x*Ƶ6EY8)~-ϗqj=Up3cy"g1E5q xڐ!TKsU:iYeW&x~[ 2Z:dDl/]D-{TEOec d*B{)-;#Ⱦ#桺 F@3{!wDᆭ^4XcAƷ;Kv8C /]!#e8 %PmFvHW  '!4nضƃBM29KsǜA$T,˜[IϽ/I㉉/,fZ|E7v4|6Gr+by65'ⷬ?a_=WA< */U-n>!6F{sv`p ʍ?)TzP|C=ʺx*LK[j㽈ީFY5A5q+rcՏU'80$tȋy8>~WeHJa} UT3*j]%$azty U]CgajxGkeSnJ>mkv}UWe$FPy&p7aP ON؁=EnWY]aaBkH4 I9 NHBgNlHo# "?Q޹EKAAf13n CxBY^CLH\|'4@x%W00GCK4x# }G~mbtD(脳)/0C^N.@GxY(G&Q۔o}2ӷw`\Z2(8L9rdGfCt7&Ø5JziQ:=7BpE!C{=6Og"0wŚHsvc^wMC8¡@s;e) 9/R>#~>Iiġ6WҘ]89L̟zf"J6jb&૒L"kᢜv*D3'" AvM>U& ̥"8]/p^tyE~h-r m6/`gV%DONL.ۃeKs\9p>&-/]Kw"6v~3g>AxjYCi535Nsd^b,D^ruKȻZ6ͿNC(ªoo _L/ E|=k_0jL -cqۿnM:.|aJIORʏ7/ endstream endobj 596 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F15 112 0 R /F22 218 0 R /F29 279 0 R /F36 578 0 R /F6 35 0 R /F12 59 0 R >> endobj 594 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 596 0 R >> endobj 599 0 obj << /Filter[/FlateDecode] /Length 1786 >> stream xWYoF~[)ڐhi]pШH8kr-!9Vi*q"틸;;3;;7#9'YPxҩ(~_:g/c'in쾐z&^^l7bbE{YmK}a!ct_ͺUUŻ^B"9ZBƎGaOᅾ#&!p/X^IH%R"0w3;@aH$"=xf IMf t$>)Ȟ!,Br0|' w#ڦdB׷CFf^/s4|x;>Z[RA[{UHV$V$tw(@m5* :dM[]ox5q \06eW NB3vC.1 hb^^)Ͻ$:3| &?n-F DH\knOD ˆ"SHPm2#g3,qV=8 ;v-$Gݢ>zx߂>$KG3j2>ܺ3:cjq$.q*7o{>{{5 'ŏܾ~4]oBMB%9Ip``$oتЫ擪brl{˖@ 0+r(i|)=.6\wV@3\ou}|K W(;A+7:HYjP+*JrVl* q.k 로ic$@F ћ:?wMe/jJ ylfMAG0_py2v8|eC 4o T@sBUhsDPҡC9+JxuYqNR> (le TWRwSOtv:O1$Fmyߞ{c o_IyEOvpz aٶo(0yqsn5^`piV9Ê"݂4N F`mf0{tadDGx9z|sS[jmv !*"[DŽ4*\ٛ&lvfѴ;1AЈs'C)TR?54XgWR endstream endobj 600 0 obj << /F7 38 0 R /F15 112 0 R /F36 578 0 R /F6 35 0 R /F12 59 0 R /F13 66 0 R >> endobj 598 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 600 0 R >> endobj 603 0 obj << /Filter[/FlateDecode] /Length 1895 >> stream xڍXK8ϯpI]Zo){d'a.ӷ=%fH%_|pۻ!<v>sT*w/]RaQD_ASӰ(R/v" OUL}Ыiq}1qUXKQ9 #$_kZ쪰, rϒP3Ib{f4H#r>".ZiT]gZ՟&u#yƅtj4,hu$3{7l{Ye0^Xea=`q)iDΫ#S2؜)rJF*˝INa._LhJq-9".,O48n̚٪%&]&{azꟗQ[KB^n\ bZW'{}ʈN060`\U,ҏqLºBnElpY3u=yF:"8g| V}D9asPF zκ$t;Q 97UN7&jS(RVˢUbe?W S$~w!eehն}uס[${)[SLْ̉Ygu{ K qԭ@&'8i&7)|R۲Y=ʢME<&u|zrQϹKF[\X]܌]J*,bx',_%BVt_k!aUo8>Il7x]U(,(uxU-bj`rSbPhN >yŝ %3A'<u,A7}}d|,x[ZXΙ. fK|*8*xpZ%y3t!v7Dz/G whm(?p󼇗h(`/$Ƙw=Htv:G:lNVW;K `Q׭2L:5Ca׳Τv#qhAU㇬3}8(bF4U(L5)B(DQd0v쳐x!  +99IkmEmN4~4jx 'U*.J5ր^@ V_+A"Aw ?j,&9c`(Eqզ砅ig<|l,/nTݐBUTMGS IIgD̶íYD8(ٜl Q+ne_$8Tt"$7z*>[cv+l8 moWOנq9C$Ne?Zqhj$(ҥ$qJ^ L-`g}?^2}= X@puʻ"r½v ^`07.tyIo np:iܨs՟9t׭O h.ޞfōҬܜ-qPӲ*89TWBݬ9.1^,֗p "ԛo+qʹ?wl\f=S&B_>M,)1h{TzzR5؂?M7Ϝgcb %>~ KJX ݃0ZѐGEW+xoTG@0 ^3|אwdH5__l}_p_~, endstream endobj 604 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F6 35 0 R /F10 53 0 R >> endobj 602 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 604 0 R >> endobj 607 0 obj << /Filter[/FlateDecode] /Length 2416 >> stream xڅXIW|1U%$;xǗI-/$\7ߧ7P 4@U'긢+Vo2l͋8Q~gyV~|cj\4V_ަeч>OƭYE9~Xo˲>kC;qH{ԟ[UG~Y*2<ۄ_~]%4mAd-$6UpfC;=g}46W Lێno#vhvZ Id^U ;5N8;ctC1^-n6M Zϒ4]LCG{Pꮓ#Zi}1̳{ex]14xx[Úڙpώϭq>^@>ۢ~7Qtqkǚ`WTFJہFc~6&;AhRG6(龗Nvpc7gN8s&`Cݑ&<7#^V#qrgs.x$DH9NsύˈFk`X܃>X*IXW `&,F4Xހ.!=l8)B>e4綇P*9JpOi&+zFBa Sгp>: H+^~{_Ven$F : YVL)O/Z&CdM'M _3 Ȱ\Gr7^Ɇ ɢn`M/g08DRy$ihB~d]*_Btm$m=/9n! ĹmPON:Vx4 k! aٖa,=GMbl';jn cO ,4LrE{#[C_nӘFj{^nNƺ)02Ǭt5V:/f|-i,-R,%q,)q@,9xqCKEXmX1iijE>q­a䡱T_*bby+dM\¿; r'4BxWayW9WlKpƔT&R(퍰`PA];IQr1>X]EI1P: ǩ$2$UR&9P>kb]{e(&4J9`rF]rOҹ',_}> endobj 606 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 608 0 R >> endobj 611 0 obj << /Filter[/FlateDecode] /Length 1995 >> stream xڵYIs6W!Ԍ %;%iiO STRg"ٍD,o۾9J"pV7w( ,p<4 p?$A(7,% m7;xl;|=aӴ?qs_O"g1- ;WpMm/ߥ U Z:[ ĵ.r}ApٜeY:5/6`@J~F{,~:o^v𝤿BɁ)2 eJ(̭Jo &n6z7z4%ߑjV 3.?n2z㦠$48KbxÖzKlOtT};9<˧C-;wB9$A $,cAh,gwЅqS$ /anc`ZPGQRN8ܹ32! f"xx!"xfQƺN]UhvPjNrAܜ#qrag<ʛqPr5l'p nK/V^jQ]b!8A2%!$5 Xzao0 J.Ix'&iDE(9buL5ճMcr?K31X5ũ`!#CV7- mx S&ڴ{mc]kUcS~rv%mgKTJ.^yQWV cPmQע!JdY@QDV5$ͣ+A (kPs+S`BT(Zϒ+nb L+J/74R[#Zsur!I~*mGV_R9fEv[2he髊":k4ˬ涼:R 1WM d!Q4|K.Rԑ(FpG%zxL Y5 .TR{i bl4fPlbF9 v@H-Ʋ46jͫTMK<_'8W]dQ R6['o}w#~+IgUЗYA[4!%׵C(LP0  , 3AK%ynmH6o? GS6k`>ʬ30}TFy؜V(B1DH}"C_sN6(nMw\vg¹EgRNbqk[K;,'TIbU%Z^[3F 2c! 7 \ qd("sB 8~r ]nwټƟ0M:PhN˳l 6o⛪nʬ?pP9\/ Ϭ/𰩋ٽ| >cyěUrOdҌ*{Y{}mz٧`JY#IzmBל_y \l?>ɷťBގG(}GV#'W`؛l@f RkSTs{ +iL&qg﫽q9o-ȭ_-YC]ҤOPȦn]uhg%I&g} BfX[-+ÞWy _PUٝ^|qGHf4_ԺtL/DƪIla\Q_LR+~G9#`W\LmŽe .YF MAX(CޅlNiD/rM?;(%Z_mw LixfIEk-+ әzxrc7¯8 endstream endobj 612 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 610 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 612 0 R >> endobj 615 0 obj << /Filter[/FlateDecode] /Length 2148 >> stream xɎP:PUCwy<m:*H[y^Yf6>YmmVr' i,vWg6_W1|?W10nhzY8$FT뺖Gv zu.&+$%n%ŎxZoxd*i5-A 7sEt^mWM4p5U&ZTN LY^ybOSq{VnR=@PM?аWLbjXqXKN~9VVИ1mJ}@Y~XֹIs+ꆖ:͹R@_*;f-/Hp K.CRsEH>ME]og) -s]v8s#$z9 v|Wmks<˙;NVc9jq? .f^<*_wCjO:m@83'dw|y -ЁᚺHiӨ=$ F_ U~?4hȟtK>Ef(Eƥ 7baYw5F$;w@́Ziӧ!gQhjJ8H&11-Rwm o2dd ;z5ńu_%T?3 !!>=4WяY<DHv;8kp^;N\4E̤ւeZ;84bp2*+ՙnB+lʜ3>>o|=!NJ!͉/1yC}|TcQE۹T@u#dU?V\7vs_<֡]A! pov1ymHHHg숅 Օ6G`*`xl?:Zd(2(3̨eyXG#TS/:;g.ZsbNMPWA[P_Ǭ ~J* : ݆0t`^W>=5|͠/j@TU>Ͱ8KxNJ{[?O{,č6С6y*o&k:TBLC3+l0)tcn)Q7Uhz>J)[J.zL9Dk:ڶT<<|I*q4ctb&#bZw&CUr>s%F6?A',o4Yv <> }#NaKW-YbߐE0>uhD0 _ &CzUIo$UdCBF ;9`1 GX ll/*1Y!戍(F""ȓZ-AZ/JpÕbOA'x*$6ce ,#vPN", 5(Pz\g;f1~X;ɔI˸SY܆2+Jܚn9dh0uYCWEՄ(iFCQwЗckհJpd<ШjsEҊIujﷰ,b??UooCnqqhRȺそ'ɬ* gb-jQ}U tBY2SuC[tqV6u/Þ![UCJ }%!F?$!H,tHeN~)e= n'RaQ]CQo. }Y}ftXGzJ* #kT_2%7g8ZtLP xS)ҵ樀WO ^wl>kA`'m.q: 6mG1uiP)/%{$0RfÂSc|ʝg>ǞhajZ/^<,ev FZ/h%ʹ^΋~VD endstream endobj 616 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F31 329 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F21 215 0 R >> endobj 614 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 616 0 R >> endobj 619 0 obj << /Filter[/FlateDecode] /Length 2227 >> stream xڭے8:EGkI2K. EU T}?L$*41p؈qi6M,-~۲~uY]Oww7M|COʹ޷Z?4`"2Y4xZ,Џ"QдCIK ګz8>#@f D\0YOp LY[>3`4/^n@J-iio{PHݽ]4]mv5@UBs3XEӯ#H{NfA90GRIEnePr3 PC%E't?Wt2AI9;5SgKLy`[)&L YBPa7(&AnF+>$h1ZYV~3.[%r1\$}<{X`W) YpIYs-gFϜlm3{esiuM=׏0^ow@!%! /+ܭG1y @,Ex%6&q_1T!Gg̵c̏柯zBm8bY^ʠcn{%C89&scmFL5r#~?#gfk!La36!X"h٫qMme+mްH+ GQRMa?ү6h7ZTP4Pd2ˁ3d֮eD!-8eT MCەki$I=vxd,DW<7&"Jz3^YpZms]WtZآ{)a`2HvtDjOo,(CQ|lH&WE6:ajB:# yh7hGkPU 2מ3MM=g% >T~֚DْT蝶 0٪}gT(`Z͎-5X-heb KnxWfeLCVdlfsdf*6Ɍ%3#|՚~*Q3IhH07Msha'U$%WVC2CTxi:pAa uOS[k`qNJ]۠c. FDBnˉP}hg tT6It7AL\bKҠLtz{C[m-S]WSvc1rpz ( 98+'8li gS0^np.GqA~m>R^oP/.43 ,]cʔM? [*;ݝE*ǥP%B2W s!(7zܔnnJQyV#̧Špp$9^àyoPTwWGOƑc0],pq$ң^k5tn zq< |I[K= Ͱ|y4pv2F{aٱ.Y˸ xExghYM@ *9avt+ÈK͸֏hR5O4?SV/K endstream endobj 620 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F22 218 0 R /F23 221 0 R /F30 286 0 R /F8 41 0 R /F13 66 0 R >> endobj 618 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 620 0 R >> endobj 623 0 obj << /Filter[/FlateDecode] /Length 2259 >> stream x]#E^ƨ4߹&zW,nQƲ;"5=MHERE+7|CzZ屨6DVU"U~ݬ~XwŪUzRjDUYZ7 avhv#Q [Lzme E9ILlǁ&?L'ӢFA:H*Y8+BdFK%ulxuL^66`N-Sd~}&J#>]&JT)oB\8P_.W"7=doob|BWm[:,iʋ𚼟{.J-9^ڶ .q,O%"YE YK@7 yo4eE 2}_3Lz8v4q?ݍ5MX[RDAr*skf7y+7o!*[-gJ%}s*n/yF*JLi\A(4M9ɶ : SpXRJd !Ӧ?Ͻz{+Jy^Ap&cL㑦ݞFÆ]QqQR\網*E^Ã6ðMpF&$B\B$yp<;<ns%I!epJ(QT /~$mҠzZh<&2V_J/&'ȝ xO"4xDž*̫VV}b;')O)HY1!4g+VQJe5 5lDyUl{we| NYJDf_hR*:+U,d˂JW 낉LXizޮ;Q#  W!{Bt=nH@?# vݍG3aS/ R3fﴸė4"t74 H =Y껤4CA1s V*~{ĉN&,,cxң֪'z؃s3\u=fm=Y^ 蹫*KWL>RT%z3)jK/7hgYc0,Cw-Q^U)78Wчd\F.Xuߴ+%Oڻ6,J'8;Ir"^ّ8 0S,PPȡ5䄻;IQF*+é[DE5!|vSkE"\ *18tnlmVhFA@g]C9wyi`EK4[qD\&]U2@huɕ-Xb\TCoe]*` >rwDuŴL$1m7agbAs5.]~mĆ endstream endobj 624 0 obj << /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F10 53 0 R /F13 66 0 R >> endobj 622 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 624 0 R >> endobj 627 0 obj << /Filter[/FlateDecode] /Length 2512 >> stream xڵY[~pчH궧I9E)p [\^%Gݳ3%='($^37*eizZᓯ^VEjȜ|uZI!߬>}jV!Wۿ;hvFea|m46dݱ[i /l2P`XJ]j7\L#dZ9^%Wy^2Ѵ`CL@{M[ƣkv7U [ּHcW$t-m8a)|U/]mչsՕߠd"sl {liA cܠOp Ag9ӘNg+ (w>B'RJs#; zNjireC,Gb]ݦۼ/`8r'eځm7MDCY0қq-uoI|[[$E">_'4]N7w8{ y >̵ZiOđA`JA 5RK H][btԫuvjfZpG;;ށU8ףO%_0 {ݻ恾y6r>(E0Cq5~Xe4=ZۇΆh\:uL_XOT 4HqĒU.2V΅>qP)ȋSVRl+B`\7|&j& JsG.ݓVd r%Y!+#E]?M^k8"(UzNJ$Tw+9w_+fԥЊ$)BT,74a>g})(b۾M''`728'a^Dx*O*UBhaц)jÖ0R'L ,ߔ><"pG ֱ JkuVy\qR(oYiD_x0 H5Fyxqɠk,MNp os& _ZQHVn;_CQ X7 HE^%C/9!oYfx .nY "=L@Hܪ0=5izl@WR Y^Ḙܪ%nƘL -pA5@o1MӾq!/EԜU%HWY-aoZodK6ɛp# 0ndZЈ?op#@DQ%D/¯vF5:cv|niՈ-sΈև qYԧ_'9ɂ T5OĀiR!+ZT!U aw4xyےk+a02<+91r&xO}#Ƽ cT f8M~A|]2{.eW'Ln`k wQ^Y\ ;Y#x3ڑeN)Z9guWb8gV ][ؠ{#5l;g>4du P?wLIЃkrGx;񚐓4K5UXe/ZUrPq{1pwehpTŅ*CvMnn\+cz Kꮃgn)&3zf8LX8ټFk9 EkcFֻVgai3[VݙM}a/x\9i<֜FzVjTl(D'1u n29pi#J" `YmY9f%>y [U@ş\a:L/gtr  iHB_ *=FI,WP<>2W\rl2.s|vewd&fɲPEvY2V_BRE]fAG'/tKϰv1"w $zeW53b89Eۭ7 N)MoK9pp5=_=_[TĖ5a!&+(F}1}?&. oCj/5A{<:bWԗՏRַjrC~`4d endstream endobj 628 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R /F16 138 0 R /F22 218 0 R /F21 215 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R >> endobj 626 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 628 0 R >> endobj 631 0 obj << /Filter[/FlateDecode] /Length 2270 >> stream xڭYKsܸWLjT40"drN[CJQ3ΈԒ+ʯO7 1|ID&fq7O$ffks8[B_[(x4C:kσ_ӔZ"f^{ G:]Mp&S)5LcS|.s79G)WONÌXFDmY p@>/(mzژ%RX0)og`Q~%Ө;=oS 3u:;!$qd+m5pݞΓ0 1?RuMABwL<&0!j#!f˧gLZͼXXlmӄfn8'H5kSGqmOh8~ 5?,auPIº,Y5Kpy"1@Zny%!eч XN.iKSIJqjGZ[hʋ^VA)>-K7 F'@ұճ;md4zBNje X#ꪘF`g5~HDHꬿ) bR`ݳ}O쵉f b0. {)6J͸)eOy$] ~w*5^O9=ɟuΧUW z79;̭eP2dJT)WN(tR'~Td} \}ƒM@ՆqmUTvq Fy2x_HAq> )^PA=hx}ju{Zx\b5[PZPb K70s*0//J|%G;(HW۹hBj% EXw \q]Kjr%hNKln7,FAe&r=j /(['8}aV) ItlX%?voI/M:mO?HA:ҦO֮c:6'! G+b/ޒfƙRڻ > S)L/,Y A)Li+bF3 >nLW#B5 u-5b~קZNGM.ӑ" @@9x Q41٫f'92 cB$wl ."ύPQ/LGcLIt(UȫiO q͕Oa_gNZ|pr 'cmn::cp~(odqT!.BI'N,27Ӟa2at hej2)zk4\p2,p5iiW+қHC`KR>7y?gh[?{ endstream endobj 632 0 obj << /F7 38 0 R /F15 112 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R >> endobj 630 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 632 0 R >> endobj 635 0 obj << /Filter[/FlateDecode] /Length 1749 >> stream xr6_qJXiҤKi<@mH/i:1@,v+!cXjϺ|Dܚq\h=@+"oͮȚJɔ{ڦvnO<ɗo? &sqQXH^2P xQ@|nB;UKTI 0"PN_q} 4 yΡ;NuJjӪ2N SF"tw ge=3|D%ETU:a0b!6Iyq_by1rRqq"+X'VUr}fAѩ 2sL,ヅ?@GoK ?"Cſ",lrWK 93àq% UPg87NUM2I"㲟9cJ rB9x>ٝ`X@%rFxkYՕ%I4d؝U%QnGE۶)P$eQTep f 01iEҷɏuA5/AuӼJb{Kg@&iǫC}աOӤ$&OO&_S5.άj>m+!֥$*d""IJH#pX@lpbn AB ݡAtJW*qKeqE-d;eǞu.UvMu$TlV{'^ u1]Xι15@)mJI& `~a4>lw[Bu@ktRF5 g[3t<ӏM T-q.vӴM'O7#$\x(כ]2j] He*NƋ*5w}XpN2=@SKښjH⥴9+L 3U2:h%\]m7CN}!Wy}DZiǞ t.(DmRڈ> Į0mʡwƄ=Г ]#i<\㕇~ p endstream endobj 636 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R >> endobj 634 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 636 0 R >> endobj 639 0 obj << /Filter[/FlateDecode] /Length 2315 >> stream xYKW!TJ_qևlM*<.GŒBQ;;UӍI8GUrG_X~,4a~*k_ )/y],~^"hq}-g>OcA\0 .;4^/uއe½~>+p{~Kk>'&䱧h? u\v>`>ŚK zijBzNV#uQW=i߈6c6m}5?y7s ,ԗL,fЌ_s)I l2L)PQqܫ=9{PWuVVlp~Wh|6{崌Gnvf &SZ}#JX:ĪաVn/?˰6k2ZslӦ9ʧV( .݁/htRb\;5meJ*NzAVp2Skծ,EQ+s[K8U vLSC$#M|EoZ<Um\*ܴyXK`a]xyYjw0!RT-M G:ipO.A>Y ̕5١*rS}&hzxCBxJ^ UD`ȏw` 34E D ?f2W(4r.x8&tqBV\#c?̉&i}^LG_]Y}c@a2iIu7Q fϓV5!?HcUۣnU[cN9A4V TZ40v-uαDGcuquαEuuֱcys_X,,+Zgrh{th ju70e=\ z5&`(c0 盦xaL4& bL0~*k(ܞj+:[*6=YYh/Ea͹(Lm 2ؒ~sUQNBX[Df"X~co_U:" g!~G LnGΧ6ynEƤT.V{24'lCV5*3Q獡 Ww5״ZԁӡhRiNѧR75'TQBO[K:N8#q9i:Xog)V;GnUof[p|iV]P=:28 ˊ;MRLP@Ku#2Rbw+ЏZJ fR`zRUkoM?| •MQ3%zmGzY}joƻ3c}QҠ@+e;R?Dv@k&Tյ.ypk$}F$`Ze/ʨ٫}dhFJ.c?I!+Fue/eו͞)W1:bBD7^wk4 GL's xG  6= endstream endobj 640 0 obj << /F7 38 0 R /F20 182 0 R /F11 56 0 R /F22 218 0 R /F23 221 0 R /F31 329 0 R /F30 286 0 R /F8 41 0 R >> endobj 638 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 640 0 R >> endobj 643 0 obj << /Filter[/FlateDecode] /Length 2405 >> stream xY[o~Lj6G"ulnOM1`l:&F\]Iw﹐d9NRrs?H} xv?K*E gY"I/ffz}r'mO Ϯ?wv[Tߵf6o+~q0ſ E% B,s+Gbe4_WZkNM~ߵ%‘շ_Nep C&$]v\P,6 P%_8;MUt%odrg֖ VY.T Y>{n8n+؉|:VK-d:7Nd͟4]Amk@wwgxP;[un9h}VIHD-Ga 9 A&X4"1MMyc`k;úNרRܶ ۛCqJmWY0#5[Ȫ *i(q>jtS$_K{)Brhq"3~h^jLHFN*4tJIÏ-NgSܳ9*E0Nߟ>'|Tj|dڬ'65ԵQDRtmaLnډvzԓTxg )Q1s [m[D0/]Av5xK" 愱uj*ErDJЭFuт `Y9?1&-KZ8Ez:˳x$gPg  dc w }Ub//*VWguy$A,G1<L9.?eimFN? Tys9'/׋_X;| SQU=!j#GxcKKt6w0+DB"ӣ17M.>F("˞@z@*T3ݚ~ !ý* WEv nr o _خnZ 9 aкt S愐N"(W$?"Rw89B@1ƝސPUW:=CI!3s F&b/~sCqo\{plyY(Gwsc2% ֒UCzjGsLr_ $E{Ot AhPW{)dӞDf$ %u8+fB5޵7zcfN?6cyq@V6n'Pnfcʥ4rIum[G@dnA(tADqJHAÖ}Uu-|ڃ~S`~)B !-{bM@adPCd`f Wb+'z"s@_ؚEu3}i"qg몤KE(lHq'naVHG: ]F%w;YĽ׽.+l8FVS%\iTz v _0GʈaBWs0.wk=:}gjG M ݌n%]v SwB]t>Iֳ98P3!!]QJR)g=OQZ[1<3XzP ik@2Um]ҵXFL| N gz>q+0PPo FnϸiK㊒bٮ[4GvYF?ǰYz nQC 9 ą>ul) rJSi H8H]6"U2 arg]J mNƹA{:XҭϰCܘ!De[BΖ|Ԗ55 :'xK.PO>ӱR\p+77WF endstream endobj 644 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F13 66 0 R >> endobj 642 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 644 0 R >> endobj 647 0 obj << /Filter[/FlateDecode] /Length 2672 >> stream xڥYKW4rR3-odld'!GW(MԋjrY$b=vQE>O(sQvYYbw*MäڽA%8/2᢮i>쟋|ڗI0 jq( T9?vsRzTSOv[my.='YOH颹1@kX?qS' pUǴLM3?&Xax+/$?Lq E8WǷ}㮰tRg8sZ(>)樏Lym]?VW]{k?pL w * XbpKs xeJp&e\u=CrHr*9Q_Tz 48$7QMܢp&k;[l 'HF M#r=ƪ1BCo$q[Yyzp=$cH4"^X䬏Jqh OL<]XE^0n=pWS4Gw} >qPF؂9뵣l'5<72R;Rcg% 1((im2Tv'oVfp*T4˗$+^|¼Ѱ ^Laܼs׆Mr` 02mU6:G\KmFbԍ=^ LV2AD<2] ?!fLd{R#77h尥=77N iM8m@2\h]o Zk"%SۗҊ"8'ҳT+| "v.̷) GF RNF]`*+Ո26C|/][$U;Mj'/> endobj 646 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 648 0 R >> endobj 651 0 obj << /Filter[/FlateDecode] /Length 2173 >> stream xXY~ϯho^44C11$v{}$-X-%lTH5ud {cd,g+ zX%j5ru\*fjw|!*gy Z>jt}ۿLw|E:lQ:cZ].Q2fB٩ 05ǢAΣ\4z,EOҷmjvTPoְYu}CcH<ڊf#i2%^[m-˵e';銣퉨8rSeS3EsAzQXMX&튮7;!ּCM՜hsY7M3-@\F]yijHYs%S+WJDK[$jx\E;k^`:&yT\ iNL[;A1dIJp=m\Vv$AQQUnL&.م1c2O-vR, Aͩ-]XT=ћ݅\kaԹAbվ< u6~EYo Fϝ \`tқ%(yw(} :CYMUuzv1SiͲέ()b0g9Tg!c&$ AAVcNڃEx蹌pPYnAGr)]cVd5={XU2Ms>STLk!sd92ݒ\8qt[dnArws MK҅g+ 6 T+2]stl 05, ٝ+'oC59K}TĈ}c\|p!=3.@ yNShxsP#Q be~x`.~H?.!\ྡh. &"b{d'**;šFLFdP)nQUFŵ=<ζ"gL옥~뫵:UL'/aye Bjʺa;x-Ϣ:g\⒃B$ A^:!g\!5gۙ-$BM\BWxSQ# UOJΛ۹Xx"gokZ˄w?R\ =b 1r)hyPkhrsrw .DM}R݄$L29T 90:óNI:Ӊ)A8β i?>5bjR <_T)4<6ؙ| f/ʻW}ͫT/,nMy.xxA PϹah<1L<+4IYS`' ^܎0qBcrDddi#̮$9"AdxؾHyo>Thb7F Dk5O%37 i|\X?<Kvk]'/]ʞ&rr?im%)G/w EĥX* u:Xd x@ J>Qsy mpI!y$"/ ӀU_S~,I +T_ C*yNܒIp"gw37u8RK4'ܹPS7Gs@8}7@O") #ː(&)R8 taѯo iN endstream endobj 652 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F30 286 0 R >> endobj 650 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 652 0 R >> endobj 655 0 obj << /Length 1784 /Filter/FlateDecode /Name/Im7 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R7 656 0 R /R6 657 0 R >> >> >> stream xXnWR}p dc l,)"ȿNU̐`bJcTikW@>kl)<;<hG~;Zk&χq0mSg J_ai ?􅷟3/n+Hk8788և#䟇o]b+3ʋ{^ZzNܳծxucwZ.dڏ<s0&hC#Fr26~||!~UP_u+0֢xVUVL͘OOgȴ^vIOApS/B1f/g^&$A>_>(/9?XYZg0(>lfaV?Eo JEӄ[mdٳ┶FPM@To7$7eu0!ZH('a(&29hYiMmh鿖PW;$baJ_(ґJނh`EWHiGo3<*Q,moPhk[i9EQE8-R1W9'䶵n=j}^6\7hwWsd]Ԛl!d2 .!K>ƨʼnb2r5JMdBr]*ՌXPLȺ2EE :Csg,+pa0c !j( '2:`tz/5Fl4X xpeoNjוL@7bd9zQ% ų$=E %ޓ>*RAo[ah?Wd\tNJQ:Y&^1sk(ZF::,ʝ7$9-5kr| _rt7x5TFDbh]sDeJit }jg)Dd>9[Ht-R0*L&,xDNu븍pMLEZq|Eqe%Ŏk-'𷜥'ix6!cT!C,yĊ`3+G" @%vS|<+P0o8H" "N5?`YBIH,؛ 9`|)6#7llZBۗs0J v }NT9Q^SǂTO>P}1Q&bgӚN铽-*>bJc){46l_%{l)?euDt|؟U#3';lb:M[N 8/| 8F:]M,F)k9+p9k7S:Ow>;GR-UQWh7*,RԴȉtѵTGmZCǎQd1vk+KrΙ>2b׋qz*juB4_w_o_4yDnHÛ㧯niܽyw |q5s)O>r|>n7َ#o7O~oDn|. i>BuY_ျ?NW)OoPv9Gn%eo۷^6RV endstream endobj 657 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 656 0 obj << /Type/Font /Name/R7 /Subtype/Type1 /BaseFont/Courier-Bold >> endobj 658 0 obj << /Filter[/FlateDecode] /Length 1307 >> stream xWKo6WE XDmͶ>Eh^zl%]pƁ A/Kΐ~\/da{v {␥ TBU)1Х~{ux)Kcos&RoOjH{n)hq7-SESg% ?wtmuBa#t?_݈Ύ b\ZPd$ߚwHDS] ^9TB(sKy\ b缵9C &"c{(@ߙ&Fޚ74EǬhi;T;CC(ޗңY"qhU9bBKx(r΁ec]['4f޾\h59CNY$l qm4]:С$z=Ȼ/Ke~0țC;]=vWYc}:KHprz.;d&P&~jprsL6u>_C$q'4S,84>s#?ٰ02N1#r2U>E ܱ3bp5!ΠB0q sa*#̹j2'k&Ӄ>[zТU$q^1!v̘P}>hI],a?> endobj 660 0 obj << /Im7 655 0 R >> endobj 654 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 659 0 R /XObject 660 0 R >> endobj 663 0 obj << /Filter[/FlateDecode] /Length 2247 >> stream x]™d;7\^gKQ^ﶓ evw<4KYfn|v)l.sv&do?ffu1^ O4wsɧqſc9,y|:, Y&H1$s.#?',W"'Ksrz60Yaw8!ucQyQ$[傗/ӸJZkơʤoivהɪkYU44Xn~W.n߻Yi{e2'?ZB%-՗*AY\6si6:啗,u\4GLf_⃎I; )SM0"bkOl`5A,5^ Ud7 `:&roo9UT" A7ꈺIQ&2醄o u.vy]8RA:gqkEeQ 3~]<٭`g@0xπ5dJEXsr<# 3 yP,ӿi$"[xΥs"MjLf6ua@Vd}(I޼8&Pڴw-- $t)g#;Y9ϳ`Ok"b 8~{OU$\<R~8ЕL>^yKj佫CŧQCm7Twn)! d[danH`.N@\!y9NV{߉U gTѰ NPӄ7?@]XO1;Ef sp a' c 9t?EPƍ xckVDr gi-yJ-K܎{yAI) Il DnCڂ|<Fb.v*1XI I2o5q;"7A33X1c'e3.SrVG{ 6 "h^M#_iCuT))tƹ}MK]⨟XnXig,Mv&j2/~]ݘ5f8J=Ùl OHD 'ۦkHwfL?ʉDXŷ,%u&n„{1!&:n9E*zR^20'iECC+FBuM {sn endstream endobj 664 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F22 218 0 R /F6 35 0 R /F21 215 0 R /F30 286 0 R >> endobj 662 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 664 0 R >> endobj 667 0 obj << /Filter[/FlateDecode] /Length 2372 >> stream xZKsWh*Rx[qe!rmL.ǕO C>Ah-WJEê~>- uw\8Ȫ, 2ѝ")]ĖpbPnx-3SFx h5vu1O7=D"@$p$ $L˄ QQ&>erm_b1a~K0bDގgS JheB[YV&48x>^/(v04Nz|gp߬3|ȷSr6l|"ƉQO$+lv5(d1iVG" +FQJvKK?Jmub;۽v#_ O?8eOaŋ/zlLp|``"]g-z¦yk3uқ2k ,::`ͣ}ḵ&6U>/,qpc=<,K8.tx:8c% zBW59C" V̬ſ=Ļ|tb<=qw/NNE&T#؛V#FhzPJ7q'ٻ WaҪxI,tB G2 !KM&UvU~c^bjzDɲE|wo *[7Hk={./6AMupQwz@MzEuZedOO0+p1NSy$7H5̣n||{/8:6'o=,x;U ϽJx{ra/뜿nr4諈K4=K?r$0@f^ DcT5 QG ¤te?B٦ ObuySgŵ<3cQ䰼[_lsV}Vw7+6/R6TpyB nvؤD N4 ,\_fZJbPqݰH\ >KuZz߯ץ}P~y"aqhP:]Bk١;gJ' \_e&TNPDFPJ'1a{=} IC}^Z gա (5_5.3є\pG0a%q{kRd ]ڝji۶NWf <ەT"9 ^QHbPHc} TSoa 6Y+'OݛFB@. V l;ςy[LAڌ"h`-hyP?$2a*A|JJ | 1@GUyRzᑮ~+~_?BNw 7'v[;~ϗ~-jEC={49BW}x?HEw _Z,bo *)(%h0r%,i =jn(,PP?e\or|%.Moss}2ۗ ݾ}~Kܧ*>VFfG=kOEtAf \ɃTNtpO" endstream endobj 668 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F21 215 0 R /F6 35 0 R >> endobj 666 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 668 0 R >> endobj 671 0 obj << /Filter[/FlateDecode] /Length 2042 >> stream xn=_E,HݞL6 q8X-Z2tNj||XD IKu.O0Dʼn}F b\W'}'cY'|Dc^%Rd$REѹ>Nj<ቐ@`AjȢ}EZ 2Nj#&Pg4k%C K! 8Rzem'ED`~poͭx8l 6X滃Ú@hYiTE"tpܡ.{8@,ko6J,KO-n͆1O\x[Dz^O}[ԓsmP]Q4LO5_^!4QE D>Vd/iom:0М"qD}'RW/_pWFleޫ2p#t/e89'+\Gn%A>)}n͠re(< X5'>شY(ZU@hޜV#:t?OeGnx씖."]UL5P`V⭠AW|^zml BU* 8z9Ae5HLCh9H璜m2&!qܪnz!+ի=$@w?ݼ9F08'ЩSGy?,-0tfri$qy%0' Wr*sqYd{U0-X3N뜭93G]Ci6[њOi5Kt]HvDlsRE^'@dgGuiC;rOF%z0*U9v ,\Aǐǖw6vn*;Zf*$pS.'aNvP@=x쥍}V94x\mBjhrAB 8`8Z6 OdQ}+r}0 j\~T(MB"RSOPWBQeHʐq-y]784Uٽ%&SgE(Ap:aCP` ˸)k3Vūbͫ %{)DTna2uS \4dG6e ̥N{\B08DMo] QL vλ~8Z~{!0, ox"Ar9,Ÿn[ :X8~"0h)uE9V9 7CᾹ\oZe& c5Us mڸY{_q9F]m$/xeG(;(z4i.t'3:( .=mS.m+ Pbq[u @y\'/9C|6C#`|7H]:zN~&LR3EضpLM9@R:N}CJ᤼uLW HcL{> endobj 670 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 672 0 R >> endobj 677 0 obj << /Type/Font /Subtype/Type1 /Name/F37 /FontDescriptor 676 0 R /BaseFont/GDRRZD+CMTT12 /FirstChar 33 /LastChar 196 /Widths[514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 0 0 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6] >> endobj 678 0 obj << /Filter[/FlateDecode] /Length 2411 >> stream xZ_ 律$gŒlپRIM @E۳{{E{IQw >hLIQn$~CyܨrKIeRលͻonxoJVs&7xRlcI}}_Foc)ez+GonjpJDU'om,HX?lEix7SoI6Hfrr?x׈c-=W4zwٮ+'= ۷^j7v}]5Q/r)@JY)?i!4L2QxF3YFخG4j-_2 VbXqx(R}0 8@ P &M\p{hR5^i;9 #pB!qw eRguI;lnGsv,Qᓅ|0[ ;2Y2M)"5DJ@y^cN]S CÇ,:tȝAܿ_.ԇ  VJۍ{JD}q-v@N e0 g4o_ٵ4>܎{}+)ʂ%2buV*͙34ʹɎH2DGpR 6Tf&.m-i=b(@`_, JZ&)GpZL͵4~#܎bErZx}]vvx7Au3_ a`!>%4dﻮ_V/{^FE&sŲt9GϪ;st8"ƵDPĚk pDxu@>DUT?l,s5꽕}ԫ>_IONA;yIIN0w%8);$Gs [t42^jP|UixEL3AҮjɡy`~tƄbm..%SMRlت&x;W/} /*Xp?ӗkB1"plXH*ryU"AT2yʤ"PW^k(% WF[dV H1 ^|(/\$L2WHY?<"~Q(%gHXFjv?4#?u{gǃ >BN~ȮPҨMol Ę%[!X}| 11@ǒnB%.ZIEDRPo}`iX%:=?-0WѮkaF EFOmӽM+ȲGCX|* ,a`ޙޟ.\}]}E"jv(Pȇ-HN"Yz`K?@Er<TFfRz f׮)9{Z~~;2Ӷ+ڸ{ѶF;P=yYya1Sf7\dm_:}ޙaް}Ms͍xO(6%*I+]"'غٕF2H]yC`WP6ω덧J”J찿 >` 6fN^IVdt>=Whé?PKW;0VSSHt i )6¯E@}@Z:{:\eHX彩K5)J z5p$D uA!$&wYMIV1|`ONj #K[}Zat60eۯF1JEz omh "ݯ7eO=º9|PffDrWߡb( OpS d I)8NPa]/RncSz)Mʒ" .c~=~d1SZO"x}')j/Ʒ}[ٕ> endobj 674 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 679 0 R >> endobj 682 0 obj << /Filter[/FlateDecode] /Length 2009 >> stream xXKWЛJ&|e=k~NX L팜n4H"}Ox7Я,5_CDQBF XDHIŔU67< Vݺϙ(?Y>|!l+7YD&9K(vy\z nǍ@P痹f0ٯ=/ bs[V$]+$5N ա2]scDS샲"QWe·KX2$l_ү?ո_-~vD:X<.8,$K:f˺}YEV`E)KPaq\x3mHK8a:ns .  7tOi_D~މ]ooHka0s BDq:SR*gY6oYu)uj{gK~'1/#e>_1rr=QNʮ%"]ZNgŲL}:eXs K?~٫'Y_ݥ4Ba~6:D'聲T6U4  )yQi§/+ j_'9?zևƅKĵqFYLQ? $/}[*)r)v5/~ uN| dח>>3VV*FJm3vs' x.a*  `_j@ܴԱR63t!7財eC)fCt1%%-Kެd ZË3}؞,! {c ֓33C<Ib5;Y>_gQow x7 {Xr=[ޔTk6u˒&[憆A14,$ r_ endstream endobj 683 0 obj << /F10 53 0 R /F7 38 0 R /F21 215 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F31 329 0 R /F30 286 0 R >> endobj 681 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 683 0 R >> endobj 688 0 obj << /Type/Font /Subtype/Type1 /Name/F38 /FontDescriptor 687 0 R /BaseFont/PLHDEG+CMSY7 /FirstChar 33 /LastChar 196 /Widths[1138.9 585.3 585.3 1138.9 1138.9 1138.9 892.9 1138.9 1138.9 708.3 708.3 1138.9 1138.9 1138.9 892.9 329.4 1138.9 769.8 769.8 1015.9 1015.9 0 0 646.8 646.8 769.8 585.3 831.4 831.4 892.9 892.9 708.3 917.6 753.4 620.2 889.5 616.1 818.4 688.5 978.6 646.5 782.1 871.7 791.7 1342.7 935.6 905.8 809.2 935.9 981 702.2 647.8 717.8 719.9 1135.1 818.9 764.4 823.1 769.8 769.8 769.8 769.8 769.8 708.3 708.3 523.8 523.8 523.8 523.8 585.3 585.3 462.3 462.3 339.3 585.3 585.3 708.3 585.3 339.3 938.5 859.1 954.4 493.6 769.8 769.8 892.9 892.9 523.8 523.8 523.8 708.3 892.9 892.9 892.9 892.9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 892.9 339.3 892.9 585.3 892.9 585.3 892.9 892.9 892.9 892.9 0 0 892.9 892.9 892.9 1138.9 585.3 585.3 892.9 892.9 892.9 892.9 892.9 892.9 892.9 892.9 892.9 892.9 892.9 892.9 1138.9 1138.9 892.9 892.9 1138.9 892.9] >> endobj 689 0 obj << /Filter[/FlateDecode] /Length 2225 >> stream xڽrܸ&q,˖\xשeV=P31òʿo7cHƏ hM/`A{f' EKV! TL ]x6RFͮLO}΃՚IoCZJ+^QoWrU%/j-_Y\״ŏB7oZ(ƥ1ʨ'˘[D^N&,KrVx"{GwDzBB/2wŀ) K Uy5 }>ǵM!3M/sTP, 6|!23<(;~c 1Cnxׂx܀)0a*g TCZ`C64UM{4iKmz6+qjwlյC|\[&" ^/L+ 1b q&C:Ѡեq"?sl~Th*!8U(C7zv4Y`%[Xϰ쐋1ˌP)}b)o.ihՏߴؓ!9gt#t(*H$XjZȽbPB6!\a?J}'vpE~.OsS3T##®gGG-N ;;њ"y?U&~Ni y?1ϒ݋SlVbw׎\`jvHX L 9eHn43}k>#|G.## 7 EÚCJ*cZDJD␗-MtivMk+[ nmW'=eQ7Ns7K 2d:("?@L@XTKE"-P0Y"k婭8S;hdQfyҒn2bW,dkdm>S1W0@0kGH^;20^,y  p{C;&vnCR96į2o't;(Rs8΁޵ {h)klk%Kér:jmgED@yy+@n.!Rr:I.ޢw I logrhٿ+) ]Q=Be3WX-͎6/ƭMk+z5%}<>RS1]ey;EPj[߭D?\;hRN`N%QwTO+.U8Jj*()?O1e{3h/mZAX/Ĝ;\AڣۤbYAeQ_BOd,y0}EkFkxx[QӣQ-"|()ՇGގ(q(*c:[h"߰z1|0Yk+|3#zoVv4bMbgS`[k.xja{Vp35"B# ob"`럸j: E]p2š_~Av! ӷ+ 7@- M'؃U үKW <6`}d,u{22[oV"d*^hrOb|hU)@ReC2E~JTYyeO?xQ:2ƃv#+mg&S }6ʈ(AGp"^ZpBRa[Edƙ JfTu-(!.uMUay0AX66 N7C2{s^3)ȄHVי9N_" 4EuނyzW}l%1}.RALզCdl]7!_<p9qi&C@^H0魛s,F %zg"mˏy@eltUk[gxz޾26XTM] ae¢"x&>F2$.9`pη8IX0awj}H~4&)ef}z3x..g.DηxGnmwqo3PrJof_V{*eQ] n]ߡugW/U[rfÿ>yS76H'Z[ endstream endobj 690 0 obj << /F7 38 0 R /F36 578 0 R /F15 112 0 R /F12 59 0 R /F29 279 0 R /F38 688 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R /F10 53 0 R /F16 138 0 R >> endobj 685 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 690 0 R >> endobj 693 0 obj << /Filter[/FlateDecode] /Length 2114 >> stream xڥYK6PLU|>$^oV!N0↢ DJ^4ݍg>|h*,WXjJ3-~zwr'~`2_ݿ5zk#"?v߬cem]vߥ=3cVܯBи,LÌXj&c#ut= `~ClU,$S3kU8J׆q9g,Sa|s6OYABf*NSf4hRHҤ&T{V?f1i5tsElid经VmWd8L*Ҍ)!<>ْNB}P.9~p4%q"mzuS<$W)K̙qq_lѳՒaus Yɷt.0׍+7gi&sGW,I>:0SQ}|. ~twƳ~Ctx?rdj0cX2rٲ9ױ>e=[8Et4. 6mq}[g*:̶R buyfӁAt 'xbaRgpar@3&2|;[U))Vs9DdzA,#ID*Y2P{YOe:{ծ=ըqx%Uo lmQyqKCD!uusK:aZ-Lv'붰P?4xjԋ (@2v2ܖE$ ҫMOS1\=7NGҟw'!kIqbR i *9E%E% Ư(i /W Mzf=F&;G\H-ymCkD?EK2MS<j*Ҷ^TI EO09=)EߺSO_]=h ,YUYМuӶΟ+C@h?:[u&#OA>,Raz~4!PJLTF@DPnhmCkqۺii+"4Dr?b6ܠ :)rĀjG4 ,8%Ϭ!Ϯy%a|49ԗiOkY1Qͮ̋WW+uXRq_ZǒJaVtv1uzkh&(s\rćqx"5sd}mS-6}!W*Gog&WgG5HIɴAwp:Q\Cde~fD#]!b^JL7QX9Kש vp@q0yKS Gv)喦n ?HvbP'PBNyjx 2|t!S{^|I!vI+ Yt9^aq}0+6 8GTXkA"GO@HQEH/zo\;>ԩ~x + t0 nff P=TnvqYDǼ8HehUhuhMhЦBVOTSd6 1u&$ٵm*x<`5X_v:q)\0twԴn{;!qXP mѸd/+ ? 'l^@5Qp4CCV ^ >.C.AoK ;ul=<|5=h']8e6΋}PHQiF# 8C E-ctR#$`tMج΃H;GOw/j!HES hBL endstream endobj 694 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F8 41 0 R /F13 66 0 R >> endobj 692 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 694 0 R >> endobj 697 0 obj << /Filter[/FlateDecode] /Length 2233 >> stream xY_s6OXE)M2/[knVSvOM?%q%y㤹I"HPA8x?ࡂ Y.(Q,.Hdя*wot< 7rD\]ȹXE\equ[:Ӭ")e*JSYe" 7鋇8G2^9o~av߃+j?#Q )>. z^u)vMB̾K3 u1n_݌$T}T;p#}"R)T݉'p,N}w.-H+ǝ=9 MdžLrܭ 0t5=﷦q&Wf(;|ḣrlU&]9z jrjL0J.ҹl"zVf-n=͹N*v>2Xo>Bkj!0x(l azSo<ɀؙby>.v\9py{?'ypT"TI40vяZB,T!'0O<X}y*\kc43ac.ML`cx Ǜ#pү8wN"PlOxplx}pԕA^8]WͽՉV6&Zm[VHi IH7CqHZntůѺh 7ѳP@4 UeW5u iZ i&H=tx5y;&\EsрqQ򣣙-h.hj$?-Qdѡ1syLz=)9$tlH0FxѪ %(Cj.G({v5ؒo{ dKB~RqӘ,&&b̘ r R04=AOqC{řO(vQ˧[)-z "b;1~14mԈß1 y(]'8.S(?D, |z<]7^m23RaN7j*;)}˽\fSoq%]c]v}^AuhDO%4U|B5!+ R^$Wej?ȣZhfC?= yD1Ftzf!mVz|K#AT$lk&4.ׂӴ]N&*]3Gr3$+-OGNT\[V֌8_/|{߅ӜﻞdgZ =t~ #8We6 j.3p|u[5KDX~4tR=Z2RC|'Q˚qIՃTY_U:l"gs&43("W@O@efpP'x)\E|>d(YƧ+S c`ۃ)ߑ:0BEUe4(^>_`#Nd> endobj 696 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 698 0 R >> endobj 701 0 obj << /Filter[/FlateDecode] /Length 1893 >> stream xYrF+` Jc6,CvʹZ9D"(ŕO7z -)RJNr<򦻧 Yz^ hڋBJ/P[yJ*}_zO_}ࡗ4Nm{DꝾˉ~U5 ]Vg/#δ¦7璾 %A a$M˲X{ !gQfP+w0XS0Ѕ1lg˩p6 KiӀpDl>MbIh i8N|@;J'OGh⨣#ttң߶;747d8}qxy^w= {j>C&wwtsM'߲_e܍H0fʖ|EK 2  o|~; ¬Iڦ?j|6M]BBc,>C$ikx|t i 8E5*g{0aT IuQ/YYЗ43׃8$Fn>q;Gݑ~r!-Rq(*1F%q X:v3 vct291a^ZO8+#i@G_GŸ-Q;h!}o3Z>ᴜg)/t#h|1bO7-ߞ JqA5g2jkQUDi_/u>m7pO'8:qIFcAg4J)+NϯI8)]l8G^=׏R"?'h>?oYXf_f}e)ÂKppFN$PʪgC_-X`:7/+ʿOvcs3^^΋r!t_K3v𳭡vmT; PF{~5ϖ T @l˪j6lSݴ)jy l{_Q$eh3W& zL0ms=,q&Y]^3s)42*R7曲OMZSJ7%Կ^&YE߀sQ4_gu] Őe CӇ^4V#lEئֱ;]5&@{dЮ`GVMsdqMlVcHsݵrX: _yU{.fJnޛvЙQh/w|uYa;aI #xHb1#b/uQ+jv"kgOmvzojp.Nq #UQ]̫>&vB&m-*c .e42cFW 3W4N1@A6ܣV4h/C8M݊4D2HciVQ("$s\3|u <bŽ}b^w:S(THP7;7Hhq[?jCHŲOE;y-IBvmy*LF)FiWW!jʖWTs[UaK*L:b*pȿ'{5b4Gq \H X% 6"]kPD~M=7f̌zwŦrcE4 8'4hZӵld <["+IbFU)gZv<%O' f{jiȏlR9^*+H[> endobj 700 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 702 0 R >> endobj 705 0 obj << /Filter[/FlateDecode] /Length 2167 >> stream x˒۸`*S#x>É6'*05b"gITDڳFHDQSD""R!0{|~ܼ|ȓ`ɥPy6Ҭ2΢,io[֡ǫ:I*S)ml(f4²CiҰdć__}ԁ" QVFLUjy/;+-}<08E(!.?=)Wa5gmݱ&ayP7xP7Uw*Nν+='öaL<01ˎ}F=()~uִ*dR SdgUa7 88# 0{>Tt8F)12*X accǟC̩1!; Z)}PBpuyp"|Ob y +hN7*=b:%h%Vqؕ#T#NE;B:eLWE#ʄ2kDD7a!?6 #T {>zĥn_>'!ipB%suL9hX}͌pl.9ZBITeDJJhQ]T0UϧabL({[Bk̷K1/E- ~&p&2/?+oZן۪_lxO6CRkӫehV0LOHu\?;O*PR}R" =?KWsL ;5^sՈږ7*kw aUA{v.$!_҈c`J~3we>ٌ>ܛ9 4|Y~v/>(؃cF;S*pT*FkiԜ,hTC="CorYi=$sz8sUiRVB~5"{&d~ v\?$J ]3>ê;L{C(}3wYAeM ^TRQk\]caWRM!ǾqZIɚl(ںZ QOԻnD%qX:Au [M;n4w#` Un;Uɋi' u)bc Yw筮A6۾IlqTŒM%&=in4wATO[ ]<#HD7BxEq<8zM!Oq[M{|߭,h3ۨ 9d.dS Ytr*>&խ * uђ mQݖ1I8 }UN-[4|7xԒkY1xOZ^sEXv?k=񤱦L` endstream endobj 706 0 obj << /F7 38 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F15 112 0 R /F12 59 0 R /F29 279 0 R /F6 35 0 R >> endobj 704 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 706 0 R >> endobj 709 0 obj << /Filter[/FlateDecode] /Length 2002 >> stream xڅXK6m媶"QS*dRTS9dr`6kdɫǸ_(Qs" An06/j~&ۜ7yVffa6Maܸ|8TaoqOQ_c1K2̲MDk8N;qBSIŅR2 z;.I@sHa%NE扐/h&칫gpbT$f%(K:Q% lsY1obCQOk`k ƚ+l$gڶS:7u&VSFFE`x;<`79ۦg*jl"%pLze /Ѱ*  ݛqQQs2BPzxjN7lNJN.E)9( sIrgp{-)+&!^H"5O7s^7pdh0vq0x"a!<ܑ!(m< Ą=eI恉~z`Luw:F=W_ pC[UeX7 j0K؝zI0$DQ\av80-|n.,2)brAG@Ի[tCF+ʗ4F;(v(Co܄L,ȿQ}N8͞H|Ůz:'j .%22K0v<\%xg?ӯp>0TUEEA`8WL (UBUqYzHFrRdt$\m'X$ +H?vc+zuR7TaD42u8tgob$X>L}bؿ)(V~x7_Wl?Vm%Wm+ ͌_0螸78v$([+{cGmg^I&*v6:-'gHXL >''i `5d=577xjh˗-Sa. EO -׈s;PCd=.rlo>57 KO|8Gh)'0[aG6ٷ<Xb4DZ. [+$S%U{mt&yXaeb"?O*%(uypIn=kr.*F1}o()T BX@q ;r\ጫ$ټ}K"vSuq x> endobj 708 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 710 0 R >> endobj 713 0 obj << /Filter[/FlateDecode] /Length 2373 >> stream xڭ˒۸-T 7)vMRvm=x|(>XH᭤*'6 OpQ:3|몈*]=fy%vY%~ܬ>n|(WUXDIK17&xwT'cAƟ֏EQ֛$ڙS N%L~L63Ihppn]s,S:wAij[e8 LwԌiVwBxOL?3nb)G5IB00Z?569{f8rbOF#7p_sW2 je nub`祒axZBX`^8$Ykvxqn&Q O䉓ɹzȢtc)Z@ +Lqs])Y ~oߩ^z날zt$T cs'ms=4hY%^m(h |@sF.C/C UpIθ8P5?4 1aE@׋};8S9oژ"49Ñj?O@h䨧PUק5oMr2 Oq=6ΐH[v.)>>\UANً, 2F9`Q@]ama(%܋q9k.ͽ #ont eveT-eGx϶:YӜxr,?3tY?܋{B';) t fʧ:rRE0$ E@Ro8̟0kT~qF\PC,"W4N9= 0itoѝ("#n>H&nWIMB엎œ)1[)n|{> endobj 712 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 714 0 R >> endobj 717 0 obj << /Filter[/FlateDecode] /Length 1700 >> stream xڅn6ޯ0RTzm.ZCS@-6J2Hi;R{9=.6 }xç4MRni wTlȓF8NlHOjgu}|ҭ[ԛX4I]/6KIكl 6M($NDjڭ<]Frj@YY/"ӴJ|#ak%;&/ӹ3t_N:ф8hEԶm{=9~ZEVI:2ʓ*e?.JR,vt#؇"Ўj"&< ?t|=N11ޢt5# 7V^BuְL@F͡弰>xT\h'v #[cNCl|6Av?gDM_z(wR;Ѡa:j\ЪqG>¿G"xҙ/<录cڿy @2ʽQ|u'ER- DE_ >A^.@ s|nl|klPJʇf]&U[MܣWA0˱j-2.d>i1VrpCa, @mQW>4GUBS~{)W ɣ(WuJ0t Dk\q(V!H6 [t 儀<xIw1Ԫ3mm~0^owKUGԉq#hU4ۼ`?W"`8t>$f<6l>² /a؁^Y]Iscn(@Z[&.^da)`I͏a5|%$)U 7a]Y-XXֿS֡ƻO"eyBhXʾP/a"-IZٵEc|3*6!98眍\M^_[u@}[ݭg3N.q*9ۘg 8O멗gIᔘ c!' Q9RC(T4~ F8m4 h!☉Z_WzBjE*xsgEr;& `񍶝 ma'Ew@+UG E[^~EX1~3M"lPp5Lxl ߉%M20%O`F-9pZ/{%>{ ; FMcp`XG |-YB;7*wJY] endstream endobj 718 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F8 41 0 R >> endobj 716 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 718 0 R >> endobj 721 0 obj << /Filter[/FlateDecode] /Length 2030 >> stream xڵXK6Qj%=M*[I0=s`K=Qw/@llb @oB}~OlPf"vĉH7ϛO?R0GBDQP"tRQ]'A8eY<4d*K8sPMDDmu5ۂ"4YAc5Y(jc1s4= ƞV}gaFbhHUCg}a)puhQ#,}vQ2(eZl:[A:ݣ\kĥݹ/tG.bgG mxky.o_qSoͳ\hYRh"ȸ }7p]odHsGMm2`m0(Nv}niQu5-ΚU}{6 NDxlѫYV -dep1MC]?Ҡ1$mQÁ@̠8KD=2pTR0  }7@p3hU1,ԡC4.E/qn M.rBؾh s1jc$ v=}Wr!ιBXQ) nqv, /@Pb<gSEW_^Rv&"͙P곛pJ y P瑇s7pv[P~:*?j 7S`Ĺ ݾx~dv}S]Q(ep"ND H3vzaMJ18*PkzbmrEw O(!>nc-DJX՝vQCK2!{gA6`GMYEF{Ad7R["߅l)]"JW9|H%q%.r=\YFp&E43!Fq9jjm4+_a_X*'_a*a">XѪ,!\'J")Cx-Z%OGj^?jZ[ _GM?CQ 7o 8veyFŗkyrߓiE>պH%!#CmS|оB㆏i/2y td 8,CjJ2_0 |O@,}hx>aBց=[olÑ lR6hZA*Ak4o3Fj0p:U'sD?'mǐ.wo9Ә#N|yi;pK']E\o|3Pǖc]Ui9L 1@br$)";.b6I@|'zo> endobj 720 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 722 0 R >> endobj 725 0 obj << /Filter[/FlateDecode] /Length 2389 >> stream x]=BokD CZz֦̓mHn}2%.E|D*C@I TiMHmD%͛cÛq̂+]?~8NzKnc<㩩^~yۛݤJ q\zF'*6T@Hhy0رoSw8~av37 EOVAwmFܹӂ2r8VeJ|)I,4 ̉##Tݎѯ&Y ['B>*Q\v9ct%0^j>bs=qdB`Ago=jqWY/Te!1ie$1z<8;MڶG&20)"3f"Qy,aQ':J(DD:OÿD">>/x oׇDMŤ0q g,ixxљI/0Uu 4fMp 8OaB&t ȇ0Bb'N!'YL:-U.b0PFע;5@&t]kecDG5M:$g.Rx#:n@YG04K<x۩Yihz|GG=ۦKI֌ `߼l$d_&J5 Rn>czOtn|SØϭmCNgN0Kr۱3; Y#g%&&Nx=~V^IF.`ڽ}bB}R_wZ.r?s)w۹KnNDUEV۟,Wc}"AA˃M *.H 'xURg~US@cF I jV/N. g0-o6~`=:Y]5qQ<ܔ)Pc:ZzCvY(/˔ʹQHnϨ4y}A|V +A6牏k3E#>,>ig-t #SͥbAd0t1`A*Z*J8Og}ihfrDa>4^KWb&^Rr/@}6P= VlPapK6s//yJGZpEp8_^ tInNWK!/j(T,IvIMӯxE"gܧҧP-xiWpulH ٱ?PgtpXY(#Q6}DEb,嬿gj +JL4]n%-6GAꔍy L=oň`Wvv_Q㈓Nъ~p=W*q (fZ1P endstream endobj 726 0 obj << /F10 53 0 R /F7 38 0 R /F6 35 0 R /F8 41 0 R /F13 66 0 R /F15 112 0 R /F22 218 0 R >> endobj 724 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 726 0 R >> endobj 729 0 obj << /Filter[/FlateDecode] /Length 2568 >> stream xڵr_/ԩC\xs6g3L,sB Iү.ݒX.. _`Wϫ(`\*dXWJ*.V?>~.^,VvD=./!cvhuT^~/uvx7 }}_Ela|wsԱ7+k 4z,=ZùkpXn U/js7~."`o~('E7D2ڭ. ݐM i|4\W|sH9އ] bje&>=GFOj}E|p9%PvC]^y# xik\/rs^x˭.c^IBpD}FDz;RT%2ꗖ ˆ ]5s_d:PEp cmv_hj³~!ﴸn <,I͚ZbskMtTBNR܂Y+0kUR0a L;Y>H}!zg 2ooq =f& 3~+,U;zm5|S^Bs0tN\$(oϦ*z,1 cKDoED77@9Bsٻ߄N_>8$"1G KbpP 21𲩂CC\*a<@-.¼LDD]I&.) "f|`Z K|*ɹ…sWLS"I.P0pi(AH&b+ $ 1aA<L$Pa=2ƑEMqY@-.{zB =ΊTUhbʬn'!DC %!8"Ҽ}{^[lzcE`& 6 iB.vHS@ a!RG$'߄w;`{nm>Wg(5<՞f m ",n{m'e`teݱܴ9i йԻKC]rpgISmlF=S<(gw4n Y[MGi.u9v1E,/(i9%! |0z)FӺU[n$oUiaY;N59-`^z5WN2Acђfʼn#&1\`t/̧ۻjM3Ѝ]l ]nFK;gtv+>dg ,A|[eK"p kBdlb7Tw!7 ., m톩oЃdaeQ@(-rAp$x26y\Sƃ.ے&L&Ϯk7֯)'o5*㨊0{x_NIeV2Gq|aиr(S uFL޾̌/Jhv6;uUO{+7Yy*I 8z3D gn YE-}Yl3#U2i1A(׻:7 A!{I,#/,M;i ڦFd'fI30K8Ou"=F նO8Tl&9uï:NtX d <4ꖥ ,$Xw{a}LX4 třsb57Vi&%Y٭ ӆapmN2?$*L\ƊϤ2 <vd!Xghh;h8qQN٨U/L hI<<"(aNF&!p1^Ѐ2DV!C|3A >ˀbwdCJddS$kǬj!B`b Iy4)ǠΖ Lg+HM\96^AL%VIWƊ;da_G.Fotl/g?<~lT=.e%L[הo*ԡ@=YrUؗ@xW_HQ,vϽMzya<@h@Lj>We$dW'yH*fT* I\9oNIImX> endobj 728 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 730 0 R >> endobj 733 0 obj << /Filter[/FlateDecode] /Length 2039 >> stream xYK6yẅ/=L 3@[zpn!l*I=:ә˞,Ū>XtV2 ~dvʂ4<[ I 2߲̾oH!M;}JX{iXIoNV=Wk.|םO7~rӹYRF8 X5˰Yq_<;pdߣpTa,K"N_8#;U+.]K@څcd@֜4MV1kkw}8DYZw*Z6W[x636aK4eh"#3*BFA2ZqΚ O $9Ird+I%*$mɛu0ID޼fȩum?$ܮfgSeIr7J!IFŊڞ*^F:DͰs _jsʤ?v*9eJ82Dqd "rk|ˣ z1لBc0Ijc&31+ cNds;`B5kc cp1XB x͈J E pV[œ1Qxyp8ԯKQ{Boh O qRxk <~YJD~9_֯74ohpRȇ JBQ#Q#bg?ȋ҅.lwɱ^?tu?/8[Z[Zݗ0 16 $*YD[ O_tST[sd_;\o%p 0:/G=cG%ytS̞մm۳p-zδ;t/\]!) pzۅ.ZinV5g˼9.ԹbVc0v5r͝L*bߛvm ; Eh>+teמCwp\v 6-SXiV{佇P0ex`^85L#D#.ԷF({ɋGJ\ w~䃑Rxx nFSU҃&-Mԇw,43x9lݾ"B:=5]1A @k g򙎑&6sɤGuz`ve;22[r:. 6W28s8>cQQmYol0SMQ/_Vd 7I9}#2zf<s?w#u@VkZQJ WypS?vcLΉO?M*JԔVARP)F$[z$`a=2T,֣ƧyQN £ת5҆\ ES93c6/&@EzP&VX>bxB?t)ӹ+|So'u.%A؜qѐPPաN?,spaWa[^|FQ5jjDݱoGUP-AXmsE-> endobj 732 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 734 0 R >> endobj 737 0 obj << /Filter[/FlateDecode] /Length 2240 >> stream xZm_a$*7kH; m.+@lpeyW8dHvnw/bwkOogP^$ W+xC&$dU#1^~^Տr4^tu/ela!cI?zy|zc<}E֚՚٩,_zUڰPW&qPWne;V١ֶk&(ժG;jyvjY͏}o[&dom]8})%b߲,{@DD%? %G ۻB/FOimڌ Q|=}`W?Ҹ?t$,QV( '7E! Dl<ۼ4좉 Epa?}V>rX2m2twP@1RG|{lF vM̀w*2UnqlrTkGNߞoe*K3+̉Yi "soO2 `~h@+N@-t+ͻYU5?4 =Tޢ Z%& ny^N<C"OW0MQK[XSCGgZ|-k:M0N\ȔQG2"k |W}9E ´r@ FS&`>) JS<&LΝ!܌ v1ϝP?㷔L$hEeigm*AGKaXw8ȱqrPx6ӈS䆧M 9 F/~wKw%ۢݗ o»ɫAS4 Ëj{ izUҷgÉS  G+py҄hY.^\l3n#$jkI>k.fu0}ٷ"B9<2]2-el X32&eXLwIxYw䤒Z,Kٚi%}>.'όް7O5C ޞ46?LHvHdo}|X/Ή벬OviN*saV7MO(A-~?ɯ^ fSf$ P&:10JϾQxBwB[c|Khy1K]0m0SݙZ{>]CPhs6k| |~~I^H7z5 endstream endobj 738 0 obj << /F7 38 0 R /F16 138 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 736 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 738 0 R >> endobj 741 0 obj << /Length 54 /Filter/FlateDecode /Name/Im8 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 742 0 R >> >> >> stream x33T0A(UFF`Q#0C@D?L%+h endstream endobj 742 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 1696 /ColorSpace/DeviceRGB /Width 220 /Height 240 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 220 /Colors 3 >> >> stream x0Ea=d(>^϶x@Ƀ((!(!(!(!(!(!(!(!(!(!(!(!(!(!(!(!(!(!(!(!(!(!(!g(<}ϲEuw\_xne[y"l7i'Vomp*p'*=pS*c[d*|*=plnͧ㹃9(lx*|*=plnͧ{nqNcI8dI:\p;1˾Ȯ ֜HŔɝۋJG&*K"KI%ՖR󾛞ԲeۓWtDU_"Gi~w˯'\=ʻsh.6t^\K %Jg%ϔ %JAD)(RQ$J9DIr2D=e:ehRV( }c>O =oqO޴95:ϛ9:-"7D!D 96QRVo7QbQBA!wtrCD 9QRtO 嶈rD)<'%EI(jW3% QBQBPF峄 QBDI%#JY%E⭡&JD JY%E@wD(D 9 H|D8D 9FI8#S?G[G<_GЭ;(&tOv6χTO]|I2zM(1SHC'ro!JLCCCCCeMS$ /CPT7MhBz!4E⭐ '%M G1D8#jY  (!(%6GBQBΚ(G~1r!(!ge)5xC>A %D9e(H`JDrr,tY%!JQ&n=Qb( ]ni%& JQfo7QbQBG.D!D 9NQ@F64߅/:I ]Fe’ ]y.ŭW3ҥJx>ʡmK=k7(Q$J9DIr(%QeP(Rݣ!JY,{ϛx㕚ھ؟zw> >> >> stream x33T0A(UFF`Q#0C@D?L%+h endstream endobj 744 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 1935 /ColorSpace/DeviceRGB /Width 220 /Height 240 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 220 /Colors 3 >> >> stream xr*ar{r\,}~mm10m)ϟ("PC(!PBB 9r%J!C(!PBB 9r%J!C(!PBB 9r%J(___SyvϲE(kwGz +(<1vOyl扴ٮiV>o6$Joٮ8 Tخ8 TZv6ʮv6ʮv6OGJĝ'Yzק;C4x51x>lf(OF(=֪V0X|O:|,CitயTBi(},/8HQ*9RFRt+%:1׏-O(Xqfa`́ʛ?Ը̡G(>'S>YgښU,II{璌l˅R󳛞Դ+S:kBBU_"燒O29 33DsYy\\^aA(_u$׽LBأJv C| d}皳P>W1r 9 MT) 7( w}e%@(Pm&o>dӬ OެJLC(!gM(@(7:=%N|Oyw (qGt._c8rX%s!ġi"c9:'D(qGٌ bC(!Pv%6B(rքr䍑H0%BJYGJ PBPL@(1PN) LLr%h 4rCY&PbB 9,rKsN(1Pr&E(!#\n#фC%8 86zQ}|z hz5Gtm+vtn+ {6c]wZg3#KO} RHT%D(Ib %8?@twL[K(OF(t,$FڤʏJyHYǵ%BSvTSO(e5H+7ӭjh*GRS˓JRq)?MH~o%&SAGw"u̅n!drԴ Ұ+l+f9Ɋ55S\UPViG^_?,ѠF|#hBYY|xMSч~9ǹ e_t,nL'Zw{>'| % 4:+fv_14"kTmJ:U(q>I4k{w%k(+S~ sTe}CpH«7M!gp;\!,xWMU=.#SX盡|dM6rod1Jc#}bK\TZTiB\M{gK8ʮvI8c_ sT2D v`)> stream xڍXK6WE"F|IThC ]5Jr6(vIp8伾*+UYf}gOYUf4!s)g|p.F5Uvwkeoquޞ- :i~~wJڪuSfA (S[)q+<3^cqs}V m5$M0y73:3l>?s 8QݍTS?<ppmY=6 enYI7GqOL=ÄjeoU]eTѪnUFU:U [l&dl u\gU4!{s~UʅDv$#cP`d{Mz Dz$\_C,|&&wpݬ(^amy#mŧ xF`}!RBFy<ܪBG]M|M wq3UCH[j<IN@9ԍ2yjǙiQrtt0 ؽ>OpI`vtmߞ-{~"# oD]X9q?HvZ&ijI{8t,xb׶! w ϸ~KC;^?<&(<|{!8gLf rAJ^[eTOmI`v 0Ȯ1[W` rī!@^IB1쿊%H|{GIB&_[ ~/Dyq. k &Frg 1><yVEtP SwJa}}̾,)MmiBҮhly  aԟf<ɟZ{M LTERt-a0ěl|)v"j٤@xKolMBX40V:5$#iT%B-B, AEG$q2~!D$Ir0 zP9D)ѕK>⸝,$ G|ahYSۏL@N6JCZzQR_:U+~!B 7#CkCᵕZ'P7KBS0˫gH(7\:)PH =OH8Uja=wR^{%ةttYO^X[*m>4c(q}޿@^}BҬeqifv D{ É@[n"H&T J,*y7H)t ϼxr7?9 endstream endobj 746 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F13 66 0 R /F8 41 0 R /F22 218 0 R /F16 138 0 R /F11 56 0 R /F23 221 0 R >> endobj 747 0 obj << /Im8 741 0 R /Im9 743 0 R >> endobj 740 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 746 0 R /XObject 747 0 R >> endobj 750 0 obj << /Length 49 /Filter/FlateDecode /Name/Im10 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 751 0 R >> >> >> stream x33T0A(U`QC M,@*\ ] endstream endobj 751 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 1774 /ColorSpace/DeviceRGB /Width 301 /Height 301 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 301 /Colors 3 >> >> stream xNWEQxN"9BjzϾk"E>o߾Uuγ a0@yv <; pH8$gγ a0@yv ᡾틟 O~Z?Hx";'Hx";>U$gγ a0@yv <; pHxi}Hx t= $͑όև30o]}3__y>9yRt-u_=> 9)a=z?gvOϪ_ Ǚw.|30*4ȇ X0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; pH8$gγ a0@yv <; p>" endstream endobj 752 0 obj << /Filter[/FlateDecode] /Length 1681 >> stream xڽXݏ6_V:l1&R4QTٷ$:q,{ [wc>.D}3q|΍c0ν|K B 2`hGwX9m3;뷟\.'|!.96Y ^<"w}wyZׇb% gI|NHG[Y3OK#k5vmifkE.>䓳1 ]8w]|\XTyqYqR[Oo$#f.4뫩8J+w79>(,!}q0_2)a=Qߌ 8<Ao8.KP3n*)9C{:UoFh>_&7tӫ3jݕ-E?K|'"Fܔ3&bx˜73#g\:+{C|60r B5jK|ʼn+N&G ʻ ht>SKU|ѪBxџ|5 ,65Rajׁ);&pG"K{1񾜫`!N۔Ev@;a]}?Yj>Hڷ%7Rc= ˷j}< &fhq@t<8jw}Ç5.ٓ1jH̥xHs E޴ lo7$@RHE׻.N,+vMYQV13]rJDnCf7x>Dp Z'vzzs+نICVt9 G!"F[ֹ t ڨ:kh5{yӢ| ֞*Z$)(|mi7nMR X7Ur t:MM@1Ihidņ'Uy7y#"us[{L) < yvڸpbl<(Y(gyĄ>/'t! F=+fh +&=T5*C[H4ŴF}1WƼTn6y4S5 $<2Lk;$"Р6 K@ڝ8-wثx~ChptQ0ZOLΖ0u#2T*DKhwIٿ_]Nz f{(W9xSد3;kA+ЌU?V5yҰCsAStk,͠4U’4[xK;,8ij[Ш(l [S{&h~fmxm U϶O&u+@Kk)2#W␪lb0nCb¬'Z^wc5dJ lmDbBJp钡FH&_ϜŸ 1<RBm~hX,S#M?0&x cP[HL V;(еŖw2?NG~ endstream endobj 753 0 obj << /F7 38 0 R /F23 221 0 R /F20 182 0 R /F22 218 0 R /F11 56 0 R /F8 41 0 R >> endobj 754 0 obj << /Im10 750 0 R >> endobj 749 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 753 0 R /XObject 754 0 R >> endobj 757 0 obj << /Filter[/FlateDecode] /Length 2755 >> stream xڭ]@_ḧ$I/@-}֮ز!ɷ>;dj8${CoR ||)Raf"U&әȔ>n2XasB?|rTCasr~}MXR&R=ls%-QU)T}[*WI!y\8]=c MV0w§{ ?>0WVY\}0{v] )r5FHSEFo mz970\yy ܢ"5z0{ut %J0"XXrFf뇮i_f[Y2r#^9?.x9?yko7Y~+#xZQnA^iA4Cݱ@' +ب,\ ޑ-DQħ@O|6LO댄⦳׶qwk<PL¿'UAq[{=)x誶>wjhmc;c4rdptxѡB0W$`{|rŇ,N1J>=5é,Ri0 uRW;<_VI8=.HMRw(0 áy0SK>tFY\:pB<+ $ g'~p "?l՗R:axlka\|$r|fp.Y$閡W]E(glBpsLaʆ tIH'pj7@X զP4S"-6Z,I`ȈJМzN*BS_rd wǪCki0Ums1Dԓ3XιKîMs{vļ$@V2 P]0 1K(" USi- y e< <`cj cLl)Q'/Ls<8ދ0EĀ\X0 ,\g,"IԑJGPWp#=X&sCMA# !D5ɳ+x b5b\?Uq[:-v=]%H$3G{ݑHTKGUܑ)cU򁂙*V$,#wR,(A ~=SE= NpOs=8*25hidEw(^\LBPp CNcE#b{p;ڦYQGWlW3@Oǹ5Ȍ0=} ZQȈ9=(2Ȋ _Os@*gܡIl;^ v@T-+Z1'jozL " 1˸tOXWS(B$o|R+NVc}8*w[R H{fܞ ʟRDcf.  Pcnkt*3R Dz4Qp;*DaSChwnǖA{*; __pX;Uhb[ii_pgcE^~ۍ/vy.v0,3fR( 28ǪE0 j}ЫJg+= C ߉7{H0t}BMfpy'߿y][w"v5zQC[ˍ)fm̓]2 ,(wCվw nge`C^9岕Er]T[js.e%tŖ&h%gw"܁%$i.D/Ifchy6u?µxʺ^Wy9P ?%I'[9e]4wxN{s~-  -&%6+IIݷ{ tH 7+6ʻKjM,:,dTFZ d-Y0pI$Wt U@ Wpz6 yo˛CD׋ endstream endobj 758 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F12 59 0 R /F8 41 0 R /F17 145 0 R >> endobj 756 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 758 0 R >> endobj 761 0 obj << /Length 56 /Filter/FlateDecode /Name/Im11 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 762 0 R >> >> >> stream x33T0A(UFF`QcKkg```h`daaRd$ endstream endobj 762 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 99304 /ColorSpace/DeviceRGB /Width 232 /Height 309 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 232 /Colors 3 >> >> stream x}w\T((QA1`׈a=4v%vc7.؈%6TbI "R&gdz3w23ϩSd _Ҵ|fŝ)6M1I|͢m k_g3+aRE?bUF1Oyt<3?v iNll~؃7LkKK)RHßlpf؃Fw ptؙ[!=x!47 C~I{L>WƠxIb3yPJs7;\ɒmJ,omKg8F[ؤXMQ@*}Z7 >|bo"L!}xƪL9ޡ δk!mX-VF#l8s6ɼ;ݿɭwiF1q'8o¿>ttKxғ 6 J?[OjFj$LQY ki {@F"Џ$PxT^{| |FwO?+As-\mT%.^5zN}ߎ˿afPzm,SQ+hpa& -j+?F͍+BXn܂!$.0~J7C=,5(# `%u&da[2!= +\y~feVp  Sj\ x5Zl(,a#$(@da>3SI/e~odiٖ^p3> w@Er pla ]N @$t0[|1 V"uY $b9T % 8YE"=|'?i-mϚ;p'@M6B5)У.+N6S?)! "bٷ4{ @R$b5Yip't@0"kQ6|I, Dh:RB!I (5•6&[⑸߂: 4ĭC*qbI]KLNab=Eʋg;Wp̿>-R f4l?<.mm$猑Z`B-QFfa$n›x{qsÒBB ;u\ލ0! 69P\- 8pS$qq&ũ]F#^&z7x+ (w5T),P1)gq ܤ : 4?| ; .0&q1LQy Uy/' O$ 1) OP\5q~ 3Hѣ.!O|UI44N8#Dxl8}vvFrh ÁBf=/2iRW,QKs= `)t.cRVG[:Xv-e#jT(~hzpJ(pXƏ]*1Kn#+ ^I&]X=.w=KH:0CZ{Qa`JXoSxiq4% 4AډU-/豉 Nq"Ek0-tV0wMn "-tT3Q;mIB_<{ Rq? cTG14K1-B#~Y<I/R0IF zЎ_ɦAJ'Q%+87krq+QIV- $Be'|G.**X[J4+mf?U*h.Y"*Y@ ZZiб^C$.5I9عD$t۔edb*`6KT$.MV\ :mKa,=xNbzuH4-H yoPhlşuY'YV`Gw#q#i|yt2pĆa08=0$8hLB ?tf?q6"ķDn}iBUńUK\ܒ0WO"l'NdcsmJ?iwQЋę 4.yOI-B \NSm`l(w Vi/:J2iPLNrH0 -j9;v%?I aX UhWR.+DމJGl^sr[UAeP9S{@ă%}+:H蘓P^t󏷋ku#C*#4#m(^N:&mq;ԭ{p<=e>5 wf@4Ω$)X=M`B6/9 -_6h@:µ1|doy!W58pFu"Xy Xw\0Q7A5h6.#-,4Xps zB0oܹaZeE~R aRˠ)t6Z)xH0ͰttWIW}[\rڊr7FJ3. Rdoqng<'>CW ƉA\| IfYW`%#OB钠`|]in,u2. 14\#J0B S6=0Yp` K#ۋDsɸ) o ;\LV,h98KnʠYa9<$YHO +I/\ɿ*VR6Iy F?FeG'w)8TD&5IVaRO}B׊$98()gpW7#M^Z$bc>sv`s Ax+Qol*Q<*y 4<@"3([nwh* Ulpjak!~ymC)uv}ȟ]`ғ/QB(8A{% ) d}UBŭ3 d:$_<`G$ޤo b䄔GOZMq -5>JU$L:1S_-o0*=;6H 0QT NH;[Om[ a`PG@M߈,b!7 }u/|*'7wtv]J mr.57!t,oqF6q+I\6ݠ\HTD IWZ籝i9 /tV~Q,r a:$G4JPDb"\$ؾ)g\--1WeYxcSF궹\uN?HZ1 QܩJ\ś?Dt7ą.7NI]ʥ%ZI9Iჷ&NsbugI=oFhueK=GkR՛ &J^2[Ȇ`tF*[;pdj"6>1.L%l`0.yUjz~+dj`R`DմΩ14WJ5B-'iZ7/@ MZfԀjRzEjA0BY4ؒ1c!g-tIBK}]VO4.ȂO-kQ4?'$ >)L 1q sE›xQh>R./́KVz7Go")9lq X<VEoD*&)'BcSLR)w.B(*WC8ȰIX(sJRHigDgv߁|EJG"A#~ ɩ/WyJ$e ]$ IY6NB3B{IES%BRhjKFW(`.tlXÍ@I3+x<!ЉJx8nB1U%͟R܂Aɱl2x;:,XqPO('s|Yp#Hs`> $.O@ )@#X%XY2MR8E%:|#t]1Bo blT\J m8T+tf]zj)L6<|$5ڼD|v {M g'qt<mR^5{ے+i]I -dKؗW|(t߸G+ıqHσ$c8W`C[&eQv!`Y^iC!.*t>& $}*h8LFzʌ%7KPX2yF6PⳃfV@9UG}7ٶ(4ILhIeqYz9l ^9:\93憠a&?^nEYfgkky5űAK~Q>qdqa  6zRПxoHb%teu d~$OD' ʺVPc )xibR$qI㄁Xe"Xhe6B93RWфlXI),3zk}3;L濉B%Şr$%tfDi#W.G6[ohSdE U߄{ٿp,U҃iEԦ' UR` !'5Y\OF\.ZнG)+mrll}yomvĻQt]%F!\Bq+ :Ym.\8WE7ƩKC"ӽ$2-'_?hu}f) z$4 C56ܥXo3Z*x*}LzfI"I\C0ZMSkL(D ko_',`U5yqc vE]goK9UV!InfOxWS`/4Hy%DX8*Ԥ5)bAr^g0h~v# cBJ` `J'7pE' Ζ՚] SJ ]a_9d!A 6(ax%Cs44]@CI\ɱcT/<=^^){e%fZ7)mb׾]p-?%ܹSF..F !:x)Iؽ<9 Rn3E F*=!I_O͒&[5vQ7cjtIB,X|v\]]#hVq\SN`L&M49q>`bc4i[288tꄱ=FNcױ6M~N^97n9<H߯mzs:Qʧw2 (KR'Mc )>AjhKڀn^jU rsNYU[_~7NC޷\tՂA+# apT.U:T XpA+'pchr0gNU K]A&6U-pQ 0$fOc1jvU[m|ԡw_|'|AQ[W3Ny8 ӺS=|غMLLښݺiBc%ձ)(gdEWEqTbjTRM Wi <OB'K|U5bD/gp#V$=|RBձmn([*/. R&e7}3С٠#H&!5;qݝW~F"<-fMȺږ*t p`'~A$#_G݈LJH=Y/z^ #[ dHdr|}ޙj]sAtA!MF\ <=B+4!^,Xd夒?rNN<c,9u<8\B-w@HDTL0:H 0(n;:q~ }:lcGNZ%|x h%z$|{uXӋe䀯uٱ`I-!h{EgL?%p^?>s߼&ۧ߹yO qcbbE'<}өDEg˻oR0_u[gez jЍmz-JwJab\ɃM %,q3ZFW(ijBy#)|̘ Jil^%B+t >hl<-$#/Uqh򕣓iFwc+ .R|pɭnҾ_GFE)%`NwyỌ ʖ&E22?٢i+_ժcާ˶O=LQ,'ԉctr<DZ7\Ӆ oy+ƥV-UOGBY" Nė Kx gFԘqއ<wJ&0cF1PJώ+X/:߄-1WBK^)\;mj !d`z1{GON6~8྽O1hLO7'׮-v#[o 0(lvFnuTXfHh8 ©4(g6"gqC&-p*mol^1O2+6s~:GBwIJd Br@ېĊqɂ+bdIjJwBWBI9IU[=l!e=ȅ.Ÿ!.+8:y;$?U-/5kqZd厍K٦?z0~-0 <~z]-bPޡSYUjYErʛY㆟ Q Gq1MsV=YA[iUO%l_&-S~OMy>/wI|`Uݼ*u\~QȐv:HToH.Bdv/H>A/n*$-ǓA8f( aQ^2\ ` Vmg Ygqs'4(_úmqbעSHvĽw%PDqoit"locSaGκm6F"Jv߇[g|H\q)kZ<5oоAZC\ݝz7j[ .dB3hTcnʍ}(X1B1h+]vN$srt&\S EYEu sƥHvOPsK$/k'%$&nqܺ~2HK]k.ك5i^N7Q繻yucTkp0@0sH ݿobbVV3N_TJ8[\؟zi$tfTH'°TJ e@ =ݶWsvÍόA6WxԕS18#Qp_L7PH(XɭiFM)'2sC]AT:*;:mjj `4>/Μ:k2C\AYe~rh})ǖ5ZwIR}A rݫI]NI'ȑ)H?l (2N圥 %tsP,7Ι6uv *?n:Ub 6uOy\Hpblr.fq Om+ğƛ랔l3߯SgN㋦v={#L>q `5DŽs* %<4dgҀ ܹȑo^-icTM·OG0o`}=-\B ڌz+l[7ح_¿O^ }gK !B.M"jǧ93aY(j(n;u砜~JS> D0Nժ+ odFp0YX_Vu[6o_V!AK!j"BrR ReHtR,RaÉB.)GT ? ,>ZK_*yU+I\0TNsNFptDz CBo\enaЗq)rI{}3*:/!}WMWw)7n?@ }gX: -Tɷ?jdGOb5V? . :Aunn-)ysf{<6$Wp{W)rG?|2₹x5jhqft'X9A_2H;TxSYP t-얶;:7L1ן:~~m3c(n71́k5h˕we@nY̏b#\-J;[hf]Zs6i AҤ(hc]vtty)2\+c 2ߐi[D]aJB?S=F{TZ ^3: McЦ>bߪEab]#Ϡ5ncB7%ARߊP TyJ4r5mn̕+Wv ~jX[ul @U\xbX MŘ;{,+dUB\SSK g1ɴn茫 :ږ4}AT*4SM'Io^ Pd{w .w,R߻M?ԭ,2Ք9P1:ĭBni0L-HW$dXI0>Y+JH=n^^*=!b` 0^ ޠNK:ϵaL(G=kT4!qgt~*a [m2n T(ip?ܣk嬔M \7(P@~c. A_sX̡}vM僚aV–zFzAEģsRԖDKHeprq)[]0j?|._J54ՠ!cԆW-5~  h/ٌ"ݺ[7yU-[ krK6< *zC ݜ\.[r5c\O- A?x7<Èobε c2 r4')}ۖ̂3`ݺ1d3z})Ὸ.Eo孧M -iUGw:0`S`upyHgQQMX#᥅3a.xJ‰A՘ǟ+aZX]t= $%bgɳ\] 8(VϢp 8)#ӡE^  TR6>$%=KȎ#_쌉 g>}ᓘΝ1Io.ylimxV*JG:@xlR &{J4WR]$mT-|y?BZ6>/ŗ Cd'%^m:|:pOef~z|WNϴ|}ѣ?g|p?70K asZ +L!7n. xt-&1*Z K|ȸt;lxIp c9bmy5$ 81G:n>1Svգ[7v]BX9_ |: Uһ#6FyXФuWTY:[H.DBK"]DU %"x dHN2{qq/mW9ι?~1v ]q1Mto*de+PKDY1]v,J栴@+Үg0{;l"0oyQ$'d0FEʀ9̛p _Q1|ݼbs~u&bT~*v n«+Ə!5`/(eՈ?lGǙ+ VݑgSuK Th€Ob!/-G9n .G WҘw-칇:Gw_Mwe9o<=kҧn1aBk_,Whky$0 hM5,#76.E={k&RhsG~N\}AAMGV^'pr4a״MVM{nݷkW|pV:s r kҘ(%(RO@i,*y!_rx%Q urI/J0 5W0N!r]bD5r6拭{]l_eiU|ȡm:DKwcpMz/)<_,0bfFx j}N% n>qP-=(dޞB֋\«J s7OUkܴi XX^n27g)ֲ{ qJ;sQQUl>LD[CeBg&˴ ԧ%e %ZjR$`8HG{e[yVggx~ `*oTP21(>%k /XVCkfkO cdZ k'R6LQ¼lw;XA[83'0eC#{-xb**OY0CD bmɆ@H5BQKx,n%~_f% ր| k$b WND(1Ιljx̭G0Q5hJ^oؠa^]?+nXuEߺp{oج+,3ܜ\cb{>Q3R pЈE 81_`TEfV,ێ<f9`l4?I1It(MIJ5X'9ZȆp !d ?fҘW`̀}r}A*d%$Js8>}vߌBAQY9tY>e? Al9hA$e [Nl@h%74l\ (/ [=x_<1`Jŭ{C̷pd,g=lHrZ: mя&ʩPs~$ǾZe&k''<@]Rg&#]IAT|6\oE-J.>1t<VQxʃi JHH.wH9>6jՕS2?7) ޡC&7p3[q6H>-]j˻ә2Xi򺷑,J؃4X3bq5ܝ`/ȏ0}AKwL؃XDfyʙ{k`&A @$Paz=|)/bi2`5lߢwv= CywEtȍ?kM7ķN.C7j̉)(*djdlƄ$nHӃja @rJ'|ֲ/ t܌=,gTt\%yI8Mf+&qZ ׏l{Ƈ#FMڻq`SnJ4V7,p h"Q[9XvZY@4ښb9>fdGcsW $ż!-(q1~D,2$?gZcNkCXx1 {6.5 ѯ޿N+`?I%Tq()PU77nuBE5ݵJ?lRzaОZ7sg̝uNsacķݻ+3rMYGA p) DYwSbB2ujmD9h[v߁dMZssA(Lц0A?\z\HV4w:Uszȫ[x0:o^c` hL>_ξr Рjx6R(-J3{v?:6o9莀9޼ m]ğ5/Ǥsߛ:Tl%t7~x$+%r*nɐ/e<\!2| Qhś{pPО \[_V^ ?TVsI'bv2lD~L<۱e8- :u~~8 <, 0QB߹yѻmO u{p;oМ܈ܳaa*ؗ>d!=s֡ s!Ƃ~\"Vl&H*?jy`\΅YÇe4l "5+r\AQMkPv(^qF^_PG 6v?pp;Ç{~_A ]6%Ј[,D:+JcQ Mi}O|Bƥ;q:H9!PXZ/6(SoW4Ze| [痥-z¿Zz\yawȆ>ztlg3pS; ~`P;_ًwmCp `=ˡ?q.zh`gWJdI~_tj@X"0pHHNھנ9vj%) 4_`\+Ű7"/w: d8{dy^׮plUၰ5=jիY0hXV#Q1}\iI&]\J.Ju518+] iR-ZX09&qg$:̗+pb'H.Yur/xUggJ׍@ g_"6ѧR2uG<{k{vO fGt)_(ח@]M{Yb`~PacZq\7*~%ZA_:@GK'@{x8UtAs,!8l\j ܼ(tc;oac5׺2p!L\]ԍ; dvPnWKF$e9ȖOoS2(0 R*W, iV] /V TꙢ3[|nz.fku>wK-wި)Uphdߌj?UF̏߉Yʱ3,٦~3!Na)4EE&z\ o tYHCё 456? |tǶc ލPv5)z6:&˚LK@͇@Cs2v %lQ3c~fj^IĄR~8qb#~hݺnYGoi7ԫb;O i"՚5+Z<ź` .=<.wWܶP8J ) GzMf[n|}#4ms1I *:SIzxjw5V:炶o qXjO`raHՊS)>7&Shg ,/>2 Zqm/)ip)gc` s&a)܃%IJr}T_B!qp6fh̛UA3np/ާl]۷K'˞0碝 ?RA,X+Y`!AB#pzz>@L^r²`Bgǰq]&$Y5- { (뀋ㄢd< ;%^t9jW/??k11) Si]it|&cvߩ0V0Ɍ@/ Y$%jUCzz̜yOvn\LEK4b!WeYuo`6=TDJر0r|#:2}˜מk tևܸLKIz(0ܱ́ Nf.u2Pt/ƈ-YѲּV~&6XD|dO>j V7됾C\G ֤iBvIQ5 B71OҫFJlQ6`kI-ɻ$ΩBMI3/LپK_rِI jڭ1~<[ƒBnx> fGEi ʸ8pډOݵ 5'$(yQ IN_t0/Q<~0ڱye<*w:ݿHML:pBӵi< ׆\8g|g ~âD 0vW|kÀ R3`)3{t)Ё`,"}q#1qMDpд@'?|d;G7,KFzOs ećSb H٘K{hX8 jF<;5>2^#>CV2tt_0d9[~J5Dm&$AZ`G%pA D戮^ %qDgE= ̖1xmHN`L=|(K/o7WYa:Er=o@\zgOTٽ HgD]kaOqG8U^0Z~h T_N$_g =r߮_yzU4»euݺ~@)9}J+МJ/Z]CWxᡷBB_-EIM!u"R% ,ł np/&ruVR#[OY,sxIyv̪^g3}}ueƁfP^O xͳ~_}|g 54̧֭W"( Ϳx;c)rNCr-ZתlY -x.FS9) ]GhFڰ A1|0Gr&--9]vϮ(j߶wˁkKXʛCԢDBYlAJCեZ\J,SWp&U6P5%bsC_~/vlg+ik!ӊUScmҿU[󓆌8jtȀ ?q/4OjI3F&Es32˛ݺ zpK|f.b)Q!E9Ԑa~G~\pd%,ZM` 7ssLAИcg1@`j?Q>Rr/UFOtyxtvTAk5[>oGq1Y ɩ'W8x;8g}`>Ǯ(!US V(`10j2+NՑ!H9S>TZ*[*Ħj ji|0@opiiQ78"\'_9}Goqm5հ{۪=.֯ھOփ3/ӰMGYzFo?gdqHdFOnFܸQ ͷa wu1|[hAa}Z n+(t~8$0kO۸-h0}!ZN KN c7hǵ6qrn+w!۶Va0=d""[Uic\Q\J,`q /UjJ9rF0Hǔxtg.5b[uۣKmv/]û!0;;zux@s}z=yܵizuuV.xyo \ʳ) Y aڛy k :/55 P<#;/ΖKm,lΜ‚_hW2#_1=ķ'|H1LBCZǼfqL%@`*eޠʉx1~B_OwM>yA.K}ryx(&m_jrFTtW']#~ #H -PG)W7#ׯV!.꾽 =~>ONM8%}Ыepkz72m/= @?+ $RaA'"N0e [E83? G҂r=#hԼž=b%]2`ʏaƄp 0;2uu'4 -6h?bOzA6B2!8%(y8 Ĩ1_ pu1]Xv>?z8Z0q dS?x~ʢȗ߼֡uwxֳwGo6v;?{`HK%ڻz>Fu,=u~3ZĩGQx]ME/h٢GyQxj5th" _< 1{7awuHÍu9^Mk*/LA㮫kMרz= gi ܵGПv_ ˛xV*S,ER2e-%..bK k.NZ_^!D|\Zʑ#{_K~<*8{hY΀{>sΛxmuͽ>j ozt}ӑVqi3tVo @,$SP\6^;yE*BL?~_;7rYɻA58'S܉fܹ4hAVeb#G ~zt[G!#N_y8 QdGhcKdϰ'a⪃AW/8™wn@iTЪ߿ؕ0t^:(ϳTeH+<#BJvRWi'J(|}(9O|[ j)a@ čzzYg_X•s[{.%y7Yn>Z6Co^}N]T[vn݄y3SJsycab+TB[Kq ml+'F͋t̢qh.IIJB?|^1\cfع7S/B0: kx;pѲlѰ~UW8Qǽt;޽+h2ww?WȰ5]LcШnavY$[ "+aoж7zY!M2Je<[ظ]bhPw&wfc÷{'ձW˗+Vtl6??1֭l/d-ǟ: (D@_kNvvCB=oĒpSJϙ<^۴ngW(D]̲Ј+IhM6b#%R>ZN9s'~$A8jĀ5X5qq1,(KJⱄ*ًJSUH, V͖ I=hQDI"3o33眙u_s2x. V $J2k4.&_#q2|_@7+' 5_1ͿUt|@lCVp>}Ji4LLWr|دI}E*Ĺ7v8frfHܵԌ9ZΑ&GJRmCu-v/Y3&0Z3 ͍߀*+H 8dqRd.2 6 6}ozu'.,!e|re>G acqsmygjƖ F-0^eҹ$Pp:?]?/~14$-0{5/mK\ ֕׾&aYV0V\KltllpE_>hW=՛aqq#jxh{Yj2WѾI}[? ./2Wc/y+Wik<|:['e[mh j{^vmDvx/[beKb Fq z6ʞ=`mbw$:А1AFL~Y2D3<5u̠7ro`8)MSSSqsuw[ncAT>`_xh7Zn=Jgyԋ'U$Nu8Ge2YX&=Ksy|.r(qg/EFL 1Y/PY.f-K䀼A vi(H*ŷZ*oJ?f5 G ShxGK|K8RҸ@ЧIPVlj؀n9t32 X'eRn+(!2GDgv<*V_ST%Cn_=U~38Z} j#iOjw臐'!) b / ,JC c4wڥhxG Ov!/Gvs"uْKt':V:}|.ZVߘ-M8W*mu;GeԳ`z?Is1#q|vGFi 5m7FWYI%L;X=)BߏM_ />ξJц?> rNaM8UgtN<䝌$t0\-P)ё][Kq|F":.2J8=LW}M3+|y8:/+OvӐ%`^e+7"9hҏY7^],P],I%颷f3=2C/EԼkX1^z΄79mdz`ʹUiIL2\E2ז^L Sb ̫n&p-d8lhm-GLOVe a:@ Օ~# l~h %i:s #&Įꔩ>a!.XmƸ&=@XpY.^\Lhȉ@smi3~ =${࣡3dȔ*ۏGB QUt֪J#Kȑ/ ~^^hhHg.FOݡ0NdGf,>ku>! sjΚ>>U3i"D8H;sz-=[BB[(> |Ν[0^M`zi(kt܄31YD0wNG3|ob"C- J$~.\< g$ut1eEf o[ 셊hpˡLF[kXVXqk 55Tsπ o ˕` '2<4E&]iβX%<|By1E'1cY|enVzzfRДXMPУ6p5ūBj4_|,"24mNm} 6CLFEJbMs֒)A3.qIu^JON$:tHO1C?6nF,'Rq,"(x⒚g~,aa/y,&мΛC/w<4őw!#;Y:7gm_Н5yӕg>HVK`!䈢D_##OrrKKݙ[l0Pq(vޮh~r,e))jW;v75*\BQX!27ž-M th#hk;y>Lg;mlG0J${@11;5z vH~7w%EQ+luHl+W&΢:TZ!wC7yYU`5\b:m2:a3 .p@9i~#kNUVQV;S a.FXmڼH{iWb8wVA٨ ?m;12EouX#ޗC}ˋ&_{[Ku=)F~aIŌVJ`0,=f'M \I5ɘ }{g-Ĵ2:d#5mo$kx;s}n%'} ={[ʍ;bzVZ,9w^GhIM8帥a}A7Y固k Quev]fM?j7 f໷Mqg1&N~i_?DxLgܧUͲMx=.}#v;{:*2祖7qr4(Nx􍳰cp`%Nhmc' 6:6 "c͝[ uQBۀ/K%L,P]G`XdL< I-(*u 2b >h}np `~/Yfr8tꪪq̃bB} _v<}M~3OúG0-;qiKNu~M|Z_0R/l*^g JJ6. 4Lq#i0w-)ݶ:Plv| !@'H|16v]qڗcX_=Z~H$3QXs= 8~``8MWSliZ R*rF|c'L-s([ WX/G@P҆*0<8CFl  3cI\yI-k h*J>jg+I OcH`ik^g\ _wf  DZ?t<ڳE[Ssy_1yu]ynRA9h gpQ~d*40>,{Ueŭ-`kÑL7r7eV[a[ܘQDXlſľeb1(̝a[/>-7c9蓽שiZE^!z \Nd8횾*t$aIٱjHEnV ,};㜙Z `a^}/\`ڙZyu_PJ|ALo7>z#yz`] W<<8;",*k+oadv;j}BIE{ bلogn>/rŘebn̓i׾Ifp^V )%R9/=Yp@r@]5T6ݸɑtcFk 9G8hVEp~ltba\RH.(qNկU]Ɉ^x-VKT\._yvߕN&V|Jݭj;u>Ν]ЫhDsY!BN%L 9؂&&Q#{J9{nH"Íi1p?_yB']o?"qP5܃ȸ(JmnڱטG\Wۣǃᶾ[ah" F*̅-yb-ƪOk`K =?Тu{ಕW̘04 @|}cǺ#k/uH^C+='/$dm,'dh|EϮO̟N8|wT[cC@H`Xj~c̛m_, 3 ˫20H0 ނ>}m}脲J7g(Wj1&oEsuÊʪshdK |+Vj<+4]P+| ٣^4ݻ$+N?7Uےh\ض[ҡYʽ^^cm]QTtޙX`Au/-͚j"V]4Tpy;lo=,ܳoY @7vvҘ3dﴲtސoW_8vsӉ+^ge\0TiWCx}lݨ[/Y/(?/'GˈN?7qK{Kջk8?T}9Eq53Fyʖma* n*^$s ~aWApE V*8>x S~G֭\LҮ;q¹Z|CUTѳ060,6|JV6HK+CA!GZ@N$+;bL-NSSDq-VM KH,W؎͗7-M2AVю NK K88yt7%Ȯ' Ƀѧ';ɡ~6nҶXM:IU%7yjk߳mVCXgV`sǯffqZθEEj0lfm0ta><+%-]' ŝ%K$%`<{gŠ+ x˲绒Uf\ajoSҼcZ,t{XwPSa5.2[Gr Qaٝ:ɴ7^R^@$zLnkq4end= ;r-Vx07J6p!1,.U<{BZzldɳƢHbѦ!g$h\)ihe盒K^o%\KpXYn|QMO*n\;U1=jKԴS/&/טY8yiz8XdOнM޵IQٵW7@"b8~K@1$nD !__tZ:+bV#RN0UL7*(Hiji- >lMYK~lhN=.1!캝ő\GTЩ@ NGimi(-A,Kr=Ƹ}SgMK={g1<)/}Kj7,1ߤe!Z==Sdqn|^[us hOy`f]YOae Q pA-j} f%CtּrOUܳw/.9H|h\,|ui3镝"U~E7N V#{$8uwăiбY i'vGg0\ܽq7[+8IN_em5w[t )cz&(زj;)r">~{naqcԜL9#L턕S޴(SVK|߮&릍^HcxC ۯ tBNf\-[cdH ͤtwp7\z1DN՗\ȀKË^Uo1YQľ.>992ԿP".>ᴄ`g0W%k$eReFnDQ`\t>0Zi/Nb]Dt[%SUΝ@ ]gp=pҹ u,['ݷ///roV̌NGO^h0|W|֡iK)#ϡV¿*%;@`c/s/>]ғlod1WfR22g[:kk?u ؛w ^%cE3OƥUlU#_|~ғqE1'!;\{Ѕp4/b0 @SGݹU L-K@96Ir=?lv[v )B@*sfkLW٣ GIQ S`s$uRӆc ^U4y{RtF0/ŝ_+ãky%oWk|dg cmW2yK.ht5g׊I\dzM ^πC˭h ܻ7\ríYkl/]:fhۼBz4ϑ7(VXW[+RFNr0`[ya4hV5`Y,dg9eX@W iZfW\ Ё!ߵaa&RvMa E)ytk2.vg=B*\CcNi7dr_ěIؽwo2¼E器O-NN=RYyՆ @: ȫ 7ہr~ F*cq:nqnc-\좠tL,gB ZIiBz~)$*S^:99bIP {}o~rȩ\ pI^`+gHo~"^aczܓps{ 6CFoXs.kr{p2€0<VOt+ /g{M w.OFȆ_W$Z| f{ N>Uqڀŵ/BnxtE[Ϻx|_/_^yT8{҃vijY=Uᘯl22_$Z6,j姂BhzCaK,ȸ 䍐E=I~Lc$ 8De\Yk01ϋz56vr&\֍ 92w{)ZɝSamil`ւAsܹWF;>LjgËZ9LH`e`J[廓e%t%&ә,/YoBO@k{e1>UO*L5|c,vnZ>9/b꣥iS"h9SO ӰHaOG8nT232O~{9*Y `ap_HtPqNFԻT;sCv^qc)&M;c֠CGV4:UU4UAʏ ?^8,8|}̹NB3^'NЙ7hsz- ։c'{ _,9vɘefX`.v&p|ɘ5uupSD s^^L.IVƷ_~'ʑ_3"f1q# g^+f`\Xi>E{+76,-(v_Ywe .>-#ldT i݃;!mPTx7$hs.puݩGj'^M)nN>=.Αƛ nfFNݸL{^|t /O&yrlLDۍ׃ȸ+'sl};,Fl^N'OhkN1py^vg ԃMdMhiC0`͇ϛ.ο/?f8Í &W  e3]z7_D!qZsK d$͈>cU&T@tKml`:L 8 /74%e) {Q1OnP }(/L D`糚Z9ix& Y a ,!ܳU7'2Tgzf#+Pp1!ݷm_^sZC붟?;Hx)Uf:l PwMA n 0CggPʖ &KYx:&ܿ;v5(_DETrlҕ4={0=A؂}*iO_<Ҥ>xY?6}%t`h-A!ߪ/͗@NSn{ #g,]#jnlRP6B "Ϟ3  \:wmiZ5kmP 2"C[Iy.^l_5>z:{ %Y{lkU1LPJCqYM+7#ҏdVhڽ{b4&*?ygaZ(ך{_DepCxNӡ_i k?*h So Z:ni2k]t:k;Aq":q)u?}–$Kfg;jhhpj0 ϲl_4'Vؽ5|+LBKqj̙jD'Pjz#.o8 X:07FVa f3r2x R7 {ÞF"o Y%jH ̬&y|L03Jדw: ;o |ZhXL6l."3<5VwD:~U䮃1#Lwά{M 6=p"4߱4b'x;4i>t<6sZ e ,**4p>7O%I8PK~ۇщs| `(s>3ձ)]઀q{PlرE}%w#>+RP:*²=d 8#YdeedӱbtK a@k&<S?7zRta+~ý}.3D7x͇2wai 5JBWh\0{EK˗niWjj*=C[mT7,^`q.zʦ^Z}4JM fZXO78x4 dÌE?={.,O}l/;lDu}M| ΥோF=Z0OA}Nÿegm=U,?,Myڜ;3?PP.>)72p0`vB0#DUv̧$ a8)dӌKRǰ2n]6ch9Q`: 5T19'p3qx7b:<-&j\;{'+quDmo7mp[ pC%0LW@@Y!42#/g~ A a#ye; u0ON&6o{aa3:_jy.a.]﨓"K\LeMHn~OvSw%h 5|o%f߮mr7H|Tj\32D"S],[ا̦15$9:~HT *"zw`hy]qiw'ۮ:=o3ނ"ig[^ɼdČgz7N8AeT9< XiQU E4dY{~]&0\~Ҍr%_dtrc*0I%PtYKcC>Vl kYnj19Sf,~UYiVsS4lttNӢL\R>fѩg#ᚥw|ii643:^WoUY1vg6IX㟰#"@ 0fBt2Z*lvU ;^J#C۳ks=x˫LNs*|wBvՉN KݲġAj_VV|D$?#^r115+?${l~s(01|(?;( v7PZq3pE~xlw{ hh"@Hư|"ThWZ:WCgJGc"<8Ci /)ٍ ^3+&+Q_}56ԗ.YlldGEXٞe  м鍠opWgo+V d| _L'cZ ]K x}aE ,Fhi52N6jڶNJVsk9͵Z g] up٬brJͶԋ$r"S<[8?.(Ե沲#kZ9UibS% LpV_@X> ;5#LٻVp_KYQj"!S}'/Ԝ^3g7!!;eK ƳRCυl=y =f,ϟ;FnX |IHR!)bMz%-]\G`~ܐ[W_ȢF3(mx88d.ݮf ͸? 0[+ش2U rʋl3hf_jH+H^mu3XivIY*@r 湷r%U\k֯y[UnPox%J.=蟺 6r[EF=Fo|q"3|Z6SbI"`Tߞ8? M!sTakLBIf!ptwi1J-5v7W'`w|#Ȥ2tb;!J*[rWYVӂς̸]Ja$s53IT)b@9H Sw]YP(Sb;GƔ3""Nw<|lU T6a'[ C/ZltXр [l}VbSb[cnF{ 7?uֻl=|ypp^n}A\TBC]ʿwN")q1#)Ye`{H.Ӧ:Wl 'tEΎd`& &q  N gV ď\mAiHS6$;`7d$[17 _0E臾+Jn_mpz^ gަ^堊P`TnYXKOH÷f\Fc3H7&f "B|{SN'El]w2};by9djW{QU+OpA[jS_+Xt}W=> W@J>z/qG7Pӗ 3mЮZY2.Acko[enCh#L/{ IIk̷xoQK|:m8ZNط;7=IrxbII1>`տyclBwC -v35\ldab?fb6}ۅ Lݛj8,@;憅AMpɻ$Mdd/IsaQZԷtEmTb} 9XI rz3֗Ey`08ůo#? boGj_rqGdn~9C//u@^˚1 Y:[;_Е6 j_-%Eɷ7W>{t)ЉP+[ £'OYrAI)ŀԕB#HON;@umQ_q44| ްwSc[~/: oRsB7mmܑk 2a -;lPUhx/7ҵO&ϳCnq͌n]mSk{QTu߽ ^NoD{Ոأ ;`6#qIgoU]MjjYܐnCsSD3b:{"-# I'Ǎq⿐p&ZWȸ̿a K/+@`SCSa{o 9$$N{0 WkBmW`N4=v#4"c=TUz[M]\GHҖDΈF@#E$DW &FFG E=z.J [?Fi>H2Yl[Ee9dv1oM֫󭶲ci0mwWP0bñ7#}!3NrΝIFC1 -\GӟkӋl6%@PCN/hT;2qʧ{3mqWLoU˴5apJGq֯sDQrc5ج1ψ>;Yڳ%cik9U^h!"_> B_3r4:6tbӠ(98O?@Z(PGIq&NMf)1'2:_:$\rzew|) `1.m$*cwZub)9{uF]<y8,pkCx\~ ޷ڸ<9R2W}iΏ;v!'lȕUeŲ >{:5fa+iQȸdGoڭ R 0Zk+: ]?4;i\D7f 9 ?D!ҏ2~ ,gsc'L8o76$7ϽѤ?65FeLA]QT4TLJH&Xk6QH0Gnh҈dm,w(׌.Aq bI~M8hE;eOP #ϱ7<|4m@',[bZlVZ}9^M 52i}]TPraGۯ_\5  ֏>_pÝ{Z]_Rbࣄ4M666{Rt HrÕ5V,DcS8I,0 ikO8ulhh\"9WbMH,w; @|ƚo9ױ(#̓5GQ[MkT\ΫP_SV38 f-!đzT/93f7>ggnYH?Ż^Gѹjt6mW솒YĿ\K"m,$DfLц}̅*s. NʨȸVw㶲 rW*3,6:o^huy+ q@<οU" 8kX_ q.p6pa:9/mCtgN ieEn`auh3cw q%#*8^t1&nJ8բsi>. WLj_$&9;Fr|Cae()\Ws@fr)=killl qb`}T;Iٸa`m5"2FnA kV}ڷn7ʂ,ï>fNԉ_2}bμxxTӧ?~Rrokksd~W[a*+1٫t  a۸n'X^jޑxv0&"ɴYو"5.ǻS4ZHx;Jbk1t2\q3y[jSCVB*V21itap.3Mx@ =6SZ0bX&, #Y!ǻ ʱ(K'/23X@FJ*Xt|Y3h H$ok?ەe)ZL J]IWeS\d~w1aɝϏ8^R2XRwȣYG+g*N2jձ{uB*\V:*恲M? MH_b88|̵NgAݛj읏=Ʈ$+kq.5COjb=Go9qa LkF?ۖp,xwNP#g87"bt -6*O+l8Яpo|D]QYiOgp3V_vr8pGEVT>{y4byYwJUW4A:߽h= o A?z4ĝ/!q4Ve~%JY>cl \68xL} ` ~o0@~jA1J~CA, aX% ؚ'%+[ "./| tYˀ;%#$:(zBޙX+`s(6<%>=Ô~]c/!ɯ5jVШfco/bB nnt4vҤ+H? Om`耩 1'6+=1Qy0Nj½xOPةh\NTx|S@?Pa)b }0mѺ=9YǷ/3,~ xxH>r y'VT_^B|w ?6 `Ń[j\^vG^oWٌ[t--+&ʹ}aO d1Ws[*`N{#UXltq'L۵YmP~kpv)4:UA`9zfA@w~1ݔbtD;7}]7oKZr@KSbB˺wL%%.)sǴV`ˍJh3v!h.+d-Pq62K) %g|䌴PUar vѧEs(ްj2D;G@me:Xu7\}_]j4q%$ :$ M@;kt5՘]"5\?ڂZWMZiIi4HhPAF J1>5 => Ȁlρ;qo ƌ"0ƎnXu(\3,Id /M@oi4 r4.[)RFlOe ? P{v']:w|ǁy'\i`)2&~3LkQqɔM6z38  =]I6W2n~KK3d ȣ{~ (Ab:5.;H;yPr̺C% w:}9 $bNǐ}@a\r4x NږK8GEQi="&a XL˗3>5IKG·4CI!w3om#͠Aז2N~ոtT@tcwM-o^O<]]fdHLt\uԅܮcTD%ٹ$fBaǾ̋_[j]9G7:ܬp[p|=mSe t]ӏGݾ\&)_ؤG[6):H^ pP*__ͱ5.qL )Fgᎏ$hmi[9?GkS >TGgc##xW8-xaFI} S ~r$տ|m.*0<?ZVxp$ٶ֖;woy|ŀu;];-׳( @jvM%|˖>'|N2?CIqgŽq8/3 zn8uCq|[F "&D/Y5 u.SUM &x۸nMDŽS poȴ;h ZGFL'*q42`k ]rp41PlJ)Xg.\W M pTVM} 07-Uh8 UuQ7XBܜpޥoJ{h=@W.0bN`0`NeԝPݻp(?Rg~=HVK%/vaSWX4=3ԋI7Oky6`%c?ur]Y H|2\ƒ~^ҏ|SEqp-qW/dg-Osױt`q}&X[PVŕL'l`Aߓݥ;ܠވM'Z[;*څn+썂yKVS[nm?鬪ǦJ+ n T} Y_'@?&Oc?oc<9kE5WS[SPrIEye^A"3X:7lToɦ!CB&_Y~m~ѫGn}^z)^]KWklVv__UMVR[,#[^^,(Ԁ_{:yǞOqCP 0# BXD j ^$X\-ӱ0I}D'ɓXFZ3)LJ\ 7 _tf6~B80ItUBAQ휬kL5Ai؝u_C~3Aї.28\13u b |2^|PfT ?wmUZPdmsKJO8fј ):X[oه~)q!.>eWZخx:R4 N\ͽU⤓/O{nqJpb~ jp>+&]`.b켄i3fDΝDΊqNℴ¸VU*7čHnڊOv[>7Z4W6Y ج[ck$T^4ƕ&M+vn5|ZZ{8}*΀8x4 =6Jn p)]ChD䜝5ԦXI ЇMja1/a qQ]f1.Yq]_zWk$,!mLwv ҺZϛ?mbdJÞ3Ud& RZT;*4uBw/Yʁn+כ:٣8X ;K!L{o3%* ufm? BZī@M+~{. O܁J̲uِ{=MfeӜ>RWtLKfX5a nt莘9n]^mKlt/+7'jHpx|+{ΖfJ89Y qbe2װa+ -0 $iK)0T!cWC3N nS <%sIxvflM͎#]1Soj`>#Ջ5.XfpKl*+j ikNlP)i=CL->at ^LgVC֝)!bsn—UqEX6.#vKl5NoF^WC//榭bjBe-ʣIPdZ(%i6iQJv-]=Q*PRJ{OosMy4s̙3׹ozX?M ȄS^a*pO4H,QPZ9-]>fnƚ;r[d] M4/wdGu_-i54ذIK+*A9lPB5ЍLjK^R7cdSM %-b@od,w,6ѧi 'q<a\x/hM<%a!)ș̙:j'z{H.iB^aRXH&?:H%J̅:x;53-i#0㌔+o'Jn7.ȕHeN>`_nN]NX7snc%63[v槀UMus@仍?,xh;^O͝0aUX)y >k]jF_x?(O6tz)=oMuu-<`za  }[: =IhsUE^}0wTuKmS=. &5\DJzPڪ./ŰWgK RwrX2b a\PUֈH**╈ mQB$ OX+-_{ᴋLYCsМ- @V}!ŋ\J C>pA~@ \<,AI9zi֜~pOٚ1zx'2_u#퓌*?#_Wh 6 >%>J083%= X+』qv)b=ýEpqz|R; Ĝ!p#pQ{۫ZУvéQ+(p8ip|y@XvFҽ1"XvnG711歪7^3=>୫$*&*D'Β p=eA V)Tr{tL*}ih$g/`(0a@=) L5gt8 ?J6ܧ% |_[yu? ż NZzTtINRt>ҚFvkV:Lz ^շ J "mz秅a>mΞ-Zf\)WT]{BB`B)qȫae,J=tS#T\RA/V+oKdH7Lih(K9؂}"\2}.cHo7>qqG s/Qцyxŀ#GiI 3n'`tt<ja<0& K H7O5L rP@2T2 p5}BÌB+7]$Dǘ]pnvVH7_,V=+dWg(*[>* qVLm<hny^%>I qWpMSnjte֯sMp Oo%J:a yle4HClPӆMےۺQ+U$KVi!|.IFvRne `5V?LHL>|| “@n-tq>K A rQs\'㮛;,!qˋ}wFDQ|[~J'k;gvv'C3oGF+W1Pom~W^eoqÛҜ U%=FC|uh1,Iaƌ(:@Ρ09W,1SI[> {]{Cё^G-Ȉ}Q ^B7X뇦zFЉf<+nbwˁYL5\Ud\kWl$,zڟNɽ{32cE89=Mb+]n-(My#073V+X$')5207+`N%\v&_01B'.qo'&XQ;6W yUNqQa:Qmܚr4Oܡ}X`_Y2+h8E7e^Th SظJc#0WcX`'1'<=ZkpI^4;-\uJBwm ȸ7T G]ë}.Ɨ в7SцYV8^q8ܐ]N[m@E/5O:ڂ5ౣ(?Ekn;+vy8>U}LuO/),Ϳ\͵]EcP/^oi `G>vI3u6ӯ_7yi;4/Uo{Pm_ 0 fzװz( θXh\2ٙ).]BEvP 9[d܄̋-^ʀnìl/\!uvl"%lOQv[0OqG5V#[jjF;nQbR]횲l19<]]N`( bRK% Lq$(nn|7^m NOo6}"{,{/߱wM^ p_t4YURwV؍{ :L p3?4 *IZZҋ#t[R3S^rɣ0/{y8RDcM}[2סÀk[\vwMZx2C+@U4*E<\Љs-FcmF2+Ъ%Refc(ӳpF7&`۩71ˌ}cgLHMt*tVePٵGN5aW""r͊ AW.z/gڢ.dzu_ĭa=aHv&j=> b`@Bø^@0=ƺ%dqSMbWluM6Y;WzeїCgOUy&k U]9y&&j4P}bX@%Eo\ 2ZOߐBX O#&qg[ޣoE CVaRgA0.f/SX3/ Ӛefމ]նo[[4*oϙ+xїĂBzƐ~cF!zpƉN3kdq@:%ƀ}2W0Й4 ݻDvx|L&VfzwXo7WF_XVPq݇eìtSL׀VLTulRJ2K6upP.2ng[^{ăFյ/;2?Uя 8'PU_Jmvi'6]#?o!IMKdi4#AO~jckdG(s4vV#\Ħ2Tv'GI Ou3yi{GvX ASGw$ S,S~~/@$1u‚eX͞8s$~T&UJ4I JI CO 2*h@~(]nwˬ7LzeB`^m>_M^)=.honf̜<=9[ g)Wvij#[n57JEFL{* >_>rŧLzSNѧ=Y\=jn,1;X=,zA lX#jH#%=[qL85NELߩj)Gr1.ɢ22K168"0낳եmF%*W HU=>OwIW۰S}h>bhcǐ .-@($ 9*2=yRyJ'2K* \7"icQʷm[p'L g7u ՍkyzۙKzm﮽>p loׂ) Gۼd-w57>\[uv]G+";{f%<|dg|#$A3L&$*%jh{ZMED=YN\;0:iEFt-:A &ag\IȪ- zy6#tG941Ag*iomk}'_}9Q`0],1 V\y\p`7iq,XCE;%0fUϞa-TϬ*5{8Q>ywgVBc5KqH\11TR]f嫲HI@`7gesZ^+>=dD@ ge?SXߵ >fb`~SC+>Z9k ~xӓ;Nm(/p-XpԨɢJMpnBRVq=#:.Șym# 7ѝma;t %pReEV:s.^ 7 *RT]nLNtxIj,Ic&n">Nw|X;%A[G5qj$"HqJy1 qG3=2CZRb vcrݒSG0ZυQP$&NȗsПU]CxܛW3mkbʊshTsam /.69l2ijquT|  vYeʚTw,]w;jv l}vTwk]qAa{l+ eoO1:Xoz"n@B K!F_7qq$jWzO%76w#Qڹu/ȱoo, Ё'v=+ qzYI9a:Iu^fcntm^)3,ޖܔzOghǟ+zQX,!():o^v禡`&9;VEy6ZR*37M#IɼNkIz{qhfmN83:Ue;8NzĠ);9]RR>OG{p1 pS;MuNUQVUT}dn~\L퍥fm:lFKM #sйn76GgOF1?0.({qiR}ZԷrjgHc3ZÇ'̛d Vچ'Q%r>s%C|tN V=yc'(K8 >QV3T6 "!.1 <(]Ez3{@*,Y4#G9'W[p23N휽:U6A&DiUu*y#?'ꝛ)?ջ0 Soa+9SQX;vgz-/`KaVD}2Ԓ Pظl)BbdBܽϋNs`ۭ\ߔD^.W!v܋}51bH2"4]CbvX{hgycXvT/h{Yl&̴RdUfj "&;wx5;' 7# Wqc!HG,qa7LAE'Fdz4c(<}|H w*c͐;^pQ;ۭ>oeFjlkm2@N xl}🛃k[ D]UUAؔ'̼-W,fj}uv/;SLZCRkh?N 0=B˾{ȴ|CkA2 IrhPH}%d!ƒ2`)2N̟f\"fXtHQ]uG3)܍d;MϘ<>cc`xTtW36%ڪpbYjdQԐY)d 1x\I}:kQ'wc āҝ!?7R0'݊'F{nwV7帛(3,LYܱ]FVPݦsz4}/s].U^quѫrM ͪ𒓓.Ftv/x8U{3.2PtbauknZ!kx&OWT' )KCp]Qeuaqڃi!Z#W?{[<_`B)=zѺ &;#[ 't}Xn==Js/懝 8 z%1$@ ! 9yc;OS5bwտS;8qZ$+&+vw7?.g*2@̕s%IB0s˖mJvv'Av;pAwE 5jK6DF!@@V3.PyNj%v6a,5pϻ:G{}l]TrG fX.EM-uv ǎf}ڋ4  w9Zk ݩ<[*ec܋[QzLUeᑃhrm\;oxqm :핔w^Rji7u/) 9kTb$p-EUWVxבΖ/<QE @ji$E\iㄳ1w ӛq(.g}slA07y{uF]ͻ懄%EؙxPa=c_k0PjG)L3=Rvu/jTS}XVrrIʬڳhB!*̑{؎Q睪Q֮dQ>_>JsC9_e8H)Gr'kgͩd7Xo~) pwpd]>1u҆qg)$?#K&2Blǝյ 8]ys6^>iaej; ~ۭ"SG!+} v3>$voN];VDMEMA~!??& xeثrso{`@J[aE{}_D;jX4\0.hG@G?:pX~C:|]KlQcdD<ݡK'<Rrg<4wbP806 dwڍ5؁6vIԾŹG 6ˆ<%%+M$hGةyvR| Ji}H,6"`;u)QtЁdt :\%\n/.k͸ܻJY%7ٳ"˥'3tQ/Ia6~o*R5}iг| zuL/_*vUqM9P,G7ub$8eqZөc~n}7 Bx [o2:[IyXQβ9%=DKIOc[ f%F|Y~U ܄Lf(H0ϵ0;h @+:Yۢy(ZȘd i@꫱Mi܏V_E}z;bɬ`l%>5Љd_89ѿ} Gd݄ix>c:EAfJ12a:K- v2`ingj|'།`G+So ܀'M'pBt O=I=~671Vvv!mU 3L$5dF N3D3ǥct7QNGc.(2'xVo64& e~j̤!zHdי#p<2cRW/jyL; kz~z#8e ދXцEqƭ $)3.͝bۧk~ {8|t~> ~YOq%'hN׿E"^2F݈f{xI zr%=G'w#36Z+d͛}^׏u8gkWavNqEe=C~P-'#}-mOEeh¦ʃS0N`89\d2eC]]9yx@K4."h3( pifݾI!?$+9p66NNvttU> ""A:U2oN3]ľ qp[/3!-ه43:mKz??r.Pb1T[1z6rEh1?YŇ{SxϦ_;mkroǍ[qczz`FxW|ȕwyUi^"b>=V̝!=Cˊ|^@XPxTC~E.|fS6nb"RTK+ig͗Eg7Q#^_}_:k[3=M+nÉ%cS_Ϗ7I$w1ǖ ujw u,@d8cv"|Ex'aT.l@t␦G_0Kn5t~4 g5w 79?"%htyoa@Rd&*>7x9D%9Fƞ4p݋3&5EU+'/]CK낛AݯOZؘ}h ǙP"kA"G\?fvJi!Įx&1nJN"G*`" +T7qt$>lGw!uz@Kana%U.X~do B;0„OߠY=sǙwv>~lINWezX8(pBVNCF!910сA)#I ws Iy9: c^#;Tݐig7Zlj$+heF7BG'x8D]^lj+Ic ٱ]++nŭq!Cezv\<awܵfr Z1g"kԆfX))2[VKsIp 1u͸HK%2\RRjH;=|#3ޯw=1:1 ^E598oZG_`P0:oD/({OX[nRF o~8Ī(*bW @_͹PGh$ i0{l^˅o5 {.y'ϦtsuHċ' > m~%Fø6l{l9J3#b FEV@3.0P6Qk-'ijhƘH/$ئ ia1dwo[xkO~) 7/GܲEguO^3{D!szsLe(%so$F@s]|~|R.8#)WL-CwK`lp 1cfu|W )GsQh_+^' Fe܃Eڕag~kz2qg4}~MF15Y g5TτD]eB ї-ͫV1~Iw:űR4֪UBm Ȭ L{791zVM8*ɻf*h}%(6 k3?'*4|;N^!Aoj^W] ;\B- 2>U+hftau \Ҳ{k@6(NY=c:͸V rrr!p-0DpzZ@KCcI!F^knUdݢr USB8Pd\-{jx2dmU>m G*Zʳ~t& om*9!/]LXIү'-NAժ|"&#Qt]{ٴbX\gCyVeIq/n؞䦹v1G3d{w3p2n,9װw>ŗpeBWJuO: =#aWG$lݫ}@Y{dgMy2YqyH* 1}@y(^ᶧήU\z/",@J{CN]L2vAF#D+fec0VLCƥ>(EIp>̲E asHw:U¸8}= hCKXhiz N^z'K,3Ew/%7Z^PR +LJѣy_I.G#=lK43 \nw yȰ m=0Gv34Exc ZvchVQNc&1$쏙WTǻZ3yL;̶;c^>`{a:)=B...7;t|??~4`Gpm B#|?dɮѱXõ`i+-S n4-T% rY0ǜ҅nyJJ_[/XƳL4\>ކGhb 98ؘ }zmSާCFaP5b Ɗex ?X+×3Gbw_O> 3@`Kٯhq%2 S.K(]{/3oe9xr/;if{J% C*ng,Ȉ5>e+?Z{ыW[MB]"݂\{TRcP6.UȮA8t}7ˎmtߴv~| &EJTh Yl%0f+V4idAf瑓nGH=oa9sl-L`=xVՑ Í4+}.hƁ;q o/ `?{ .ӆs7@+ÉxO|en:Ef0vc?JBV}Ų6/nHupb["!p)q@o7U}?HN`8;7*&Դ!1%7\>DD1Ap7h \vz#tW܋~_q־7[bln|r3,-}*,G#huq'$qj ܸ"d\ }SFy4w8m䙕3-1,Sw ^1hfXf}Ok~5]rѫj£*SBRͻ?ħk"[TB|T^mUin읢GmoF љEMosr ߹e țǼ6G1Ow)! l(K3g*,/=im]9j1|(B6@\Il_:T((b9 vA8,Zs^w+X7^; CIB5T8֮Tu7ŘыQ\9\ h@SKN#Řl=?Yf" l$Ϭ"ͬ9D sW秤odO:EJFgz#;cv`eā6uX bwg1)9*/,3k ~ƾy,2[St'=jDyz]}'xh6>z=Fq4هָdAgsd*F)h »Ӥg"r=ska$-L eJ6H xxy(AB:CbwxG=]|F0RW/Q pNVaYW$뾴E]^21|qWM{!VQz.EKnZ`%;/܌zM+Gx$fYY.o.ÑH`zؓ[܁:"vǵ.d>lHMk!ȼ)eàCmpW`SViЫ^Gn> L^{&9 3nw܎`ݸ*_ a>$y7؄7VƱM+;ս.d}w)ZA[o٢?!W/I^KLΚ:p!q# /^csDNRUM%gM j4[p:­KKH#bRRY.M?vԹFN=[ruܚ,oCb[y.9RۺVgoP^;UmD˯RqECuؘ`n^Aӧ1EsAy.9 }!)B~([R#)g-$ɆSF~ 8uyٸX1FkRz s6;veҹMOOo&Yk4< khٲp!{͍1춫{+2B΀n+ʸVZ7SpJ"1hKVVX͕0k6?y6J:pDH|OSZI^CS"Ec3I^" Gi9 7Cpʃ72vNH^Ey>Ѹ [ 0X ;-{<ؤʎ<`kސH^1.[84v8KK ]|cdd7p{,o%**.{m[U\/uZk</qO|R`q tHɽw-)9MK)yno`88r=[R|TSw2!prR + +8dЂ2y myY.E_ .}á[`h^➄ dg)t}in$/ƭɽA~0=t:_QQjarT1gUhj%):U!Xe6gR/G3ȬNfZ::޼ww>يL:^~xƯ=?̠o_>xyxx񹕚*| cG>F~|̄  goZu {ԋ1 79E$ PQ wN8{e_\xm?o+W.<4.shTq7ά)7 f_?DȀ4R1bou/@d:Ceɒg 9ocqE/ 0S=22K* d篩~`j|8g>W/4a9c%e8%?룅~3ιcΎ\Tr?;7kwg`,i}tΉVqfĒ# Br"0ډ%h-dΔ{gC:rp$`ːQkȰN>aSu` &g˭ܡ] qeynWؽ@NAdݗ1>% ~Z~Ŭng1]@ꊭ :_u"#_Ӓc 4k B1K!jp?[Z 11ַQ\8Z|_a^t欃خce_xy/~wFġ3/_*/oqmDv9gPYꌋ7(q=ֳ2֭3~njnةV (+ n4U Kp6>q%):I5;o0 A: >WTc@KQْ1ȻhmA/twY̩ހƌD*vk #;qXw;AQ\zۣ.(1;h;I(K㫦IMHчu̎[|7H?%a;j)kDu.6SL hurZA[s?$(8|>ҽ'O zy'>Jh6`Kk?t7+TV~_c,7iR߮v(Ex~{/KS}}|SWUivRȽ;Aa dhw/1sf>?N";`0`k~][\if0T NAB K"8 _(4[)k0?

{ V".dxN Q R ~iEU+i_84]S=GI=V(`\Ӳ*1aI11=ؔ^pӥ=OxG\!RwOJa#38fܥdո?!J)+9ŦGN|_mOM)AY;%|OmsoW/Yp/紐WYR4~lCUIuc`Xx^DY#m MK.PT}j>uy)!Ϋ]]G͐z~!!IaU],!^ia ǟCMEEk^ M!=q:o!g`_@tچuQb_"+tbr1o*J*jh(W1KfP[ufiX 9*KSD;;evr"k<3Ŧ%ǙZjkݒ_-A)<|%Yc~{! Jbl&郫D{z9d7;^u?3ž_WKBcڢ0a-eĐv&1׳sn}zj]kaiO]NښΞsx Y}C8fdBȒ ?./ؘ 0Pؕ8֕tUo"j zSփoۺ}ςNn Ox'<;!f_ +p4ƚ:4s8{(TqLFp\! iKC1'42&᱇Wa6'tYb#%^4vk90\|QlhrϮǜapn3|pɲQ槼E 8ظ5(+.8 2 ~ /㌮vnŞpp>kVA ToU|~@JU֭U^6k> N(1s3L/pf~\bѝ Ks U\\8DƆg9W#d&N+??- @\R̎sJ(LA .dp|lyp1?Bļrz :| `3GJAL;Kވmi._4] j9&LzmC3.n^v;t~>SA7zJC1Wa>t?Slݭ:hJƒq{wa| Fe ̙zvqcN=;Kh.y¿$m6hK4.ϗ֍˄N.+g+MXj?#wBOv<`&V#kfڒi;k`p݄/.Zui[)h 刊=NXXD^+<'̀Ke/ֿAɏqub=9 k_<r`9~p.htvb$nڀS?`?3=S{-.(?sLƽ°IIi޳E gJrWt=R|jkoQ[ PɟrcwP[vQ;|[>okcw /LXr伔ۤ ҺW90YDCY$<^Gt1`ISV/5l+  Ar/Iz+PKt1R^&M_>Hd^ k&r%3knގQбW#֛KW03N|>>]܎qp&Hv|r;R}(#8~hތn qtqt `f8U?$6#Be[9G\ j|`q!""%9mF /[S25}𳍧 T~  I3Q=?VWqkGv4IA6D@k;|WQٱU EX37v j's%36Qr"l&.JfYؿIe8|IJ niꘓD6)lGamp qY 悔1]ޒܜHgc  J 9ebRvN=IOv8GY 0:k3 Ќ3Uv gm>.5sӫ{dowVV>=_'/3;Ve|  5 WH)Nƻf&Ҿꐎ1_YEǏsnrwma' d:ݸm8[@)ܽW kd۫; vHO1'`œhgK ;mNy )Ua07wJ0LOпA> as`:A#M; ca1Qg;&IO^ tb{c\Us@P z$ b%31R[* 6HL9yvS)/>hzx\dd8"@T.-.hm+>Mՙ _pW 1Z)8)KvkXYĺ0J;m*̝>qڕR*إ2bRdl\{-9O3%O`HNAtWyp'eиPmJMcS.]o2)1.[Ff@?>< wO_ǢKjCvŠGhJ-yy1$o72S+to@`ܡ Zi:?+""5MWJ:F~Yم}I|[ǎvS`V"pdL7A@ ܥYsd%;g1EVP d,5612w0 3".0\ז8|榬gϸszc1p>ϿX nZ@EF]:hn 7Ųg=bϏ2;jjczEWavPVrdX9'orfM^ohY9> /<_:!?i3$e' 3*8&We Q)QV.r+d.@īj-¨MqҋE;OACֱiFiN(sEuŇ{ؐ˿s'^<3]n]P=:b/;dK*"HCds޾?no-="WCilrՒ ꝃ$ &#qb⿇Y Ý)-4s쩘=_ K|nQ>#py"`TnD"FBƿ4iÍΝ;i%4`8UϞЕPxȞ}HsusYe*^`bQ=Ηm=h{7;r)8cRYJź[-O'Ml -pLcGпS_uHzQr^ޑ1?-}ڑrTٞC :EJMez Kv,S{?9(NFz#ȸtQ2VJ410qoV8{}D(=Dnʾd- Q)HZH(Q)ڵhJsz>3ә3gι;IA1vbeEY~jN,1MV a8 #x:PT'MS F5D'w4}YnohpwN*/>sk}^θwԴ^]b~Oq1&0ahA;+Ϩ׏C& ޷wh0Xi.!AԲ3! 1u%17Gu K ND#H*vOLA_.1`B[Tt1yLWhbq]qqzI6Qqt >v=I]:M't9'}k$GoG!tKf[xv]M=\^ @njD4v)&/2I:62IbQC7}yĉK͑ODu \M*U2ߺVs,&=o#-8FU,s.6' pѽsZݕ|#Ջ7`(jc +xbelc7dHbzZJg̷5uW`o꠼e ˲8q3?5gó$,[hC؞ϚUlHIn5齿o\=zN^jG8@ G!y=z O>V%_ +5'(pct,q^7MdļgI@9&Nuu '4TZm/QCYjJV gA>n lt0;XH59E:arj^wiuN~$CyQ53ʰ)O<0ku/ {@7 ޵GKjqa/=jT cM` -&SVTOOzG S.1szX3["|SصdB"SeM0gD; /\=0MOY.=},,;nhNXv{ZrXܐ.OWg ;t1E$2..X11x CI?6`j0щB1Rp;v9 "XI d`w(S;8 9&PgoUn! C;W"ap)#wUŌ3*jl~{g3| ^&@?X ܾ2Q!4%]8Tp&Z8e$778gDgu)+Eҧ•U4T0u0Z٘>WEO!Pzn5%{ }_>rXȋ8Sx\x ~`45:m"Jѻ߾`t\X<9N{ Hؠt q%Gт\M6-X9‰v"[-;Ee:Z>t<cF7pG\@o*|3b|ނix"<\]40svvWv`g-?٫Z{ rK8rs~s+Մ@/..sO#01ax5 *[%yc0G*)>cꅰ\0⡃XM|D6[+o5V @؈:ZʼnN<3fKnCE ً>zh! -tMn 1л1`΃1jWWr,:ubjhZ*IRB_cV AxԌym9ׇZo"E۲Fªm{0FBX (]#'O7_UxfR~!e-kx8#98䣉Mg,mvu IG5>PĩifG y`cj`-)5;u7h50HGby)1E1,_̯{XJJ&#?SHCJm`E0.p0E͈T^U ǡD"G"5]#ښL $& kLś)s*I{wn;PC0L-ϊ](9ԥIփzA†-:uA^̉;@-*vb]N\QR6*:OFbM3%aLE13DsG}Wc Ů^f׺n>QRعDBZʶ3-9:%>򆕛֞EW[XG~z͋`c뼣}0kjvuW=~>)F"pA9\[O-ҫ.^1nkJ(3:tGF; ϗDzSɜ,B@^iŚ~эw`Ch4qI?wY? ^،3.╤ pl~bO, p@nE%\u ;x5D .0:qKk"-vd*dm{9yy+@?Fx-ͥZ7S.@OEs{4?㴆]s)ĽֵnIE(]okyo%bwCb#by t~Wdm)+7CЛQ:*U'mUԻtcЏ8dA0wD&.Fb2جZ p pa;8S .Wf6ihsO6,^"/ISVz,_*Y 0+ 4E*PR,#8P,׀q< dIU >HC"I䶂ht!kH{5l} p^uuѣ(X"NΥ'GB> !~Bv`N- ЊQ_뢜Nxȸx!pcBAd!A"*.$TZсME7 a LU('?otό3S)IEekI8dAQjJAe06?Fh'~ +Aƥ{_pGC)?qQU_]3%NSCͨ֎Zv~M.8DDlƖ+WO p/#={a`RRm=-:>?=E׷MY<ˌ #|Hx7mV:I"W~eFC#Xf&NŞB ui;>GIG%A\rҮ 3 jԐRRXւ"\$v漚! 0p( ̹f/Q`Ơq`d^8^t?2Fmۺ6PClԙ;>U8ES\HWec&d ȍz#pNt.溦PUӳn8lD%6`8.k? 1ctūNZ>f]pBfˇ&QY*[e_ k+}+WHn=qFQNzמ q|&;-.6k'`}`~ن/$/qI':+,ޣ<3.ia, R38]l^`FTYkqU u,< fuꩡ0g2Tl[QFXCQyx` w]+_OhIGs'}B,\F5@  z9*LBU5 ,όR,%6JNPKyHR틛UO)ikK7voSa6^"@Ԑ[ /thT&0Ԝb9 \;"j -34̊njʡvfјrfnޫ,@tMɳ&;#80pu.1V؂@./&s;n2Kz[UNN.]nv.f" #+4)p'Y v{`q5'D.<$g`9elq<0V2(HiPecq %sM4uNC{>×$ZDG9cY|N1c)!#3Gֻ=ƘDdKyŎ p'?tb¯oat<*Ns(O8>\Ԛ P2 VClόEE6]>}6z&/ wьOz{vVClax$TyQWKz@cxx|s*G5n/ef|ػBt ˆ hm-PJP ƙhojKV.c&K$8ٺһ$+{}JʎX})MC Lp=49gjV5/.j¸E顛5áyD"p1pZ܁3n6J}s-5E ^qBaesu7v9y[zVMk' aoIa1`gE6 };qhabb=iY L 'z`_~kDG)˼U@r-ߘX˵iPN(ePQkH^smG'K6ao&2";A_?ٻf;ee~s4K~4ez ا[LQy{ŽϜgw}k')P*+7w2jlcV]£d@" Iv=EMبtp {J@hx.JéKpLə-5E|9PۘCi-UemH̪<~~巴wS0 ^_̌`;;[֖WںtxUɬ[gO,ł*Gĕˆd,m#H8 8FjF)./ͅR8CHZ~?""Pztc NBiBbĻ{8a'j(h܍h.L54ݲiwqUyb5RtVCZQZ^I,Rvh2%*A#q d&-EB}F.]L\ m -,-M+'Ekzj2лqIg^8V8W1wGs<[gJ ~h6q #3w;{I D9*QOVގ>ׇZz6B>;ý -:^0_ ~Hy#|Uu/MCΝ򻺦 ŔYoXqZ"˙z Ri etPJ!~'–@~kEG@+`,Qm2o_?I];S\x0J ۳OA Fۼ9<OܘkڔNQK<9Կ-3@*H^j12Q8vœg?9u.=1yɶm̱./-zkTǖYIߵb@Ϫy]:tHzwlOB*AdӼ8J.LO# $ | =`Ʀ8nCs`"',F@hqf҅m1D@*6SH~z 0/zÆF}+.src?4!+LIPȸ J/S5~#beH c?f%/\⺒$n#c>)kVb,z2 U=.hpXM`0iكM j8}kn.v5e|6O[-KonM ːҜ/4 {b ϝ^'f5P}K~hU9=:k ]\sjj`P=b^,{`Km aWU?ʭued7pȝd Òo=orc;KDҁKju.f߁E74T.6c~09a$QnvW-eJo5kjŹZ_e.>E^ F ]<7G/JnUwtU<~ ȮKsw|QOoYC̮2**wLZGb~Eqq)qQ>>-Ȓ9) 7JfkW$#sf-S6@W GpЋb/jÅu0unڰh:XYNɒ-"}[am}cGN$TFVZ f4u0>0' ^[=zHc2 GrvYȸL5AXg0HI#RCZcO|A悦. &>6ulɋ$ p#J0L~3GeX`Uj{` ᚹD#Oe&5.q)KO/ݭV5e$)lpyU[\Zk|UrniN"pk=~*] ec3(F]uNhXxy{DW"bO))ͫ'ceU6i66aGF=-h ,uwLPd)NZ{0nQ@H9Q paMnzhT&QS b~7@۟z~{[#]cڳgFZ3{+NǞ2Xއ>Nm"SV+ 5Toӵew}R" `M2S?7&FZ  .,˃NxQl)a&NN:zZh7}dUO9û nj0#kdKEx&{R5wVRJM@a~YHY/XbzB\SfVH0νH!-gYaiE&C G;Դ:xմ7וM9@].SYY/Y=EB:j 쓜8,¦KEׂX8~3\p5%+ךznbLZx~c7EԲ1ܼUM@Y-ѹbN_اĤTذ<6LB^oK^>]IN%1E#|[“A~,%yeat*F WnkbZ@Ist?|Mt߾9ls[ l{$gܽ)#6)R[L ;MjPXq*]R[ZhFD絫*)Wl]nqˇB,M{̀5VI߾}Ms= aXV4b2-{JK:~E(#p#CQ~ g\xy/yYrr( Q;-kKBl2@61Qb"?{,$KOx6EJyXΔ4X)|!9{on+Nu,hf20&Cav 'I衆5 .ƕb@ǑqIOߥϯvS+6ylaoG gDR=(>Q rF/A@ Gg'ណ{LMC"Ӆ.y^TJɜML(ja;?Bt@)ĩcnېXZmjYYOyNMJ-S]FFt)L1FHգ8sQ遌 &Wa9S"(zy=afã?>BJx6 $I(]l"p(|j΋;%@H-.ROzd\2yS5uD1kޗow5&? TmoGr!_&m D$Wt:~)&\ҡ[ 0.L0↰BSݫX-v!Lm]TvJVTzAr8@{5&8ٺxΣuL`@d ؅,E/=#M(':3b aX6w,3p4єX!VZݠxHXO:zB s#T Gu-Q݋M m4MnSi[ XDҶ]KJ}]=jB1! \^x9`/ H9+L\6f<'U|,%%8)(÷x22qtsV]vY}0Dǃ`͛呠+`GO'F::x;9pR^b]waHfH5a,Ħ]aoۻ"`I5 㶴ц13@[~σnٱ)Ȗl.lo9:GT̸]Z[|kԈRW6h'mݴAʶ#2.Jq mE/9f$-48e yzOvnȝdӤ4<+i)[,6}'HҘHRB_~N@P|x1l4.Gtp85Rppָ?;ȵS+_N0"υR)/nhĖTMp)2K*աy(> Pf~qv8?uT4ly[%yf_V5##871cs`K'QNQ ڴxڬv+*_lgX q?PCpiOɯkqB!!(2XІ{zͣn=VC3o$:L l2H egm:iΒq7F^wdnɸcz{DɴRC^W!'P*Uӻgnbi}l0nmag $#₡V*/оD/hGOgxLn{W̤X}c?g0^{asE-F%b a Э@_g~@-! {B$c|{ZCKPԡRqIbI-"{WBm(v2 5-7mP̤}Kd 3;d!ؽYri'*`84$ΠTv3 ȸď7]lN78ZC9P 8; oZrT8y<;-)&+xL;sT6stQWQqK&]8'}B11A`чTQs)) Y߲Ҿps&w7wswP0J@dٓ # \`"󐮆$%ٝ `]PНΥǙrӻTnϪ\j--8Cƥ$0*EK,-DXg&!I7݀ql.QB %NX.RIQs|kj~p@ V;n:_4`TSE#{\SQ4b ]$QRCM9]Qȶqpdgsw;Wmjj۸Yk)"uzny^HQ>o==-p^Ѡ76HFyc+,ŕ=` (_< &.rZAs,?ĴSƂYRa"gu MNmsw oǞn-:߻VWkvD| .RWoh2|?3vnȢ(8FHcvX3uAވ%ea `]{N%EJƝ=jMj!5T|)`c!H,o¾b6|9&= !r$*/5$Iؾ M!# 9bX聱F9 *09U:lkY|]`$jANg;o|mvo3WNp}R)YWcFW"|#cZ8}jmGGfЭ%PfM&SpGB_LWW(Z /K5>ء 8rIM(f6`?`5p ddbQ xBaWA]/Ȟ4->vKS36F.]; mX -_.;qxUROÎ9\zdVS}_ήjP\ 9"+''NU[t?!i*O9iޟ ߾Ixסܷ>/ &u6p V.P"OLKbا,^l M\`S^]Nx%H7Dj)Z]t$`.8vOqfߴ~|0_'&Y 6K;,$NFkrЕ^1[H$(=ؕu3H"f)$ P;91VDkrI $VIW6u~M rKV[JW}JʋO(fQVe6k 3W4Z; cRiI62 iC{ZpOeVTsU3K@0M5(]AC(VgYQԈN\Nxf i +(]6-DO'PTKLnpYu2e?KagMPC!ƝQн1=LХPSz'O)J(߶u= )K/n51eSȖ>;#5[MRGp?__!N-ke[$S{cKq^I USæ?0S "-,n4 ,p\=Qn:WgnpC:tg}xr*_"TSe} n_VefajAqn|bhRm?\8kRR]m-Y 5Frѻ_1(IMT`Í:Cq |r\hL/yFm|FK#3N@%uk%z|s` zj}SFjj~D3:X`&s c& CGuH bŘSF7WsRͩn߳߰%TT[OQ˒ҏ;^Œ]Ј<%NCQ{N"Aw[۹:V,Bpc{uEe0j-wz12+ lFξi `ݱ tګ&H'0<,gD?$<2.MyrFYZrDf;g pRcc M>T^SXҪbGukqAO;2*9„I\ 𗑜"+eI}TH†#ri:212vBL_p ݋*& <q"D*P2#x XvH-ѸXĢ`7-9@\bo2lC 9+t$A)I)ek6KɎv_l +%Y3#SCuALC={$6_FF8JIFn* 7bjވ.Z5k &s1BK{e7៿}X0g`!(V v(ڴf; [~lX쳿$7<0i9"196nU^`xHcW֥`a ]rHX *lʝ?͞9홣VZ k]]_kb&렴wTa$#~(ZjsLYh|DqGpKz، N(0}wD{[._^^dz}&I\Mg\M̩5y4g;* ~h8X6? &/ҧymcQcŨ OpGa5e(/C{'D\鋄5 8őap)YjBgZ} z$#nx?f>}/M&K=t D9V#!{l\Tljmh4bpM  2vT><{Œ҈T=mFNL /$'~z"5Wlc۳?}),ʜ *, +b긩qr1f6,v;UMv:, 'tF_f,L|zSGLI#f5 6Va`?\ӷPMBkT{@n~ĸPFX9ENQCL>ŅJp!0 N0nuM  ! Pqqh'0_16*2ڑs ֩ Bx Kom |{BAhSw.ssP c:S`?h~v}L?֊ej (*tMN|zX\3"qh4Ӥ} Z&πG?59^ nZ|攜X&pUϝ˽ȩx@3ńm8"/zi)wIA@ZY/2by?m]J3|ZqHgEȁތ8>9fdީ( {M}Wk0P4uRp.3Ʈu #gVS0eҸں3uz1M@kobgmcPZ i,n_=hw5fU?=^:I#`2g6Fd@ QP̆ʹ;O Vgv;+L =bh,G5n ڪo$H K$}@,V(*x-?oPūE~.5A[a]ADYjN6+"K3<22OlUd|IGŶ ,y_/01`FF ϙ5;.GMl*8gSUyM;l̬¥?!Kg*tu3{[PR4m@Ju];ሡfE9 Jj8b>*`:ྦsv˯(mM29z)}gFy}8l @ tIi$1l2rHQfy <c.9 naNlvF $yͣ{e}`T "R吴16>O, 6&+ e'Z)ӟ0@;9;VΙxxY#3 ozlI|+GO X=-j7؉.I.̈}wMs=4z@f=2 .p3<)5b,Zz~L-,yro͸地}ΘsjWs4K(X38j},?8)0H:Զ3HU o^ pM>o۩dkO*Ζ?0޼4 h(٢L֘OLHtG.|#*cb+sv֔]+&xh+Ր޽z]\b's`/>3럜ƾI;x < JVSԀ [5M|! @LGxP+[c3ܼ`ƪoαp~_DOhT[ԮF5p` TS. I` |m`܊ԐddracHA@^$9vIa?Y)_'I?dܥS(8l|16zd63{жfwjm͏d;I S~Nx5 ~uH8o,Y_Jgh.-rŠu}fz hys#w oVNpxH:zVܽ^[c¸?8 ^+LjZX h<{/)`A{z%嶼r} 8EtϞ).C1}gBDG2٥cE afh/ |n@jl@pVۻQ1\V>qJH6/^Fw>&*9%˿8:Є'Z3䥦t{ǕgJօ={8cAM[In;">W6U{r\܁bLOUAcj8S_[-=-1Nr  FgӥRk lٻxa@p8*)u!Aw$ފ#nkZ˛jс@RӘ#{|o#I@ =jV=@/nb@-:Y }T"p__>va6i=J-u1*#aIȁrCM?um8O1A ÌF6Uu%pUIcǾT{(de[zGUi: ts!Z$ ' !8u[uչqL?e,WGk.P -=$!+2bӯmB>%Ezq P4$x؟A^ilM9M{[|ForooDlAR|",K"sX+AIm}G8Wl)[M|ftJFu{+j<,{ch`WNon~30pgCVc ']pS*l*6M [N+w\ײXXLvצ-gyا:`V)E@opEWP z=4I:U1]["Z1 7;{{'// nQWn@u<|Ni>>_I _߽*1NKO%6\CӼOSHRx͞vACK #R|sLH;@BatlL̂ ߼wĴO?Xu>ˉm6" 8L+#8p5%t<kS,7TGu?Y w6wOкE0O45:|ژ𗯿3n @_oΏ;_n&_L!"8]j 6= 9MA.<@[ٹf䏲itD8E淶-U_m{ (IMBvha!sz`"N3.htve֨MQȸkљ33`B:~炒h7( |%U/ {TyX$Td\]]-"=)mዔ/u{g/?qk# d//@v@< Ob&KjɓAsS.~ʒ*/0)%-_Qh1>9sGT_q(XK c |dj:~(pڤQml< AKCB\Bw}sӣC^ zr^HLsoL[$3;,&d}CcBHX(&t]UrtbfGMq f3Gx04@ࢮ(S8,J#^rПQ䖬.O& ,Lg"׾ #<$`¸wZTxT@OXи~^ӹG~2WR|ٱV6f>k>M(Z5DQVV^eCH464h՝wY옒3FY{2qa,ny=,Xch5a;Oҥɳ̞>5ݚVΌ3r6@nRBKp[{~bJAf~8Q۪:j50Ab_|jk xb2nNiJLEw-Ԯ_-FrN޿xdީhYH'FV~R@#zֈw!1AI }F<~`45 šy*"ԀA*`:hϜ]c(azmj@HPCT=yE@Hm&9bEn&0e( E_i8) /m n,K/ vQ!gCPO =Pz8^1yn2[Zw+*e&)'?ǎ oX;| Iˮ4m467FK۟>L{6psrU6b ۈg,.\I}K-[å-}c%5܁;kّ쨦ڨKv>LrM : 2Bxpյ:vaK|  fqS#U`:̵uHIW5 V{23L)[PUv1]*YĒPJ.BSK)"/Zꭥ}ZJkI*> ƒ4T!>w9=w1;wܹss|&K2f6Nn\rdB3'ź]y,Oko:N 0r7Qg^W[D㟒ǘ 2)S /Z6ML@(EOp gB v8z"od [*.55Y}p/~?R; خ|y6{m4Nf~+Lzr]3n1nv,;:nU%~>ˋXb#Տ;mޒ3"#?8q3p>:̯9vlΣNr䞸Kgr/@mW>$Uh+z jf?w}5ς'Bz|\Z%] t3hO.!Diy A,""7]%n.3]/-,Я0Oke&``>?ͷ"{`~Pb~ѰˇOHӉ^5 dV9Xb n+:/g4;7`b*1gVQ+6Eՠf@q"V94+/s-9[.ww5>Đݼq#t riqQ%=Ά03g?=.Üܪ昀]e9q~Z JE~[]3iica tڮr#;uysϾPz!`%P**lٮk5b\M!a#P=ǣHd&GP_>,6oA;W,_NkX0 {]WPʥc=k:Q[`WVL 7!rKnz6  VӹήbY#k6E0zB2!F2SElX]1|<6Q+7r/&0b;{Ϥ&̠bYF2 2SMQv\AE] \y^vz+[/)>0X?E@Sؚ&sS&f}w'\*Ώ+[x>Me,)VR6&' asbA=1]̏z9Wxs^5".nP?ͩޠ4w(_/m||[w_BL "ӡеj3rd0) q&,7:ت񸣣f7]9yղS?+0s}ۙ|96ޡՍk'ǝ%n|JzRwGwfQ=lG>G4V~;_TJy!na"kkh3b <+ 4=njRO~М?%<̔n/R$YszV*J͍cWI GōMZ|ͿK4 ./~NºMZl|;{Hy6~҈2%ԕ a3P;ݺ ?^εb# !XXGlM;[#ܽ؉(w1y_W%{πѓ4287{x N+ 8u[G7K/ro1*hčD){)Iw'NYbɰv`O[: 8馲RK>u,ե$ ڛs9IL[K;+=]ƮwӦ `z߼r!6#Sӟ@V5'w91淣9vZՑ0`[Os$/)v۫e̔^sV+&cTa`tj^p;>u3ƉG>p`'ԫӜ7Z囮GIr'[z -r'7jdЀz.:oh :u,T$xԅ|g;&^Au.ݸva*bH\OGⵞcSqMW^Jt2@osSZXrYSf,K1y^{lMI yr£{j߯b=G[6>e}e+jF ţzS?Ο1QJ䖽"EB7xH%4Y.@ATԬLU) s .j>CujN) ; 2KGI[yB+sXL3XֆGDTq+;Ԕi|cq.bld\mW]ERজ>nL}{VfD9+I}ڼvts| " x (#o\Զ늅ipƮܩP\Lo8.DᴙkLgTOkGG&dykG4LR}tDB*>T S/bT|Z+_m:'3n*.ivgV?''9{\K{РR g՝ ~7ʸ~5aFɳXZ;B0|4" ߌ?r1iĩuG`IjEXo7?gq^`bX*܆цKx^MW#F3S-PrmH1/ Gs=yhڃ ̹ Y8y[AoYweL^uvCbS)oU`ew YHq\}Yc>_Ie\`@WƆϹs6wSyi&A~1h}ϟڄ>|ҧMEȘ:|KBِRyđ|:+7vhρ؇%h;rW\t2!}d }h/|ES9Rd>*znwqRKի6pG<|que?np#&{U-:ƮslVI8ئK7=k 0ЯAӗ7&޻/m![8apX;łve)7#R;~:q0vlbC2r wG РY*')J<#0.lWZGb{5FƵbs7_rPOi.{IVQt= ¦PzlGeu`ڽ?$C_-:bnZ٫  j q@irkU_yÇkry H$߼fɁŊL7]ð{6 ?C5b2Vk$n٪j9&%r| ]34'g7CuCaYd[GN=B!4yXVeȰj@^G<h~ݏ*n{<5)qDiv_q5(g:"a g^LV˯Е&gg_eeA[A%oc%ʙs2so# [qm' 2.PkSxt#J,>kұ`S@͘tSYA7}\hлWX6ȡP׌7p ƟȤltt2 \y㇦ cWQʗb" ֕wt[ *~خgY^ܧ>zh4 sjt~/8P\fЊ3fL4 '4?F>7Ȋ9Zj) ee_5Ъ(~h`v#XxA~n0l0:Q^{fq{ X`&MZg?ppY٥e`^AebK%cU\R- endstream endobj 763 0 obj << /Length 56 /Filter/FlateDecode /Name/Im12 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 764 0 R >> >> >> stream x33T0A(UFF`QcKkg```h`daaRd$ endstream endobj 764 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 99312 /ColorSpace/DeviceRGB /Width 232 /Height 309 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 232 /Colors 3 >> >> stream x}w\T4H{M,A`oػ Q,G-b ("{v{<;{-s9:V W[ >I1ܙbfm;?d|%Eq3K壩z6)tYEl|(iCIh۪Tm[[ö=xc ?+mΆO/EJ'1 g=ot9hop7L -#WK{۠;Gnzn :-;u|eૄ01M_!p-,ھoHΒkeSNgf Jz_ 85cqN}gWߢX@'&”"Zr޾J@L9҆Ў"PKo1_l4q0`Fe-4,k,Ǘ [+S{M7O!=:p`CTnDmĮ-ce[Vq.pH%.V6f.lHR ޙ6 ebq?Wh$\q'-S4g"Ʌ~چOU%QG.cSy7!! a;=%>o fТFc$+ܨ{B/E-H(AIBt3RiJ6]RgB_Hm,a…򳫘X=Ch0-|lWņ6Lr@ 9e~z_. Wv1@U뤙DB k5/R'nA}J"#L I_b x CU(W{LLj߇F ?+C |Ԥa#Y=:J|wŖO :٠OyTcY4CъO&BFk.rߑf- lˆE@Ej &i2}} ϶{CySK ]$8`Ԝ Wj@$_#q?u4h[ T8듺ԙ‘E{L YBm>|5j}Ox ],($H#AIES56Z $q)/‚I\./Bj]MfŇ%Iwz5 aBlrڡȹ&Z(#g >%vq>ᪧDII]MыS.LmG)L" $n.zVPjRX<롈cR@iIA,trh~"@v]S awbAe%_02NHaR@.\1k@g$hyG]B`i ipG#V# qR%BCSx4 -|!HcfZ_IO3uR? }m޺I,п(l˴J˼/QKBCEcWh>G2~$RYrYiHJסdO:?q@; ڒW] TDܺ>w( EA/g"'2&и=%\7p;M9-ZkP+MjSRfQB1Ur;!0'Poؕ@_$-Ph 0o`5V_IFz'*)"#qzɉ*nWAL볮LNUJp cNByBrK5ՍHtCJ!ƅ.Dl\HP1w -6`و: ׀(:U8’PcW4qnF/ ټ+7٠,NB* U獫\=B-Nhc}Rl(GנLD`A- qXžiV"R5K/Jh!Ho41\A&J^mqi+]!*Q^θ3I.ŝJ\*/'gsKAh$.']2g^ B`dpB< }K§fnGɸ,,ԆVjT4jGp<q*`Wjg %HOhdQ|>cX/l/%04q$FbrAB0YȢe L_l܈dd!=sSJ$8p%dXe8>6ŗɞߥ`D0Sݛ$9dʎbpS[чJ? }^+BPD㠤q]eBKGJ܌#4Jzj5=QE $ 9.ZD ODlBDi$A̠nQ_p<'nCdWMrE:S Yىv#vIOJGepLH. Q+(b;H(WiqV΀*逓T|hJ+ox B*Kfb"R=Cyl`*jNB _TK?)`6)9|% W8:cdF~jUYAf7m.kWb]Sf!HowWr}f,6]EG qˍSRr~ɩijDNGR-ǹ'XsYDxZ]t~wiUКTll`+ElFh18ޜ 7@RFh#4=!Ѳ8㒛W5;7(zBF (5I &>@TMZIsZ(}uh pTzeF &嘨W&x$ c:,@\Za-3r]B$Q} *Y[cψ,J79M̭)MS?2Ϥ@0W8wE6#備8dGzs)$yt`*z#Wl,0-H!kyND }ƙ0|GRJ2QKR'bI<RtץJE!Hfʽ.]`Q7C/ǽ'_乖=Y %UJYYN_U«` &ӌqE*VFn \NugyIM5&f"*}"Ecrų;Iu%mbSB`eUK)B_Ea%D~}Ӛp( ؕ=|.&UJ{ʑ<ҙy1G_Up$OsgO'nM廒U+T}Rgz:_VPD KSОWE*JY)2s;IsK<YshC} {riv.;HT2WoslNe+xu%$Rr ŭ'(d5hG`_RR.A-@LȴwMur^;~ç3 Ѐ@ pb9n tXj2A:$q(k6oO1}݂ aS_9ejvwsVnۇŵϋw;k TFZS&YU]M!r 啨b㐢^LPפyAC6ٍ$ ]|(=bj1U}.+zZEQ+.b8ELrJwa(4LmR*%tEˇ>MӞ+o4(k] b;zs՗K~j*Dp!y5e%`A:ډjWcBHG-Ko5ʛ %] }}$L>衟c)]&F>G}.6b|WQ0HlT&J[UXq[/I$% ºGL:plϮ[X۽HutPjpAYeLKJ*U-NIj3 Cb[j[tЧRJo\::^btQ$r< !} lPtSxK /i`hD5+ c:^x:7WE5kIS^:Ye^M.ܿwRxz:zx5Ө]*Wi"^`Jv/OELE3JOHW};yIV]Tc 8:+9]RwK$oWwuH$-h48Uy`$Ô*UK|G*MN"|Ļ6Ms{ϼ[rk3-Jg[X_۾۷s,obn:6W1CҷXnKW[6`Nin>Rjjo+BO"H#_# C|"KakB;+p3t X]&W3~  ]>9 8O@ M* rvnDDdj:17>}50{_r0ezTJ"Bc*HB($M^"gjCs v9AUK q7Y qɴNGwE@40b+ȠFidz+i?zlħ[wX7978:}cӌ /?#D/ @ׯ>OӢ% PO_H鯂Q[IE_Ĥ& 3<ʑ7tH`+YK$_\ʱAKHǥBR>42uPr:/u#~/hyIV", /ޕ?}֕k7[6CF_g@`@⺚{լo^{_ίS4{F:W_֯六x:n tW0[AJ&!b j5<`4w w@ %%Y;A7,j =K?Kw6 G2T;dz^' A\uf䳕0b7>o|aq_>eiܻٺ{ҩ}ɩ2>~LKC#@ٻ~v1w޼.ߙ+DRO{#@?M5$r=Ol,}(@U)r͍T}-bjZq)T{cq')Xd-yΰ%,)m0H ͕Guj;Kƿi nN ` e֨~~!xVV%+&V^%<+x͙6ڡT`sH JY+/q_IU *Iv"MB RhRF2+Uҋ.Ƨ}]ZYj%U_)eg^bی, Ht0jc21MM-9byA2PO9k{"6q@a7p6@v>73w^x9.4|[]Nac_hJ8}^j2^3}rSlg.!<:; {_WsU  , XPTTZs`S`!E`k|= ~J `+]{FU >PQI5/\e2lCG? -:/q\RU5 ]jV[YLUJu U:vtnF|l`J ʹBf`O#xר"ǡ>I[7~ْC._0 oݔC'(M3>9ؗ7 uV|b^~T@ềHFWtld{,+7G bBB3պ> ʃ<`BYxz VhCVeX±*} h%z$|uXӋe䀯w~8sK<22pY+u-K:p6i`zHomWn&jߘojoI qjz8Wq~|o'm3^ݺz?0?y3=+ zz~Ѝmz‘-JwJab\ɃM %,q3ZFW(ijBy#)| Jil*:B+t >hl<-$#/Uqh򕣓iFw+ E.R|p|nAV-qj7xzhf`NVVYR M]Tmty8.CZiI)-]mM+ܶC.'*fa r:?dНaa]m˶o]| t_ڛ=ykogOGBY" Nė Kx gFԘqއ<wJ&0cF1PJώ+X/:DN10WBK^)\;mj d`6Ю;ވ<ݯӶ=ڷi}hLĸ#[>U\[Q*v> n۹ (? w>!u4.@z;t'9iK\Ϭ^9ҋE$WPܿ t'I|@R%w ^\ z7f JA۶L߻?]_>Vӛx5m5/q}<ճN.[䟿k1\~mׄmb A :Inlg1["AXEL4pEC/dcv8 gOo}jU=FK sG i1W$T=`EL)GIgWEᔑ|N| K(3&`=C xˌoG缺Pڸ^<}v}9~S/^Q >W ^R<\r]8|yƃɳ$]>c|S?S6BЯ t']-%fbVJ%# KzA5wacR[8~qԔQV;75xӴ׵k+xw l=޾)q ǏL-ͻەĘK#EI'Çr@ymع|䧷"^ϝU=[PQMiucs+h;JO ! CRtunMJD#XA}E Ҫي\yfc_8҄fLY##\ج)0\7gd7k<r龍L  R *ky7.t<cUH\\6s>.ps1We߸N#9I ' p7E dU =O-!j(6o\EbF4aHֆTc0)&VcBpcp |Lĥȹ!X˳eݷ_;d豳ur&Ғ@*դeܹr]`o㳗͟rt)ࠠZo6Z%0cGkSrb h/~/xR|8Hq(X•|u D/}Zݽz"P=5PqZuy[(*I5:TI+)\Ium OSM,9WΫNj9Z_'/ b<h$fx1k̴tC'/^Р]6׎ BKk|~dq?r$9u9uЫwG%m򺱠IЯ6[DpD\TE:SA K+EBY K 65U-)ŕ`xi1j`" j([d{;4>; [_sdȢ!gdI]G\J~ˆϯ?r B ;ܴy7HtJGkPʊ{ˎ>bc_WeGEDE43DӇQ aZrރjv% QPƔh!^XYW?7;?hEf O1jpˏ)^m;b8HG̾^ ey݄x~ `*oTP2P(J:h /dǽxRV-ssՀ dZ ]wL!ly8;b O[Qr{O_3{a(ڒ \Ek YKz .8;%JԳk$b WND(1Vljx̭G0Q5h5qʄY +6i3bx˗7dЄ鳓2c>;ܺqy-cܝktZI3R |Wk 81_`tM'N<oj^9`2_ڵYƎt(Gf!9wNvp !d 8vZeàwd@*|bnAK ;afz$J9sO*y ƒ:$EnK:'l{jSU' p'm)Xa״`C$'X 8:_Uʖ5yژW3$ ᗣnƖf|uv`߆Uk^|݈<7j؟K[:$Jswq'׈{BGўtX0Yt A>s,O$e C{)@h%74cn~& (/Ҡծ _S8K!}'repd,|y/3B_^R.59ʩPs~$~ZeD<@]RBW.#]IAT|6 \oE-J.>1t<VQ`oǃi JHH.wHo'޼~|=J\+ثa-[Wڽ߯ .ݩHo9~TzәM;s|r^urg4Mca/̏h^˫LaLŎg؃XDf}YL`%@חQ/@$Pתn)/2`?upwnFyN C߾WnOsrMe]GD t2`Q5RH6cBV7$jZvO1gy 9% JTp>sJrkYq:nF ZX*}U :Rk.ђ$RD&3`Z֥O&7(ĺ䮥A3*nUxq# 1πa4^u-NsB, ;ӳC 1{̬ޫ9h+EPτc+H(mkYj# "}ǮأW!V:jŘ*^ Pae*} ' 0hɲ7z#X 2Yj=H`.L*KIxK' yE=B?IZJ Zd$}W8hX(a :wѥ5=XNݹvܘY)v?TpˁǏ^n/?vpfLsׄg|Ljo [Roj<1 ,ӥtABV 1ݳ8R·71''gqG?< <3(Us6t)1l 9c|d M=m{ |z\ ߡSh-?vЅ77Ss4|`.."les<^/C5\8ͨNT+\f%tNan"p 惸x"D~9)̗ngO_?QK+)19}:ϑ;^Vݴmk{k[fid10`{'@(Lц0A?l\Hn<}>g=˪VȻ$ x0:c` h J:Ө1GpZ܅#F]{ RF]OW *ϝ7ÿ.u`5^ ?Q & c9eSF:`IB u?2<PLxd2^JՐMνj)j:#tX|"* Iey)pݘa.dž?Ξn 4xН6%*Ǹq33w 'NPƚȏVj Z?p=aZ cn~"ԭ!;M- E|pV&& PZE9R|cCp] rMl+$ V&KxQ,%?lՀIFaF)nF(қPKM"$ ߯Օ:t፺Ƽln<x9{/S߀Rws| ?09;,۠֕k@?.aU8L+BSD$Ϗ Y_Z5WX`0.f,\V-2D_pB.x|3bH’S^WeuǍW&YRğ094ͻthC-h8C+׬4b`23 GXTBSZߓ(Ю驾q_yNrpd5֋G*Je1 `Y;1&o(9lK +F'.4ޢXʂc[î|tXؼi!Kq8F[0i] Y 8 r^ G4M{%,$w$[tF88;؁X=u1rܑ{ƒU/[<vV{С}۵ 42ȿq.kYؖZc]Z͚puU 8Kp6r Rs%ӖҒY|fК 0z -ȃῧ.ME y2v\p~@g܋' KuϾ_|כ =㴩 <;ieMFKFg#K!\D‹l+$Z0+e3&ٮuhMn_3PCVzsM~_ݮd.N 9bzHC ߡ2<B+aYw(퍛W8IL'&K.舮5t'n5&!DwbGyf^t@<;ngiߪgߵ.ti'=UJum{I MO#%K9tufPĥ3 M,IRˠ< 0v;^0{!dw-4aǽW4x(aG\m0xAO7bZ5Co&mlLbr +':.~8 ׯ>Ra^i1ssoBY\'%CY֩UbQk֢xǮmȀ}+=IiʹGwQN-AR08;uOO)Ifj|1j&)!9 xD&W(tz*7RIjUg)@#,Ott!D./.zM ߔx<&Lp(e>kΒC~99cvx1?`|&~e}Jp8{ʕcm93 f-+Àvt &XhX[/#AVV~sŎGħ$8Lak6=:{BL v`_ 9|PG'w~?x-& 3"0n`a|[ױ)z:VZ6"p"_xvo1ØDS,75ٶf7~A= ޵en #r \8sE 1S;/WO`޹rb8/o݌4o3l%]zU,..9a$R< BV+dSZ׃/a+-PGaBk %$(%t/aCO,HB(q|s̑YDSC:rH!$DDu{ U+rl ?`Q2h?ݏsgWwO_nܴ.>0g[X|~k[)7C_\(_a̬о]l7ѽ9 ] ~?qPQ ۢk55 ߸dm,%)g4JpDz78gy^P0^ Bѽ#6u¯Bڥw\gx"`.^> xUz[Iү_C: q Y&X ١&E*E_ <RH5+-E􂭭'''j:>i=0vh^fƄ8?XA`Oq[_ oM-k+U`!M3ua *xUh'n` ާԜPE=l89nߪ_vn32E\nUr vG/]aAo 1̕Wn,*>:/5&x. `uҥdE?;㠐//51p | ]ϑ+l,La$|ӨSʅ$eU`)gt<Ǯm`CEE@ܐ"ZyEU ݺQJv&FrCb8 H' Em?f.Sju={my*hLȑ <ǧzU2:e$s@0f}nfZxȧܼդe^Vt,D 8U#o<~&<"TI15aX#pN|C B,#V˙ZlBk(cyRdwO[=Uu-}{ھ\/Ǻu?32};}twCB[~]k{Qv z-|;~99;.8p_<~ɺ@^.JAg22hԸ ธ r$7'dCs8ˌؗ1n2` AoȊգ{t#4G2Sn܈xȦM> 7t.|=Z_D媅jJg%fJ9f ߰fE/JCbU>Snq9vqs?w1qǾayDŽ=F݈<wA5qUA×Nl2q٥Kf58\cqgI':7^|JFc$&)&X~4*2,QϾ&tq'O+ܳpތ:W ۼUڰ#/#M<n.4!zC4[0zUu ul,P'w_~&`5zunyv;™f":L\u!ۨZ`0K B řpa 墸  5-́zb{S.6nYk@A qРU5<d95SaVY:eD^NvH*8CClytoPѲ4325R7fFhCT1k״к*9qӁ5˻ hOc,<-.$ b m^6uzEoyVn&!ʁt db.҇NY<XG0"juZ4VB aIHU_ Z(Qp# .@ʒsorv캔dC}6l؈>#L};c[60 (A~Y#{"8c я"nr2վ1!Vt·a?&a"+;7Z= fKLZ,R8]XD [˻K+71* i'4`#dӆvaz=-Y,x<|$/ߊҶ.4N^-ܸZIL qF<;ujU2u1d9[~J5Dm&$AZ`G%pA Do DgE= ̖1xmHN`\+=|(Mj6G8վ#0Z o> N$ =q̜ߚ4oyO5{ɳVI 2jL*&@)9}J+МJ/Z]CWxᡷBB_-EIM!u"R% ,ł nP*Rwo<}У^AC:f} ֦twgoo߰y;6/rRv֏pЌ%uI!a7pNcr;/ ڛM ns QYwr6Qt̅ ;0*̱ۗB?7)-E؂$tKX@MljTK :xUЭ>ϟ>=kɉ㗸{V9dn뷀mo]pSj֝~ө._ XkSy|̱s# >Y['k8r=hUjSGj}-B49rd.HY nA*V1]c<9j; |T^.p<~Ż7ġk6=}%5]\,:NXe=:m9vD /Wh=BYQ Y w Aʙ) RWI&6W7UcPKÎz߳1n"ʕ}v&Ukӵїor;с5 ;zƭ'΅=êe˿6_ɾq ߽/Wª}F7\ۿq)hfވB  PF4D>F!1n4;i>x6@Ec׆nrg]r: U3F<(ܿg? A6H (U6uU{!qKĢ &?H̯k@pbI^Hd._aĚ tL'p@c+_'N{={lFY{C kv~0 hn5m}͛3ƶ5[lvy`ʕ} Y"ީ { ! T(0*@Kv=SQ U"5."ZEKViL+ݮlB/5)5o13<8&"Я?8{X@7YfؤA?N>f_jׇeكsvlZiѹ6ulNE?.%ƶW3WnE@,Bv%d ^ٿINÕ{1Fi,dtt cSЋ0p0-q 97n=qpܖ mm>&^[M]pA3k4 fC?L5*bFm{c$Xûq\(,&u}lyh06|$d׬ɩی';UiѢ^O[BClzV.:xC.yaUByX":SZחJbĵoqo,KɅ’t7c5'trP[K7' 6dsЧ$ٙIdMat%wK)jLUquϜE}fZ61tVU4*6%'qMt0A\^rAFS9z{o'&!NapmM ϥZk7"EfR@@Ina+zeSbZa :/H [&nW26)CD1k) sl8څJ/67e *ѕ+$ֹ[ \mzp: Oy^]}LX2NJӊKCp^yզ_OmAd?o$?dp=m<$@)}ZBIy&YbTfNKѿ`:aLg !T:yYD?49 oP] )ҟVcm ᛒ)+:,39AY.ǻ֎[ CM}wߜud7??+q|vʸRlX9mig p5K F|V túAߍ%ku!V >qz I_&4dY&KH)̟%lq尖 Jjsg {~Ϡv-8}-3RTJCs g\;w }tX YBOV!#וF~l[%_;jʗ8yY# Lgퟧ/,̄MP&UW[eY:r-CydId_z<hZ zX2<-fױGk @-v`x ~a?Leu=˖\:_ U/|,gRaV6:[XdzO>'f,{qh8ط_\dP>6Joe*d4cH!Bi`,6._!Aa4YDw6 k:ʼnY{Ƿ3Gw`kӀ:nq HZw@7.vzsǡMߥdŘX; (qZ u ,//_AC~bIrGgI.L 0̟!^G}1WvGu're:c<lBP/, x?VŊ?'xԞ;j| #mS[)`ᔲ-߾u۲z>ZE ^7K))=[D+Q5.U|%w:V⛏ja@Cbeeأs:kp1.N^+=nεgG''SAKJΐw ׺XF^(}[Rw_aG #+!_ 2-ס-"\CO;a$=X}b}Bp;ܒ&m/_~릲ɲ9KUZڹRےKiDثWR74u^f٤8E;;nWYKWoUq~.Ҟ;n`a+,`fob"C- J$uޡ\< g$ut1eEqiCD, 셊hpy.S5[-Osk Kt, ɐcʍ&b '2<4E&]iβX%<|By1E'1cY|eg]+5g(nx]wDUwYUe[c tgt36#CF;U~ɦ2 AS`2xN0 U/> eGv)Å6mk5r|"Vq\Gxw˟TeGP'Ept lUOD,RRCPlΠ5[ TH swtrK8 X O a=K]hf.h޺c\V xC# bl @Eׄqo惒$+Ģ~l[#kӚQS,}:Ɖ"#vl)zYk\mH!11OعKeƚr]X.wY^**vcaX3BOH/ ddž$k@$UóUiuKSvW}T`2̵ݽ6qeX [23V\7 E8"sFkۀ`-6W%?/R-ZCӍ-,/&{` zI/I]˸2!}y.d#T鹅W}oђ|kP6VL|7l^Krdc}kɴ˞8&_k{'J9{(39-t\V.cimlDДXMPУ.!__ IBjL:RRv.EK "2qxL(IU&Vt=Ic9Z h㦵k+D \{{xꁁE18f$c-MIQ/=Hn)&FhT\_uHsk"sn&(%360- ?~^^V%&E:i)P?/k/\"{SRDኞ9!`>vzc}-_i<זK*5M%ݖ,lF$'m-(:M_8UIImw)/TH2k%ΨÌe7m HWȕj Z6~ ܼF؂AL+C;R{Fºv 6D(poսώV8dFJWƓuڟ<Ŝc+cX[Fgtcu_ 6Uw!_a7{nDӭF_1~SCt ".hԮ5mgc!e_5z&Q%/V~tıfuYy: g,wU9'Gĉ9 <.,b**V2&Vf%Pll.;L")% h0Bѧf,Ē3d~ 9Zˈ$9΋o--XW\Y,p3fs?1NWi'3@gNI }<Uc`Ѵ;7C ?;{">;߼.\ZwdwOq=E/XX^0)Ow+ΕMUf\x-~WU%ܼYyn]tBe llCbe޾O~4}95!Khύdu|B4N BWqc Ok)mƎQ/p)CEΈ/?"Ç;#e9%lOfDeJڰ#^ggHs1\mҐi= >oSW21-+U,r%I4a w ,m͋K7kάbBou@1VN^[6:xr]V+17Y~ܥ+:p额Y#ĂJ=c멬 ڔ Q98yQ^YMDTlm8ݻM>K*Y` F~8HkMGs_L/rq 1l_9Ӈlo}exk@0βˉ *,$^t]8ɡX?.8๓Ł.N<˫ D:Y+5 J^vC/_FGOaxD7/^L5s C9:;<_x`\'مnF4U,ڶEZ|VډOgT:r'E!9-yLa\飒pdgT!j8МQ"cF^>Ybړ$)瘾utN#H:8&`TwbUYvZT ?6b;N%$]im.!7\\C,w/[N}ʔ-5 ؈ʉgwȈy@ \%ի/BFEt\|8GpL8n6B'՝D!-#OyVLnռ .=Yuωg^%&6;WqtHߥ4ʩ(\Sl& 4ZS=B/;΃0=b>{A(Ԛoۇ-|(PMW1G!$cN+y'G!k֧dbڸ,s$Q[`dh\?`S8Ŏe2P͋\LWqn'SxJ~z*yYG]98ZvJ^<3?Cט& 7|] ߮0.L()P#O-hbꚺpyzCez()i:AO{( Vj348}F:4\ai^z@ܥ "D9#NKNXIeɳqd!s.p_V?I+&|(%/3Sg&F7;*+Aa'x2Hrf7)tw4.A`zdl)z4Zkw Ko{=7(@ _y9A-Y̟g>a{g`MT_ҬO;Jeo|b 'Չ:Pysdnj 9=wQxj|F_x?+/2]o+sG~]hs0)cKFefZ kЙAOwäI-n2倘gNni9z v,2ܘW9Y\.5d UMԚ?Nj_m[<}64Xz<n뻓)uÖNxEx`2oB$dwCO2pIk?x+\vKii58vXpd|⥳nkhP$B C ϚOUa`tt5l}mybn{}:LQotB/xu̾rt6Rlb'#b*9)Y@-̈jt*u(StxXqCWt"w{JM' (da^ ta~H/[ףxbiF% 茴:TqyGN{Œĝ W *$*?xK5Sr)z´G>-Zwh#z@/RRCo繀ajM֧SE#+}=A/B<)bnO#sm7\p)r->!S%f'ҏg bc%SY <zw5[L$'y% F>)\-]>%$VH>l;PQAes SBJdcqZoGRœaqdWeVtTAMӓy |?FWq >Wܬ 'rEIiy`pgm}e.OP+Υ'R`Us M 9 yف/8z0wnع+ZÙ^ e.+K0/Ġ~`:Ϟ  v=JJ9MrJudnT7'/Mg:ܿ`[碁Kr|]VvX` 6u 3 yL'+'ǥY-V= ]uUtbق  vLvbhB,yʺB0/^yaixUǔ4o$}X4V' V*:*&v-(u*y0JbWTܶ$ta ?bL .AiHĆ5]^[ѫ!K?z,Zhld`E.3􅑪tN8ty&v!rvɂS 5314OZ۽g4eW5?U5j=@zU,Zѱ?f۔ydԐ_{/2ם4BW/k O,c(yi92zJ:}nYuxc4{X ٫{@{sUig oH1ROC3}̜p'HoGhzp~Xc5ᲓΝJ;־N)*x@d(GkkL{i 255MEE;8n=YRZ| #@{#K =={UCq$.t*:4kGkM#㈈ /ܴk- Y ra\/^W³h,*>å[qeK Look-F)8\v_h+gD6N]V`4t,qjL4? &̀/;8G Nӏſ$銎3:R1e V5{b:Eޣc|cgIc±2|PQej/dݴaЫ@v߲i,}oquN5>W pp aWd͆q9N֮[u2 F_hm mvM2N ̈L*z(KϏjP"z6M!%.D)#2`-Fpu*pLxvYyg&5RCʍ_@Oa8- )U`IYdԘ}1Dz0Q%p'KƬ1;=W#ܺۢ;dORK ?Q7Ȍ018#E ^y5Ե~1:66'-:Y۫Cښ c##Ws'Nuoh}gEyք)Nt1[}@8SL*>mz[[PZz% M#}ٿCF.a Z:R84'F^ˆj&N>)^d͐P\\\u>#5GKgd]kN%囚֤n;ӛC\WH[qо3' &_}$E$ѽm^~zRzϘNŗ||u4: y`]ȎGfx7˛S<|Hދ$2pUpBgf:]f `=GˎIK5RUeȒy dƶ"FV983Y-ø?m1 Ahvoet-Add9kbm/02?ĝKLbcw8y%qoy6c0):;ML|.=15`On ۹]lOJJj- OigGR,  M{&S"6S:@,Cs)Gc Ո>Pv֠;<9` ~eY:JpldQxXй0nK[B0hQցRUt@br(QchgI@!;ih1'On\cZf{"BqԂXN6L^_ j_mOw.s7\Lc]?7+DCaQѯ?اSCǗ+a]Y v߇ЉOa=m]n$L'YE*NJ:LK@C؞KWVDxӗ9]@Ǐm{ɷ}zԸD*kKFzʱeJGPq pckb6#-+6efD |Bb?9:P` X(H&VEgf5#c:4VҼcHx7f+ȝTkA 2KCWmxp׮?W^9*kޖ;l23r^ߴ%R"%s?h@+]Uy60f_9) BoA'H1JדP:j_oKG?5dˏy*Ǝ8bޞ Y{[ƷF6ebk=P\0ܷg^= ;61wvʸćO \Wog5k}MdC=:VN z/߀`J4q?~cE" ro2jv \_Scxx.=,CUIK&pO7Yhɑz|-{1q<E14nPs[/4;,%?b遜& y &0vo )x:`h}>jà'ukiZzbD!-k^2ZjbSuѨG S7_&s2ΗpîU޺ǹ:j7Ek:p2 g Y1)X7J1Br.?m͸$&9-p*E7?n! 38.dԐ_tM2f:8W c*:k3܂a2+XJ^f%SuAN[G~z}2H ޿*_2S{t4B.9" B82W6Ӑ^N4x%ahGnӘl^i#,JiI^z͖WU&l_YZqhFĺhJOx|vZs-ӏ%Y)ۯ)q`4)ۮ4/Hoޑڣ5#64~D.4_ĽFh+$C聚\W#H"l|f) C̓-E8 4v ! qFZ*xU}h}VE;+0rh$f<;E~>6**p"߈T]$}!RcbH e'.T!ݿ}2W x@~GX!{t9q׫'/Cv_)錔[ɢ3Ln?ǽYTږi wܽyn֜bH%vvcNu]ҫD^LWT,#9},jy"^w2Ž$m&P^0 tjY+%v8V9F.v ;DZHw-ee39\rmz+,c:/,ѧ h6""\x`G }W/GBlzkL`6=!t^ bI^ke]8FQaLBV@+Z~ʈſ@Bf\ލt{Vv_,!tmx3mUU v/*zǂ["RBwWNUWX6?9gNw\tQb9;t&Sz 0u_f2ax-3xXy2Qn45~&K6@$/ hh"@Hư|"T'W-?:͒KUV9a"G?9{.-=Vp[;~^ǡn;ֽT*;*{a",! ?V4NǴn}Xx%oҖke mׯt/{TS=IqG/\4u\װ& <-M4;u1)7F y$:#WMk6p P&skj^ɞ=~Ns)K>]=>4pϚz| vG⛌D "T hucDB vg:9Y.2di_.9 K8ӮDa q)޲b{X(t#?'҃kil_V[Ы-]wgxsTb!6EfP"qp8Q\Ի]q|`}zSOV`i7d̫@)jWuFJ}G22qc/iiw,J[5s9sZ 7y?*iÞ%Om۸gZaIm#c*'4:`MtVҾ|]y)xfF$!bL:׉йfpE$ ĽܐIZ-2?.r!>\VRJѼ,DEk8Lo4znϝ]Xw3(◯y- bj*~Ko~ t"]|^J|w@70Au71 VrSLgjq4MqN mNHi#5y07mM Y%2s|4岎nzY=6m40 Rkg9z4Y njD>y0mv=ʒ t eܨ'FaJKe>Ap@[XNVp[J&9ivN s!Sѫ,8rfD(wrJs+Vp3q[I.`46(ǻ`luA$ ^KA"!;P hɵ nL*F-+x;|k74&ջbᕂeN޵g[F?MW oL+ય'(l=z%KUO_wl b$zu!Q^%I ]W? 2Ԩ^֌i/_ْ|a'PnwPU76I<$IapWG ۓidWw'H)k27UXea1%wrczPY7lX5.N瞾>U|('{ё324\[蔉ngFcBȨI{`er4:p#>mM0J d=YD*2c:Y5:Oa\T S2W]ɝۋdjBzWbKv2" S'O.ʙ5eДt$3 Wx167_Ql[U_K:xK~|pk&߉*cH&8q8n䏎=kꁴ5кBc _X[z48 ;dh۷Ο;pGɑ5d*|%&|.WxI 7޲ )p&Y_`I9Xz X6=u1bQڒ11hD0J[|䑱A6H"3v-k! q<Ov^t(GYNqM17/9Lh`cьU3 ҏ%]A5&U}'f|Bၿj7m߱c%fnVW7Z(ٱ~6F 6X9H;"~cZtA 2WejG3n0f8aoF<;zeۇۏ~=st^e .0\<^e 1>wI\hNw&ց4yUhgӧ'6Ss>^S)\yb 3D$ !42Tv&0T^f_ZÆ.^,' ]e$2l,Ҏ~lݤ].7rro V()U՘UrƜ2 m&Zٸ-@& <, ɫ6{{(r K+# [=G:K jnxOŷ.f&$*jr~E:K" c:eœkjJS޽<~`\,K*nܓnކ&\_gkp;?$c#G2ΠG .(~%\w[@,($#i74^iD6;ySkFuɸpt$iGզ~ Cb4O76q"ն>"dzi]?NM3d- ןeq?r5e99 ST]fH!ir}߁>?qÝ{*'%`ke5e@hbࣄȈ߯KK_|Ft H© ODcS8I,0 ikO>}pd5hh\"9J`MH,w; @|F(# _->s=Ϋ%&V38D9_Α٧K/77>oenȕY_}wFxku֣[<7z{8.9a o7VH1M!1`k!+ڕ+1' '9ռ/j׌5'{J@E*j\wI\Hxi v *$Yceg@e3D)Ԧ~K!e!bn"hv,9$0@z&lW~R♋;譭;F%^VIw`'S&hk*aQ W:g arZM5W薗,g"JI +iR,9! @n铺˷Gx}zթ6F O]U]C%jGc"s(VTܭO?$BN@uι'SqlrP؇Q:>hf>I{(SzcW B G)YjKn \O!ڊI}80ӡ§O xwNP#g8Eq"bt -6Aaa{Ҁl8ЯpoO9}V|3g0khVgPR\pY;(*byYwJUW4A:߽h= o ~Z/v!H+4`Oc4,#CܰHbT`=mZkhgN%q 8zkUQ\t2yئp/¢L`P=.ݤކdnj"~2>kyX1W(FdE>n~o0@iE1J~C,6X% ؚW_S#"./W링;%^wRx_7s(6<0vy $auˮ}9o$O-qq73R"^.)VP+JG h:1ºh}hvOɼ&T];bk7az`˓3ի,֓"?U㳅Rr;z+kM^Pq;P<%o`q_^ne?zjxCr>*^<;G_?װ1pHވ`: tƤR$ދ_̈Xdph|6(ȼsL'5mn^k"I'TN4.rTbtnJ1:{ot0BNEEλuկ!vA`%Z-9:%)1&:OfcZ0Sr괏;xApbo?8B0%E72 vpFZ]+=q;*4R짎=?cxjoX,TF]^"gULwK oroBt =bJ; \fғ~RnDMk5 g7fۅ;Yfa e^*rcBc:yV-=̛ͧ6~2o$(R]lAX4Fڋz~4f"Tõ 0Ig7wM 5NǸz_o`pC5" *mAlSWWCq$4XY fesWt{R߃ժhx0j3 ,Hs\, 17ɿ#0\f8cueu%I#Y^k#ݼrl\۱ 5?|餼z{,ݹ$#ۀuyUQѪ6^VO X `Ӗse#USk{Ĉ6PG;5njʪZ,Eg'nLjsc+xKKIy{7e:u]b*ltV¿ʓWbE$4ä{a_@*QPvne2`d@6B?$QL쇇Sc:ObIO:tz&i ҷ4Wi9Kh-)J#tKiI6]'t2}mWʱA/^s{OL:`ku6ҜÑoT6y4ژ9O5i¨ G$3 WT l2IQoMEO[F H!}NN3W!A%:g&-D pqD#ͳ Xk#~ j_3@0A ނniӃ51q$Nך`g,xmkbUk{Onրo^YV s橳#LOM-,J4-@wm!2R&ɯ@u|= n0q`nIWt[6ppvرs{oAi r36-wt>[pVOq[tF P`GܼqsڰG>;}G²N%jvs[@|`4Dު{NZ?tl*mb%>80׷1w2? 1_\P&FZ'ſUY`%qYfXH1: w|$pņ|X5Sat}Vj`s@^zz Kk2f> &36ĻGŕûn' I͝`c/V0W h5$n7vWᱷAײ_Kر@o ɀ Lx;ʅ܁M}|2'Rd+>kKZr^"UV\e}mBqڎ|6 EĩB4bՅ1אV5p՗ee1p<6d<&ݏ^w I؁ljڴmt2nū^k<a=!%7KŚ.ufY_~+$~(q8XlEwJ1T6Gg@> t0q;j lS.&7fzړ@rtCW|5t /Ct2>ۃ|"NɌ;5-={;9g5+V s2D*v>g),0,/=@R]0+)}9eA*XkZ ,&vj{h#e & /Gk+:|>'8k1%ݟWc5u@5IMa9,d:gOtlD.6w'.p@_ao={DvQ[mc86v9H ~@ԟ0>P`*}KHgDgVxwvӭqcg!vBϿ5`lCGԇˍ_p8j(VE9:3\!sggU%%ߧ8%f̟EXh`(o@%bHJ /=Bi:j5!_L)>)jfL3v T61;^#1a.@0| '5^Q?'|sp)z6+3 K86֯@o1˃%&EIKn ?_U"] {N+; ;+;x(-5 㞾gJI#7"MeGGoKJf̱" Y 8H` nEȞX c{0Jەj#2x˸|]Dh2<wgtyfWGX]r^]?[B0`#n |7Eh )ؓj0FEfҐ>s{a}@$Sp߽Ӟ=y`  WɅ߮y U|P9lͣ?,`!'ى;( Ż1b\F o'75IeJXCژUXQ/8~:5=4"=nW_~HvTh \^ sNJ Bq  oN*fJ,U@̤{n j V^7xU`>0T P}2f&I*& w޽۴Ýt%1N%ebR76;tǸO &K+rth1 >-)KP^UYG/. 9A~`׳fL;u& 5g:0umpgh2(²FvI}/K^bѨwb#g4 |3e_SP6T -BH{*E;)HIRJ٣=SQEY({zm3gΜu_}{֞YT3/?9)e{c^q\zA%ˋb=qkwb`J޾-܃}ءA`(D7`쪷 EpE M%yI9D2T/ꐁjt;,UXes R')47~c<~͍[wyMpIܙ;CE Z8dcxtr&[=eFLKМ(Y]} %aR.5RGGMZvN"o18Xxh3% bq%pS>h[S@Wf\>!?p235)d KkO^W~k2Kq}jM?w[Yoh{x’S ܵ/upG Fsæ&8Q[3L#.!بPz')M=x@wnmlYڃEۙ?7%[IuWII V@1: J` qA*$1q4!wWWL! s[r ]EPQ 2Jt]u q7Bpo WUvZH\:*mv4^xK҂Sp.(y}~74(7G/ tE4l &l)2;Bѡi%gM|pz:_33%퍑 (;3ivkE`7Z1 ; j]9Ar'_7Xonj",yp˹<@>[YWK%1.C oBsM.Nσ7XJ5ۭn.j_x0z}c85j4/(H7^#&&|h|?yG.g ~Ak1 d2 9prwv_   0Ea1f\ 5_Gb\\4Եd#"s]yUN.47ӀeN%W&/5TyPE$«. pAػ2IQK{E5pB yYE G ܪFPX[2,BEbnjJtC[JJu0.]].n\S"v66\^8hua\.r9>jk]_`ܓ>{Olwi>{QCUT8W72r;BZ?Բ|2mh~ZqK} 3 bFx;)2u79 1t%{*жcfp(:Q%1aKWpQrrt:C:x:ܮ#XN{98v pqBrp~)L_F'0#gIu{Hl íeP mbݤ/Zbe)$ٹbzҩK| Ã&F(cse3.3#L|yd_?qM6OO_ʦ_+6c̜WmeKss֤3bì0bKPQ @3urcw { Lbmm?Vh.k),$愧?{#%Uu4. {XbkSIMTÜikxuWBٵ+~ᰘZvqB'189^K11muTp%r[V=NzR`lX[&oV5Oyd^;0}m󶱶Iν/xo 2;{ "A]L I)N$ſb#_tR˶dqI6i-æOd_b/wݜ~x~'Neu?S`:ziXAN644/ygV 5鲳' L6S.wYFgk\#p ާWArOpYfh`y\廈σ1 :`bBF&"@fZ^Iݓ bzx¤1kFqQUcu'U\IK7$Z=Ec3{iFqB.N=вVEo1vE CǮy؅Lv"y(DlLHT2wRkz#Pa1Nƻ`<&tŌĬ4sz@=O3?*8|45WpFT"۷̖yXpW%dP'y_?2BB>~N2H2C3kϸ@:v {/ɿLTmrqrV tt M̵pUnh]Rk@)W*m6qme 'Tp }qZ "xm9 yԴOg-5EFFO1Z?RW~^  JWBRt>0#d8XIl-H ii9+gV-q6 }pk#rXD5=)n â^}E vNPRplQw$*!(x њ3ajwUKB8a=~;}mlh9SJI>P)v)#^W-cb)CQ/v;vl;g nV:Go>f<}{O|'a0Qx޼CCzB;;WFɍc{ *N.5F-QL㧣X|y8'C-e-~_i dq K]B)`zwWs Fҩٲ m_H.E񤩡&PGfr-I>]t#1! *_I.%G }2EX $a:ǵ,4ACd8ϹFDrI:9 }VF$T6N0w:/<Çd$7Pa ;8_t,a[f蔜T?čڻ?.NCw~~zp) G._=r] I1DmWɎWe*Ma8s,]iB#S/$hEEjҢ]ԩ8ˉkcN8?EC0cMu5$5RJ#x@dK 9V$u1錡'=}Iwm" wt!ؽqݷO1-VOe4}{N7hƥK}zS8K6>kS%B6~?"Qbbw5=%YĔyR/0gX$\Ɔn$@;w98k~w*=LOW9yoqAU=LOb~+ybzlH *a(Lȵ4Q%c4[]a=²ĬվQZ"/њP.,"gjssj\25vcޡW#C-yGX-M4P P^'n~Ux>|MZ^0W 5KJf8&\앦Y*q)3}H{?vK"F/71Hq@{;Bp.ipƥItxRũj_lhY j`|QZ)|X GhUx4Djh6dbI9C^ļh<#с0hxVI \sd3h\Ү_>w#+R{"q!߹" Vy֋"5aFF2tcu~t V">Az9$Ěn`D%~KܬM׭O;`RSߎ!Ȯ"-22Ԟ΁a8*Moϑ)^ (p$ɡA!AwqxnT?Ȁ%t;q2j qvfu" 0_ Ye8Hr7i4=cG7Sу3l;B`9dTMeFvnΜ,Mf 9p%!E Jw~Zt`Hhtafwp! pHTEǒƮC`vξ3 3"]ޏ ^2)2cdէK^Cxo|[ocr:"aiqR`u}s資5K%' eFc.ɀ_-P%%7:ɩ8y{9BmKl푺q/rC%Htz oULGV d\Vٽ(;z`f&t }8}o^:D3G\*hIdiʫn<^~ryԆPENg7#d]#6txA1}ZrU+42T_w$ݺ [lnrcmҤI,5~]>=0OiGY7Jka;b2v߿W+WnAgޯ-~@}ֳnOq>q7=v5.دS@vX5~~;T8$7VЉ/ 5(_XO'g^@dz9 v2?7Xs3bϝ؎@%pKU,N(\Tv>O^>M?*LYI|@uS&hі}ۄa,=nak;Lxv+.cWnNl[LbA7ԥ)lܜN9.w\Q[ܥnMH9[?|.&L3& \KIq//n]D!2U+G?ɵq|fۆ_EIzu?W*-۷fɹK]YEouXf*vfo9jsU`Ȍ߁`Pvm:j)Q[??eʒ{Eak,6y=s㱴d pv튒ݩQEgʏ]|ٯIyP5bE0+R<:L\9/Ǐ'UC^"K<|AE0p˰GzpOwvK^?;JQMGn #qڻEzi[a~GAfKLY@|A]w"-{֢Ef}hjB5}AyU<{[Rp,2qϻI;NJӔ-ޑ>AuZ⚓G:ۚ(ꫲ4g&=(UTܝ5HI R@pBYdDbZa:ln \vipLO]ϖ tG\iO$)yL [~rF֟eABy$ԩw*2sr")f ٥&{[`CgPwe:jÑӳ; Ju̟8_l`.(; y"*+}H7}O^ gW6pOq̨ea[c3KEV{D=t:V?p8"d}XcTUޚ?s2) mF Nݼ\g`M p Cl<a~~zuv%v7.Yt߾"M$w.yHǷ^-‡A5,宽gHb(m%% PbnN?j1#v7KKcS `}&SI˚D+-+{_b؈ilsRDAbӓI.&pyd ,L@.n,8WA_6RCvM{÷bݟ[y/Cp\:F}@$G0NtA;=Av|R 7 -owđRPZHOL2DFRWrd*#3v2MiðI83] !#|MXm]kGy;TNr038pq,&Oq W|ghhෛ;%Wr&x[$K)cԍh b'4w, ٜsStC_ib{W5yYr'7E8*8뺪 {jɧ Zy#n6|j8;"jlv6`z. lږ۶-p6CũLIn^,a8d߅,JWG $`DJ-(L""aЈ1z`z Ϗ!f֜yt bz! bw߮zk7LGwYz/.`^%Kʸq@K3qӗ GEt MIӾ߹;=zxWFFNF H s#q*ٍЈ%Ca3.WF*\T&+`"0\UYds7ߘ 6pp.khe"zѴc'oL7:؊k-WwaWMbM'=aUxؠ8WAIv&|xb:OSGG`5T^~ 6U?ȜtLr ù 'x,a{KW//O3f֫FHF ;eߙ:5ñvdm6}su7&ELrİC`t,vGaL;bWF Y G+ӎ0¸\p0=:'9<"v'L)-!ϋg֥d*W] #?.< \'*=qAj\Z&$+zǯvss{0zSlpWA0J;UyGI6^9Saߕ 'h%ED\/1um~K-{]kGp"щi*:0UL:%9r[z Gݵ>~t2R VF!V{{l$x"/s<hIsdKK2Ppv״Xʘ+˕@"^D6=i]`4n?.1H6Ƶa[.-go-03~M1R3nn:U=TU5/?:1MDz $cV(O$ϟ=|LgL0"I1D&O&/Qcz;okQ~ (:{ p,$>>{g$5PH([C1. 7#om t6^@¸J<]%c KPG'1nO9{:G]܆!C%pf7g!Na}\t.\n}PD*#cBkmkJX]=oFaet swi4f*%a`%[xduމiyQz^mNp,Vʍ+ 6A.?0l2@̟0ًMJ2~%=E惄1aMv݅ȦW3 =ܬjgT7q@0y7?☡&[TpCg<0^Lp+n RST(<., =*2~GNz ֮uUXu!OO>kSo=EN'2w #Wx~%wvZTuCޱ9Go].uQWGNyPQQ2@nk/0jOamwWjjEK0 vOq{fN]1LZLxP 3u;@*Q4[>$!`'Ms]]7dÃC]tr@_`\X~|{qxw`7Rh$r=WzMWG 5 G:k!2.-<`\.JӘa-24HYO`8FX-I~Kp2Zk80-M qghH/..ۖ{JnޑFfA{`5du\]} L+B88D- N 2=Ra{~qc+5@-q*,pXAD,b 5?mޣ(9$6rBW {Zh&ge'&ǬP9_|5QH2J(xsikTwK'Aod*>I:H^4\6w 4Qъ;V&١ea9-6ԨqApDY~-P k8%T)@+N 3o^d@/ +~8p&/uZN ֵ>RUy`Z2Ss͍[u%)j?S~xqC; ֿD5BtW#~Fؙ09P>~A$/W^(6Bdߙ$+1A`kLW UOoN.^^m( 瑛q8[apzn h3#E̿rW<oj]Xac\l匝ɒq#[8z9(2^>ut -*SzBU~ti G띢eg+ت~moF6z&w ț\Ja]!G/?Ow)K^pxl(Ks,;X=]dם(B6@ Hl_:g+WWb9 A8,Zϕ+.G wBT8gkSwoTu7Řxѷ\9\ h@SKN#Řlu7?YfsmCңbjϜoKdmIA Rc9D sd'hfecO]6/:A͉čq}g[56XibK 46nNW,BO_|Ju$'=jS$DGE:Z.!y#'xh6NV هָ@gsd*FWh »k"`cHā`a$-o? XbD6H AB:Cbw:}n=]|F0<.+S_EQ pTtYWW[$ X.| |i{{i&VQ˚.EKnZ`ƙ/܌uꃣ޾x$fYY.o.ÑH`zؓ[܁:3͑1rv]c~a ;㘌] כ׭;qIAv$CO{EǾTMuv3_pk# 3WtY`_|'a &G;ɌM+;ՍQc 9c8)ZA[>\?!W/IKL}?&ܝq#\nDNҢR5> j4[p:­KKH#b'1j޼tu??=Z[r&K?}RM2r c|0?;^vd4EK{h ]yC*KO_=uZ 䑽ލ$*".Y R(a ːKKxx[T->yAl+kw_5k0E╮= ~~lba[[RSkOX~r3{~&ƟHO|~Ń^v]Z㹖K8@Κer!I h?`J s^YZ-wHL=G2OHw"p1 ck+%.X[PW߀Hie-%1dͷnL ~}| 1H3@xkN>-$Ɇ t_.89d_*KgX1etöDERz wzy033ѪkJ<~5>|ֈc'08]qNP9+2|WnSq_:yypJG nj/9X͕3XJpDH|OB"ީ!+\Ec3I^" Gi ;X~G7ҍFu1a)wq0<_:;9gxEz 4-); Td4t3O?s򏿭\O÷7v}v`zVhFYYەaqYZwlϒ/0(:ܷX ⷇i`͢Wk,gYCsy4ox\'o^VDŧurbz~b|U]lɜ7=׈- a U}'Uhaa0SeYn%OFO=OKSQ!. @w^>Ua˚>C1l\B&Pwe$b3SQm7NCq;3cwі[s& pVqdN߾Xg4\IF*&@̀c\ lPk̰v5+NZ `a䟙zՍ2̓RGGl>+4՝Ã,ZXOFy&̦xXOʠm1:6KTghcLOr☷v W_D@\ZF;13 ÙrSL0@wӽ5Ba%$;L|=)>|4V*׉`ڒ^PplUQʝssL& 8ϴ#m{WgϘ65?çbo\O)p+UH==ַٌ]lh0D^Ze%qĈAEn z6̟+1>N;cڛ"駄3,9Ւ\w[M%`GͰ./T{cҴ[VU7|D߸? dCOY?oI4Ri-:Tō \g݆'UU4]qn`" PtjoZSt 13~6ń5>˭TꝀ[ϹRs4.;f8w)Yqz5O}pDžb6kJ$耵޵/)#fP 'L1 ~ \ WN,2rʪ/nT*ˈLV/tpSEfeۮvsްC^Csox NA#@1qBC-f7'((j.Nhlj4V+m'J)fTVCz[T7+c[0[| hr qQW%DX-mX,1j[RB'<(:Qb5ZTY~R~:ڝrúΉ³ I;( B#BW1+hMXJg0,{ݬu\^BU{ORB\i%/8V-)VkvK!I~M?#,3.exS?Bo\f-)N3s^lg@G{~Ic_[t n^ߛwѝۛV&% Hcڙ;_>cH%; [ ebb:T'spPp 9.d-6_P%4j'ac^VWNbM7>JcF=ϖ-w9y G>=CCN3[Kp(B%^*lMsŞv]L?liAj -#iKfu_WN ͸]x@\яiwOn♘6&cWk+֟t(..AQAqA[58S^/Åg=Sa~> #T 0agq[~(]{i/-w:jhtq f&Љopi1uT#gYHpT޾tT 8Bw@+s*et1c?@qu/]J-'(9ænz>/)ޮ?'?\+O:\6<_^~p/EKfk~! I Zs5,03~f9ykYFӰ3hՂ ݍel z'(11cJvPo*~$Vv5A\cr`Peǐnyd \zv4"Mv&lls- I!+P*ρX~Ϫ[(M}қ-AboJ.X6$jz&bD|hi4WAC{sQB1R֧{{dN"!YYEGpۖ pKp-II vqy/1Ga`+1>O͸Kl8V?E .\ ={?=&sR_R9y F"Ѭ#);Z-V0~Or1r7.H`\lVB?#7'9u<&&com5=#1C7d~TŁSO L.t}luT 4ze?MtzoK*F'*_Qrrӭ64Y_N->{#xpQAd{Y9&96 ݚ(7i3텬9|DR%qQ!)g$i`>r1:wgʉz'8=`=jۙS 0':$.$C@ }UXmn9`fo?,r{HAXME?-X*Χ5OaB#( &W5!.TsE H#60Q~Ls OU~ml$-@Q| ~%*c9Z.-NP))TE@T^Z_3Nie3^ q L鍫ΊdzGJ-ktmR{wYy,Se-۷~Ҳ}Q o=xvԹګ7ՉA=C4YgaYL&SnVK/76<~jfS] ޟyVY&k]Ld%ġP[#t\znMMk7Xpt_[r@ 0wCn-J,ko廌Κ@& +d8O]5ھ$-cG)U0+е1G -;wI坦"B,tNǰgb2JhDž X a*d k?p&7Ćg\9nK|at_Rv,=P' }s֜[l K;y< ǀb3̦G;&npX@^60WMu1yedچw֜H 9Аb MuL~bU|~SmK`Y 2r񗝦8L[o>~oF*EY"mVGZ#!/F?}ϧ0Iރ+WK5meىgUחOcϏޚwd5,E4_{0Og kmK񚥣j- Я4w vF,J W}߽:ri `*a6ɨaϩqyJ,qЇ[+G/I<RMNѼN(-e%s^MXձ|c=luJc#z: ^w皴I+"P㇭Ke?E9Bw0x`* )V'#XO)9kebr𩴄ZN2!)`tshz]kT;22lF"sH}epDZQq'=WnJ_~Eٮ@]Η^AXNm -8n ks13ԣbuEÓV`Ĩ ' hY( g8Y- qDW ,˔Ȃ1! -7$7lfy\=03}\,mxNǽYܐ.OMΉX}^;|1E$2..X11t I?Y[/95g(щB?5̶v9 "XI f`w(S;8 Z>wP  ggdo+>@lh)#w2ʌjl{;ku| ]&@?X\i{?bcc-۰}ʂUI ҂- .]gmoWP,q̺1H3}d(ܬ;6oxǩqMLq`砛[>ylA 뜩g( ]ջOgM`YjJV](@۪r83P F:ȳ4Rp.5z˵V`ppP6wCK ۟ d> -h Qt} 9_i`s9m>=h)5>K:dN].dokjA|{zVu?7mם.0\{H;wP~U-KfY Bdr%uNT~1fSͻ tg̤ u)F{˶ۮ~yrv Max7 ANL2}i+ cM8[u Z8)Yk:X@( ҧ•*K+"0u0Z٘>.CO!P̙5HDIȪ }l:ZXXȋ8Sxz~`KJ=t<"Jѻ[\X<9N_{ H@@Jt q%Oт\P 9‰o+E+TljtsC8d@SCubSͨBMnz ;. ^%=1$|[\D9濦֮}-Uyw/w0^8Y'n 9K3h\~k`؉ܲo_&2'Na_{[(57^ X a v. C%-ǐKapZ`O uݙ1ıeq#t`akxsX. e oNER],&>` "蕷Daq@ sYlDcDUT7/."F_ /^L԰-)]_ySiɳ0o݃5j˝tPМ>S]JBٺ15<-D?tB9icݢ*Z=r{ВYMϞ_rK#aNR@Vjo'*t;apߵCcJ1ҙfhu @ܞa459媝qQKx9x4XL£ÎC>qL r<158햔p G#1nHd#Kꢇ=:T[[g+)QNJp.p"8"RTfD*/ϜPk"wb#q` u}VW5mM {X 5w6&Mv23agnIq;^<TcOվ@ aÖpGZQp䎣瑝F\*vbq9ƯZXZ~a}M^l؀ܓ/݊(ӎy))~{b'",C qDrQw_OC ǴդW]beeًJJQWgt鶵kyV `/ F9n{EݛRw˜}MWU^8^)<ݻ.K ,>!nbи3wT9n][ߩ ^ ?尞l_ZID%s-eboa,EϔڹMhlyc60׏_]@W;=%ԫ:%[Th͎6f)iN]1u؆Zo2ٲ5R ]B#̏8b7J&lîGa.a5`5y>:9q A O.)}/9 b&6 ٬'B/Gf᳦wb&ԩ\\ H?S'K4*/‚=S!P9>5\\ ._HK с@cs&zT?rnSRLˀ뢂Gm]!hϩ`w"W-I ۖè5l֙z{*o]|80xf =bZK.SfϲQ[8otEguC]qޫ R"Gk@NچKMu_ڝDqINj1鱰3gRAvjm7 f`ju*3Ԥ"'#bEhYR@Z_16:oˆ+q pkid2NODqs-=ہ84B pÑۮy}g[cO_0,/#>Uj0 ٮF6FS]Xi(+0צ1g3l--ߜm`'/)jjMiۛ؄`y?n!o(|p_$ȡ-<`r{{`bzjx:p<̙芌(Aii"Q)PHOCw}<SLQ>'h ,9Zh^@/G`7P)pp\=s(p:R~608NWOOtI+u_?-9Fw:4g˥ݥ9yt8< 3͟`!5V h= 59zÁg'׎ZCˌo[};mU|XRa~IzFfKxXKٴyp4fחN-6(]lSgGBr)S_4 ܖG\' J_E÷2lCofN=_޶yZ'Z,H`?Rq4ч--|6SwO:,G:/с 7Xwrfa\4ϣ/ lQE,IZw`\B}ӫ[|꺛tǞ IkcXz:6Fu3dU + /#$/wh9 pO#ݧqarAc˜0O2-f,E'#X][^Pb,6V{"jzQ<¦^qH?cL|@^Gep`2|8%;sU0IAV1nlN.ټ9~IOG#RDڈv ׍-^$wD{VmYs <_KDjCJd:Ez014 ^wOuyO\TSu ,8lV@*gXC[! "l!=?,XtٲUUǭL.}FQI)Sšyʊ`٘{yWUOzOE('L+-֧e@ZCxC8%=Әٷ "}.g^!~1.TacsX AԽpae]E*Q< P}cX3~yf'F"(3!v|oSz4Q~8j\D`WomёFc*߯/GgFzN(7&;~ Br%KmR[1u^m!Yl eΏPrh0a%+@›/0ӣÑF=h#KG>2\&`N׸ĥ!|Q0)+>ĺٔu6FN1,da?K"P{ZBk`ǎk}ݺ+ gn-w9Ԁ=>ue&Lw\d!D i/۟A2jkUT:IoTšXPCo4okk%p_Lj}ށ ( NUGst\Nud5uZz 0Dޖ=M'9qXM74c p(nJWCPh S+3pӜ%74\!ewB[_`~|Paҷ+?w0 y_L{P죥gJ-øqcXȪx_lmmZ d֦t^ia+F"|Vbtzd.^6MCT=^ݼ$9{U)^*O֟5 YE4l l be8MNJ|;p ɿ$P)oXw@ ߅M}[V2cxqȸa.WFڸZ oG gDR\>Q޴w?]/A@ Fg'a.>S'r̼o[<}!uɜ4v3S},r"S}RW}0`_ Wuq@a{K/\,9hтY qMI>LXZ'+NxN51дu WeuR`x%Ft)mSHտ{4ĥQL.QDVޫ.7YK#̨xIOw j8ahx`6' tf$pZMK2"pKթյяñqQJX֋ =ߕ uP&Fiqp(< tB'jਰ^mNCK!A w>on:H\]L_;Ί4vQzA³<}= _%DBFD<@ё񝞘 BkoOz ʉz tXw,{3> @’6*J$S7ŵ1ˡt( % 1]{;)@E}-9 ${&VU,W8-v`g}.LIc+~./]nmR $Vq^ NٌMɗW ̗ )ڜo=,sU`Z7}RԴ5kmAWb,7  (](=A.,8{-ҟܬӞf0w&EyfsscEu- F=`oywF #SuesYq(GRZU1ַI3M:1_S#ą%on$dtռ5{eʸ(j Z90#i1.1'N _L& &eda噒rwƷ'>Au`kKt"m ck*66cAL0MCéa}܇ ׶#|nEyHp}Ra4oMznrbzs뗭*Sb{j[{ |Ör`]"/.q>A`mMtZږ7B]g{9;wu`ij Kw pwdA*|c&b-&Mf' |-7~踦gL[>IgnG^}|xG%>A5qz5vwuV养IX(J"o8Iqmk[<놋cU_;T;ϟՑ#?9<9=Bt-PڃI6@꫚~Wb9xckxjp݀L+!>{MO^sqƣ%@3ȋ|[ cr2S@*mM5d(֑aiØii-V}kbΛC/nd=ll tKS=<m쭈esq7O:p5<#a(飏l$ʑqKq('>^fuzL[ bOHu/pw)(#p7:|5.`:[.`2CXdJ n\_ȼ==JPʘ4"loITZ 0&ݛ(raAC JPAi7|kK_˶0+{cc o֐uo:OeMl+=[_uMYe-^W1Lwcc핞.vTO4>ill 0 ;}&JzR[6ncc0Lx[[HHHWovRU;M& ,x%"i% ҼQM \Ym z,ǘNO~ \19鮑>.>3d\j8@:f?(EԸByt 74z~/:uսoFmenP1Fg`3@F2§ȉx@RrGͶz"9\jk\ ]ί?U.76'fǶ+~v;rԉ\`Ӝ耘RX/\*АWwr N͊fa&:a,ox4f Vd̗-`+s_|悘HV/zQWNH)UWXNE'U?aC$51:f4T|:x_cR?x>(ʨT` \T1GQ"eW|ueƋd)'=ԷX;A퍂m{7XXG7׹>f7!w7:*cZKXRAF'qp T|[D̀ MPg񿘮PQ2Q.enybVOH0%5dܚCw,nM)nTfGo> z8 nOV@_2|l\SDdcC>0yEh7{Bރݮصv1_)+,kp~ypnpJٔ3|aI]4Yp6q2㦣"Nޞ^yyD0.h g-8\8OnRf 0b" s[NaИ5XPUƖJsRp؁takh"6E-%xp+@2Nlw'R N:Vp+.z037 srkQ>XKRg`i'q2b]{lxVݗ7DM %@e@0KA%I][ߡ9%m>=Xs4d~lKpI:VnPrW[z\H_WWt0TEwnU2bFGZX+vǂl?YQsPT}nUS tBt6" X mFut(xfL&567x]^Z<7#>y~.1S{¦s*U5A )P0iC }MN靀Mf֯c\Ҫq,j0VRG091( C5QM#k~!pN꒝S'Sn ZMQ HЧWSW[s8]㤽8+B FL`BKD[XO6Mop#TOVdò7]}NJ H4S֙xO 1-}A^e34Wm*jobfaj.~]]l_;._Jl5s(c.z!3UMCcXft9O]_p&A*kᾍ՛ ?5l8YTkǏ kSGD?f2R fMu{5;j7LRckL2x>DpY*֯ŊѲN%kQ[%zraK(p@A%%+}RЈ]q=8/{N".+{,Bpc{Xe03}-eg]b6c//ǻ'D0n?ʱ;(/l^jot3Zhok#&aP)XONvFL C#\3#iSK+䂴ԔST3oTVUrAF%piW Z@ *tdŅ$Kh\?x~W6԰w ٶFmz"I0YEV& k&RŖʓWl>=aExA,qSؒ%&Ö8kSZZ ;L!Nbbb =pr=q w[zx |៑Kf= Y,Yl8("(QC~LR~n zQda_K./- d%3=@lǮWQ&+Xbwۚ떤+gOp"nml@mҲcN6` 9Kؔ#3ZO4Eeg+-: ~ypq43⥧ˣ `5|wqcg qY °1|SYC98lT5s}ČcѬD-pATݼ+tȪhf+BsF1g>wƠ*437?&.0Ӆt-%Sx\PӵQfN a2 $|zzdI$p/TԠT\ya%IFtW?6 ^7X:QuuEYw'*W, CCѦ3aꃻœ`Ywu_"Զy!v pe]  Q[ c3ܝsj& grn{7ar\7 FD {Җ?'#s h1FqX.Ն-a3l;aD"qՊQu^6p mV{q 416}M#5`PF&8J,jU!)u[/I L*x+S[԰ $ Y5!/VW e}[q+ƈ/5#-Ss"%Aʯ+$HF3,_/K@l sM@Bo8[jRWXل*Y_6\$&7b q4qsM +QO=E;k |WWoIi gSswuEEiO3">9T`o.X" }o%[kKcl$^Y쪕A+eD#X@/&jt7{=Xl ,45%qen . ewXS>#BN7 m-ɂ|` <:6 ix{W/i)YZ2[}ӧ/ENz|1aĕs8uf# ]8У KSB4xbz6=y} %̂`DӚOψ#cL̀ys~cΟ~mm ~%#Q`kcw͵؛6"J2ÁrÁP\T+?h-_Fy闈dBMS=۵e~f4J0xF% {x70s(?x3RkSL$d@lY`s8yDYj'Ea `8}e P,VqYT(=K8☨z.8GWN-fu`oFȇSvn+c{\#Wf׎q?` t(CSNyTڔ QQ3W^X:m[䑞{B|Ɨ[(1'F} rqwq7sDRV3q~s[kqI0ԼUvzq P<뵘3tb&oO\mNh&14š 7K zFR2IG xn,8 ^MG {tVö9g9R4 w_A(;+ԬԸ>`E^HL_-ݿmkħ`jOfZ): L XC֎-%/2xUP¿>Xy)~ؘY+.e,52KCJpLSXCG_s FJѴ,ٙdA:c6(>v|3dx W?(g}r.YUߙ>7.WcPz AĹ+M 27:%)%=_?y9}5Rd<Z5 Znk>!3Hy5yzZ%D{a>q{) :j<%2_ jL 3 3r+g(;}gfnOZ륭38zBs9aɾ ]0^d1>?ZqXAVrmkҶ5ݬ4c p,|~7)!b "4'dLqm5`+JG<¼6Pc1pwWŁMAEaGdN_0AtܹdzJX O\)(Wu(zT%pvŁ**Ze$ܕv0tw,g|`y٢?/bóQ>.'+|TiMyƋm/}{[wjxP ҎS[=bc9yi3 _P]WK2<"A XNAKr5?}pꥊ2ghXfa*mSn)\li 0 k c\P$7YqL l凍2#9&~򵁍?Z"O^J,Ȥ $Hj#+!_oF=`x Ouh9;Ν1;Ɠ±,bZP@d&mp*Z`|C&Yj!4'h6 ~RV@;zdBDG2zQ3٥cE jjffl/:?،:8LebTt;t_/m>^edm wt+c,6^$ӳ88Gs rxik1P Z+&D:whf,oв`0dJ3 '-9b[ jYbUw#(n%r+ϰ%H_YNX0_y=,iQiN;6o Fj:NeU8d5UUJ@ICiڳvneE97qm<.@aEIJX?rIĭ 3(j x\\FAO$~jGjXVѕ,JcF}KNkƨ!b2ExNwOfzvt D14yU4^s) Szξ넹]IB%@5 qwLLB'+pTX!3$N>KVyE1%6J}.f2k9/ 9жx juu:l{@7LׁAm/y1-yپ>g^(5g2aa^N.7Dsv쁔gڛ,(++:y•WP8zTK/NEЂkܔC0̘!([wp7)c.,IPH KWK Qˆo@$T/Bt8/D=3*aCz(JfRw$p m}!c*Aq%ŸphgMeokEb܇n -u}[ڲ)Bdxz~#]!|'&x̥# O}bâm._ɪfՑKu=ͷ;lǁ?ٮF1 =M=5a5Bݍt&wv*IVTvr#߷ZxVqxeC]$)4F#2S/bQn0͉o@$`\{p3w7uLQ}lmS+YE31 lm?= SC1 [EkF6Az,UESy-c۫{7jF6!)$:݂H{Ϧq5v$.Q~yRGKS=b:&, 2uBYvY\ -v@kErpmKKN1,ûgw  'V/i:t@K~o}5ϥD$ mQd#!KؔS跧˨M-mXh\b\4 HCmz4oqIdku08ןS 6EuI/t@s ߨ.`oEU:حT& ;ҙ%m ѿ9)lk;;Z7{+ˍbLM_cB?_֞0E1\QQMc7>3&hRl4t2IG `x T0416- %a03ao+x>Q\ op/mĆy>tt"@ p7231(h\<x\5[Wd\jo;2{)%!b >_2,nY Xa5˗nf6E9M>6܆_ie1b3-R 3"!-{ŀeIR~^C+Fza^@2[a?w80#77x87 Y%8{pAb )^pR)>@`̑ޮ8S3zp晛)/*WMHtTls& @zX6&7 ء edm8ɸ[R4iD &#㎬%FgN_K:z'f̀ xB J.vd+ 0m,"9 cii)l9lMil_T9#c%_uWP |?Vx;08YPK^ ;A E^ު8IgXP+9\uW##Q?y./ %0^[d=aH9 уum-bLk#_:Й@.l_W[ 2|X~CoϠOUÑH5K\:fql,"G2.pAY#wG "o\tqRLbW[?]@,^i0,ilf.q+ɀqs\fR#Y]yqfYv2Vx͛[j}볤ņ0vU|3!&fz ,ĄƖ)ݺq|ɬjJڛne%H}a5ɸZx*?0 ;=ĂQ}yɑ:yޣGg"mٸ^`j5㞷f|KُY#^6vc.ΑGX㯋S6 P}d^n&3!d+_n(gB6zJh͏/׌\!*rSeƳ&jV;9{Q1!JL-`IZ2Gl"Y' v55fZcscD'0{wO_('pQ_δ dq)|b /ą$)m$`a+;5w88 c˜X/T0mJqԺ,,1 L.o4O'u- ĉ#I_aLv8y08w>w3yp\?o);;J975? gH8 t6chbXpЁK*Ń[@BE-б [@Ⱦbo8jPMTvr<'{?##D?@`esXvH/: 0|ilǜq _eWUտ/ : &*騨( RZeV3% 5\ rT \EtYE}GN{?sΝ;wys|dI"¢\yYPf䒵`h. [U5Q{Ǽ̥ @RRt-MԢE9$1&L sf.WL9{Qhn9S.$8yÙPSJHۇO̞II߷/,+.`7LxMOYuYc_n"\6=e1z?^EǭEywK;i^/&>2qϟ]xit{C~]L0!z ;t:/ k93N|4Iul G~;[&i y@Q]BzV:je͂.OoO.!DylA,""7]%n.3]+v*Я0O. 8|PvPSE;׳^>=Ãau/{r#OPZ!`ٲPj+lٮk9b\U!a#_xǣHdVEnPPj_wvUϭ9oA4ofmF,_O`,RSQfJ%ҿ(iڬK*--+P+&{GNV- ӹΰѣƎ 2}&7anm#33n*sCWD-ݽ "]]i^Q^ڸ̙8q /~N6!ͣеjd0) q&L7TWTq7E-ʸgj+٭]2f  lߜU0qVum!nhwPЈBfQ=lͺm}:7G4{(*;ZC@,*J+D(6f)\ޭozoܴYk:: D\>v_<67I$Ԗ /Ǣ/Wd!.:\'7D$ %u?hc;;G1M YI'[z놽O\JMB匹 yR9T힍oz^FOpBH(&Vix̬>~c4ouNs=ذ ?u~2g$A\od0zr;w%nײiaAqnzVܛ!͍XM-FXŔ-"⚈'Y( z:o:i,3/U~RZ҅.Vblɢ/ވ%N&w.K;6c֊gUUA6(DJ mS6_&U=d7EY+aY#e~IH>wd {JMya qaŴh_PUZv6qg.6V8cEV-G+q_W곊:ul^o-m,f!pw? l__L7:eDmAQ.Y /0?X>Gg䦅J^0UdN93ry{a9"DZ+3gbe°duR}ŕ+8G<[xڦJm$ۜIX^-s*WY/f3N1MFx總jOƩ? &d=.{۰}n'v=e - B?^=thYo) ;Yn>Vбm6AiaiP'0XyTĠJS }&nnVbUe1p'e>Inի `zl1ԭ ]7{wOFGT.3 ~G~^t,e ̘}>^h52UWTLʅ{Xۉ!4z>!SO 4u!uT,wBEZ11.5,w+vǦ;YwyXX{@[-sU!d,Ф"HK>[zKl]C{'giܤ͙X "҉ =mI.Wb|s*ŽOs1fZ:3d{nkGwe礥^$& QܲahQ ]XLJm*/D1P07;ة#y?q{K R[`ԁdrW/ L\W T2GTSʊ)km[L̓" CMi}a9粯!^3Fe݆M?W=LqWb '"̗7Kv22#ʙXMRp \+[§-BðqVXO42Bo_Ok$ABB@u]1e79hHهW3}/Ox]6!^>l=07ݜ -nHǀ)4+Vׯ~ba*jӚ<7R8m3<=g==v鷯Q$ɶe2T[!](ǀ %Lrr )TW1MC*hkX+_6%n@&}+D٥:whpb_i_<$_7+5Z\ \ 2vWW+vkc^x/F^?5a$㛡}z1F`!W]Hl5Xz =;Z%,JfaRit蔷gƈ T \u=m  n\K`B%s.BVN}VЛ{Ӿ#U %1k*\2;d,ael8.OC,Чn5c>_Ie\`@}^]ƆϹs^`Nn;a$s$Yy?OpW'AMEȘ:|KW訏B;LTt-|:"2 a~,Np|utvA݃]#cgcշm0HΑ&c }%/&x%kE8"?N 3N>S~vB4M:Aތ&48n -#vآxJm"nǩG̷f.$UV*V>Nev,Z[w\!g8MN9(hݱK[3'YTZQTP !grY\ְ?([nMYE]4v/ܷH=H Oܙ4h\՜B1$|ϟ8Ԑ19hW‰akų돺}xbLC,6~a .u^gІ'&f9zfMI0-/TŠ)׮_Jlf׳nҁ]ز:'M9qm0N`!k A_V2ǧA2&wO៊*8_vtbe`5!Ex5k|QϬT P0kZHJݨq a_Mt 2&TuS7OZSuMHWv%Zڌ{".>H)>ڪpQQ~h1?"~ʰ?" MUP_0{02.CQEdF^^Նba"`Y/rz!J&kn7]ð9 ?C5b2VK9GBbfhP R/\xh9tr| ]34'1CEIe[G΁Ĕ[gX!֍xy`,U\|CtT /[ DR4G7\|UJ"QZWb\F#lNϸLcTD7S=16+tWEY cP Ebrf\wG'_UIƹM2C]2.PΗt#ϬXDDVP/W)ExƬB렛6nxt4hӶa,Ekˎ(Z_}+4tBq=<("һu+CØA`eHCue-GjYV{wj3UC. 3.1i^ / F'Kq,D?A ^uZFE.ZYg-kfiLl*$bF&=/mL endstream endobj 765 0 obj << /Filter[/FlateDecode] /Length 1648 >> stream xڭXK7 WLX+z+mM$)-'x%EIzA$IQ")gqdny Ke%g`\fL+ʹ ]gٛ*kXSfuL6_l.uG;JEg7@_,sس6K;{QƳx6 }dgv kZI 5(B MVenvYhC;NǍ.wnx{w-arݎ[g[j@زG45ڡ#"D>,=$<8!9A&=XghWD ZƟ'$(~=9zdOMfZ7Tێ`3x-蓲-Ud TbgGϗ'~jm֐߂\0^A ްRG#H9E3yuoӝ`6ippY0AI5KPIPOP) jՔK=Uc)hKB؞Z2QDB_VIpхOfh& F95PƋ'/\WݟEDD gb(R#X ,8&Krg[=֌cx5] E@m'\ UAD%DIzjl.*˹VL[E蝄54-?CA~y>AIM8< W:n9ty悤 T0n =;`=#ήq@[mn%~pd` kѬT8S u$7 LOHKə!+OD^sȚ_hz4- я4D]89DkQ'vtQݰQ~ƍ4P$3F_!:'>%X]}o8(M Rq!{ HYNjŊXc«/iT7)X( 6. TT"qA+cIZ=%k`\5oXq5ܥ4: 6J7ʕ$/H\aO__qdZem|z*?t 8>ATM%%HpJ!)tV@ CA)y.p C  K:{2˭:\dpfJSA,K=Qs`fJw͎k.=7k]kތÅFT{Gӯ̗5AVڮ#b-m0D\cvX:BhvjWRF<`m > endobj 767 0 obj << /Im11 761 0 R /Im12 763 0 R >> endobj 760 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 766 0 R /XObject 767 0 R >> endobj 770 0 obj << /Length 249 /Filter/FlateDecode /Name/Im13 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 771 0 R >> >> >> stream xRKN0 ^Χ![$P CA- NR:#j0>f8@/:KNpf:+ȄhO oP# 2x ?}{~ 6yVz2ZN9⚖J Llh.x,;vvuߦZ骸kU6Q2ip|RLRAz>c.C[;ׅѳ%bO#e~ka~n==v`V֌2 endstream endobj 771 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 772 0 obj << /Length 264 /Filter/FlateDecode /Name/Im14 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF] >> >> stream xRˎ0 +r!~@] *~3,`1l0<ҍ>1=E\f ;^G@6: bH7I64Uvsy`yW^E efW3}Ўߺ.qM,UݺiW vu1h!aR$#аAâ7_z+Y5}ޚ()ѹ|S+U)mc8Baʖ.[0Gc(.QHOI endstream endobj 773 0 obj << /Filter[/FlateDecode] /Length 2009 >> stream x]4_Vl}'N BPMۈ4)IEG|lb=>o#$*g x@fbO oY.>[A\q-6Y}̓_Zt.$*e{+p¤ t|Q.WAETϒBñJ:+6oͺ-WL@| Ti[Łh{1i*nBLY_jE}tnUU=Өd5sZ*ΜsCunS%GYf83 M'v{s.gb]ee683B{T6]O2MR9HO6)QXzHJ#Ǹ6aH o}^=P}[z?+yQHh$ԩo#idn iq NEYX(zB:9Vv (zLZ# UE<.zǯ&y Z6My)OHyfgf* -EVyUa8Cp*@|{8$ :>MfDK6H i5QmJN٦ vGu5C6f*릣ha F~Ҽ[ys4( q8ЮH7T E9dP}fֲb[V7E.ynҍݧTD)u > ]piLќ am%*[8>0[^Ę-J")dj)Id~ بI2g.Z[ ~ fn9D8 (" hfBPS3A]F1?!fK㼋;jpTr:7r5[#;8Oh =Pi8,Mt~g\)-o=}iTpSkB"k|ya iw[.J?Xo]jĊ hEqjIJj{)踾r')\|1Tnh9ϟYa^$7X=_ :<}1dDR>ك_UIy?-"ˇ|6fU kt1|Oo6D^"Cϳ0i>g.U!~~P} tK&$D,7(>U⣅܈7|imbF$쾶Y8\UX1~_y˟JB2?=@un1{ ϛB`UWoO>>R9粏 YS<ӫ~>ru;/yf?_0TWv> !E endstream endobj 774 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R >> endobj 775 0 obj << /Im13 770 0 R /Im14 772 0 R >> endobj 769 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 774 0 R /XObject 775 0 R >> endobj 778 0 obj << /Filter[/FlateDecode] /Length 2119 >> stream xnFޯ`ыԘJ2sH-zj ,SZ2$: |B8#lry0JPDבz|G"A9bQBQ*+z{,r|LLjdØ$dwm],1lA|ZvZzlH@/2s(& a*g0'Jcs,$Bir*dsfjLe)JzmsXgGK}n\2IFbEq# #.XsR6ϫ;)s5v}r2nRVU =NrrQ*"ih@6Wko1sRc pʍ=|y(i.>Aiw 9$#aӐPnFxB2D& r8mޗr,[q|~gQG)62H'%(@:C7+gb\TiMȝ p(->¡[ E_g>7?y,sH!+Gab)M(nk@4GI1FeFph`ͻm+ƗmGEv $r, h%YM\ %+ǵaB%w Z 4nwݡDB9n- ,Ž7\Ȇ&sp M~+Y5zbqn%PZNGYyKn4wIoz|i=W}aa ʁdW3<UUk#,݈`o'jU9+6P_VCt* fsYuTi9/>Wx>zϻYYnFYɺBbMՉ"ن>53}LRTrT.iy=o(auqe^BxihYήˑ>6`tҐ ӪZA1Yd}JP0mzmsYJtם9ByD幽C^ _ߙ8.gGޫ9'tbaӗ{nDw0 ۖM̿2"IO9eG zWء Ch̓w$ˎ qbӠw^=Oulg]w{.D/࠳!mlUku4K{k&5O°VnROgrh_bi~&@9nGj;hH.-P6yglL5H{X:_vTt}r6],UZk;0.[0Nb endstream endobj 779 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R >> endobj 777 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 779 0 R >> endobj 782 0 obj << /Filter[/FlateDecode] /Length 2118 >> stream xYݏ۸_!/rbDR/}- +{:!y}~HnPo831` "82&D'$aMo78H&U>%, n>|W bi64+f~}i EbSپ7"kfPEe"]?T اm}.hgeòFhYurr΍Rm*MWMdAvU~kJGXn7۪&hnӌӔ|i^ՠ^pƒ(tS4ΈLZպ2ndzDކI^h uG6Q I[};n HR"22lRO*^١(!Ɏ;KÏA,/Xj騧DI+R'Fj + 'WƱ*Re^ ʐUe!UH}U1bUaUU1U咓"UD%}U1RUY{M!_ѳkz5>tgL4ַ\LST%vIs1D'ASN G6?z=k3N]Wŝiglg~.~͟xUCo#v>yX GzGSw>T8,&6dhPpf|1"qG|S[fB^v~MF M~N'}(c5º%7T礪&1dJ<[Y}ҕ!93\?xx QY8eVCɋ Ô}_BIetE4u'ո[9+lwpwrpoH6&((حԑ=!W濞2PE:C{0aQnM#\S"&WWi??5X&x!ҤDjIB[*_ (8a㉨ Cz{D#5֧Ym7/m{d洅]{|k}^ 9:~o3{18T }J( w_0Ŗ *(ckK9m:L"ǞfƪWmå/m|Ttn`4,P_LQ(6]ob=i℈ZJHfRPO D})%ؗ+QMoH4YjE[&x(G3/P/9hIK  % "*m6uĦO6] z5?O,ݎk,~cG kv\vqCd4i^¾:ط"c)<" !cUdkYԊ~[دpD6_%*w^*jKZx*vY]OuPY7BSN -{}w "!_BN2paبt9t>^}2ێ,%"2wtX:WښhME3LuER_'( QZK G?"yb1*rm(J8`zÖG C$BDGa(z-.'DٷGO}3@D z@> endobj 781 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 783 0 R >> endobj 786 0 obj << /Length 51 /Filter/FlateDecode /Name/Im15 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 787 0 R >> >> >> stream x33T0A(UF`Q(C@L AJ\ endstream endobj 787 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 466 /ColorSpace/DeviceRGB /Width 112 /Height 112 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 112 /Colors 3 >> >> stream xi@8N'NRbZZK{ңrtV:iMQ O 0 V~nV.] 4hv(P@ @ }Sh\(> stream xَܸ=_! /j)RHxb ΃FbhWTǧEJ{*}6 Xxw=Cy^4R@x'Cɤ+n^wss&R?|H?g^wcJ_o({Mw }}h"ϛ?{~8;KcdI(/jw#(q3.F,Vݥs]iUv\6w~{"oo ({3T{BLp1OX­7ee}`HDunt @CцѥM`Ȑ8#0za@KYJj Q)+u=,_83:f EkiD:g͝~Eۦz&To}<']2kkPƂr7te_"9:O"D@K:"E Zko+];ƏOZޑs&4dr?<}~ϦPֽ$HKofT駲1.B UT?B :~Sq9/rmL1Buvo N%9PR}v}~E2'6;2=llmPAGl;X%rïY!.+mK_Y!{@fe(%?L?@;K2VHLt+{w4谥cRsATs7Wl#A` ڪ *緃K02C;@@Umf}}!yI<x>;R IsFaf|ਠK{@*f<$HP$xHBM l[a(.]_"5چ#[Dp. w;ymoGRldIȝ`V48 ~Z%,vm'RXZR M;&|}R~@Eժ>> endobj 790 0 obj << /Im15 786 0 R >> endobj 785 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 789 0 R /XObject 790 0 R >> endobj 793 0 obj << /Filter[/FlateDecode] /Length 2499 >> stream xZ[s6~_/rkMs3;v[NGD[[HrwE$Zqξ"H~"E~yG$PHa% ~@UFxu{ߏe}dP+j۟!Gs)WI9,Zf@YBt2A(W>fGe(fRxPJMw?݂$:%)I-֡mkǕl"O7(ɏ%m{#WHƉ%a;,IIm[K+!ó^b.h76{Gj DrkynIùI`cXhl:?ΝeaH)ɤ|O7{1FkVE6խϾթ<]چ7ֵxe?_cAv4uq҄v"| ;H|ΕbcXI_Q iBxϾ!_b9/ݟOSx cBE;ou(4ahjz&pCEB:'0 C%td= K"vS$֚%ӚAѢ B ~djn^=% 5'zPBڱngkj7ݩu.jeEb{P=EG@qadPn tAtv6V*w];U"QiEi P[忙a J|OP+tͮhZU^} Wo ØvFd>aGp(Y W!6um^鏶0?3<C$1ݭYJۚ;VQ z!֜0G#XĨh6ڪUR ve{܃ ÓE19[C=X4:ڣk=x+ijI\R}VsfPHz+m\ "ZMq1hUs̳oX5yA5T77N(+ ʻ8,F7K7ix?h;hE-RyKDOwO(;s.^ٜF&VfS++pTg*d,=3 I ,@rCeOS%ڡO/^U2E8 ߅rɷoO)M&h:7GT<,EžG{s>Y}/C"RVq8 kiOP3>eA\_O/#`TL. bX_X0giW :Aj"_~7+9B/WlG'Xrpn>u`⇍ra 2m .(j)&c,qL[KqjʧTe@pE9qiM+4a$u!{(hĖ\;S0 KzB`Dnx=K:̅b=Q/@[!N8Ц Sh* Fe =^wM|Aa SсgP9%_e4 R;0PCı$.u$ $E\eBh2P WtK ,|dL{y$PHgg50lkӛMXY!z6Y.WDpe #hOұ&L T-uϮ/+dr% ,XEX©*kVCݑ *<ʈ*̈ITUd"dfٴ*QZ$DeMFip@0v\1 tus؟uQS,CSFjq-ueGMKÝ=qNmaVkS?ڶ,N""R^I)J"!LHHɤFPb>MA %Ka{%dm!Gh&fbNc»<1C~m"k/> endobj 792 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 794 0 R >> endobj 797 0 obj << /Length 56 /Filter/FlateDecode /Name/Im16 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 798 0 R >> >> >> stream x33T0A(UFF`QcKkg```h`daaRd$ endstream endobj 798 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 1861 /ColorSpace/DeviceRGB /Width 232 /Height 309 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 232 /Colors 3 >> >> stream xr6EQ?:f$l${4]G4sp?_͟gBo%%ᒒpI)e@k :/ᒒpII:@K!>unqC!V8B;+,A% }' }CJܙpOd9ϝ DܙpOd9ϝu wue$ ^WF­ue$ ^WFu#_0p`;'aK1m֏p;[?n#%\Ca;vO>bn N:o@$`#&`#&`#&`#$Ol 7nb;,hv_ ay#+/܍p7Joqrrrdt%.E.E.EnIAwaR-¥ZK $ ;pyp w.6\}걿pOYm$ܹHs a­ g pn#%܇ z >LcaCDm1pw2dm#ܠEEEX5%܎""\p;p{p]( !u w7) ".y wA| r"pJ@Kpn ½N:&TX-n=n=n=nbkÍhp.) KJ%%ᒒpII #& #& #& # 7^'@{pu K(SD]x<;A E>OvẄQCnGnpKE=D.> _Зp_p_p_@MF p'n"1,C}pn#%FK;p w.Bsypn#%FK\>HHs pn#%_t j."/\E?tYKQKQKQCyd___hZJ w#+/܍p7]]iv_ a.hEt_V=Mí:BpߏmO(6?X wK̯|yS{9q.) KJ1!$\R.) BcgAS"\R.) R}F$\R.)u d%%ᒒpIih%p8> 3p8>DДGO?x| nsp';ܗ2ACu t|nsRz==!{B p"A% Cp+7-t|=!{B pnA! Cp+7>@ Ox}pnGG v$~ۑpO>N ;pn N:"pn>n=n=n=nb;-ĄKJ%%ᲔKJ%%ᲔFKJ%%ᲔA ᒓpII,%r¥H$\R.Ky*h2pII$\Rb7 J$\R.K)&\R.)?Hy endstream endobj 799 0 obj << /Length 56 /Filter/FlateDecode /Name/Im17 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 800 0 R >> >> >> stream x33T0A(UFF`QcKkg```h`daaRd$ endstream endobj 800 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 103741 /ColorSpace/DeviceRGB /Width 232 /Height 309 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 232 /Colors 3 >> >> stream x}w\TK#*v(Q.{o{F b (*R==~޽{g|bo"L?O*d|j0i~iL[c| oa_03,+6:RXD0t%D/a/Oi6<ކQb_%-*WUDBǦg-šVq.pH͔%.ϙ*+r6LKC?TCo2yQ|FwO?+V} īVR§U4KF贚GN,:&Qceːp|mekZ@7s-ihQ_1ln>]Eh-H(AIBt3ՒPD!l΄‘L#6LSa…T 2 }0-|lWņ6 L@ fBʰnhiՊ^x3>ˁ~[Ρw@Er pla~c:i&+bdEj-WҖ \Ir)9`җP*Bw -vU➂'FFuQO߿~5i]֤.+(4¨Wb҃B+S?)! "J{4 @E 2$bՂYip{T@%^qw6!p'p?'|$@Fچg'l7D**7ԟuH# F͹p aVx$Ƿւ!81S0  c&.S\Hy``~u/.>'%̻&*>1PvYQH }$M &";2!"`3`=iV-xd[Z'z'%?PGI*Z(G6|J$|UO"3ɓnD/N80b-4ɧtDI]Tƺ'kUSxCǤ Ɓr>]dg!e0UD./p.=$JV2%nu/|*L l W.i5h$ Zh@j/!O|U`i ipG#V#t qRK -jˡ)< 1J]I G-/$К C.|(tޠsa h 7 R7mcWh>G2~$RYrYihI7)?l\r{'"ꤽ¸** t@I)Ws8n ]jEV3ٿ@Z=6q\i*G׮㢵LZA+[mW@jh/{^/^a܏"_ ͒x^˃ˆ"WP#E F7@4D1+4H$ߣdfM.n%")|ܪŹDI4푋 *=}E짔 KHf#AK?rY+mJyL aW5!;/+;1DӿS}K1||L P%f^R9#K`ɒ@0mK`,b<'1=:$Z$<荏Jo~4/xZN)1weRݍčhkI|ҡDr b+inMg ާ_5sD*=h`i+$ 8V4!Uńo0 $Փ}کזLrrnA<u!C'5U. 8=1%)׸#4́ O.w%np7C dYpp1p`HBLt!aӱkH'$V# 4eg|@;>2녜^sr`Jr%g]-$,HJp eN^_p.`%@4WVCPˣR$Mڸ7n8#gǾ.rVrÐ=eঊ ١f}қ^rIR(cW5qnF/ =Òc W@ʼnOU${3 ]\|8"mOJxJ0Q7A5h6.#-,4Xps B kR~&_gbT0^Z- $<+Nj%bKD!G>+$S߫|[\rڊr7)_-D%bZ("]o];w;9J)qпh7HޠsKA%.']b"+HRo. (<*?)0qu,\iTB$aJ;+Y/μВ\IRʙP )MYpJ}1AJkqSHxD sD`p;ArdVuͫ!Bz"ؕYIzqߥŒ-cW <%] 6N3ѽQC^^yCJXaJSН.L^+BP AI9tʄJ%y gBUJ"KLsȋƣ28&$ (Pp`ҁ^+)d}UBŭ3 d:$_<`.)7!)Fnz|зI0*=;6H 0QTxg{3~$-'+`[ aWG2az{mFldQ,_) Х\§"}K;fīhˍD2'nV We*Ab)#<z-vŷtu#kRXе0HMr %: ;efi~Q/ a:$G4JPDb"\$؊3I(|Zg.U(,#gC-Q7Aՠ +*]Bש"(Rt87;WE*3ݘ)̅^%w%jh&F䄤RÆ&NsbugIdLd;=x2֯њTbݶSRW`C0:E#٭J&K!`тf^繒$6C3!L 2_EjBOtsRi]"zm@\)V/ Di]eȤ(zI+Ru0HC$@ \Z-g,,C9E %+_g$dyዅn`J,dN(qէ՞qYO{ {xJeᔒl,}}qC0zFHJ)R\iӋM-VRN+C#w E2^GM7<fu *5Sv \E_ILZ t6.kB#ʟ_REa%DhL_j߼ hꖝs !}|y&~(q(Z $%m<߉8G_ɼ`Rxv;m([to JVT)P雐b8%.t(=*a^ٹНȨa9r\%RQL!|Nd058tѧ`JὪw{˒֕m\sH.A/$| w^ )-O#/G`_)1N(]Z.%i=Av3Z?=Aݭ3g%*`Sf"RS*d3MLfS$q(k&_?-f¤6?}{OC7)۰}BlY̥0)7(qI#&%+UBW ECzI2AM_"$q)Xf7 (NCS&)C Ou$u`Yd"Z WD-~N9ٲ[W2 }acWy֕ag%G~J.5r#}xS2 KJO.7}K\C-&}-NR ݨeEAjhKڀn^jUJe]2uؤqwn=b)r]l qc̬ 3 jXYʘ~Z W͞TVFI=PR"sv1wΜr)•zy.c x/9,cW+Ƣ(@9W)Myx,vnDz:/ rBڰ6˥BK4KC/%6$ %)PAbؼGs%v<-@mױD'$ſ@+=ԫ7E]BV߮4MX7/ 7OxztxHQ"(!GbsHHr&y%|ꓔbVʨ¢)HԥSД+t#6%,q($㋨DL!pn2>燒ի<ށSvnKmo'[4bی, Ht0jc-@SiB▛^ꩄ{vԫ[֪݋YK% Λ;ޭYŖͰ\rs-ɏ6snv\0)77.|&pq׺/H7ěPkP~3W'i@GW{GT2)2u_)R(W fB ckI?)L-]mF\A&lJ~EѲlQ?Rd#eƨŗoN[ɴvަ]^yh ;u_ꕌB|]2-Ŝ;p&nUW}}2һSsPȊ{tQt;)VO,F%ըA|:Ihy㒂TsX(V0bP/Gʭd/ %.LHw@B7ZZH%P8aGE?jCUʤkZ45 px$ĻF)byjWThXɭN?}Y>GGd]rʔYs;8eɤ%WM-mls>l:|w` "'d'..N}@PLD&4_ake]8 N:.\԰Y[`w$`e*&߾X4t#9/*dA'=hR]@/(nR,aet"MR!EH)njˠƆ:6T[\AcyԤ-PH&0"^B |u!($_9:iI#Q)hFq"uq8/obx?zO0*<6sЯ\tǗO~J24HLˋM|na 'vyqW6fgdN3';u=; 䴑IHMxvACU+&V/9bqB*E7\D| i ݶ\PI>3*ौ}# pd+(FoM/#=O9ZxB+;` BWg=qRέp Z5BCX, &V]Z+]h-eABmj@Ȑ|fH^mع3CpǢ1>]읺leҊNv nAF^ ?tPi9ظ^m dl3YȾmvʹխԿ!  i_~Z'9[%kOqZKWRȒ.^.e+Y ed6>V+BAv._U x:W%{H:|1qYI {ZI&**mekvoٔZ*ٓco3øSgi`X Rro??'jYZ>h:Rw2|n K[k- LMgēy;GnB _*أbh?7_1B+nōvr(!A;ف e DUwnqSn'n;W~'€:> S)M(C7Vs?|U[&yTp=:M[Vg8] q=z gV(nS" T2mрH/]9#G&7`Ɓc˫\\JeeZg눌Б:3<۷QXi^۰/7M'B#"C~Ӯi̅\8_$IӏY_q|S;S6BЭ t']Y7%6Esջ/:`%[Qmjs-7-4grO=\ǥń8tڥˏՠkI#8U v˷9ۤ"Rn½VE m.]|3$8չ (s4Q־F nmy _%}8~ꕻh^0OíAq*anz-M ЮW,4:sW5 R$?5SFr-*(U4l83%(!+Ꮱ\\i#ja] V$gpvBp 4ҷa5;YK~)oc۷9yٱng;wml2\%sRbFD"?*Wn)N@٪o.˘~Kٗ}f@|] t6i㥎 Ꮦnysq: WCU靈J"c'0awZǨ!En$ÂZ"]ԫ_9q%CǶmH!EW[^vK\Z7LQn2(ʆm~?zjA#aoع3o HUӘI3|-6NyS(oB҇?\Ѯ{o?eڦU3~ur0+k[aӒY p,`50!> 9CZW6VѩU) ;;ۜ "u?~(0 xhN=D1LIq*ǢULO$ޞQP l%SMP#*|)Wlzs`xЍ Fpdb@M${w 7=<`ooZfM7u5n:&3C?¢9ǂS򀹲7^YЎ+JH=n^^J, 4u远n0AG_ m>&xEOFpʾ_'R q#˼:ydYhu3Vn9$H\3F7uvg ]ؾr/Gµ9?&PnueV|Rn 02gv5vQFS1\Ƶ@]@]`1W0zY< [z ."ED}qZ5+]KEeN!m7TiܤfVa*_a|)ZJ(%(B4T4SnVzWW(<@JA 1Ki%w[?u Z~eGZZ~ bnϣ ]\rՖ"Ƹ:Aezzx4·}oB#AIЮE wyJsiMރ4`&Y1WŮZ9jdjmWwP' AZ2* Xi:+B+Qe0n:*mPl޸; 5R =%v*u5$4H\Y3Bmp-݆`eaW;O/OOj{|Χ!~ Xq9N6eik;P߾zB`*IYQ5urK܅Kh\3NL({wl g~= Uǁ3ױiٱ䫃QQ z eX3US|k,z[(qdS`+a wT?ɸfN ߑn hH6)M9WΫNj9Z_'/4W¨v) _*Ŭ~I^^ƭ-WΘ+ݛvj*&׸verdκ? 0K a 4VO߹tn11LPb^bTknZհK!gN_*ؔDlۍӁ\1I=[;9Juرeͥ4`^^)߅ЕKuHC$oF%*x1)E>"L\vJWk ~<tX_r[c !z!nݒ%$V >us%7Xjqwl7(}vBh`T v9ge* PMDqI,J&&@8 RnDe6~7 ?fO_$ſpssC@?`1 sX`kO$dcmS8&ZK]r1 *?n%vAR_ZI\Sܑ Ί6ʼl?lGƙ=\d?̟J=KK6p et VU50 %  ?-#&snyThm{-;gQ1=jiSv9m7o1B=ڔUڪ?>pבZ*bp+@26ܫ ; b>i N60< r*_& [ٹR"RXkY! ]U5}IBZbpዑb_KF,p,mDgڹp1+yغBQCs3JuoM;woL;󷴛-Z2},Y`Psӎ7ԫa^Nsw_9<5=A=R {:d/ 0WެF&WYt1ʍ@}X'o^Fxp'1*.4u\EA:QzE6 hN B ~gO/n0JSV;8o&i_Ѷ]]uLͨo#u ,-ŁJr(@Rt$fjs~3`j_`+Hn75"UvR,=sˤliQ&O+s͕O$ 4c>yT'wvwTv!~9{6-]wNFZNJH20wKٽi:׬CY&N hU K.APԯ@ FrK?9yNfY*́"2ɓPjVȅGw8?e~ ct߰* srP)a!FqhL._W.gEø66Ҫ@?nձNjI2bR p_# `.$q qgЖ7xVU[EP%{֪&H-rOv>HpļB֨ts)_,oZ-~^2鷹Q6*[ K.סׂs~Cnbķ=zλUpALˌ0t0\ ƃ{'}Q\)]BݩFj487JGxm߹lZ lR)jކP@:\eiNb$\U북kϟ9٬CͲEk/GǯxEst%I9*Ks#bg TOIH,R'YY5P)JhE5i9AxTELUFiE|Ûw+^bh> :sdsf q-MN<Эyejz'9jVO YSVn'  Sa- f/Wo^. VuGx{5_DgtyFkAp ua9lJnڰV1fk5uvu)QHSu{Ŗe ̝3m_{>tb mS1 cʬyFUZ#Bpx0pY (t _U$Jۭ^K):RWؐ6W,J0hmiYtޤ4W\7fq!Q_?til>~xZ$a7֍]urϲQMrܔ2V5s\0-}6 Zb/ߺk!ctߠw'` Fq99lQyjIܺ.`` 3*Cs#~XF9=ͫgc[fm~p67ͳm6#-GMk;3iF6N\TmW e!3<$`R Q~JS݈iR!U C@]!R$r|H_%^Q\)JC.09w p:_W\rȥnWIJ״S>CF02uFqw/9:ͷx30q8 Skv>x:5NRL9sꕻX1^Wâ qyꡨ=xu 0fSɍl KL3 pzF? bcy (@! l .=&O 7:A*$ R$ V&KxޕQ\q+ЭE&[e$aZ7#қPKM"$ "jaR8P!ߏ'a̝ϋwox̽Sogy ^g>/;n4lؤq%@N ,Byc x~Dd1<} q1;fOѰE&* Tw T:5<^dfwߙ+0cl{z/ݰŭuMv]]N?'^Au;g|Ε+hL&ҫTf@>&e!qF5JKzʰY,e ͼZ1IHsU:r态,Pruizr*/\_y^Udg>]cW68"W-^c?>Q}l9>}<hiB+%*;{B'oI\oB[ljMZ6qww &g4O7)U# -j.>rxçdNvpO1hh r{Ny&V0eW\ULǎVMۏ=3i#. ZˉЬMV-'|9 Ay.% phVH8Ҩ@3^&}Rh"g!ݕAG R{T91P#. uٻ::ͱ}%GdM~;w)auXX^&&XЉF S޴ 臥\*bHnO2w[4W[F$e9ȖOTM`>-vmؚ[`6d|j]KZw>-^Bk}նa a _e'MKK˚4|x0-v,ϻktƅ4y -p'}xB8ڥں=; ;2 k7:}sݎá9 XZsisK @Xtih aXFm/o]!;yzE6?{/QEkRM+7w!DqK ͽ73̤ =V&~gj'6AhN)}gBfYW/1Mz{X0Q(3|4a5/ˇvm<}Z5ױ=̜e?o7hç.}^^14VY=z*" \b%S ֽ)|Bc.-H ۡU޾A._~æe f:*UG Nͺ:**uWBɐLc?..]aqVFe޸{8!^ 䘯T$qI`W0SVD$Qd- % 2p> hx,)ABa[ƟNe>QNDBClF%kcq@Y6@kEU Woj!W_:<ٯ ^?~9kc:Qo=l-5O{7>]H¯xw;InSfN9d'Ǿݺe49q :ҐwhX!׎]979_hczo0o"3X0YwFb" D;Hh8Dx|'Bq.^(gʃύ ?u$=:ko\KttA?(hQ#v rG׭ u^k7yQ=!!p[VVFG[uLZy:+Zv<`;19z|Is->|+<Z˗d< ;?%W9j7=0pǦ 7C6=)"&z*mjC9>6v?dÔrdOJr+dFCfB:TWz!NBZAJhr45U(Hq=gGގ`ٽKHš=< >lM4}%̏@2~#-t6,(f(ݭyS Qp3ss{-th5[Ve>u7յrnuK~ ƃg_n1/ uŚ ?_PhXH=Z Cg0vf`D֝{YݢeSěT#ӭqcUe'9>=!А{/T[.SAB9G{5󪔬EGRvVjW:` ㋰2P^S :)^04BH[aoOI]R'jۦkYۤU~۸5߽e€q7]r~йަPEk7ujTs_ljBn/$] TCZz pF]91Cc__1#J-W{w)ώ dB Flz֓[we:;wlmtrܩ`=էk\:}ubnx|^պm0~>~`Ş!Ny|߾DJ[>ʛ*(p%P:7hPp!\10q-HH̸|^@7"@3N:\]7Ǎ3>OZrC_1}]۔Ũ `mոJyjU:_PB'5;F»g)Ą$eASx]eIbR7'H"@b nHTXvuV0Lh׍p Nq"FCb8 HESJ ҿ_5~w ݼ7}+9wNȝM?IzsIl[+B5sG}~vw1ГKWh `_K kj86=wOq_ A?}GV¸"XrEI(W2”5 [{bF6=pTl}oFZ@!09mp(4qt7;e@L/HnLv0Jy=z݋;pgKgWuh07`GO‘IՕG,m7t.mĈ={.=6v@CG- N/FrBi-f>CYlgvφ=\7S|9"-K`*(.\^VR|K~^1\?|l=HW'iS-w=UŴgAk\k>{;wi=:SL_~-6Yhܠ{{ 'AtΝqS #q7Ei<$+р<侀;Ck:xN^2u9ujyAlmأm}jx^ْtm[v>U@ >Eui4s':;T1aRg ȿUͬtIf+> s Oh$gN6kиgAUr PiΡ&OHX{70<`Ĩ×7Hs'6,;{ֽFvs9$M4Q W9^0/{VS5.<#ẏ~J61dЉc" 7r eC.K+ۡB"ICLћo.Ъ`e͕gz/#e>HN`\Z!u];uzmknܴ^kՋm:Q?wz\L2tރG:䨡+hdWC;wYXN6h/:LCX?r46̨=}=``t#ty|ЀQ_'R@fDa>a4j `=D0|Za90bi^AUYpb?[z6.JɉPܧ P+9(c$.U]exf0)m|P(}BQmIbp P-L 2Pqժ"ٵ㼋Ǧwp{q 2o֡?>?cy57JfٗtJW$xaGH!w1~A5 QΖ[O8'hj 'Eak]~TXRBv0\tIzW_aƔXRRD4<`D7yu# CVaT޷/cn+o$0= )nc-}KŀKX\T@Q/3zTt9ir &;c8r,Z^UcKZ=A*\wTT4{3q|ISӺÐʵb6oثϔKEgnL`( u:gIO_0}ب踬g>EoGO]{D PL muqE2+N#Cr\XU\&wdb|zQ5z4>h7sݔ[<.mDf^-_sU7ǯIL7yq7;Ζ.h: ͠dŬKF `p`Xߠv? m臃uzOO5Qx_;«}p6rg]r: U3IWuk<57ݚ¥Se᠄ AP+\+Н.kfS0  H̢5 8|$RCLhnP*SK<TL &W\szvm۴sjy6 tOcRhwq͙!}bqm]пo>  y$9!&on`А~]zШBS@@[3,'o>OL :=(R 81&zwaVm9bT8eOqL%@`12Z3yH=!03' Zps غƱc j߇0cM`h1DzҲ_ܒ@^tu2RD]5REzB͢i8)#7ա<~>ֹOG>ݩoNsc|g`k}yk` N O]\өF^L䨲5H,-\?4f=w2y%#t2 9{_ߩDR1}ZTM(X붮ܳy>Z + `Zݹ񃆎RTdT~{- dO;Z2v0Uq੓OqR#n5~{m^DĝE&Wm%.Cg qEI2Y -U*bTK=rv66gplUGݽyʹmjx{WŚ% @zym[n{`!{Ԩ;C#e~_PjPUKf_ [B#tRlgSΩa",b>D`EThcޔ ڼQ{~8T9|c?ưINC֭HJƦfjiԣ>GٗоR%B!!JQ!R"KDJi$mTH(ZѢZ*Rws~9Ns9ZBт$=9KqPK,PQtWP 2o6*`>^TB ŅYu7ZyWHfe-!ST|kƦ8[qz3n9T_O}l(2%Ȉp72dhNL~lȣ(smLE{iZ˲ŪHim9Sxlve\s XVh/M'J_(D ו$~yfL"4`b$ 4(^; v*H/u=$KN}|#Iz ikVpO8i*o\QZ4w"m3mGKtN+r'p墅ryE/,IW~OL^Ks:7ǀLi)Fw p|~|)it bCUq2YhVA<#@2%o9rTs..8pxIbҪ23f_M$E->zdsGϫ(oj|A{5]O_zsU?G(iWO yxuR!U,څ.mV[hlje-(Lq'8pd/t!, 1=bg g\pw@|# e^k8K d$QfƪKp;+n:{ C$wíQ^pTcsVwMM\I[%(>}`n){ d_C_P\3/CQ&'tË>_3.CI"n_^VY7hN*)BxuS:d*`f0C|8W;Gϓ!F1*}P hCeq詓0,!7MQXY^ gw\YW8QzZ[Tt`#fmfDZcV!=CY V;aNЍ0TTN' [*Km *I 'GR}3cHZ 8;Q}G3B^5]?K LR\{ECOmrsSKܛ{.I =_!= $/)Uvw/msÒp.ןE m2ZaJ匛Zt?G fZ۔S;J sm=" 7r0# zѫh7&>VvRMe5Eg8R.&FgʍERQn{0]mX4s,j24r$R,B0+uD_zr2Q30`*_z|a-Wlt[`v&uҁNq`20zqo˧O xXsw3of.VhnaYRbgcxAPTtJ0A.^4I_Xz <bջDߘ|ĥ>!`naS9wZa  $%;8V|Bmu?CGS\p;m^Gp/^<e B@\Ɖuv9?\%gւXy:^8l*{Wwlr y`Ck*ٵ')Kl\1Fw;{ӽ ىSFjpHHe ҅Dx($_8w}u,/D%$\,xaUiSv9w~1)DN9ܘ+Ӎ;^o> |m5zzџ+FF\Gl%q.J[mcٜI\ * [,6k@|=>^R'B7|^.6Ȟj?1.OB$ɐsYܴGo|q$2q, 0R8m0psc,3~B]u󍌻4ׂ|\oeAG%ø.9~ٵ'HykjH:wg LleCu5FHl@0Z.QaMJzYBßjvw!Rl4ub< GF\Takڢl\2j.MtyeZpZ'ΝP_\zemO cq1ŕDO,JpuI|HP KNN\2B4NKg$F9N ]Fʈq~x8U]<Е.kwz݋j_cG/GꢮfBE.zZd2k ϫ <ݐGB2 X r,;rb)z:?Edlw9=mgkO\, :X); Nj.bgҰژ'zBxà g, ?ŏ'N_2Y·)R MQ4J T`QT~7 38g!񰧕*.%_6v k:˻yG𘪤}sݣxgTB~Nҳjxp9? ]J4}}$—.˸xXNc\=>Ζv *,v)å{ 1vPN&MsE"S۳F]M{Ml6036]y۩ >0/ {5HƵ+ASbPFlygS+d\FJr$Zf>4a1ʤ5L/Vlޔ_յVqBܝ-Zup,=y"6je1q X tcH 'p)%W\R8b+a0= fҥ83r E]#m8)7NKm"Pt2dV7}gGugMP݅h$T2'ת$,&2kv:ba}irw%b侭+`<(;%ymާ3^QSW)vAŊDmhajC70-.ߺqgO-!As޳^n~ K=DQ`kKg?SAAQK[kaҺY!^م&`p<k4)κdvmN1ܗ 0y8O<xL?^F%&'ӥ0E2bBi͑STV{N;n Zm߱B{ųi7"Y};UΨ fJbXdT3]VGhyˋco)Fc07_ 3~ToTaY'N:m:+`Z DkT@4@ŅG?*@lA`ML+ vdMc,qVڻcz'߅R"ybtTn;g ${WX&55-.+Fhg8;|M]qDb? 5/5uq<?tGD, M~|i uYi/oZ%Fk2ot=qɽqbP9'v3f ٰh+ߖkΩ 7ݷ,? v.XQUV 6YAA :?iDkq$ + ~bPL-';;# ök0PTt0*>q;IY ?!vp-}U%0HOxYsQ/sbWkR5$#Y @(o | R5,{35x %\v(ɸW;PyE}Ύ/CדּR]vFώ_zzFkZz[}A xqYyV/~[JQ036{5IY^V 5^V )l%Rbռ,8!I9EjMUplj!0o9-"bmCBG.m$karLH^3J<ŮGvo  ,C̑ HƬRUi! C<OiiYOk$ 5ICtP{Kи p;C %\Iǩ6:X_: n SG`W6`ٲ"S\@dvY.A7{҅6FAI%/L*LIly:_7gHi05C_T%0m{@EANO0e'=貧ykyeVNNF{&0nAyA. RUh^&tM%F}!MV,޾i,Uӧ #btfX7ZPf{`y;굽@PSX^tZ-kZ +keCi()kL3bacUݝ;~PpMi"]+Me" AcD1rmSdRĒ`&͆م4yrxʻ]FR(-,Y9;%(k8Ք$aZ_99!ķ@OϢ#~iƷ}{a3^N o־z&=c^78XYt/ ou,+)tGwQ[-,N^H/9sFfϟLgFꤗzc k>̧` D7r@-4XdEk`<""; cP]ers;:nK*DEA<矩'X!Nq=t܂:[B{&&M)-+wʪ̕5 )W P^~n'*zdfy8I-{z s0;/pd̺Ig{$ЕE='le26 Hw>t24׷ @5qwmjLJ&{Fonki*ʨY<gJ0_:~$X͠RSk+rU<_FPK R-6yOIo"V#&q&OI~HtwX ]4h,3Z4m0p(Hz$ۜQvV❼ؐ[a=}|G4jpߤ&vXIu2#=|w9#OlyD#@ »1.*tWEsqn)ڸd6Sj?Û|+)Hrǩ F$Y_Ae9~֊sƧ7.&XqוцsP+ 1d?FH R#oK~q'0%l Mk5~Eڍ>W/ ⻀5nг060,̶|>MNX`\-[y(6(Af`?Q7 ڃn2N0?k},bk\0Erhm-2`Bewg݋?9"ԯDƀ<˻mi6^&啨iΑ<@إO7JOd_rhy ~6n֡,XY](nݥ ?6R} &j #q͌Ԍ4V6l掘ܼC 3֓nmKXRW 4}6! vh.[Q?PvRZںgs;=^"LI#xt78}|/k?X &+fu7lPL휋@m2rmafmwO3ta^/+*w[[| +ukd-5%/F>L6uH.ZC5{Ee^j{ V*gIye#gHVWU5Za.!w /Wǵ ~77F6ptpA,NUGn3U$ɱA= "L.C죬ܔԫj(Zce.GY2n<.&g|(>kn `GJ{iS[S;ERR#ʸa&ϱf|{Y>5&|pDUvO zS('}w9_AΣQ}#/KұEN nMɥN>87ܸ /+;\_ /}rѴ.Iu%IWlW.(m2MOjh캛l|a$HHVkQ?t1A'ޔk_@gRL< 3VQWCS"^/&gz>(Hiik@7嗒"51gN ]Nԭ;G0D<;n`=> :5NcxBg ֙QZb|"f%9^`\zaOwMK),iYptvevatΓ3 DrZ#clpxXEwcnt5Tdgi{->#/;zMAC{sA?$A,4Vsp}=pz%_jr"c b )*zWU:w+3ŮF bR}sr eV?Joa8-!pb-HRRP~-$:)q ߤ_` ZXU1VVӰ!ta <`觯>}:V~5k&$ҟ@IBN`e3'Xܳ0@QT3>tKn.G*} &{p\<_u1w`j&<꺝=˂V8^d]Rko:iĘv5Qw߼(xBBxޓ 64C/#VV^YFtA,eғqEbNXbq-L ckiL_`VDz8>W uK@96Y?pp:^Ev~QvVRJq?> Ò:`bT)a 5m9G"Ok+ XTV7EdT ?~p:k9ɋ0@(50q }pt;߿%74ZptoeׅoFm:z&VOIx ~܊&Vý.'7K]akp  0^grs$hgE2Aܫ$`˾ y)>4hǶpVc$p!;) f_x 䭊7- Bb}V |9 lY)o-`5W>'? 1|O=pmbWc، u"+\Cc4vb;:23r֎XIHfs[`$K @ ,8gϏK=V@Hh)_o׏-<#$Sl$׏B=~sid 蝚 pINྪ{&,c:bɳ \(+9 LVrHH@sTtB虓&V@[%i)y/|T0Rө5('.<} ^7F6?/ۧDK\I^)fcGӲ/f|;mЩ/TD]\«On.]SsEk牍>a s.C:#LධqD bFpE F0'd\FȢK4RRr佧} OXл/>RlrP-3mtskם1"j#ndP~GxFb2[%.ck 5G]pM\mAt9؛Ծ*Ά֭?k̴U`qJկxE/'NRaJL^p]e/@7nO9 '7!dq jοRsw9/U4JuWW)j˕iHa.3_"Ϟtm\ǻ6.0e&ks"-fḧ́sV9>$W Kqd@4n2hǷ5TF KZcA@e $'a8!~.8˜n9zt zç%k鑵h*3i')(vN>PsYC $ # ؃at+/˛KL#f q:‘E_r~ecLۯDE82Zi' {DEpjn&?{ E9[}Wϖ+{dg Jڭ04҆ˁ-aoZ -"5v-'8f ZfFۏȾI\Whl3O`ȣ8foAIF 8L@.N;@M%?s.i3693 orQ>Kٔ^V^|>lOͥ /޵i ϕWd, ZQ":ڸoȏ̦ΞAo+k[:k+/=TE5g{[CVRd ڱV`]*v=޽3;׸<zrG_U[|hp`2]*eF~}ʲ6*m6Bx~8QԁY 00b6#Ɇ YȎ|״V+I #3t**!(#+F'X  5M1To6 )>{fz?Ծ8H{u< g#R@qO UP?^T/Yhɑ< Kt:fj:=cXnS1:[`@NzLM]g]*⚠ @Lݴk)038u78 0#zݩO?>ztO -gO?|l%%OnK .r >]VdDֺu./NY`Izp;ovE)[cZ)gxb/L/ѳ pR0kkܙ;atQcKRǰ2P?}K+^*0=bzN=qxۧqma:<|<XUs+AR!n눳loژ> 5h*A'g4_| ! R&&oEޅX@ [sF!w:ӫQ `g飌K(<ǕM6:6Ajsaac*+p_MMPn~{}u\p/ω`|xF~il2fomjH|ި< gd\0R,Wp)JL]> uuOGN 'TP2y_t~]PwgH,li_Lj(+qtƅ;yc-3@^9UZ  8ctN:hl{<3@Z<u_D,e {+ eaȯtΦf|嬱G/92*P˓Я_F M57M=rxnqnh+[Gd*|SNfakKkid]lDDNL:sڊA'>xwXg}9LUeXO"pr~bŠ$-qPutaYB|]Fckq"T)2S0xkĉ#yЁJ9]s+a*]584ȚjnB<;QD u}옘Jӏ9 â'eF#A,Umn4I~Ώ芖~_Wa !w~:rn͋Αv]ziPR_ !A lxq3z:}yM 3/GL^4o~WcL}$W#&n+W =sΛ`}*s )a>{J-'( v7PZqٷh`Μuo9t0qRs(MA4VcU`Tg4.m -\^υ֯MV{N\ @}OH W@1JKgϹt[ׯyrhh[Z~f8/ ws^gYyq=|/1N^Z!cN"9懒k Lphh*fxSZAz2KR-mV[n1Ͻx.)t ߰yJ3#T<я?c901Wݺ|("kX>Iӄ%w9|9 ־<flL5lVA|-M ֯8yF%|"1KVP'()>gG`w Ȥ2tb;cefŝqt#"AHjуemי6P]X1D;=ߐͻ 43moCt cPN;}b4Ę $rb|o^Si(<=~Ұ3[UOߏ`~g}r9U_4T__[x<~ Ģ`H\\I[nJbl4+/tQ`oxʻg-jZ1k(9xFHYߠ p&iq1 Pk#zj+$ IغwIVMC(LQ^\?nڹ;W8ޯRRr8W`Tn/pAvP-|5bW=WS>K X%fONzŜG/R/%_bqKQU?8md+0 5궃kRMjsHK~''d&)}zRvTϞuE;JL6 nX!*Ul،$=SI `k ) 9@k rKD*c/$p*zo&`AT?Bp'0.4[igZid}āճ1vrEbSG{݇ p]ЧLw;/k;+z\K$!ku^v'3dC@6W%F8.YDROzDY8Q> mJ6KY}amV 9M:X\M[A*VۣFO\\"=~/󳽜۟'nP)b _||HU9E$+^6k:E 1XhJ nCDϕ[@½#|` ]xDbΘ  ܌/"£_EU22m#b!^La|b_^ Jѹb2*z֚ku-I)䉳83W60N"%,">E.]0P[nFȕ(]_5 ȸR)'L+#Ȼ$~FJ x=qww?gɲs^Isіp 2$ ^K}f]K{Gt?Udjd caK0Z5\G i=cՖ@\LH9{|%:z-Y3͈ag+ԍHTΊñ#8H{8_K+dV꿰_Ф5 `ඁqM MF = D,B;y%`0*ڪp1w鹫, "uEIQ+j;.=C%/ZbQڒfM)F~(,=Ws+ ln1>دSEѲ)ÇyybCDR@ZT77.=̋Eqړؒ e$RX-%F{̷WH\v}SR)yz[i醟Oo뮋TiK#]<wc& GR5}E* `C|i!=Epr,:~ ^T_ƥz $# q#,ۯNqh a%f0ߩkն:.-uyϹ[at^}n\[ ׷I}bmurgd/#u}o:HF"ݪb\X|$ᦏ:m2Ox} $DE R\ r/'^TBmaZt9ꠢEd@K*Y7P)b66X.^X$ xZaYYN{?`D8["<̰#BSg#ai'a6#긠IqwѤ aÖ#t\ҁHJG-}{Ar&XQ8<-,Z9E_Va#EoQaw\ml@'2Nce-vfˈ/g&x{~seuu[u?f\?wgcc@!mXOP1.(,fF NGQl%eƦ-^nL0*jZecIF D1"\u/ Tn]K` sg) 9C]D +C"oGf&Yb۳N^lM0^V`c WeNlt~x>.`j ͨ6b[|MpwsF 5&  &m" '"K-Yڦ+Gnwآdhw*:'zN+>ZZd6{Qy䗯Bj~zvPչxx^h$3 Wx`6οf?aR'$`'6)H7ΆMt*kœpp'd ιeH6ņ)AW2I`uۻCߓ80^6߿q?9=勘4NOIyJN?= x^9Y!28ro$y@)M%8lbn)Hq,~\s_)ylV#=|](Rbt@ƢN%E=N `cY3wA"c~k6cԐ]\Rk{ gXDeGݎɳƊ$w2@Hcj \{JpL ,Z$5ena>~`B&4TV*'S: ?)1{-L[(OuK\hPgwV:`:xIaFeS>poDq8w)"j XZ/HìRDrb/p~wNҲ0bN)U/9۸]= (V\tt 37Tiwv1ѹJJ>BKmޡ<{jˤ+qOj,۰j.1cZ~=t\MEپm+{^bt^^:7OA` QwKtĄz!JTo$d;u5j6*."kHq/9 A좿BF\zty,-Tz:H,Pl/5U|aPYjV+M|:8753+D{ "ԈD˻/}:EckXÞgrg@EUs'©ή}Udob\j9=7(R) nE8DG|KޥկfԆx ϴο){Mи=zڹڸY]ah*.!1(( R y)M%h=GNa3A'}=^$Vg%xEMKt\(+4HQҸl}h>, M@;被 k:'Wis"5#?ڂXZWYDq$48U\Jܵ]<*wUMq‚t)ZC _"20J (Ãf2dջ]VtF}oO2N2jSShm+/@LHFlxE9찰١ 0n j,ܼ0Ĺ3`*=o3v/2w8!kKnZ\XNx^_c )EűvU0E5Μ9suuaw0{-R~amgNI˭R` . l&w2뤣8&[&c<::Uq.h\{@98 $wʹv._gA^x(!B7_+1} :xXGDCK &㒣[-M yֶtML$-ۙ(0;Qw3P(=Xp4uc$6vݤU?IZ:痮",Hh m pq RO?) 9<Aah:vXzǖ 1._p9z%F}QTWGp bޞUo=)})EM .<VygD ^XL- -xn:OX<یl*mb0,M{t 2? 1(z71ׂ>)Gg_Cu/邰?/̙Ȉeb\SMb!LGaԠ/[6|,,`)n>$_&cvເw8VUw'eVߵHXM̲rxxXփr@ݴt%€n W/;"}R-kdz7J),kfr|_߻Vg+)!wY\߻TnmH-lK Ng/^1YBxK=yMlU/XbH`ʱOu(5[F 51I}-xRk:":/_75aE;8{xL1ܶ|S o@CUEtۈb\O<ü/&@+}rL%ͯ;s 2>o>ONh\hwdAΟAiQvF' nz+AKnƺ<|hPXw) 8+-Of `_x p>}*{ogjWohWJl.- \/eIX6}c\IqWf.J 91GX}NanZ{f)gC#ܵ45ΝnnbрF/U0񈽒|ɏR, 0BX[QC%d^+~U`sNzEÐ/&IH$Ob-i$"Ǘ^7 ॳo+v >Nl)) U =w n^c2>_,3օWTᥦO2 DB۳v|e,vjps( o &d_#PC/%;w~{}dM:f5N>߮)4;tӹOn=z2&ay+BQ{[ VF}3_ E%gN+^@?_xBQܠ,K_+|8_L>x įWƱt 4Kˋ .Xb@`zژr=p092i3zĿa3qW.R+); ;+;x(US^f8KsDm}>D<ȮT!<`{ލ聞=A6+)ײa%ឬxf~%}5γy tLۃp'#gFj$'wDD`Ň:`sHkQ,\l$fMttH_ yKINМ#vGRǠ%ll/+. 7ubWt'av @}OdF ͗dlO.{}?'3A%gkk '$ oЇ}f s 1_a OlqQ]fb\jF 9 Į|0S’҃qlj=ZD9lڱTV@Ol6cP8@ D݋pr 23\G$XR&|ă"E?"e[U3{;1P^-UulxU}FtL"aAaxՋg}GDBaIf몲ѪkYKݹsҕj; ~6Y*)(.vF=GxdKmea}{"r~ŀLAQrJ[mdJ_ i;!(Z!c7}8]ZtipT3葥뢅"̵x ܙM͎#]ީZVQ[O []+&,Dh_\@H[sbl9 XPLI}̕> qv fǻ2VczYvrY"t0Q`ҟ#v% w䌎ukks-'}G;_Nnmfr>GB`uaWS{X,bNu~b?h!'ª( m:A~ܓz ) [khKO7(] V6?{ E@Bw=; IY$t[w4 ucd[aC @od,p-\0 L0`@]{Ak,vɢ42#y JXHMvqt~L ,]_ɨoz{[M[xogEyE~bJ$`2PDÿ{~DH;Qf G3sˆy Lআ̲;^ثm{"Wcg/[8lsuМ֧aA\v^Vś ^>R+ۄ䖇awæmI(}k`!'Ԡ#bc_􅞤 Ln7oڈAB0wGHlK=#̖[FJzPꟿέİ92iTv:a\0.HC'ӣ]ŕ8oͰOk="N"(^%s)]}.'v=\TT6n#dU~W;:^xIxΏLP8ß\ DAIsҁwB{QD :ecޯ3ojnhq$覜@߅hRBZYAa:*eľ9F58v>wz`K*y9:UﳔPUg_y֘ )Zڀи68˄UȀYn𱮾Q# kW%I7@IYgqSM%J$%`A>?δ? #?,6j$}3y§L@N_'bjM.VJtĨ+hؑ4؀McNU;ےݢv#*ah%K>[р$|9Cokwā@*.C `56>JIM?Mڤ E$DtSW]G@Q8}p;O`},gc: )@`J. S~&s#Ps\;" 7!n]{,s @7_QTqEkfk<uTMO!Vg^#,w02`j 0uӪ*ѩj_5D~#PSWj0C ,T\p(zT R -@ 3o{ A ֚Ux;HS5FЉƓvkc;ˁYBQ(ȸ7:$'I 74W,{5̞gY,.>$KXOW[~ YpK2㷪oVv{8J+i$')rDĝ>5DñK/&˜-7ݙ@|axpelB ƥ|G?nO%p 7OQ^H4n.P@5׮n6>pٻoNl7֩\A'8'ge%_)hbhv& 'G0:,xI /&nLMĜ~od0Q> KvChI^~`=Q!%ta1l^C~g! ]8^qۘ[M\~WipW1P.'PbHY ާoqnŷw~%Ё>9]<S+nT\k-(sя ^jHLI \|kGN|i;瓪{\T<`{{7*=npi60[a֎BW#X$5鰴5蟔6H+,[ VBF趻el/bbꙘv=7t, յ%-#~fz(̰)| v qh@8؀टʭD㢀)N]VF_&#ZEEMɼF5l 6i h8ӉWkZY3]qo !Y©֬KPp` g!p p J1!jی<I'P4 bRK% L]S@/}#p|$2lF,>9Ɇ~Z˰\&e\R/Ns24z/9|_Vt=SEnD]mV *IffZCA#So5Gt,մu7>y\t䠅ĶʀM CIN{Be 9 (R.ZwX0"%f$&`|tD0O_W9|֜\:3Ob E .ܭj¤q7#4FqQUc1sd: p_:6W zbteQ4zRbJE]b5W/;wfP=в9QXb l 7+1 'a1q1e2#AO ~3PHaLHFJo3.lxz\tM}{}O=Hx)c2 Fcs®ڰiWOQ=UWNS'TU pz'} 54\LW=XV~.i[^ifz$WsKb zz )d5|( 7WA\?|&sO`Iq 9О 賂 IщF3Lwg\sf >~:=-'[g2+ ͻ,?ōTgՓ/?1K|r,*Z<2BBtGzc|fjgL C~E=#oAp/ѡ;|L&Vwpk8!prlg^Sy)Z)!kzȭlkes_LN.%X{6]"bi/92A֔,~zFqY|;t_ۦIOi}A&׃~Htگ}|Լ9>ßɷ~z覥diߥLap|,D-go;_?Mtg`:5{\rXophp`C1}|RbhuG .@($ 9RX2=-~'\&@ON`5 tRt4?., a8WSry #/p @a <Eƥg*iZ{AT>f< F;{G2&]wlL4XpqSLt# 2yh[;u ]wVF`N=?58QyKӧ!̬qG "pe10n]}Nۈ@΁QkTt)8 " tfnu&5jףbEG{ZyzDNJXXRXϯEC> &m]g /:perOOḓl̶NUa;Ŀ 3N²=B*b<D-5TnE$9 `\k-|KV)_Ԡ`X^- Ji$/\Yy砱Nd\1F[R^L%hD^=#թA5/)&{NLN)6wRz&E -WBc}^€_;p,0i~gr71 0 `?xh*QKpwa_]eYG< zt4_Pދ tE@ iUYq%qg (% X7ĝ 9k}7e;w3x8h5,`Fɵa`zqjǷ>inOiq)I&i(49fnsObjhbEdzܕ gǻZs@ɝەKJ, {Ј!m1' h 3q1I`hNKg@ Ϲtx>oZ?"dͶS[jӾTσf\Iq".RƳ"evkx=!lCݜ+ pqE`ppia7h 1.P>Xz9vF(ce%5Tj7$/J߈ѰeS~Go kQI6$Kb`dgz VP4=̤kG/qo1遶S{ȫt(~mojx88z:'_^E*(ǛWS9ЄyOV$FU}fa(Jjl=ݽ'l9]C.TKiZaf}i[A#Ӣ'3gMtX)F/ѭjDBW8>{r*R Mo*ԸM|#Ic掦:*kf ϓW=uǵ}yOp FU>>?G<.4aEˆ!]$Ŕ8Fc5~R-A~Wl8Hk׊eUX0mzRr|5Ib }Od d%kw&rv빻i b$==,2CwfmmwO  :`KGChF@AӪ'p"}ħ >+0kޭw;·D^75-S"vLί"y^-yI Q3𮚪hl_Lh2z:_[Rۇ }l\T'sYu#-Ek "(#z1 nWs jq%ᝊE8z\7N.솩<@n<|ͺ HĈlҧNB@{1K(=,Æ3.RI:CA7ƍFg +qM<}umȚ}TTdf olᵄ[yD^ʈHu.AyVۣJ<9p׉}nj|di)`*$u{R>@!嵅aeCB0/$ ޗ vz;qRֻ#>n_E8sR_#RQ\ޮ{A~E=J <#"H}?_9zAxTt;[6n^OS<]i?rnO^Yjh5BPÒFt֢N1 Jw~Zjt`Fx5ȸ@B[߉죚l=nɥ d1W'EF݌f1[ȶŪyK_?}_K[VEoWWl0G,O:e6iYj^^/4b.ɀ_KsђDoc՘8k <$y#;(Z4nX|Zʤ;* `jzv/`t8V׿vfKs.heP)t11t6Tl6TV5iIP %KߙP"d~ֱ(+#@`iwˎ犸gdi7`lӶ'#-(.ʡ(dH֖gZ^Ԩt4Ξ5Q~XgԽfikD] BtQ;UP3#WfMߓ!~g`89& Y<1_:7oUqqym>vʊuVl&ԟBVk$1d\B)$c#Mqԧ[r\r_ҧD=hi&Pr%˼dR6A pid`x^1.aV6K6-2\oxޫ𹘘(% ,Ԅkec܋[QzLU#]J2|-"t?d:ݖS}Gxp,#,4{L̜;e84+lsƍοS͖q\Z^ JYlN^ճ'+-wUD.ŪiHCs)H[]ba_YxD!'.,x80;hҖ)mp6ϰoҥc‡b=y>G`P{]-MdMoXjq_a[ ҳõv .ѱ Uŭ($0ndOK¦z=u<$7h8QZ^{z{lQC,UZLh-)&ʉChjȈĴ"j-A.tlo[tSud I?RN.DOK uw6L H]7F+ýN>dq=G?M r")faIE\+?߸86T]P^iԸK K&2Gǥб9s9ܿƅ{u $xx/!+8>졥OjMcR)ӻ9p`J{7!$.- ?v[U⢔ە~J+-=e焗dd?xҗ.XZ~΄Zk:=̀ 3>& =@ {ϳ:l0K\_2Xd KgM <RRDce,f,w{L;1WV698t/SAv*+ N"0>4SVZ`#(8K/OR4\La*y`r{1Y{!Ȍ˄`t`d<^=ji6A~CFRkVm]N402+[5w&痏m:L}cDzHFk4e(n\*{w,;sf6CNOo Qfa?aLxEH@Nbh`jfk7$m2)uwJ<ѓ n_rISI}ܸ\>Ӳ ]"EjVM~2nnQ!6G=:;a 7y JcԼ9 c(3|Š-֤iYdtz?FjdPU][V%*#3vLҲ ȣaXSQ>4] !l\(Zj`awQi"鿋þ3vQorW|)YIJo$YM©H5=Q7F^bP0>gps)0o_@EEWI yK$tL͗p ~G3;9D}ԌҴwcҴ󬭝כ9q`*aٍޡJ3Vq3`2N?/2ܼz2o k>*zzGDm^c`-6EIc3c.]NH~okefPV ;(b@u7 :.{ZpN4ku k*ѝ tU\l=5k07akjpCm^ Xfz$6U?C2ؽAOps~ToP{{2\X '54f'6.T@MR~ z @V:w@QשԐ%%i,y5?qh g=vA=~.z܋@K̇ @(LF2N=qGb̺peY~~~?b zicbѭ䜆;Y_ 9vlĄ+"l ?Wo Mwb$;Jgʸu/k> /uk_JSDHL$^QY{S|w01HOsq.俾 Ȱ<`q)6/A6zXIdg` _!p?G cJ!~3yfMÌoЬ `w{6YY]RO8!+V݀Uk+0 i,Of1u!而{I壒a1 *{Gr!Lwe 3*BjP'l Ԙ9fU[ HyYpOxϺVEl1hG)«lw"f 7JBX@ i$Eiv\ZV<؟ gP鰣{8Ep|7{= wVJKh+:ڨI`̥GwPN3..R&s.x)#*ɏ>|#gTuS\)?Dlaڇ6xbU>gØCGsxlB$3S*_7N,2,iUUvOX%cuC8I[]u\갭ږ3=ƅ=xTw7ةljgc'Ϯ[_DyN)~1Iojqn=}`bdm7xu}B%Am܊H% ̽CX;P* HI !TŞK`lp 1]SSWW 4!'ѾxW0=A0.yb>pIܮȃ8'ᴊ0.L \9qEIZ'bUW־T\{!,9E'5d4ZieY}rvaY#ە44\`b EٶP-4>(f*z(6 Y&X>q #N^lc6~U<3;Y SsE,;[`h}ݫW[y<;=-1-٠8np upKZNI)xo!bj( OneƳRbhl5=)Ѐћ.K!b;JyPV\¸`u3ɯ]XGko)/Hh;>ғgLaAƪ8X|k0cP +g!Nz2z_f;  2[PS˶-,h?72Ξ>da &:K}UoٽP~zM78O3wFXgB:ĔL hm蠾Tl%@ԟ0UJ2j~%=.ڎj_a~Cd% Uڽc7"XO 8!b+cc_|`{%feASVkeEd2| L!)n溦L *6hz;!=#a3WNHY @Yud{eY1yH=i~xGo2]`e8'U.|ؖ#|┪Ғ̌q,ۭN{r9C@dy `mh{6ۦs4oCi[%V>"3q6eg 9 m!yISϟ XRջξ0-b \@N$bjHp_JN7d,VN:9_ c/0. Y6xp~؍G`9!vר,-(>D c0VK u`\.Jj1?,[dhNVK6h6xO A/Lm%Newvm{Bx sk$8w#~4ÆDcO@.7<5 4-KoȎe=HI*Mq1<߾X4CaI|y+= Xo\UnOKPw4ã+8`a(ڰi Yj.Coܒs|ia6kRw!'iZbJ`9g|嘑\nDOIC! eg{#p\}a05ǩwװ94O֭F&( *'`;hXh}AuQ)!|~?3꟩3 4IO s/徤)S2X:ҫ|O%q`jA?gt@v~Umgb<6Tl)>%Y RsB5{X7ڬks,oX|u dWΓqDEQ)`j>DPՂEp~khx>! gx&EB@|0::< s#hK2 LOհ˸qRR+I:/.=M@@ĺ"#+7?/hA]jP*גXo|-1=2.5J>б 8&gOAwJ~l*_*<xV4ԳRԵ,m3>nLF nT.R }J(xs03:R[z(a#.R^ۙɮm3T[ƹ^wO86;9;Uk[[ WO+̡ Q/sN{zen뎽[/rv?~d XU/@{AIn RC/X񏌬Vdg7fIy)9-[9)j/>eDB>¼@vnk0>68BXZ2>撼Wq݇vuI.9qcy =4!(+se5ׯOoG;F`VFn񚕕/ʋ\.ƣ:|hu1'$qj>5FH zc?Qm֨hoi-R±>ڊBu_- z 8yߖ|fiBtH.͇NIB3Mwn2Z8-hִSŧhZO=yJs}sπ%K[#[ZZ_~۝N(`+{5q%צOm_ te7JZ Lۼ .}̛Dt8O?9\O챡,9΢ zcw SQ8! {lX\= sXA~qE&E{tBdV|XذsBHDT@p>QCNߏKuP+ s13jhfl@GtfH1&qO{?iKJյ< st&a: OSԻ {eobL0p2'þ-˳t+?W%I (x mۼ@Q7ay%[V,'3v@0jU-m,AIW9B,ײk:5Y+q *H6*@ΐ؝.+ٝ4`h߅01UVņ 8~'e<_⮬_ped%1ψh |Ip%eRCr.A@"˒0</7QJ&/~9f& 62Xgl8Y`9G3;\zq[e7oÑn830Rÿp)᷼QPxE`g=3!pD9?ed5mrSg?l,#Vk-7OTK %YS@M$qΝ`Gw 'yȠ@oypI1rK!=҄;sG nؔ(~~XBy'Gk Oirp:G1f^vkK2qΩDҽ t ]6yC*{X^gtxȰ$u ,Dr8DLA>qڏ ^v@dTվx4VbBxdЁNvv& TolᾝiޡT6rem G?#ëUzB:c1WBTZbŹgN0u M_q rq5:0,ȊbazRWo'K7n4Bbք8j(SnQ$CqL7YG" VUA}qe=E'qM P@I,9.mX),H}XwYЁHGg.Ӏ#ZfqI a\:-" ?m㩧}*) V̟ Χ\]:{1% ^=r|]\wjRDDDZzEnIvn,Q\BZƻ6/մr5ؤƇw}R0]q>$趒szI((fմ='Xp2MSX%BwW%2LɊ 3\^੬ׁz * Vg#]NEo N~Y(2e=yD[ җ$/d7-Kܓ8VFBo׶5؍%IkA 9sO;m^(~\aRbqLdL/9drnJ(@r8\ir\K? #:'jG荛Z٨pwkEiE O,𨛣ʭϙ6`x-H}J-m}`"r|An'Щhf~[6w8v k tHII~" r:K[z1__Aw3BhefsLs\i50gEmu:yJ|rdV;ao?y` rs+mzMltEԊNgz1V־ڶb m^\Q ^](%*\w0 сO,o֎:<`&L` E0J˓ :M'`px׻yYJ F+Tu]2.&oHO^1aXH 1 +Twn p>K4ܔaӶ5W ĕ- w& pVXvYA{-ɽ]]\(As^}$ 9A }N_ÁN2qCdf՜u6 w߾:b@i~'*+H-@pV@P+]Me p!Uj2cw+6V Ge@(w9yi}s㯔V<-=$z.?JH_'cpI!peTz _ Bh™`pINj$7Iv92A+2]/;逭2XKGAXri5ۍU=! 8OKM.ŝKdM-<#q)99XCz5兇}OaQT0sL9NfGg9t1!=\ 1q%³"b'j'|dn8cZܲ@xrtN2<&3͏iR\yq?yApVt: llEw|*,mKyL`q =XKΕ]"]iF=ݘOi y@Xq įygrͭ~|A>i,d"B` qbB?ۚ(;#y0EPn.;{iΜ</7 HQ%͝"CaDC==~D"+" 5kQM;ࢢ`b;(3%Et3Vчwϐ/HwH?%x X.=M I:)+qChd@3Ku[q3'P{l}\wD4ß@)Tk.>>jbzIUAةs;x(]<.atK/u>qJA|-Z)+IQ`O@X*:6dOkҐ]7XkfMLjX"+tdHDxZ.b0vvٻv0f8)12  vagH P9[6Rfês_ ioD_AEˆw?ǥ3pS&16Ufn8<`(\ey7T6TnݵW_U;&}fqB"Ll" g] UײR~Np9^K{ym>>Fƞφc𸴌*XU s,(sQ'oV5̽l&[)es758+<))J^;'W'r qvƁL h+ˏn=xXahF&}VQTNK֥j:kon$pPVVڰqTRtS4(fͺ)*EP"RY!Te9:2fT!B#"*귎{t~ڎ}g~k=k8KT۳ [kil\SlKg5D@}[W?՝ݦSUY6_e+{~po=; ]+,*Uɟ2>Xb'ͩg] RC]swMQy]Up ''c Cx٤$nӒgpt5JxXѱi*إgR9f1xZ0 & ZٸV…yhrk. TlKOuS9T1VXQ\A,Kv;$p B\}N뎜_^);)ػ VG3)n(+ي/5V7.TXKE_Ao=w>_TwhꘐDVvlUZfP0.+!컛2)Dq6u:Ax` IyFjP*V8IJ YգCRsf\4^f7q}u,zxnjͦg÷f~U2/w=P_jjJI V0-KZ<ݫ(n6{ɴo,;s{GޙYMS9Rqw#ыNfp nSVꃲ(8m:_FX@> asb:@#%ƍJ{v &6 M u독c@#=@~)pWMlp 8i[߈j " +FZդ$Ljx gwXxxA%JR oZ+^$`7ΑM _ p L[ =*rFDq7/jߌ9q8cf~a ~rjy?9[iɼRIbFJIN>kb3~ozqΔV^v/|O>QcNR(`v:aݻõᄒ{s | z'AgRxO$^ ,RK:5 D))b?W(`j # :&׷@`۫ (]U3#<Q,q"J5y\ܬ2픙_1vma; f%KQ:> V3h`<+i\?KrՑkǘ jhb䧷`u`ҖJh;#0N@esiم@ p ޵ 4g'-9g8vw/^T;nw2 [77o5y6Er=c?) :{& ]3_+L bTԚ+5[`Sf-us A~uԪF).Fe^>_@^ONy Y}@gc)BU֬m"ŏ-jhb0ei5{:o{E 1`$$&9u.Ht D y蘾&z~/EyA. %oE@ϴh1_-2Z2<*-l |[68X#mc[,FBRZWkC`(!yu:{J+Wz\6ܐ{aEn}/Fx"l3r`礡vE8A9vI1)J;^]Tod-1 v; NaLv̒Pۯ{\':V@5'v?ua7];3NHbb@?.'H3x$<]Oo`: 7rI܊GlkxR5gUȑsɣ݅#fo@ IlJ/׀+a;NǍ)j.7;u'=B3EZj`n$!5roxb,,pzbNv אh@aXnu ~#~ jv?.YE=qqi-Z1) RO9iUY4.#䁕;T MOa'ڪ77zUo pWeq`ѺuO|#2o_OH/x'w\ {YWj-WwK-^<KkHBW[aj(pVr& H'(j}\6gdƀm| ixM@0Q3%5HaQ ɂV.0Y ;F̜'6[hlj9~9UlH񥥥c0T~iglmf%ۊLwtuj iʕXS:r+5ZV۾1I3=m썎(Wءs켙V44VW4M6Wf_SKO13&Hw j iKq%mh{@$'}+X^*Om-dQd GڗX p L}@w3E݆[kmuI$5}ÆɌK'Nd\[) WM:.*\G-+HI2ΡVI;A\t>D/>Ȓ+5X];ygI$G.U^\<.ϟXkX:F_ [ƲlNW(I0u1"Bumɯ|jxZr&vŹ;O<|w`Y՛߰^ֽ`sPlyE+kKLf!8eRtTZx*x1b*+ÇJҀH`e+栃98G@ >‡ Xyऌ_C *`ckWݮ_PKX#D/zaX:{j\{,)̍EQ4% P,OYcZzM}Rk>dr08{ r$b=Rw?~+sSpcɯ8x\s0ϳ7>QqWrrwTT-ߘqG?am'V+ 5orw^Ī[Kϛu&zxQ]S0VGPD9vW}`efYO '7H亯/X|7G ש\S}׭Xa [`.<0MO훶^ \jH'T5}޴f߇ܣpR+d}l:@qKD"ta(HDOcMw] :agr` R,*i`VW=e蹃Tk)Ye_E܋iFk"'u(k*#w #:ĒW^<U&D/o;@O7:yn[hw(wKΝ8Z=ǭ r_ڥ"2SwNPߧhyDN7W?*Nzi9ל ~*!ts`jFNx‚I7ڙ_"/ QY1͵* 7R7X !K   Su'= >⌺o Ί> NF <~CrV%]Zэ˕Wx**\6Rɂ5Xȸ|bCsgGr'wt<9+4g$/9_iW8#櫅 0ްyfEAaj()Q11>,} ,_3]`&'UA U 솗h_ ݢe#:*Ʌ0d.X*3y㞥eR_x{-px Z+a8뛹]osJ=gY.'{H=?ܯgd0 }H)L#yS{*8eZͻOpsw+ScY.F+((:iDIw!9VNX #HP )eּ륇T!„^_gȽXcIAvtplw\ ="8tJ.?ܐTk`{:E`Eb$~i8Gy$p +QoD0 8s{;FC(Kl c0\BWϪ[q90~t :t5f6F7HɣW@/* m|^ix L ߱ !ᆲX0V>v7~Ǫ7\'[6p~fֵѡoc7׃לbXH^£{.8-15C,HI|c0~eaᱬ>x5(*y*M=ʫc0G*/=}ْy0]K.]Yh͋8DDBYhc"Fh PZkک hg u U჏bO). ,_3/ⅵ! u^m-ɃOҫ1`CD0+2nj*GP M*.f2*Vcw2or"]9׮ރ f9@y Ll"Bc0V!=֍k>+vs+5sc$%?7ܠzJN/sU{t%rlA)XeH\ȝ.*H-f5p7.Ԃf&sW8A ;IޛY`%%VFPEÈT^K/&r'&9؜7[6Ci>Ib[@bܺ8H2'ʇ_t55v!܎IϚ`9TAVzA†#aDS?sqJلF?PK.e `9Roe:>JU[A \* 7(_zRZd|RPQꎆ H7}/-LiilS2t =h5K\=n__2w$; |b2kghm WDrX&?_75Y[ZDg, s`O6zP(`dvcdQxFד^¬,#%t]mJn  Y*>QWlCgy5}003.╄ p׮WO "kc:ЫYSKUfҜŲ֗;dOP!k~h Ӹ9yIw@?֦Bϙ.Xx-ͥJ7S*'9qvrs5<w|ݦ.g^{+ v@XP .)'abeZvǵԨ[ /\j=#6UTF&-~TL'Ak!MSƌ~UUZ ٰ`81Q<YXSUQZ|ۻMSQEZgOXL%._c:|+q=|9z#&MYz/j߱]M*lXѕ@1F@0t~<0oDfxHhl%|%Q2ڊ3FFfx"5Q%' :~j$^ףoxa3YB`}"087\`_#@N:[G/@Hw/N^`fL[8aʞ0P`xady#`̧&bV߳޽|/cE&3=hr5CQANQa]i'LUmXfTKuYWCG.`̍>wl 珲w.pBSLqўC'Z44rlEztq.hou0r L#o{RRfm@y5w/u~RNa<ܡ^R7w+J[\4 Q.ԠȘ=G4vQ(g,b  rƥ^ ?O@5$,wR9 .@KKKUIqi!6!;˼  R%Ƌ݃>kODgcg8KZAkT[ P3z-,}rXq !]v~XCpTr{W$7jؤ?,!2=Дw9xJ{FkfW4i$иpl0J@`,ji 0+3O,n~p{ |m#v`_㚶: D||qEF`#>4\j斆kvnHN %/ލa&<$5[hJr}O: Y ŭ@S#`Nˍ䛿| X u*(xR-4p@ `WIbJkY/񶵷G|%bt ͊੃* Ik08LРkΟ:zps`\LOv?i[U9TsFe91jKV(q)?•0xfQ <4e ZF)7c2 2@]o2Yrؤc`7sr1zͻ޼Gqqd줆wdU}r=9DĎ)0#p04Ė>nQ$=_hƹ}#p&#K Hiв 'T5082=#}ݽH/.uKN#&$Z0 {=0$ 阔.7 I l+8[Pb2Ww tRtm2r8 :]$1ypeij\¸pXXrB$D*PC3> D3#5q|q1" aJ 7`?).ES0y{}E3T#MY;EE])IEnidaQlJEaEL?a !Xb\Wa՜:'㋞W pqBL+VnF~7!%# M.DD,VVrqO0O9w`.8yrҲz ݯz^z\#1sRhփڒQIMY JMˌY $1o!}^C2*/#$-56Rs{ S @1!ZQ 2X,rԐT04\YK >pPT>i 'CL m BlJ)CG[Κ=SRƅ+lzL: ^5ET{8b 5Tne%*/:Or{('}\|Kut={%n^]otk ~ \B&Rj>q;ji`du[cMl_DUkꨨߎ s2UwTSڴal>g7?s!3b648}ĮY›J~ 희KѽRI!Xpw@%/?n!㺞x7$_Gyy|F'$ײS[؅+p 1QBypjt=jnr#Yy_  s?0jW xpbY BעD,SgU`rMcg I1-t3CԱ0BiY꫊[T0\`gsˆ0ဟqIdDӸ> hk,7]0 []bxqMuTsC_Q(ifYw4"jzQ&JX_}~=#=!b2TxlymkTzQC[{/=ƼO:/[*a0!\ > aoc}`kK% uZ?h1} X* 重I ;OZ5ϚS-d%*?^}|[2 0BAh66;՛۔j:і< >z7̶ʠ;niO/9y%);޾fbfxdr'OfmlS 344e{1#fjG\6n,RX˷m6?sNm.[YaU{YqwRn7 + c|y":Y L\Xb^J3*` c޶[WRpuR͎q _?n `R&84gLg?~\rU'VE g^>{ XM  mD P)AлaY'Á~ft緎?"9{DcnyaC7zP`9z*eOFOˡCJl^<0)($baDF] ~S4CxG g[SD 3qb[i(h߶͵O`fz֍V0k\%Wcdl%s'vVzA0`exo[b, ;lT+qLEȵ*p1 %q9̩g/q3ibR\1NcNLJձ΀@-)( W(`$| cadֿP,}oZ8v4!ōsM޽B9%H`b@++PC&al݆kXVcǑ"ؠ[OZ-[#)I4Ե!k6,OQ`du9C9פHS'('@ +O+W77YǍL9X+*N{=wVT%ݺ9a! g7;rw|F ^s'%ySJK.he5bfӉߏԞ[n4i@`n ۏGi?yG G., kh+oT<0.PJip|3) !g{xUyJ.`)??#P`sоx~8> Q0eUL/cApv 5?|FWfEp wR>FnF.݅#S8#$\!nC3Ű>7?QXLT(*1;A&\eRϪ^O<[hʕ5/{Hj $/5"/߰cO:QL~*nVqߺuv47+9P:\Iڊ[nJK8u!3ZOw UgdbLݩLY8'bы삏ϫ_C@kg~ v΁//u/Ε oaQ̢lZl% g7 \( BKqJ23C0]g/Qͱ3.<`@Jq~ I34y2> `KmoR" >¸&;Щy4$j`#?~z;6>,]]cbOkW%1s)FVO6t{)1@~UYݵ.;S5vkGYYgV6Wm/\ۤs윉Ξ`uOJ.]pmk M9=d8GdFn/ݹQS^38\荁Q=M[@/w63]MbwKo:5-o~Q^~#E#HU p39#+ޏ)NZ{0Q#&r\s@pe=)8cs3N5iZ)t?x5``P3g+ٲ\|q( ubXFmIUGICwKҡe%2RVwQU=eX,l{}&HF cw-uM Tœ p&1le#.}=(ef~#\ O:zë#:_{?eZЯWÀA5īrF-DF4oY+ W{ǾsK͝+3-/7/8쑣Ο(`?g?,gOD7#w~ iZbI}t\PǞo*Rne z 0wR8'qE7a ~G$:(̓m}/W^ge.Ljn&0mXi$uAp@0 dp1J^r>_ 6'%߄˃cqa`wX3.p!xW|(ZZ/]u/ƮLcpNb}nJX؀_F,:Jr}[JNoY3Bpq?ۦ,vH^E[++|Iim~Vɧ>A0Vni4aCa٪1EZeKiin?~tEwuCC+k\Ō^^QU9f ?Sz`a: \xm>JedθC۟TiiCь@!(GFZUy[l߅6 IS S3ba S8aaN <\.U9m)8S nUїLtQwn3AB`ضj1jVV BՊ+=BA V'I :;3]J_ #&TL$)`̤3«Z>Oa5o #.1*+ECgF;5wk&doY?g|%9s@*K~̫@;65=Y^1"+11)U28W\k&²6͘p8"cX cb.ޙRv(v^K&ăBpDlٿsCɦ]D3ۑ0oEVg=l"lv}0~"(`\em|4H՘t;EyI8'L&D{$oXԷْxNF@㱉_ 53IEg7/ƻĦ /+zbfa/~[sĮYRDl_ ms<'4=\i@[gla~edv^ ps| z?,|T_lar {A>AiғoajTDh p.<kf旍@R&l :(8| ǶefhIVj6(?2rݏ$u*Z|/Rփiqᵩ>^q>xΣul`<#B6kﰘ{/Jo&ʉ`\QP~tVkKQYi (Vx {C9NhWq>Ns~<礚(zpsЃuQu1!S.E[iE e lKTi[^a+cu.?Aaͽ+$+0Mo OjcШף><"?/,/Y[ZGOLzU30ǁ`HS*6摕eK3t#jcNNCm/Z&z9Lme޴6&ˋLnGsmf{+Fq8Uy ޼cE`] ^־*f wSsEt|kЈo(!NA~1h æKbwx2^߯~ʗ0#a1Ɨ.aٙ'pӓxy?RpXm eg c$m1z}>$d8 1 09/95̐ i܇ L}pG~"0+nˈy fՅ7-͍=s[jĄy,#'f={lgEE |1zΞAP\YEvZ:ixSE~E﹧&%|)$%yNBL0Â-0 RalK}s`); X2qEko78Iᨡ y>] `LDW=v"CԎ7's=pԠ'}=~#v= f:\x**ˏDDp#[|ӳH(O.z&c 9ǃ7-%UKp)X]m[@ %02.IC_o^^;CTkOc/a!ӢW+cS;izVFEƢоfHfz_$˵r%:'ȱ,?BT9OK#a ='1 sK :%󚁁h0M\La@l*-Ap#;(tQA.!Vkl:σ+9g .6Kb{qѫ@\D5< HoEU5C~|1@ `֚ݎ*j[XA#X, ޺w"Cc ,_՛5vk7}X8PA"wspC%~܉;"RCj};tNxVIvvA=@3o_%[^B\D]sI\杽ī_&03s O;{< O~AY~ciCSx*WಂfOIqa?SbӏTm[K Tpt$oM j:30#W!=ƫS99z <K v.5.&K -T3AR! l`\`;20ѕT_[[0xKU+=VǤ1Nh`v&th(s{FE=e.5Tt H8cGad߲08vr[X(>;mjljaʚ5ɉ6&^ e>s2_٘Y4 ^uwmoz?ТU1~7Z_55mp"*&L.Wdw~aus ζ,k ?y4O1-:«9- Q)luzϿ(f8'0:ݟMQp&cߌmآ`R̚憾0Ȇ  :ȸ9S+d桍 CTOkwSmBRVZ:)SvB ue(2J<V^ԆڪqAc='`Rzppϓ]R}O}]Cm!)>'+lu,Y~E[+*!F"O SNEI"=q4z.gspE_hzH.8EڪB¥q#@]0o\9 oqx6?HiuλaAߩGʹU 33]_ Yaue^`ƥiZmoQQqrgx2yF exaN'$JWhc!+0<|2N\N WfS_g} -Y+'~1&BRl8?HG/k?}_ dS'qG[o[.I-0`'ZX0 >,F c "(|L7G A?|PT؃dUJKFXk1z=586n kjjA<Ͳʏ1{d Mj(+o @hK+^`wi}U̴гg͐sP.1= uzT$EZ兆:[/ť==}T@`3~T\sb^zw%ũ_\k r;?r:1_5eFT0{X*+LUØatQF5c!/.mbr"wi`: cAo 糜)l}/T?{} OJ31 xy|?6WTd%FE[[?ն-X V5v{AK#S؜7|q882Ng =YKs;#9E(ֱ;g|gcYoG9%%/: `Q1:O*,͸#2%/ɝҲҊ(o:ac^}5_߽S4Tw$a`xͭH&14+Q \yT8Dl=Y+ݹs55Y9q zGw/I1S"sfkt`{DIcb5\Ŕn2Fk&@`wj+E5_dFz |2WM#+ɘv9.A{ˈۍl}8jlDI/!wK`:nXb7~"KCPf+@:Png,wXP{)gW;~n?ix1zNZ֚{EW}=:ރk`U%L!p؛j'݋O(zZoTܲ 9pڠh0r23cLJ[jxO;LDZj:-\ oUpx$}ٌ }$ H8:V'٤ |a@a0%tƥh΀q- wTR']рlAEexY5ay1Vw__kf.yVYPyފ,0%ysm6q<A5D nfa/I8AOb; ?#QW| Xa'dč;Gϡ%I#?Х~6&ٙ8ǨMj;vDn)8o%"ARUڦ-ϳ~ե*jqNj5L U0 5$pWoZ\+y =W7\Ť&ɶ~dA #cg_Z!y]5 R0O$z@(  !);KxIdǥ_ pS[[oymMcN_rfmdx="ibfp~nwLJ#GɩphM 5U99,=q`,lFeT Q ,i}J w#V()+éw{sa #,41[) kW56Vi-!`-5:K =zkzqjl=c};gNIԓJwUe\zc}s*]0YISr??(f+7Ti|W˦ҘĮ6^lVv`mF #'u,7%pmݳq1 :q!6 [҆]лyM$_rB-&#u&'qn5hS%fj >90M/sZ? 07=6IsZ_5PB% }{{9|ޯ"̇j ~mF-]i+/DQyaG tT` 72>aa4I} +vxP-}ƑW!/<dP@PX}hn+S.2khwP0aQ{QFd옷8ٻ-;h[e5O]nƤ>8E &9,4މ3J]\Km!WL*gJx⪩?d:ëZ*vHmt0ߺ⁥w u. z{9eޅg@c LYCY!$D}W;cĔbe=Bԥ@-]wlY@LGo7qee tax0 ى>)n+`Kn}ׁB@f`?Sz& k'>q UpJȡ29zpZyO^uT?>ԃ>BԄ l 5Mm5-VZt踾v1` g_QT5AҎ`c] h惉uW J *.vNX$q67,yqX4nй Khs[Rt7"nE,%;$/.`܂D[2=~4&ˌ~O*?n 4mY]-&}SЯڭuI K"$$uRhe:t] f1;ꘫM(: ]y'.0\,M۝9uJKϰ('n۩ؤ$b_\py "R,.ߙ^rl@gΊNSA B_6]l.rcoGE$F}DWx+ {ՖWYyIcp1Y^&vYMR+ÖZ0m+NG2iqolaWLSԺNJW=K}z2a../il$~t:*nʯDгPgՀӢwB&? \O~ؘgi)qݸ S">/]aLK5[Y:W4b>N4=uaYNw@)^kKHcvN Ƃ :1ajl1p /D=0SA~4UB}^vfg/ .Cسmiu:VGUgs'ZZSkL9+~?$}U O3m c[hlG4o@MTMOBš}w|NRec{{-uS;UW5@QnNye +rA~с r„o ^.[!pqNأgmKWᨲf㽰42R(XҦq5*Ng{ShuriH#,6u"_5Sk3rPr8EdBݼz(ѣp~%wsBB@+M uiş:SlNw<8z\]}/ƨ`f>],&ꗅsR/$.^=f/t)0h@J|@oJF!A`ʉ%.k&(땾'Qԭq+SeB+ŏc?3'#dSF0 WJ{RuN] } &WvQȢusq5NHѭ`hO:Ϭc"u.Oh*izրP' ^)"̗~>5[ω:3UKEaƭBw@]n&9}rAFlM:lVG&!K,BDaL`ÂVm<@ Dƣ o=|S kd٨vzŧ*WuP u=n*ą~"cvw&Kf 8MI0664TsJ!ش\)JBf}EFL2L:hA:,V~U"%d٫Ǡ!X':pa{lSRTIR*sYq;#ghP{94~vonY6zԙ#qjk+(,U[Px0jK^J[}_+5Vgl^y?fa߂Yvቖdd:r"_rixuYf:HM%"'oAE5TRe;]檪Uzz4%]@u8SF=%|o ,*H<\r;-ޗvzۜ *R$ U^Jun:!Kp?KTq9o+ ٺjqo˂½?֚tgOBy {ummet pXΪB|k ~^S팗٠p +cn |jV>Z/.ըS`g7m_;/(\*?b^|j2aqG] k /nvܥ 0#ce@ ԌcX1δk K{`RDG!)A0(?nhў<|<؟}o"4‘* $[ N f>z6jԟ/@zDYᅜ +-p6Ӄ6LH]UGaiTN 8y;1h*` 0~04im1"97+~I Qn!|ѧc] gQB[Ϻ "b(ˍ ݁J@ƅET"sx#lV>q`%,F3#eZ` ձP+xuiħ|>`(VU$PvN*-+x(I\Z7OWʢ"q~ ~>JDY*0H ٔA*Xzx;R%jj1c\p:.~_gI, xg9qC]uMh3p<3;o_?:'0jtf#x@_P708|`ӛM x2^8X+g^n/ڄUVSϢʕJ:T Wsq g#Dfx]K+$* Z[$5z 2zij ;gA{ZNQ4c.p5+YKYRznq)pE).'v:*|`jFsW,WDBe_ p!D*7= ?1Eb& I4 8ݘjum3* s o͞עr@1A N<ߴ4ل7qMS-iH͹cyNW=]p1xJt\ÄU9F%}< ka,uN|+̬:*ꢜ;{oTvz\܄{$ ӧOq*+>RH}M3g,Ao޲Rv!KuB#.1?( +upnwYtXgǗ1|츨?CZbv(4Ste{~@ =k): Mh6DT`ߊcNnfRA& jbW`0 ʺu%oؾҸUbה̗[w^&z`VYSٕDG'>NYiWtO+ooQW!Zͻ,0#^)ΆvxG6d;i{7w;F8xz}gt*vLM˲woDcB⣣Ҳl=pDgz ݌{7#ii'.\"\Lj}iZuI=/o yW^LT Cb(8udQJsg1ǰd"t㯫EkRCUQ]}E 1F9 + JX9! mY9-Ikka,L+ƒ?础J"9P(ʕQ mwumReǀW{L8"CU=BcpxQwp "mN>b- Qno0wr/)6?UYpsd,_ ^rP? |ƯqN#(Yj¢-ir04[{Uo`1s)0 9aVFBPѢ p ߫-rO1eR(L')c`rEn@!N% >6:s G1ߵv|@eP31>5e ‚hroQqy>1X/Ҋn,^\6"z)EoZί4mCإhcJŋ?q׺۴.S16*~k0ӹ\OB]jH4wJ/@eiJs@9~Qa*sҁu?6W,|O^3ښrj!zѭ{L㒥 AI.2~ "zFSdRXM+|Ce96"m)XqF#(|\ἔoq-I*p)N2Φ?H׺Kؚ870J$Zxti_%ˢC4WO)믘5 '򲨪<.fqcgBaٽ#b!3dHs\=q; TL:{>u !49ȘP׶]j|"hk,l4)AO7dGL #*SR҅.W#l1{䰻' -K+czsQT$XJfS6LXVգjY^2pMRA]YLXdox]kbV)MZv#`\U.-RR 0:B0^j>*+k|!5+ܾR #ÔCf_ٔbW0_,(Dm#hRHO|sEݴҞ^yPU% `a3{j~1XH֜| 'R°$tkGi8G ߢ%t7X!*[<d"twXL&ǪdܷU^(@Pn2|#zc3|ܷz5̘?Q$lɀE#DDSʜ'm8ڨluVӲn4Ռǯ~|]x|8#ɩvqb𭐝xЀmޔXMҪ̀wn7K*(Q-XOHe;y55hYh{w9yvÝ o۾@F̐}q?gl}-.ܜ ^n&~Ih OS.V^0p~\أt'/hLgsRKDy* 峺[[!_E>zm&Q&eJ+%Q%>qMYF<SR$N^>I0//',kUY]5~y,Аm\ JP?֋HW;gp)Nq_F=ɨ<pw|pN \ @8Pנr__7ߗ{a^~׶sס}G/@0̙o݊EkgM9`h",;>({KX01,/`Tm{:nPfNňAVZ6_GĠG4KC<(r.BN8[FoYso^L`mm<*!ڗYJt+ )F(aKpb[jέwUx(M/% 2(Դ2Vff J }|:|9i X<}ṙeuJc?_p=S5Mu-X[m3HcDžN5֭hHwgOn\}_,ɝqqI8iYMݨ\Y(T('zeRや9R [ ~  HQ /v̿ q\D~48aKc $ŗlJQ h `v\iCOi : Y`zl:#5Ys E^ѥC;B<, +L@vy_'S3ZZZ]^][^a*d.kmqd~9)t6b\SK]) إT3씪=<㮙pD]lPd7S_f&^à@0 S Ce̷ĸm+UA@*Rp r@W_w!Gc) #3'B8ΜU:UuUcpx4S WWZBtp5CﰌK.gNz08г{[lp\uA1 6nԑkW}av ~kc7n50ZÕzBX8-ۨ<{t _>D'uJUT5AJCkM:ƕJZ&  ʲ6sp_}0`yQAF/,8A=l,ֺ74nh eu7 Rb}TcK O'ױUf| kflndRaPcX:%&:!һT(zݶxP"Ruݠ{aPg"IG$~qEsrꖐ[!h?R1!P>[-bNO'+}.(I}NC4` )_u4 -oztpGN{mi#ЄӺ Jmu ?Rt؏]{<ϺOբ#9+j)eCUim0[@.WqXAxm 32P_uu OYn9yE ؜sV,d)CAr)]_5TJ #D![K4.'!#Ǒ0I]/`t;⮹6z<׍͂*fМdL][KCOwf/B(NbƁK Wۉn)ϬQ;K.?s)! LxU*w)0 'uv[xY*mpOrn%p053-GD.xMę 4F\Nhl#̌LttK2Jf"7x@.6%KNZ6;qa@e,|sHN Dx6#7Ы .n MWJ|iΞ4wpo4}#T?w?"SV͚(j5wTL]g|J76H^k ɎUrWn|+ |n=hm^R:6Z/ uZx&Zb1ZTt21 endstream endobj 801 0 obj << /Filter[/FlateDecode] /Length 1717 >> stream xڵXKo6W(WDRJi@AeG]=pHt{%"<'Y|GYQ*Y,&Hw:YΙȃ7<HBdq,M$i(כH)ޗj;}-7RЋ ) %b)w{TI1~BSS 8yǹd ǻ ZmW7 W.يf*;2AxG;,ȨKH<,ρ uj:z} ^CEXD6Tvd& *2a$&"]ݞiM`CyyTՂ\ј+ȬT daG$dsUֻ=fA8g4 hu Mg}aǎ`,lܗ 9)wƍ$C E'9(>o6ſ@sIErx:8,PWώcvm#kX $Uțf_5ml<D9 %y24 L ,@eШn&Op3@4][l0-%ғĂROʇ96mRTL劐!0 ioHi0+F%dR:xZ y@ꉴ@]j1ܨq\&1R@Ƣ܋X3/l_S Ht߉cLψxȘp߬898vb0;h_(Ta>Dp#GM`wcst0ETVq`/PB*z=%$Z j8d7/uI48|0B$MN@b_TMh[)#pbյl>5'7>,6j HziE( E}BTvĀm,Պ|L#Pcl}uYx-zpygjeFT_"he F47,_7 rC2HE# qپM3Dm }7%PVLfrR=mGPgZvkweca0& ~o2+KL!jOk eDZ endstream endobj 802 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R /F22 218 0 R /F23 221 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R >> endobj 803 0 obj << /Im16 797 0 R /Im17 799 0 R >> endobj 796 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 802 0 R /XObject 803 0 R >> endobj 806 0 obj << /Filter[/FlateDecode] /Length 2590 >> stream xڭY_oX/rqˈ$R9@MhS R yWUk$} )JZOp7HR{|+RQ>Ev]3)?nw?>\Lw(mB|~ l:"wcZӼR; {Ka@ʄԎq9(3FNM߷4/eVX~E!CL2ԇ~8< jCZL5Ma~l؜ϩw zxo#}seYfkl[e%5t︫,͓ZG:wiffHym_@3JVh(s*~:LTvIf@+gwD%S2NfDߵ۶'5>vtHT<}k]8DO1ԟe/6I"`e»r2?B TJ395r^9OJ% a sa@sItTMTl)R?ءrl:&i[8pL $$a -xL^>aC Qu%czhU&?֒.tn^&u;u$Yjh#ñ(~qxXm‚b1C6ہ7 W{=To\bp{%KO芨?uk | ^ ^Y\fqBHGqD3 ~6Ɯh[ ʺ6긲`jF@rI!>gj*oO2_/XO*_o:!ɑ\y2{=GM]ڜnZ*ׇ!&43%z+ͅQKZigEc%l;_J/!>XCoj44@!SE޹3#yE茛:,lkY 7ȁGJ5@;- tJbwl)SEa OWܑ83War 5*$4? :My2a}ix%-q_jE~  y[(%mՐH/! +wB2ivaʍa0ps4g&A\m'R'ocME|Z(CBƠ< ʔc#Fb4BfQh] 1 =ө(AjՎN`a:.~  ǩm0aև2 %`|nV±հN p]Pf"` }8.K FR^Y É1qo[χ̇U *̸E;TܴhW/{`&VG(D, fTK˅E-u@;@4CieV43T?`uߤ4۾';^:Jl:"]4>6YϷ}JhYf+=+LHuZ0-"Z> endobj 805 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 807 0 R >> endobj 810 0 obj << /Filter[/FlateDecode] /Length 2049 >> stream xYm _C\t֛_k~kk)^G؁d8'{EOV(")!`OSG,Zi(w5 cYlvDl~GȥZE$Da{ӮRPG߮q?q_Sqk¬7? n/Xkv;.1p 4h9˴zWoIFOY*5%˸m]A"hzݿ@aę*۪ln'x04C'MG5&JA s"}N$Kұ=ۦl@Ԣg\KCexh9DAn 쁪On痍 ]xZ9DopGUyELEa8c[$vOuܾ~KorG@Ek+&qk}\cwaYפay9ׯyu}~wߒ?/ر&IEMk^DJ$v|쀇 ;]oh<+s054̡˵ٖ}2lV SUevx&۲;V9R;ތ"G84[4yƐ&iX,WUZbp{,Nm;X3[6%^Nso~̹.Zsl]PdE gIʝyqn$4ˎ89DĪ~CqSyםØ;%YGTEab+i|S0#j z*L1# Q;>P2LJW$b/ cw!U@i<-3iOKs&OkDvHouǮMkKWcӈ05 /`Li\':|v m󧉁4P¾P1}#7663\*ZKhE1Յë]3iO,nq>W9a:;jur)^铢 uw-(_" |QchB灔]?Kbtm^!—/ ]I)Hi"{G$Y?6fׅ8r(P$z9G R@yy">u,~hse LGp@O"k=.D}٥WuG 3g&w ;h`R,@5 R`Լ9ʯp8(;S.]ũHՆ/|t<ԝP ~:۾jv<`ø0Hu%ûїw0$Tt NZZsM7pLahz*7{AeyFȖ uIXߣW5PxwM7-P\׎! unuӇQƷ ;F'ڼє# &;Za7I2o@Wq2)""w+?Pu:9mٚ7q߽8 d9uo.=f֐fmwDq~8=mcm1lKhtDGTslmpԹSEY1y dkݠ!<v{ZewކcZ# XU7nLfL>养NXi{v@SGM_nдn5<;Q8un\x>lltE[:EDw8 r"Ealv oz[k_=Bj&lp1 endstream endobj 811 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F20 182 0 R /F11 56 0 R /F8 41 0 R /F13 66 0 R >> endobj 809 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 811 0 R >> endobj 814 0 obj << /Filter[/FlateDecode] /Length 2602 >> stream xZY~ϯP6W0qw*)[WOU.DͰVCHjGrǧs4y4" A]x" H+!I ̶wG,O Kkh_͏eL\j+C]1A(W]2Z &Fd,2jԋ\6W,ZW+xdzQܪ$fպI lS>kN7Z?SrjI^WM^ yg3Z $II"'MVò=SӘ v~`78",j>Ҭ4҇& #Zœ5YEgf}cmVjOv8'IߧGѳћǂg 5 sڍ7Ŭr>T&4+'ix'9~Ƭ<ypwW+܅NI SXnZ6}#V*}PH5. !DI]⎠cըj_:0M^w4jbiuƑYySR &/7OxqG"6쑈x88rt=9C] 1O `, p.. ܣƾWb#>$z1[ /H"| p[my\p?!Z) ԘaǠ'iΐIxH(w  pP1,l4,~\SCؗv+-}d~'!,>S:( N5ʯ% z wEnFӏ84c[q ԙ~hH"#=s?;#R0DE@=*7f|A&Sڏ5u_a}[,A "!FE>!YK3{B$^D.6j75rMnCeZ਴2slt3d<5-w~7BpȼboA@w[nIxHJ8 8i|Q+Y1DFBj'MZ|]<~'?௛ %hCx\2`G}.}f03QL.jHA#>cfvG3ҏWEL(8W5H5O%'p} >e1%X< Cfo0!p&jfUe--*>$ dE䅍 Fq(?l0B!IcNF+\켘3gJ0o^21k:?)Yw)!A$Վ%!:c<[1DpQFݬs>NMcͬɜ:ADm cQ d=ͯILΣKy9L/hf_,h4]W2lx y S%-;1s4343we<& %b%]y(d4cu y־2ﯡډ)h at[ pk_}(ˍ6SFy5RBϫyg> 9ݧދTʈv^aFfyՄH,6PJHȋMtq;%G.9L8gM_Xpw= *I @P}ruS2} ҹDP٨MGjIb@7;QY]`k40gFLхBȃ2L=FQ>8^#LjO5wGհ5PS`@-18Vp:2Ě)BCQi(˯2ު7vlYNŷB 6﵆!zo_\U^cst!YHg+fHt[ ^{U:ޥUgc?+ Җqq7F/V߳PHb-/LT]xDS&ЋE+@zq8$b2މTѯ,OO$Id90$xgxFxqjyzvz{nnzN=G> endobj 813 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 815 0 R >> endobj 818 0 obj << /Filter[/FlateDecode] /Length 2416 >> stream x]AVnΌHss{@䂴-L{-CowgCJ O"g|q83Z, ۅ|xHB*Y(E$# ;]"gy 9ۿ\&˕ȂoCJ(#*IA8+&,\AMUק%L渉0բ`mTmU{BveQ|-sNb=K~$DO%w"ˬi 1XzmY=qVBY t'Hc2*.;8G)9?9U|_Fq3]|k?|F}n3c:QL,n)*\yβajea9 3MT'ޕW(C^/dy  $aYV0d5}b@9-*"j%|3%,I nQbQz;]o`-BwhNĩ,yZAKZɲbsmRKNOi镸z c̶( ј؀AWJiOKӮ <\m_N)R1k6%waK||^OP%DVEr$F~hjh0?.:&#_`ck6L,A5di캵>bk. 2O|۠q~ڹqNr.*iJ% w IgvQh;V%^)v};>Fӎ 345o̗hDU ն 6Euj<.l[@(V,EƱScMc<`BR(}KI+֑%e4T7wm.8 3;6c,m~:ӾڽV`H1qZu8iOWUD^_'P&Mg z˵M[Lf&ԪS+'zU͎oyP73OqriE`NgQ)U׿& g3w~ 3kR'C|⹉gIg|\8*#gpi㘳L͵g`.G$ >ڻq"{v.I&p8 ,w_!1XLB֔ /?^^af2Dfv_dq,ܢ70/II2Y}Y^)f+EW R>~$G8mA% L2'XDwpP> PĺQ>٤}Kx5Nxs7oɔ%+47p&H8Q;S\l(/4g36̛wQ'Yb> t'@~/#PWL{'wF> endobj 817 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 819 0 R >> endobj 822 0 obj << /Length 54 /Filter/FlateDecode /Name/Im18 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -160 -283] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 823 0 R >> >> >> stream x33T0A(UFFzf`!##SC3 B?L%+ endstream endobj 823 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 1230 /ColorSpace/DeviceRGB /Width 293 /Height 225 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 293 /Colors 3 >> >> stream x1rXDuu ]w8eP'ӂ ~|}}T| j=zAޠGoУ7 z=zAޠGoУ7 z=zAޠGoУ7 z=zAޠGoУ7 z=zAޠGoУ7[ۗ?zKv6mMoٛuToΣvm/Go{> >> >> stream x33T0A(U`QC3#kgF %Af .\@ endstream endobj 825 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 1223 /ColorSpace/DeviceRGB /Width 150 /Height 162 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 150 /Colors 3 >> >> stream xq0Eq*%\BJya!VHZ hos"ʹP=( BP>(%BqnsPXW~m DPX&#(n^5XD|&״o&(iu+Ln+73t\7;QH i#{z(L @atUȅ#Nc߬Dx;S&(<+ۜK($5A|P(A|P([26-Ÿ7cWtkKܒP:zns {܂^8(~ru4gt}ӎPGmp,nC­}Zhyue1yzr o,nA! S?nv\Rlqn* ?Sa,L' }za_Py޵iAH'5=m\/í^8ܒPx<vtT[b___m6f{B|P(A|P(ʇBy8qTP(C> stream xVKs6W` ْ0i{S:Lq0 SBRq_ ET43INcwo"֠\ CPR2(  X]a$X qJ똣HMsb_oή9݃WK:)%w)wd %@@g78z "Pd^0'^J#sZ9tZm!D:lpf6^kTT@`@Q)Pك^7/EŁ=3)VςRlcl=ai޳>t/@BN{6 i8Zi]1a~t:CM PVPi&pAA;W )u py~Bpb. 

|s L@-H A' <=zPIAS,!|8eq1Q̶' _ݲZ/NiQ#vǨI}uɣj{T=8S@mQlS٫|RuG!>tyO׌TH,'E뷪?JzV)zSuwpnK{>g{]UUw.V&Nj[]O7o;#'#㓪*zRRMSu (mOj υR~e_tS_ R8`0AgG3x2NƠ> endobj 828 0 obj << /Im18 822 0 R /Im19 824 0 R >> endobj 821 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 827 0 R /XObject 828 0 R >> endobj 831 0 obj << /Filter[/FlateDecode] /Length 3049 >> stream x[Ys6~_x"y,8xM֩N2TeOhY(i,o7@ BIdn j{?zG%l4r8U?oGK\Nʻ&4Øy:&ns\^{;Tý|1ߵ&GyFxu^M;a;ߵf KEԅE%7_m 4||1S^xWщ":k6K \]gF. GÞJmlp9^ neO/.^2Erû/)u_y+ƻEn~V1.lS [qMIDB82EYE"D![eE׫ 2^&#{tFAS ˥@HA4މ8F1_գB{ʷl;+Ă'8J>W]TRvWJH0$$8ap "x($gx_ø!UJ磟n!s' bќ &pnPϗ~m' A.K%ęn%MZ wI)5 "AJVZ&o*B\I2P.JxKr @ޜJ-@ȡZxDTA E+;LsX'r Odg^qz(IYD 5 *SR\RB1W2vi9QTm?+pC) o.*?߯Ȧ0K8ذp#pqj+oCw$W{ C|Oky9 )Nb0xW.fE}[q } mϨ噼~h%("EGIWڶ4"=B2= /*!1n"~Τ\Lu8cF8)&Rzֺa4At2]`JJl'gl,]&͋m9b qyǔ tQB7,[J L %pؗF4M)mYosˢR2LKz 7W{*SI ;B(A]A!q ,>A*KI0QX]+_yngܴMOu% #o^<4 E@ 4A5aPes}:md6rBwFzh :'v <VQ>eVDߴua9!%+j}7`v[!x< ްY7p1JB5amjk9"Ȏ( n8 0O-fh@8iB3%4vgሁFȰ_aP{°t! }koG8a &8L+n#3'͔ U/J>&cxic?)6Wwљ8m(H[q){SVǗ8U$Q34ӞJe@bo\(sA鯦u*Y}+>ri1b@$3!Z;ad5 ęP0H6ćA1!AGA5Ag"zLezt~Α.~e@X^쳊RtyPbK,schx_Ke'+`}`BbAĆu_$ qwˮ{ditwǛm~̬u^@<۲yJ]ꡲ]Z4"3IN"#^{* "]d_m~탭!Gho,1oQSO*Z:Fu7\MDk3J):aΟ]:q[v~9Z> endobj 830 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 832 0 R >> endobj 835 0 obj << /Filter[/FlateDecode] /Length 2491 >> stream xڽZݏ _ٝ,Gv]lQ7ob$Nb'Nmg3K-ǎ{DKIQT&.sz>Ï&"9yb2rHhs(=(߽六}|;N?v(ݠ7qoj{pݲM`=ol>$wx?+QX8؎u0foɖx3Lx#-+DS\yjw/ ⧬M:Y1|%HWJP|L 5dF6ɀ&Y-)k :ˤ\xs-k̤۷o{u}IIk-{1hXYv]-PkQwio͊1} A "߮uRS]RC_~47eF }NpUoO$^;үvLgǝTnْqV4(Kaɾ4\i40K*@n:ɒ'ⵙƥ:>7C$pn{ *kL luO3JeJYO=A8bIxk/9MN\yV8P7.kڠ%SҕH_ҥ%l1$y#jM%dž'dD4'?W`Br78Ҭ2}e O_,K/2K̮x[&K1uhM[^*!ԹEfͻ0qa10|) ~.HKHx) XP +fKQdk,1~.,u)]|ڗ }i! X wVꜧeNC0i`RoiG /)YI3S^0bQۜPgt',d9Jכ:L?d!,@*BT#T:- x3lCu]1cE9;>6MIAB^X\Bxҷ~. v)m`Kc6A 6 m`PaoL?s\f"Gʨ">-FӰH-m*<7qYwj\.tѲ eZV4SF홺ZtegPDͷfq[kas$ a44FiA'ְ PM4.}51䥇ޫuzK85HQDb 2q4'EIwH4CC?ݭiPLjU$*ޮ2Sƫ7/Δ~2mWw ~ýo.!}FY,t[wb?pvK5?E-^c+sʏa׋an7Yj~w^mM:]d0\?I 0 طM $1[?l &R,A!GC\{ BU]`{/~L棚?=< !+rHp W zּa0ߛ݌J-7^sģI}k-{ýs ƶ.FoQU$^?5>\/ƧYTh

G*kpv7dJ/W^WGODgyאlבbn/4R$"7qU?l 䮊gU<[R .<Bڡt(o,̓i|"ܯMD599MRm;TyM6,$2=.,~˽j#I͂Eƙ6[R0ˌt|&T]T :!5H-HJS.xJ䘺u-3#ʗBCP?kn$?\fM?̟tÊoE iU͉8lϪp!զ6hH f? 1 endstream endobj 836 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R /F23 221 0 R /F16 138 0 R /F8 41 0 R >> endobj 834 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 836 0 R >> endobj 839 0 obj << /Filter[/FlateDecode] /Length 2709 >> stream xZIwWhC䤉&V3=o-S6)QCR^:) $Bj *Fq[ᇯV"F]Ev+Fb>WpSP&V[7#.<"1!}~n&TN$\T1E`%C 6/[mU7WR2f~ )+-F+9Ja ~ʕ!GrjY%̻@#O CܸPcniX81`[HR!2N!KMlk;I:nص9 pICN8Kyw-cPY&3 zڶ p sv {̫vڿY*Yy cfSx  |fr2J tLSS1jxtt,SGAhI=lBӡ6*ҥ>CE4CT~cb<ύ;pLɕBT f6eK|X^m. {Fߏ1 (˄W$G07?"a^&_yOL" P}hORcwucd6:Kw[*W un*FIֻm;kui`J@d*ncrw:wR,?W)Y'I1|eLH$pb+SHzaX*:p@,2҅ؼjñhv,]1+x=B}tAZc_ᦉpK9`(J[++?P);)BPވ͑/!غkύ-#FF \^4/DzQkiFlI<,ch,5SOz@[~.~uilcT H eoGtS~[7ކlʸx.Cۦ鷹6xm7Rc)c{Yoʃ6I?vw#c$Lw5 )%AE6y<l:sX!s)lSGyDsU>,%RX!줬EA)b*!)*dcXQw)^ i=/awKuOgqvq&t=erGD l(7"2Q&䪁 ֊UpGf U1mMrCnA)k\lD.Npk\tq0ΒlUd*,yS0=lUe r Əɽ38pB/W atAR)͝7ޞ'R18NA@B:85}b}K%_!XTi &bB H&*YAύ3WG<*AgN8$!'J~ྨx/"Pn0!̹ܡ1b'OKw}K;XϑdٞOk`rl?LJ?)gj;FRs#O]Q{ԿJ^q EF}gv+guЫ7"r(+ endstream endobj 840 0 obj << /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F16 138 0 R >> endobj 838 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 840 0 R >> endobj 843 0 obj << /Filter[/FlateDecode] /Length 2673 >> stream xڵZ[ ~Зh^fۗ.Ѓ(6G99nJd,wi[ 38Zz^46c=nnwdY)R1s=KCXlwR93*V BZP޵R-s5HI Fa2R΄tI3Iᯮ|)a  [!%QP+7,y9P& ~Ԫ釲أI÷ѡ 0atO4Nv&,fi=X-n;ӊ wes}uQHC.IKPBǺ\d#)nSKWօgMIa;il7@~9BWV>-Fu0Vb_^ eҠJ'tIhsg2ܚZͶ)J|aT@4b̙e#/x'v0DkRsp[eqB1_ 7q6w}puo2ZL14_MMӤ}2aI!-Aޯ7h Z"P}!瑱%{=?dDI<ʛb`6FbɞĈW"uâDJ6d\3P|fJN r!}Y}+Uܰ2ܕ0rRԴOjx*h*)MVye'x[b\oVMf^3J͞H}՟X qAFmq0Έ`{؅C`2 _;ܰ@ԤV]yx즗骙w.c5q±JVp˩vXf9T!Yqa-GTɺR r:\}U_\۬OhC۝"BENjoI|$Mmc_'XO y,[<^GĐu_$Q*Mz|/T)Vz{J'~W^0.#a }C^2_ʦeVy$BT.gٴg@ -f ss8ZC$ߩJN+D6Ǩ2@!L5=|e۵ ۷Dmځ^ վu`+z'P̸9QT&|@n_?+=+1$u>OU6R[rb7:4]Ҥ!ܿLƄ㳮sO~;)!),bLt?x?؄\|ޓ=<73cc*k|NMl'=p*QqlBt5PyQO3$5MFR*C^>t@]>|UD\2ޏpwHF @ Ϙɞ\G挛BF֩`9Q>*azvgT6E}^u/1!q&""2Δj(儐= XgR9ylҎ"i7Pw'`o 7N̑edQ$ܧ#>i$K0еq2_ 6n[-e\}V11wb?? S/efk=vu).kˮTu'lØGッp{[*!+s.jQ|{d\ endstream endobj 844 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F36 578 0 R /F15 112 0 R /F12 59 0 R /F6 35 0 R /F30 286 0 R >> endobj 842 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 844 0 R >> endobj 847 0 obj << /Length 48 /Filter/FlateDecode /Name/Im20 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 848 0 R >> >> >> stream x33T0A(UF`QCHU)s! T endstream endobj 848 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 1085 /ColorSpace/DeviceRGB /Width 200 /Height 200 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 200 /Colors 3 >> >> stream xݱv@ EA?It}.9+5???/X-,E"BXDa!,"E"BXDa!,"E"BXDa!,"E"BXDa!,"E"BXDa!,"E"BXDa!,"E/aw\[11/ ?i1uO){Lac SX4º=?i1uO9_Cš^;|b]#; kkYX[;ڹwνvs,{gam; kkYX[;ο|Y)%w3|ތLXDa!,"E"BXDa!,"E"BXDa!,"Egz\X}bٹ7ZXEvV{Udhaٹ7ZXEvV{Udhaٹ7ZXEvV{Udhaٹ7ZXEvV{Udhaٹ7zliq u~ -[%#krխ[/~GW;k߉ׇ k`'^*x}X`ׇ k`'^*x}X`ׇ k`'^**_CZ[]}b]OC,}'N ]85tw% k߉SC, }'N ]85tw% k߉SC, }'N ]85t I5 !2qjVK5ΥUR ªsaչT\AXu. :jVK5ΥUR ªsaչT\AXu. :jVK5ΥUR ªsaչT\AXu. :jVK5ΥUR ªsaչT\AXu. :jVK5ΥUR ªsaչT\AXu. :jVK5ΥUR ªsaչT\AXu. :jVK5F endstream endobj 849 0 obj << /Length 48 /Filter/FlateDecode /Name/Im21 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 850 0 R >> >> >> stream x33T0A(UF`QCHU)s! T endstream endobj 850 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 2883 /ColorSpace/DeviceRGB /Width 200 /Height 200 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 200 /Colors 3 >> >> stream xۖ8 E.St@`ȳfeE@ᘯo!$/E2X$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERX$ERXb}3us)Xu,Sװ?DE(n zpr)3fDw<+:zx JPf/۳Ph(oԊQHҢXtK>3VP"ZKO=ɲQ4/UT5Yq"ZYTZO FRn[+}ݗX5]'s"jP,"Z&mtbHby&Hź=)%~ -VӪ}@_Dww ARu!ֲq^B@]1r3@Xji8 |~WgAxX;(h ^+}d.VϾ4qX^_*سVTzXQ)yr )^W~(e6-א åRct'֕pd~+'2Vӗ9]V[,YR^b 9`H~_H\ :w-G$XBRsE>Jc˺y^3)^ b ck,.bPzMk7%qpke a6 bQwXݍ52]b}r?|2Y\l.%M8ekz"GFѷfDF[XbiB-f}S%K7cb XXց{HZuŒU|c߳l+Sd l}W%-ΎU#d?c&*6Ubzrɰ((C(t pb9\ !%VMNYX[eO*%bƝp4BKqF0n8DS&0i}r{h<3UX9ĺsf y(~4+%bR GtC״/|vSW73{X[+yx\QaKZf^I:Χ(1%EݐM)g< ׾{ĒnBY[3Χx3F᚜dό//dJ%Q%O?Ϗ`XϤ7Rb~X;KpL5 h&%{f_~Vh>)WSڡగC⽟eio5CƦ*dRtIK9 YG2Yu0!ۑTJolɋL,.RlOZeS֋m`?Wt嚳D+*V'='-!)wǯ`.Egఖ4˨(GD19_[VoJY `$QbdI%vl OLC?bŒb0&#F[XpH'FLSskJ~XV)9*)%X d_[6*;5),|}3ӓT V%B sbpէPbJ]s넫8"߫  )fgai7UdIH?"~RD*CJRNX8#5\OYo>q̙EpjEjpjV We E4m w`;넫8o԰V`ًb2R.!?VnD'ֹD:Xȑ7/KxMErʙd-Ht ,԰|V~ EbTV9Q~Cpաt6B>*R}/ iJS,ω^c•'D];nẇGɦX8HX8HX8HX8HX8HX8HX8HX8HX8HX8HX8HX8HX8HX8HX8a endstream endobj 851 0 obj << /Filter[/FlateDecode] /Length 2030 >> stream xڝXKoWh$Q䰻3^d $zn-[Hx =U,R="b;)r_F/QYDia~~~G+q`Lc&\J|(Ͻm7R:N6I<]Gz#<;rg"ϒQrGmy# 32n|X-~/&aBf_Y>H3b⢸b^Ι _ߕIRKb&jƑAb?n=߾-'*gY:\HP8Q"+Rǻo>5_$ϙuD{՝gx;&+N8a D޴L gٍX<͘g6kx{W>*:P%OkT҉kBZ`-ۙRɤS0)J)a|4ДtP 3a*I5E ^!c y DHEI~I80,[ˌDD8VJX4P1Jpbk+ |GKX\`dRC]@C@@ XX_gȧ,/0~3x" WN .Kc\5eIX> A~ O쁌Y6',g@ZsHۮ[/UԅJ{x =qSyFv_/gk](slDs 4*ޞ=NҸor[H%QSI;xOE?:UDz^Ү9zO*AE% ^*e!Tl20fA1%`}LQZş7Fƨ NC-NDm#I.;J;ދW'[wUSwWuԃNW$M|7{$tt ^qr⯛q|lMr_w3zWˆ6d_0&?7cv9;K"8I)l[vփTta6U! \ p8*~|wުCf`lѝ:%؅nErp&4&0z̘{C`-h47zt5nBZ %2,8 vJD8?^``b7!ˆ߶ ݊Wzjh(3 x[ z[٥)Kl7Ӂ5Jޥ ոcBTf]|Ѧhڹ6#ڼq@i0Ewv)Ղݾbr|%[VJ]s)Q\CI-p.sQrGsٖ'K?ppD؄I'`w4H\BڸhesliԃGUCu~f1%$Zk|JIھ, {V+pOy7W)#}V,@c$m1x(|P1xe!?cx0\)5ZОI+*9jNC3tL2`(w`}*CLǪ499=$!'GE6`Wִ 0w@.M 0eƙ }=m>*ۉ:=|=M?0+/7dQ^toa:L}{Ĭ\,\C*̔Z.kq@eUI2@߇v؃Rq@|aK6N(A_mxU]h%hï+W˿d74=ur6I23h3aBL21/|X]IIOc endstream endobj 852 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F13 66 0 R /F8 41 0 R /F28 268 0 R >> endobj 853 0 obj << /Im20 847 0 R /Im21 849 0 R >> endobj 846 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 852 0 R /XObject 853 0 R >> endobj 856 0 obj << /Filter[/FlateDecode] /Length 2221 >> stream xڭˎ0@++d6H.`!Fme$ё|}A,{{v|!Y*VEy 74h#MJ -ue~ M&xxC!}|6. L~)U(j2h+ RPv;ߴrsHd gl;l|wy4s9MI3Wge/T&y%yͫ,t.p#nyVaf%7)]xo:Hs#δ$uwW{lI7I.lU.f^|Ho0 W-v~Hs*(^k.M?9Xy'cTΏl8eiڼ/;|*ae ѱ2M׷:e*"HȫTe= r 3B/{ ^<24NHx/l} &Gc1GЮϕu8 xx&6,{;ۗݹ2%ZP.S-R= &Ң* suxB7 |b?T"y(Ů I h{^qNDf&.jhMp8>29I{u})KdmVAr#u0 ۚy)jBb,6c|œoyoR|}ftpE+YJIlCeE+tx [VB?ڵd":OgG˓;K''$Dn@j, lx:xe"g@ze5}u0>K/.9\ 9ᤴH3F"inkD%CH3@ !mPМΫꁷ*Q7eWaAU!&Y3$r;GtwC1#)tx2: ۶ c2a-IYsA9 h/[C.1>UKܢ*$ep Tj[[ qkiˬs 3Y:V{$&?8Q M9 `ŀ3{yuIoy;cd]vƗeA@#^vnBqCozT(`_sCѓF[@_&{8:s؇Ƽ, $KLN5tha†R^V?V= I4# n>p"Ϊ[ga?KN9tC% t'?*IRݪ 8dLY?uP+4j Mɋ C)h(w9#Ox2K۸Aiw KԂne@4l ֝Ϋy5oBuq{1ǚn$׾-\; endstream endobj 857 0 obj << /F10 53 0 R /F7 38 0 R /F28 268 0 R /F6 35 0 R /F8 41 0 R /F22 218 0 R /F13 66 0 R >> endobj 855 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 857 0 R >> endobj 860 0 obj << /Filter[/FlateDecode] /Length 2508 >> stream xڅYKs6`_s&fjReeDc>Tes߾HF h49/ISELJE$6PJ~<(`4B_R,‡>fXD)~jey2þ,~V2 7O2o߂(XK%bs)$Mt쪱;uWn5fYnǃyVw8 ={r=79N+hrM܃[rXmV Gt6&?k8pbV}/ lx%2q8*Cx@.n,%nBɻqQ"+ݱ**lCSw£E .bs:J'g;׶2I AMI30&*syvr*~_bEx-ЬUaԔбO_WFpżSoוHc7GvĉHӥ?h/ie!2~M*[Nage|^ǡE)Z5o*.q%|7g?*bo8GI4'4!̰.Gn;n; 2!H:1XǑ" CT4T"pݣ =hk%[Μ\p#xXr=;ߩSpAy3guK qq;Eá+Kʕ1fRlmuc]/bpD北6`i=k=Cm ̇t~'3Z?C#g uu6c(Sӝ|D9'\bi^ n@Zơ}J w!~[w\*+'XAh>rl-˒1pۙhJ `ޗAm6W6U;e"u ! bteJ_# sсᱟݰ&ϖYbotbYWq~KfcU"b(@gQTg\K8 >8Q;&g2]p"M0uLG>[Z<ߴtO U[X T'"p$k eI™55h?UR2l l~0l b.pFUNG捻%6eڐm5-]X{}Y sr۰+|r#X&i>6| A&rzKG"+G7|7dl"uTPY3㷅 vPp]rL*0Q#<۹mppk3 k0eOdeA̖CA޹&u,3HV)"k`>>~Xq[*: ! L &>M*OW1(>=`@u`O=>ߟN'q8X"pߪGi}W)SZ.R +&l@1b׽pʫ<|9(㢑gj*sWA;0 O+z̀ẹJ?#Ur1Sg}`~τutBLꕺGP;n?<7fc`G.6.oHq#X 2yŮ%KI4^0$s=shI˿nNUfO'C˹DL,Y2C*W,q#.FX?/Z,PFM"B8y|Q2& 㙡@# x]O7{ڟuA㏮.O+-l0yYoBܩ몋V,1֑+f_,IM ~2"+ "i eNN c)UA= ڛ Lh9s7: AʌG ܹG!j-Fl"K~|;el>|NwݍWonةhLX;9S6PĞ玗30P?C endstream endobj 861 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F16 138 0 R >> endobj 859 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 861 0 R >> endobj 864 0 obj << /Filter[/FlateDecode] /Length 2727 >> stream xڵYKW4rl^S:6p9hԜzRCR{9^ԫۏ%K"Ywӎ#ݽXf*øٽ6w*ğ(]+˳ۛ4wQշ8̣S|aB.n/Խ*145>;a/;{otj7(f2Qu>7P?A;NBy߾Ry鎄ti;n$Li#?YyZ iAy?~De]?<<©4 jL7y֪F~ٻ̇yOAƎOkb״4zdUș'rI  j?4~,];JLSi.N'i\k;~TOmHn:+"!L&-:o%šށ/A$)D8%N`^+z`ĩ;?^穥@"WMH3Yf fl(xUЀ.jQyF3A-P2Sv_'QXso3aAQ].ck/Jo{8Ԑ==[;̄'϶Lr18 orWqth"v)CDXGwmGRj8i^OjUN6Y̆Yq(XR!u($t9Qаl0stggOG,A+*A$ ·83Q?eQnFGgrjVP ikb޼♗g:\}38\_2buxF>t,>jB MDheI& ~ H+JeKPTl$V&ƬąJr`1i#pnWjU8I\b<>^^^DkW5ճ:qT?TG0ZGɚځX|d4b7B-hj](Nsk 1-7 (cdXKD1!&IWfWFOSCc 2xvTEC0 =Mc~W&c)oXB=(69R;|2B|_{orxu 2{6悊gzD5\o9[f\r@[U@N hIGAgI}iF3;B*Dܚ*~,-Ĭ,q/!jAF䌬˜%=tZhd5dt1stN>N{ t:`j5NN] qq B1̃]`KM)H#)`cVT q1K hT!OLes߫"3A+ TVU ](mK eů쉫}b!إ;L0CEn<47VtfLgھ~Gw)~oTވ k:zBlxel]M=_6O-StY,S2nR~TFmiY-S;**1pƋ֤mVCr0Mtvݚ W_Ԩ&SZaWt!hFH"51w.lUf'UbKm\ U"v|`zٝN\]\sBO xMiQ.Bɗ;d: *OmQOq=4xvCP;*"q3Nr(eh{L,Gy(DdA&{0F"~T;ɍߏ'CKkp(S Պ+[) 7|UIcPMcsvUn<?%V6y &/F`|,Uia 3,NMέ]G0 Avէ@ q"of/ ! endstream endobj 865 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F16 138 0 R /F6 35 0 R >> endobj 863 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 865 0 R >> endobj 868 0 obj << /Filter[/FlateDecode] /Length 522 >> stream xeSM WpҚ`6αiwJ{XK=T=8t%LQԓy3f0RԢy]QAIQ*rBv@_s*R> ~LI(cNjr&)6II~j:f:$Q5:]qB~{m oKVDT(sR̻]`p *3RV@ϒUH ³蔰;7m7J7t.ϙ4c)n w{PYUuWpѴ,AJB˨)W!VGD"1nN Yecq5nA_B5ݍS0p1vbBfn {4$^}wy;yjSd`fgj:(ga~&-YnvF;Jgm/AOsq[e3ZS!<zmXtigL™㟉( O~B0֪:x G7!S}'`r܄dyn_Ŏ endstream endobj 869 0 obj << /F7 38 0 R /F16 138 0 R >> endobj 867 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 869 0 R >> endobj 872 0 obj << /Filter[/FlateDecode] /Length 1856 >> stream xڅXI6 Whzfš线hK A8`}vOITUTۗ l2I gX BiܠLǑODqBRozC?26] (ސvoV,.Cg8HW4wy179:~7L+U+Pô۴֌Lٞ^V~֣1k4!ШIc޻@*8"MNæ0;ތta4 421F8F Ðԛ˶c< חτXU̫"^.b#l]Xfk#4R!xH- 6ցh{Zm0b-e,N^L{(y_;8a>ʀ2eֺg|ata%AmDj|QH * @CKq@ӎ2"}?(ES綑D6Z~Ik$N6 l@ BK͇_ISAU%2hiTpKn]ee>G_/ٲ҂[w x6s>hZ*R.aƈ/EHcfdCsrtv\;B1|'H;F^tV$=(I3ۊOq" 7f>bvyC$޹8<;T,N / 0_߃e+ı켘ߖP$)!QB t:BMu;{CG'[`q 1&>\L|!0Tx6`{};*PZ5zP`&o (8Kl$ׇVsS!QoWZBw7͌P;֡RҧS)}e/wa$PDb#a g{7к|M$ endstream endobj 873 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F12 59 0 R /F8 41 0 R /F17 145 0 R >> endobj 871 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 873 0 R >> endobj 876 0 obj << /Filter[/FlateDecode] /Length 2092 >> stream xڕXKWr1U5IHb+Nv!$A#|(9~%jvAt.B=dHC*ND("Vo^"O=2_ܿ_r%,ۃ>[RA8(4MǪlpDHFdXcxxԏ4RA<ԖKLUϪ[-D}L7C6#Bߺ/MV2 3 đ?j KG[: S+"(yB[4RdwZte(9TRKZ{SDӽh R ?4321߃V2>V6 *Ӂ|{k|`vszt{D 5:@h@\f^w_ɔdz tЍ2 m4D =¢,5 ц-D) \ 4-P[u4-2G輫Z!40 tܩ#־uS;C]O13JЭyԵ8q~!q09;]+Ws;]״X0=jt J:$`C'*62[pJoodyȁq\)&wXW.|cRg89ӑϖEX5L%!qsr!!x N; b3Lӎ2=qԴES޹v Dގ DRQF6Q(|+]jʚj A&p$xS5{r4|[s˫bf ?8Gvaw6*Aru.pMJM*2:/?kۣ^>A/zy_zؙ T1;Bo v\z *ӔOP.ADǴ8kfCUS ")<ϊ(Ļt?n @pnuU7-e(($ 0Eܖ,A#.tu ?(Lə(v*->'bj?lt9bw$ɍ j-(,Jyf$_ǯ-٭X$}9^o{2( 9*loj}ZwpЯCb&`-ȹH}OIEC9=]e\k_RRǯD_nSƍ/&?8p$hz_B3,a\2C:_o y ϸ/y!My_u1䚞ݼYw|&<sS|*R_tLpS>O m\ڿ t'=u4**tV ]TK[[׶)M> endobj 875 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 877 0 R >> endobj 880 0 obj << /Filter[/FlateDecode] /Length 2996 >> stream xڽn_1q^ƀG)hl/H1Їł=j4#ikt=7JH5ZO"ϝVa}xJ V^V&2Ѯ_~^7*\An||uZۮDqXe]7oafgABL㫍@7*\fH,]EV7[mǽm̲e^澵ͱq FSUİe,vjT#2(˙<6xjQ"5UesֶN@m[ >$iQKcoݕЙ Hd]n|@)Fʊp4ޕtkư`|ǁnTp[+U1gZyDѹ@ m O*MF0ۢѾ<"dH)ڋpsYopLx_gh>"6E$ehMXnZW *JO[.؈PM#c#?b0\))60f ƭCtGڶD6yjok/Tm%qp#Xth&"ONH:Fa ];f_<6#Y3&SH hs2byZƞbށ*;7~%Rh}SI#)@3]WV)P,X(MB5-ly(H9.5u4vwBnW&B( cT@$DU֛\nEdE);cD =!@`8&lzD#* ҀESr~* $]A6c{kɅ S14Ort!4&hA!%r]VL ˚?aŶ/M?:0oc(Isyy7JȘlAE۽diC5ݱ޲*g|[Bvir*NM[PzpT0/j*pSsF eKu |T)ʤ⛐u4 +DԃC!z@QGx*WRIԠrJVACxp:wcŨr!I>@rRvUm?bL⋅bh¦%b%-,R2$:|.3XHϸ`S63+/v=5F"nW*L* 2=(tvD]ϊ,seC cdr\D>TY 1^0i&lYwv=2A T{IFI\ 1z)N@,>]XrBJQGZv>۞R&H&OWɜRL5P=C6 DǗY)=xL$ /hp\M^[:3Żanw˦Rl7VAvn=g0NM(: kr| xAy>7t1,8:=9P3QPg4GD<$ ̷܏XI J@Eͷ#1-oUy#]a/Dc=m:)ܝzzIt6vK.\";5^kUqY:3(ڀTAS}b#:3+)lN{'8I8 LV_P$)X)c :aDUծt|F'p_8Zh-:ʁo5->YKR_)*4Ab]'C]7BcvrɏnUE[|n籪9~PR_Jls`cQ]|~Xg/ͱ}-=G ^v[KPl['br?| f sMJI_O;R:G=v=mgKɯv lsl#/ I%.BxK#Y۳+.Oq~ Xw6c[<mp`o\.}6!hXcE'QLy@Ҳsj|O9c*.7l󪍟I8yZj@?X4]`7FJR||3H( :@JKqdq#12Vo_wDW)]л#tk )Cz ɅF)J-c[w>h8vKE|˾U<7;8x*TmkyM0.dbc*y" 99ygQأOXBfP7blmJqcHJInI%I7Ds6aL8ygpBwZ.&'L9 ^ .sT42v55nN s.w*ԕ|]w蝵v١Ì z~'JHgB=<}\gWKzV9yvgCGnë; ]pʢ c/BC߽L^y%N&DZ<bB*'E/8bky)>.u架Z" *6ȡk #W" 2/ƒ\6szsP!)ryG %Agږrx@y7{d~`[㓬Z㌮@&F}S4wwPж9<JlF~DHS !!dg:T{V-LEb&}_ BS endstream endobj 881 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R >> endobj 879 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 881 0 R >> endobj 884 0 obj << /Filter[/FlateDecode] /Length 2490 >> stream xڭ]ܶbh[F$EQr۴ hmS.pH9!9o w1}ܥ1(iȄ%׻w;Yn圉|wW L}0nRF)I|H4zPWe1Tmw23ƥGr lm7/eU>J$ѻ78+wiЮ3b<8gzꄜ%|qؚ.7#ji,#v M@<,KwנR*fT&$ř֞نd|wnVKĭ{=v!2}~Mlb(XDNK3@&iZtgZdL2 \U @OXKo9 K hu(jQ@ ]&g:|Kc?tU~K-`9aSlYAwPә#G.BɣŇý@Xbxq֍{{ECԽ?Li4ùڻ7Bi+lOPH.k;E>⏈r66b.@ RxLq$dT )_FjR Ȃ[/ 3cѿ߁:U@sx873:Aljb\'ft'XZWn-GƤ $]"tv?p_8xcў81slCDv˲%b? _rcGXG*SQ[eDl;gmU` ,C L[75Y|p lnV8mOͦ<~5;ATi LF|pQ0)z=:9C3ඦ玒6@$=, k'EaŐ%> %/9KS!K >Z]D惕gHTVey4Ap{rlpq2Щ&fV٘6Ea,eO ]i)>3/Uvf8wFS+#-raddL)Gy(BrvW:.0C)ǴgVuǖ` \60Z'3Gӌwҧtb19f(SdbV6~ɋS =iǒs,V9Yz{fEb2 댧SWJԐ5yx$o[Ik>KS㚕Q154ks_\)+? zB4l> P^lp-1mxʤZ O yÚE3R&!R&Gᔳ\h_^OR-B@N_.W8M=ttFJ#"3Wq\@\[&.ը$2 qn-.^'At Ŵۭ{zlokpfyN֨3ȍx 6m-ePWƛ$F 6!MN mS?Tt?Uj7;Kᔪ$8ZlCmN9[PaW~[8Oy%ͺSn +YLW)Y$hDI%~>B$ ?kyvXf+[Y&1oc˧F56%Ç'|Dz9Y4׋Bt,ֻs9;1O`>1I/A>y֩u<ۇ-m{ ]>rD_s[aal ' w%Iڥz|;ؤ\ɝz^Ϭ#ϲ-TGe5새on=sV_Z j endstream endobj 885 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R /F8 41 0 R /F6 35 0 R /F16 138 0 R >> endobj 883 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 885 0 R >> endobj 888 0 obj << /Filter[/FlateDecode] /Length 3048 >> stream xڵْ۸=_g"[]zSW1_]qqױhˢ;sN2NY"96qi^yۗ j}6{|uUv==5-Ls}\Ŏ}C[zWЫy}}vm%'m@-nxVY.ޗ=ΐr_ֻM;bJeNJKACsۂ(F|Zi- 釹`2ގa|li0xЪ`D7KBXkٱ'OfL%}xU,'(eRhQʔi=b 83Ik&=<_ܙbůS[+zr W`,i!"t KWzaђG.j/I$iHT&4-IgDIN7/-WE}qM4JxjJdnhq{]3"F"b& DpYjL"eo3Mb i"P >S*:eJ\C:mhN[O Β\~cE DZ *hmF|4sXAd*eX$Fۂ,اAGQpnF"l(N^rDfsdѶ!qpRISS'd~3+b|gD =0Og^vS(,F@+_rtm Hh$`d&ߌE8L0dXؑK'Eo܀"mi:#01|3R(1C >a%pZ 16CW}9A+|8aHm~7ѩs.S.a'kT&P} OUB`#fL@[01+\Y2!7I=#,Y90?ef & W|rC|Gc7`8IKo88HgJ9g BOcT[ i'$n=$3GWPMN%"9Y*Fwͫ!RJWewbJ=Ż9vz:UOsFUsPb 9F koarC ]yUrmfrf:K AUc{)UX寃=p.t!?K6 *1FŽcO=GþJ~A^ mɱC~y=eIdd GRH}.WhTL `>InX $`UO IaLz&C?!R|Seɠgr\/8-,xy0F!P06{A0vPb Ҵ4H$?}$g2@*q4i1I)s |2wbKf K\7k: Z [a[W^ ^(`Cb˪fy=Y󃓎:ؙ)I5l3D>Ũ3?ǴiEF}lŠ'fM Hmg_jCMfmxxkb4xf*$dJL:Tʑ`Jl9>FWi!dG߳3t2lUb/QbZ3䢢FOh---0N SMӆ`q-hXՈ7s1=Z٥b񂄕L#j? u~+2{qcp.FZ|M6}^Kdٺm/?4=^06gQvT,`gA($y$_xl"L@y&tPKd8:3?R {6p$(ajIx;9w[QH(7f,tvx~,f ^x@KxU=~ǢXy)$K#HwFFϊuK_)a]Tβԃx>!A gc[^7emi)[+ٗ/_AjZ"Rel)q_hQ2nYoCe.HI!Rŋ:{ejKA]@|U NaW^ ͷǪw^؛a0hă ܩt qt`rp0QTd5њt Rc 7l^_$wH38O<#WeoFxA4|_#kޑﵔk)y[̚5قy9ԴSc,> endobj 887 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 889 0 R >> endobj 892 0 obj << /Length 1308 /Filter/FlateDecode /Name/Im22 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 893 0 R >> >> >> stream xYK6WZ@ρMػǮE!xFH%SB{LߧׄWZz)Γ6Q:OJ$TڥPAbigVyv98Exˤi 9G.p:e0 Jf2cHBMxNv@B 1ne qJRhrP sS8R ^QdQ2T/#6b$m!H'W 3F2D!HNTi!@&-ٱZlLx-~C΀Dvj<)Il)N8#S/N]l:%4&M4^HZiJ3:vvÇl=O- "kEv]Mԧ1mѵ/+抵d1ab(RWQhF]5BT\s\Kc> z(+ TƤA"RIuC_ TƤAZUltPɶbsV^.Q)ZƨU \lxmnNwUr[)peݏVFO۔7pum浢oFt5 ܳx=͞S(4g}]tF IVT=* \z5}U'3MV5%U#FlHDKNa[-i# _/ ld@A >\Y<&#fhɸ^ 0jݵvuQT$kZR6I昚KQKiQ$z+F(k[Ub-4b"h}Mfdy #eR:ki%kxd*W|;sMrEkC<h "FW!s_x7U+ tD.kvrgˣ켾Mo1fjrmiUmPӺ\2jtnɎ2ܵR#k4P k&]WF';ʝ0`+W>)zi fG q0sM΄)[$1# I^("h}>?u?'%d&ޫ/>^<܉ߦSG}O*Fp8>=u /cx~s;AicwS#<O) endstream endobj 893 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 894 0 obj << /Filter[/FlateDecode] /Length 1401 >> stream xڵWY6 ~0Їuƒ|δ Y( ЇNGI%EqECR?R v}|G=xq2-È+P,{]]%^Ʋ[mGuDH,"L#۫CRJ2cP\u_?1.H| ȄUHRUoG{94BgqjRVkC֨.R.;>KUC_I?WƩjt4ܨN1Zf sE6nMBۺEFtl4[|K=1,|mgNmTo_֋e3N,TN_7uSI\[3NWQ w’@Sw \ kE(}K1mk g HT:"Ҧ7Nd ]`dGm*GQ) aJ`pmo Emc.g}%gzCB8=(Cn;}hoΑ'O.+%1E,)W0W|E)ilqݎD, m bdV#pk5R@8wAmE>BŌJt]LՉ;ޙ]8k1.GEѷ])n3`g)Ttk;V'Y #h"8!=2 Y{y]X ὯߠW{> endobj 896 0 obj << /Im22 892 0 R >> endobj 891 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 895 0 R /XObject 896 0 R >> endobj 899 0 obj << /Filter[/FlateDecode] /Length 2206 >> stream x]۶B<ʜi;yPI.@s.~Xqa_aHY,JXTrqXh{}&e{D?WC^ITfRi?6wmwi$UX1t%H]z,Tv@KɨoHU?.W2̦  ~y^4}KSon$YTX%(S{,_NP[L7#sg*TVi)[-W-ithzC㩩9쁙96^éy3X$%blق΢agp[ ם؁&emL#7m/=mh3[Z,z=)_]㹡phкinœ{ |p< FX,NUl8JQc>8+cU]uܑQ1ckY)VD]2}xuke7 5>c4gIՇx`o(CQM(p̙arDrPi1 l?m tvYū욏I>)z3Eln;"~@~Akk-JdU "Bivj">S :gaCL잿:30frC;BGkڽ;) Dۦ6,-8:j}ȟv9vэ\jmiV@$FNE! = [Mo 5)>2"tWQA_ 3x\(K"pe AAIt G!a$coPLCd:(3[UsAe٪a6-6D'mLl*Bv.E|Fڦ.9"jsOpܶ :߇iEG Be&RY/w4-*lbȼ-X>8-gPYXBk[L2*"J3Y9?I cUXKPሴf)JJ!uI L"wJ{5 r!s[fv cCӰ. PJX&k OX@!e7`5/Ml.X|s:T1tt$nA#ٮMutGA'45MޝΨOTlg(:=Op#}FH[ $˾,Ec5m壑|վ[\ =KéUq&Sv6+s8BGW+z! ~1JD(8 55CmvM^p*"f>%94+NɑleH*YL"|eYvd*]t;w{)^+ļ`^`h#S1U \:=8Kn/~w߱oK؜~sR߹M)c g1Xg(}{Cؙ|;TQP Sj!lFqyY;2)Pk7)3ؽx.m'TD&1zl/,jj"zAx2e.2O4dQ}J*C"FnkW޵iuh7-1WXp2%r)V or" |k%Umq, ΤI s*, Pe|udyg X4KqS+L32-i"BL#T>䡞R贁\J~}Yo TX}ֻDz7yqcvӭW ˫ [N)W2`R"B(b;yk Xɴ(f3rdoڊpEY؁ 0zfSպg|Ή~ #OVs>uLuYas_hCLgkœT>.@#< a\t?:s_E;S _ NK i2)EATY>}2'O¾cxd<>B !< Nd endstream endobj 900 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F16 138 0 R /F23 221 0 R /F8 41 0 R >> endobj 898 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 900 0 R >> endobj 903 0 obj << /Filter[/FlateDecode] /Length 3179 >> stream xZݏ6Z/f/QJh_o{ BlٕM3D4=_L |qǏEƲlfa~?x3VJi~bJߋo,JVۇ03Q.n|Z]*X^I)9Uf]C_xxf `~N2|ULKnMu|۴lgh ˊw鴫?t>쏻r:qSW;jkNDϛ˺X+t9+J8mkjtfy$7hde ~{lK/;h?"@;M[lh ZuMhLŠBZf$y1 ޲ͦQ=n?uMHhZ:ߋwaJ&sf{ixhHIs6'ATɉ/;2;i7σISH@ծ?j;`;j]]8)puEA,[|hHy":[$4[Ssꪵ2qY WSpu]3L65kzCF0mb5HR7Ytq۬$mM8OԐ7<=7FKY٠y}o(8m]{S=ZJi2fFy\1f뮩_(W/h]~Ů_wOeY\PB6ǐE.{);q&S2) j]Y+j$ TPPdXx4РPQ%baVʰ,?ov@؆Xd.sB˥˫  m1imGvmt: J2yOΌqi4Q(%PAi.zIJbҸYauQ-z)H=qd\GEIrV*p tɱ6!}슪q[;kҒCA$] jܦ8.mm5ڽ^FGk=BXS( @הuc;j62I"D&oS? R3L;e,7f&< !0sfʘYb(:N>"JC tiʄ4尚(3Qs(r&"<;"b!fA;w$3S>=b  3 !o0FR+C؄KeFu+;d oV`wYC84FsQ!IzC;u !vC !O4nPsER'x7FT#2J_ׄ<&H2~@aW-bepw$LV3|K~GثBB<껔1HouCJ-u4fXz )O,3֭rII9j4Ubx%cگ U.0< 6`..H>&G5p_W#Y?(1)a%e72\1=L:CO_u:s/z&|zœs"y!1_Tu(3Xr ;EwND-Dk C"FH2 ֧ĘҰ\^·B yM%gT{'c :/M«Ylm)KVSbݤ!#NSDMt3Iuq0̆hF dyD;|b6f74w=ЦU9n匐[_П@.q[^d+@PSh/Ԏ-|fKZl,vY}UR&E[⾀XN\_%s "UQ3As*\N;x`l)l:@< c&R4f&I}hBKW/\s }UǍ(c̓D}$%/b=yP"01z5S<.=p(+T׎(6S|őfs5bӊ=n M2CKԗΝ L4#w_uH9ׇpF?TY?*wEc]u|@]4]N=^mVjWxg7bwj6NKY$sθX}XxK2KAȈq 9̡W)v8!,/STE*(Uzq\y2Kd|؋3h@gO87wGLTBxfP}tnw$w=! T!3gt̞Y/ȧ;E NXj[1+ 9gJyG / \ 2=~M͋NĜcko,ĴT1jf h*jGu 5ɍTs36_p.o2Q5#ޔ +cp@ٞх>mh%U tWdpS>/s5,“ 3bho=߈n/tr_Z@ӣbWݛG|+$5ڼI'BACN] U+_kVfgygb]q ugnFdj r0b48M<%xH|ﭿ?1Som ހ[wQ(tf{CΨ]V!?> endobj 902 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 904 0 R >> endobj 907 0 obj << /Filter[/FlateDecode] /Length 2418 >> stream xڽZs_^gjB5wmz-CPe'I{}w l>QXM.ЏCA\XHA,c ;>Ips&ج\J?Ne[럾oJ1Hŕy!,XKZČK5aQbnx܄31KdUZpN9gpOxT%˅Q]"FH-F$,Wk%b5;,VltpϗKSG\<g'"XQRC4l'[/{< B|n; gـ`pVkrx\(A5r~)$DA(0iibA/4Sja껂YSqr݁MnA!gxh_ߓTtMkǯLhRuIHʰ 54mY϶<}Ps7r@cύ!c4)Dq8f9B5/h/) GI6A$f/DÒ&KJܕb$ēW@XA\%,S.D1W)c>bJb/&a vJ.RƯw`O`'oV DK{| /UD~}(rn{@e%2Hï5<*F,JmN\@nrd` 66s% ph$ 90'ޖ20``tŌ&2,%N.8ܶU cs kWÅ]G,MeW2q0.>;^C-o;ݗ5.#`-CҮB~Y)rsbјOGIXdL Q0F_ ~ijWT N, vft,ά0ȩ>2Jt?kcn MKjd>_qXOӴiԘZja&--._B(1Kg@f3Z" )M׆Q4FI4PGޭoO> endobj 906 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 908 0 R >> endobj 911 0 obj << /Filter[/FlateDecode] /Length 1989 >> stream xXmo6~}XYhi@nC-NJ+ɛͿ hKŽ|539Yg5|L,LYgL $ojvW?EI .G_>]gB)|vX盿dz̙Lf %Šf_A\@I$'%K{la$Z-{g2"Zmݾ[+s-}-Wj_Z2D6Dz2z$J7kܓEv2f}IK8t=I;Qv_1-j5IZiRG'FGiضK"} Wv[Zsn"$ZVCkڗX}e}%U}"\FmS鮣(? vMaw0\~m-vWg%]i$Bn:ΎnY`fCZ8%Wd$g`Dnr/)7QcCX )U2~I,>nv\Wѝ! Hmא;qzKtQ;Z&k]u |5 L01ypKm{Ux_Rlr' r8ڭFEMլs3M@I!jEI*%S?MBn*@XcEm vkN7(l KNI_)x DL8[a>cpEzA¡KHeʲA[@ *S(@0 Ätc;9=/)oҀ.SY%*v{2DnA\&,Iz'iOyS!;Ԁ r G?XH&Lc('QcCO*๺unŀo!lsLdyvPRa/_aCC(巽N@ѯEp"+Bh;I S܀bz}TC5TE}7yxwE3&/uJxt0 ÿH9 7>uY DZ1|Z V`j|:IpPsnk'5S9>E36t dUA\ [ ACsu*b݃Tr^4 oO?+0;6섚Upgb*SGjNwCv.yxq{ ٧*n7OjoJ{SxKb_N=bzw5f_Kz}}cxT~t(eA/Az(Op endstream endobj 912 0 obj << /F7 38 0 R /F20 182 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F8 41 0 R >> endobj 910 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 912 0 R >> endobj 915 0 obj << /Filter[/FlateDecode] /Length 2130 >> stream xn6}B>D1")Md`5 %I=}XĖd'׾4bXU,   Y!(Q,1Hd/?A49Epr*TYI?]~7ټS)fWsp EDx?@*[ ˰[a`qjȎPAdaD1 iNdiyO4ohtwkhf`7pi;:{g*zdk,ƨt96{TD}ExlX( tDc:8 eeTgˮv:C=!'} U?^ 鶦f[7u(%NO[tMV֯(xVJdU&a!m_`hO0=S?ǃ|1矴,XxYY.UEH hV$Ar"g~T1ٚgal7S8lC3ÚD%;)+: W'%͢/hN= ZlT4~Ng7u."Zy5N&iMk "P9$k7}䀋S]dRg*:qsA)g<{Ҿc'Ʒ@B1&1}p0\sqUۻixw3D#R:K-ŗƧ 2ck>nھdҗS)f> 1􌒌ŧ$$sJ>RMj-p*s]vB=@Kr1VҌ{/FP?5?_w_H-9reuȳň uiU+*PW)}putE+\v;Sn&hܔP(J4,c p%M2bm{-`j"3"S܋nKVPB=7`aN~ub7O#+% m\&|P8d&_*N*v}|ݹ_7U@d޳~jLJYvԘ OZf,uۋP?ޕ6\۬Ũ|k??  oL\?!,齸d8&,,V{J 0%;ޡ`QIA VD% XE%/J3.C-’¿ K⹰RJio^T":*TJ_ K,@X/K9*o` endstream endobj 916 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F22 218 0 R /F6 35 0 R /F8 41 0 R >> endobj 914 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 916 0 R >> endobj 919 0 obj << /Filter[/FlateDecode] /Length 2296 >> stream xZ[۸~pO_"NEKc(6@X,P2%93|z X|\OO(`\U8-TLwxEh99b/K&PI(sZJ𡖏uE"em^_, Qi $]'rYY?$_=JsZ "ty|Q%`lv ψ>]gLPd'jG`B~mjMhe,R3QyC]hj+J$KM`k|0_F VT?ҧjU}ڶ {EɗA4M;N^mfɲSu)QclEr/'^ ٿ:BOX".|o7 ,C{.$ ɰJH2lDB-[|=OaQ=V]jQذALy|aw#7).pzs޴쐗dD1 #@!aHq2L{A7YQ̢yQ,-8$a\O$0|ScNF:|]&8 KcZD!ΆwC'HmQB( Y6f9 D/"ȑ ZNX1ɘE_~pylRT>'6|7WCx¤wnS8Pxn8@:5Q"n Kډ'Wks?*ƺ]5D&āUak!\KϦMGV@S8Uf%oåvB4ІAecoh.EKmr)Z /Ds9VŮ%%k/&c=ROc'KZ_QZ3 1KOb[I|ę@~c^'9K󩿁yD퀫|,txN4L^B;0搇Ьh^u+d *,U97 K+Qƽ&5GqC" Lm* H?2za8*]c}im -g~:ȌI l BN[ASS)hfC9cl94ʬsG[SgfH[*Y?O a]LO%? v|̛YZl^>dEcy;(!B =HjggH5r_Msb=NN,@RށG*Mc{d1Ip`vͺ?W<>aUY5n唎qpSKyH0btM%TNSųmқ>o8_gg5n`l+HmVVimޘ ؕ:υ!@InuahBi"4+f^C .T{^;24i/l2!>$ܜsS|65EW݁zI;i+*FE0d"6#,b߾6+1nk) e,rQ{J_/}xךYY|B弘_n{2L;B |'?0u(x ex8 فo ,5N'Rޑ^+Ujށ7ށ<¨prW,A~E=s!DrC%3tMs#9H/g9AJ Y < g+r+AG ݠh3?ҢhҊzo=x_1 ;fWHb}U#a36`<ɻ\%,OCOp ٜL/O3F8 "3]hC~(gHҙG_PEܹy;Jf4`Ew9%Mrn^ۀ)O=MMChƥC]b;зz[WX~j4/FeI endstream endobj 920 0 obj << /F7 38 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 918 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 920 0 R >> endobj 923 0 obj << /Filter[/FlateDecode] /Length 1806 >> stream xڭXo6_aeIYrnH˰5Eahvʒ:w,[}xΓ@d3[Dd:l'Zie7''W7a0lXˏob/+[ixF{Q%"&ќC5 P9|K-|7yU|Ho?UWVN$%IX+LV&}llQge+Ln)lwe~*clOD',xfʊSOY.iܖS>#hw{7:}`4fij+x qt棦%ؕx'mmWNqZܷplla,IZnwjP<06"cKw4eNԉϚOŸJ2sSlZ]CBߑpԘu%.9 rVb̆ a,]Ise?ܾ[8sXš}toWM>A(C) 1{zE7ziaF)9QBȅ0 fh_tzu9?ꫮ‹ׁ # _XrC33㈐4j8>cE2NFmLVw-pZ }Vs: Hgi~b|jsCc6ձHLWy͟KɊ?!^IZQϮ{ o:s5}(( D_~z#n?뎸S@q3#!Gr/H<983n2ت6> endobj 922 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 924 0 R >> endobj 927 0 obj << /Filter[/FlateDecode] /Length 2071 >> stream xڭXKs6W U_vǓ` Pljv+ F7I/`A<3C=yIțx"LpwW<[\`6"Zn;Q 7'IoUYȮlj=g/qҀ'~cwk߶ͪZFɶSKZfh6Y/ijuʚF,7G n0-Hy<6r,j''6pU6[wRƅ<ŌGtSb蚶k$ECL\_ȃc\0ezS¼m 6%d쑥%ĩ#Դ7•wAٲ)hj 4MgUY+8;53LG e&!—+aa.)&e"-hZqi7(v0P8Z#D뭇}( g!KTX -›vm% .%ƔVoԳ1`^Z_Vr[&PzMkN`SVisXdGRs^BKczj \SJ=3Z>68[0V}Iav)vzz`v l(C*ٛEo[߀X~ q&'/1$82M6~;9Mw]U ]D>\}Gx]Wo?.&nl! e$8ыe'^ ȋqEMś |чC-U>,׿MX_bv/&/;\~~ד_I׸ϰ?ex뫫ix }uo/N"Ui9z)NBcy8N뺛39¹ك{CJ&۷(a]ߤ,2 endstream endobj 928 0 obj << /F7 38 0 R /F8 41 0 R /F16 138 0 R /F13 66 0 R /F10 53 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 926 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 928 0 R >> endobj 931 0 obj << /Filter[/FlateDecode] /Length 1327 >> stream xڵXYoF~`SZpDvUaOMDVX(;mΒ\rĥ$=fuB ,)~NG%''T*ByHDmr^38t.1x0 ˻i·53屩 eR -1}Y蓼XrI/N]/'P6eZh&q.I"j& 4Zunʫ+TnkEK6Lt) @בQ'$a((QF=J??y0%Y|`7S ㈶{Lb-\mf|LxRv[5Bw5VcQly-<rدG(1-l:3e~(oTa?#ڀ&u>lemm@d“gY >Ѕ?|F>Ot-N1/6w|9{sMWrz)V&86BXxUY[R>IBDd8VEd̜!y"ꅀr1}׾ƻAd)I%%1[A%زgެc[rѷs]~eA#@J?NγH$ۮ0Ya0خVu:ZvthTp; O^\ϲ.)}1ԥuХ!fMJ7j|yϒ]d~u ջW.`29/xCKXo 5ڮF?B'E>k5'`a3m0KcˣYzX)I /_d׊mpm+j 6>Y{%۶Pr& &<.&9)eqF-`ouׇYS9*?M$ u*LY,M&hU-sB!|ϐiO)+Oj,!01=Kh ڿ TŅ˅0Uv+Xw+u ܹ~,)o"w.B}YO#0P@̀n^_N1 ;;_Gu[E(P@`| "o Pe? LP aĉ< 3HNTH{8-}+DI'rm= (@_L E܂* O0~X||fŞb=C`g16I>Cڛޑo+n5XB+SFWoid endstream endobj 932 0 obj << /F10 53 0 R /F7 38 0 R /F4 29 0 R /F6 35 0 R /F15 112 0 R /F38 688 0 R /F8 41 0 R /F16 138 0 R /F20 182 0 R /F22 218 0 R /F23 221 0 R /F11 56 0 R /F30 286 0 R >> endobj 930 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 932 0 R >> endobj 935 0 obj << /Filter[/FlateDecode] /Length 2553 >> stream xڵZms۸_s-ճ܋g܋}%Jdh8'*I$(2m}'.sD?AO&b1I|H!䦾|s",&fx<|:2*i9 ! LgA8g:[$uVM LbPkg ' IUASP=!:yr;סǫutov/p0Ü0 0'^?Q0s.gLxba:@g`׾ gl՘֐d&Y>3>O90bAlϗ{7o=:a bqi~*FOGh}xSoH±$|vy~CO^(`/x3gAGXM@3ꋋX I޿y>o`> Da4WEeMP-P;ZbR[;uUӰcY:*NoY'}E{b>vE$MJ 0Ώ! lTI2sC ;(j JSy5$aWދܑ[$~|"te3 0uy;A@,e,)\P15qt{me3m5gxGmmq]) w/tש3]PCR6:уVf4pW)l7Oo|8]Xe=VYyg7IA2]dKn _%=6K7n?HoڣFMpQˬ‹sU+Kե,["ퟌH8 Jn8w+^wJBQa//d {Gp~LdZ$'\T8oh@yxJ˔@G 1-;68L˔]],2; ʞ!gt{8o\GgJU<"\[ 9JP|BJ*AZ&VKz nNJFI灚C-Bgn`W+UL'Z`/ !7@,Ud5W&ItٴU:Ѥ;=( endstream endobj 936 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F6 35 0 R /F30 286 0 R /F10 53 0 R /F13 66 0 R /F8 41 0 R >> endobj 934 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 936 0 R >> endobj 939 0 obj << /Filter[/FlateDecode] /Length 2191 >> stream xZKs6W(ED( 6iR'u'M2K]wrؑH EL|/rIB4^Dr .oЈJG#}y pHRTj$ %$٢f?6~)qWi.'!p7}/S$ GTHn(vĔ p=K3zP-eZyytm&ӐN2$c :9p|#""8 NAkH+k@P/ κ7沩,6ӧ}snq?IEa*'ZY@q6GAaaZyw2l"& H$= :=Y7%[^zwTr/eoRm^VLPaNg͎͌4f|ԓ'$a75+I8<†$D=AD96FaDÅQI.L4\ΊњcOdӀJ Hk(̢NK3^*e^B&򾲂l'''r;(̌^iL8d9~Xa?2}TV 9 ph "F0%@( &5 ª]ǧ;چq^SeVw@!ћD݅a[Y-ńΖ48ZA0—,7rdDXO@#(1;5tIr!?ć,?XG0emlӃ\%bA%ہ w dH"Bt&2vhm4ox׷$(#1s1?u >xMLBJxR>!* ڪxmf,X !Ho_m) 2 }.ӹWG?}2EOK4^H=jGeTo ռ {˿'*ښѺ*2h-gs*T/]e3Eao}彃WE]'K#OyyXLIQ䱄NQԄyZ'a"!t#Ps<EO C;2([R0$Q|a6жFٕ#D׸o[@m=ܹ^6eQyZ@ $ Kj¢{094_E٨YL`qf!&G sbYeQUJ UM_;7#nVd "˹O8v>4!p\0O6M`K贁5V7f7}'$!+iSߥ+kvG>n'Nm_bwˢ*lN_Ƚ-r^f70PdqMpQ#o1C4ZB>'Dm3'B߾:Wm!Jl_2"QzbV&8=!L,x9Au(z=uonop.l.~*%a}Pz=o\R ?_랉@ K{)}{<ʾ.~<}m-.v8*}`eQGXDpL:40ihGoq(ד^qןGCfXKޯbNg}θZ^l+FTH} &#j L clK'gh_*9-*jl0n iyLUϣ ԮՊu.1#rEZFu2SH2V~^iIeW9 ch\:c]12TbP\jת=Θ-猲vq%TMv_&I2q<6upRUa$ShNTXnRmU ԏye*akSզ([f[ `5L0ߩg(4U͢԰2ŲpU-2$-#P6%E3)˯ҵμ_ėjkM&F⛘54g9b5 ޥ+/BP3ӓ%pg 童ugC /N? vunUفW ɺ (ϑ+B'3 /!Z1Ɋ endstream endobj 940 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R >> endobj 938 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 940 0 R >> endobj 943 0 obj << /Filter[/FlateDecode] /Length 2475 >> stream xZ[~pP cF$E]%i ڧO ] %CMŲM{^,jH oCǙzb4*$`,! }>v,%i4yp)a_4 +&-+"Gx\EQx8ueQ/Y0K,DMBBܐt/rgp7$fL0@;3uS@UD Mf+JI*.g%FS'+SxA2WiJb!|}e~gYuG"X=t<*YנzMBFtV>fj4aY,YhȼhE`_ Z!ZШXeT.˜ЎK:gUEρ3jR-XC#< X۬xo4!7尫KMݔ*7F\|;e'8@M-~BHLM8 H, (Q"v:T#,8f{y0CIDfۑ}>7ʘtogXUh2rFIs' >$Tߟ&$l;ܹpz.q$fN  "j7ns#M:"ս6؅ jǏYaP twYQHbk3v_&,ѲNhc{OȠ(=w3L2?Qn]~rnW~o8T>~Eo7.ٯ=)rA_LV7PS:!.:&'zRL_k;%ڼ\CBq<]PF+~ dwxQ3iխ k[k'S'Ӏ4x\T.۬r9t{Nem0anWb PWQ _2p0~ʽn U^nkr)DR*wStm J.wA!J{)N'f0zu83no&݂a4%4-Ĥ(i@h싒r<(eVN07:j v1* ԡb$\L .Lgڔ5諃2 9Q* S(eRyOgBs “ HhU+!sWj&?%,$/Kϰ̦̓9!t> _ ۻ:N'[kjx4\Kcx[pt5_ a#8 RUNU.cRY]{RJ'j8(a;<2,wJԵp_,ZG;U#)抅 ^vNC\ C K5k/> :d#M}Є>N1ڞ D6=01++/UM; 緹H\Yb` 17n>.M(X9_uص@=:it7 MD:d"rnGl'buOg樎Ȱ C ./SgħfPn8CA)hө?| !;ob9QI3Gs#pF940L*qq~'0Q3 VCU`B<:a"B;qw030 endstream endobj 944 0 obj << /F7 38 0 R /F20 182 0 R /F22 218 0 R /F23 221 0 R /F11 56 0 R /F8 41 0 R /F30 286 0 R >> endobj 942 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 944 0 R >> endobj 947 0 obj << /Filter[/FlateDecode] /Length 2315 >> stream xZYF~ϯP #<n@`K4c }xJr0BaZ{_*oz]_7yroxn+BNӿp?*"bU `W#x}oMףw4;'uu7xuH0c5:ϻ.&#q׊5's aN:ֱ:Ӭ3b!+'m3i",4BhL @dw0a9X<@88vA2x=x7U]h kjf'PzvAp4;?Cah?D0@E*N*6 Gˊ/HU11YUzdvO[<%H^/`<0ᯭ=ko/7$O{ Q#ֈ (B 7G=>D= Yn=Ax׈h Z}VפjJ+ qIW`aB| 3v6 (+Ev YFgyed3k,q\ш[4\m!)@fA7t !l.K +p|*5س̅g풊 j:qYZm[hasNJ͂p z {U s %1+u(;& :)*Z),.@xlԭHi@n'"͊w ͵JA5p uT4U0rf$ ɕ㐥roQ*+cF-(4&T;Cm*@P#i:"n|.>Cs͘SpK)UJڇxwM{DVifh Tl_]m'xPy(I"Ø;ZF>V2D5pjx+&ܧwƦU.׌A{4Ҫ\U YӰ(f5ޯ3L9ƔRbx:rpCPJy3@E!P1ZQäT\HYfɞ2%.bh]ăPM4$ʪ`L`2hz<>f*p5C ͓94ɢ%DNAl[۲,?U6h:@H.wy2VC#cnD]焆ϢhpyQk8B9Hhmc\K4\cb*PcpFh5}!VQMe0,~N@Cݺ! )X7apn{g@kDK #}ԪYpy ME;(#'piM3ԱstVm(#zq( {UxK>~[dYa9u n]:8*7 ^d-X1:vڪ-֓; <&sFD2̂ĂL<ށ-8x@RX>CTEWP(0 ONdEM HȠ%cJ/+Iy30^ʮxyz duO5xdOq[ `VoX.KsCRsHۊ~lRfZ)2p܂O0CX5Ok\*9ݦ8a c]@W[ -]w47!u}Ahiu$` WحMfEC8(B$BDu SZD(hC3VMTVqr8 >!J@j\fL endstream endobj 948 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F13 66 0 R /F6 35 0 R >> endobj 946 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 948 0 R >> endobj 951 0 obj << /Length 1691 /Filter/FlateDecode /Name/Im23 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 56 120] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 952 0 R >> >> >> stream xYˎ#7 W1Xk`6?0las裡nJږgl纋`ev,k VB ~^){OB&0ǸB!s4X0|͆ko 5-8+%6!Y [FL&"w0\+Ռ`}K|mJ X1%]X~ œ(DC i^no}9O⽲AqD:B&*!;rg\N\ϸ&S:8J Γ(2"P6~r,K\[2MLAK(Stܣ:E'5}' IQ)Yk-#[^o`cuu@$H hG`5!OkX FjBy2r)ZLݪLJP=cٸDbMު] HVLɬ'|~=ژ$e # Au2;/[E|>$< 'NPwp| Nep| Yc+< Q*8 bm#ұق6Џ,uw ;єV 3[YgVu}`K"Ci$/_#U i郑WJԑyX@ ىdp c-fa7cjC}8MधpڲL~X{c]' B^Y/0TL @j&7&Q!NՁn}5h< jJ4%%6pB֒w@Pvh5tG祃-ܨئ@q_Z\_xH-5øT8 1`Pcv^rʂ$mK6sDn8ԭBG3ͱߝ&j _XI$m!B0/,٭='/m, %͗.-MD"k:\A"Ymާ|J5_|2^-[MХCQ&/FoYEԕ2Bz %AYaBgI^Isaݚ#K3i+1.:FM/KCV,0褥XHlhN"l<`9ܩꨪCXuo,,#3yREl4%9$[׹nl,Yj'=<9I9p6;kMNҿRg4ĹFXK(UcFZNdj訥N G2@>_!0GR4$emC2zם;Gwy9 H6Ř)Ps(bU)a^*WB1ͭ>, endstream endobj 952 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Courier >> endobj 953 0 obj << /Filter[/FlateDecode] /Length 1773 >> stream xڵnF_hA%{rIm.[n uD:JRq ;*i8b/'>o#MY74_lYʚ(14$"NIf*#E (|8)E5Y( h*͇k%0[/PlBye U GI)i& ߨֲ2("`KD(Z8eW8OpTϋ%?eeuL7xTOi!$F(ğDP蛈I-q )1 8+c+uGaYʙԮ62MscC^ijYK =2S#^s'lj C9$ £5@'` HfW8YO@Ą:c#PK#GDl(1E^4 "&\I?:irY}qΈ"q$ :kuM#˞ZG2ne)DCH] 1FBVj+2e4k&WYa5h #l e5uX_)y uBû&"5_@$G8G؃a rK?Y#.RC`9F fT)FS׵+o5V af^#'mfFȕLmd]^7B{ežZ {9igjWnELKx|ܪ~c|d>މ7'Fs΄/!46B%&lHZ^4ǩBwCpͥꆋ^UoɪRFS`YlizU18?FzWE\qhnD.m*:y{@ۃܕcEwܰfHHhz"J䡄4$4_vlҘfҢ0ʼn08ˁe8CcQBɄ3 D@QӭR6Զ~ڙP0e;P3Av[|_PЮ{i,}_cz,o=_t LؾxهPAUV\dA9X𮨸{@gQDB]"ZI[jt]IX GsNlN+{O>H]zs=3flVGΞ:k9=]= l V Q[Ա~,J= R9-`N'[%ODׇ'×]|W8b޺nHѽ u~Ԩ.fYY {$g0$4È$g3]ݹmŀsXݛ޻Kq9Vlh'Pj<0e;Vs[Zw ՎԵkAD  Oy7Tp`N~<ٻEj;ڈMي0ܚ/Բp{w qC֤ +-]-5_RY"]kPci17joFꊭ&rzu8>w%>JHZ,L%.;fBVveol} endstream endobj 954 0 obj << /F7 38 0 R /F10 53 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F6 35 0 R /F30 286 0 R /F8 41 0 R >> endobj 955 0 obj << /Im23 951 0 R >> endobj 950 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 954 0 R /XObject 955 0 R >> endobj 958 0 obj << /Length 1129 /Filter/FlateDecode /Name/Im24 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 959 0 R >> >> >> stream xXɎ7WC5vcX|Ev H^,lv5W޿ܖ680n+BˏŮ-~ ? X+AH6! /,v'f-⵹dV0m) 8,;XD2sd$)J%oO%IgU:O#VF*{Uy]dnweC6*}YBL+!jtԀK#nԈP#V]5yk$ V%r &4JZi2~}G#Aܑ4iJZVĪ494gZ+r }Y:LPGnW: M#nt)VNɽO5NHSE3T^;kD.@yўʮ9uf+3 Pc #nXy,V<ɽO YBє5,\K#&pΞ #feM?Bf ,[F͙՜)Z˙}j>[[}'rƿG/ǸڸzU7o'kXGW5m6u^\> endobj 960 0 obj << /Filter[/FlateDecode] /Length 1522 >> stream xXY6~PX1)i6IEZۗk %(;<$Ӧ"G"g73R22ãHQ8JIeq^E/pjZ)o^]~;K(ڪOc Ȧ(5۰4?z>PRxI D%Q_n+˜G )Fz9( `(bvtQvuۮWefF/oĽrCmcwr]KdV Qr5PwP4/'[%n:][ fFX.,Yx64v;KbU: SQ> =М8wN mQVIs9yT13(q\ zT/ia100Wa#!D[N,fiW+ƛ7RnUݏv0t`S) ƨVj6 USR}U\JmJ^X]{c8Ür8ˎwIWugGF+|Qe׽w dc(e}m hI}o!\8 OZ7,;uپ5sq;WeCb٘ ;OedQNc%D얦RTO İc^ 桄 T8/ {C(hw‘,ugY??6gg(bP %: t}5xoV9Q G H* 4 (x"7ns#oh%f'8Bϲ$@, ?Ganiiϟ$S[?M(Hyx/OIr/eI+RuR û;%8K)׺xAx|n?A~!ȵٺb?_Cb2"ؖǶ5~1p.+3(/M/+x7.+> 睕8W\ Kơڝ]y#&3|}/^ a>:vTi x endstream endobj 961 0 obj << /F10 53 0 R /F7 38 0 R /F6 35 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 962 0 obj << /Im24 958 0 R >> endobj 957 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 961 0 R /XObject 962 0 R >> endobj 965 0 obj << /Filter[/FlateDecode] /Length 2419 >> stream xڽZoFB= -rw]@ܧPiJ@R 􏿙Ⓕ;'R|oZD$ q'\E-B.v 8T.>,~ݏ٢ E1cB8͗!yBܕj!c,Hƒeia[˾n}#Ic0!1 dWKvͧJ&]׷Ǻ?~ VmeIREEݪ\vcDLnK%Pv^QGu'op/u- 9v]ٯbe4|/Ks= kVWˋ ?Z? P;9YVDWh{Wfkez;+j7Q3!-_#vT1|^&ۇziC`r8i ='[EM s8 PEU}H5Rj,MD37OĊU]yN`zJ| :Oel]J"v'-tH3x9}ZoZө=-qh*K&xLeA@;YV6}b0b3J] ٝ F`BLV<9JQ$*(wR( VH9:EmMuEl_m}/n2ђfKR JRAYӤi ۇ Z~ŅqBO][<m!23nbJBrݫ8Ⱥ=*ח8K*|-Z; n$q!X.@s{1LYڪЀ7 vX^ ԍ޹X3rg9 nv']탾HBwJ"U +`d=beD4'Gj=ȭ:ɏU|qK{rhR=EPhSx?y3MycW<ǎ,n8:~fEpYl7r4/84gTߨRVF8hJp]t4YTM7q޵ztOf GH'+ѓyF;46e0UNH#ēQ!?O9< y3EОjSYʹf>C(x#0oR[}/mUnR^ךWe9YiYEovƋ7IdA!ʮDwC8.uXLx_h(7}x Êęͩ(s DN];t."vڬC`X:*ݟqJ'>7c=}Wu8LwfVN.? $ vquvL"ũJAPHC04P95y@vdl2{TRfՍI N1K4{Nڞ&n*S pc8J'Ǭm&[nON2{'l|9Ԥ}՞nĶt YTo|a׾gu~3{~4$tH Bn1I2C0J h춯 qaSp"Eբ8 73w9YTɞ3lSMe]9H4y01):S: C~ߜS>p/L4 "h/ij ?7~ڟ9p,?{Z B@<ɘ!N`TȤeEQ<@"S 3 ߼L2B7+n-XgDve'DEA#Uk96kE(ʍ,H Xţ્Idɴd*U@3hW5]pyohp+89 Zn ë2AZ[hF'd4j= G+ ]hnJTHw endstream endobj 966 0 obj << /F7 38 0 R /F20 182 0 R /F22 218 0 R /F30 286 0 R /F11 56 0 R /F23 221 0 R /F8 41 0 R >> endobj 964 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 966 0 R >> endobj 969 0 obj << /Filter[/FlateDecode] /Length 1907 >> stream xY[o6~߯PXtk8], R{XBuD-4~ERMZuؓ$:<ckz^QʼSoq{}ތ(u zwo^8~; XEmGq+ n#Q*@#ک? s ɨ?Jؿ^r<O,+Ǵ(rwBDm?iʺiBKsӼQt5y`4=r#!jA#" IT9d<4'S#vdTp+wyQByqT2G!=D4UOf Ta FzZ<>7d&_4ՙh4 %HIQ`MEU#LȤ¾|;::u\Yb0Y):ww9Ɩb لpp>NhЃR[Rz7۸ )Rkٗ'[N&OK #(qm{bX0 1!kMlN҂B81|2B1xBRwǡ|r,_m3@SQQf\FY|ȡV50)R.J>bh!M?Wb5_.4A[62ovRt$RK]dۡm{bQFQr4B4eG(]Qĺ)!F-j]]ṼʾIs޹T^ }FzPn2(u~[gz][ Qʸ*TwalJibmW> endobj 968 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 970 0 R >> endobj 973 0 obj << /Filter[/FlateDecode] /Length 2600 >> stream xڵYKW!rJ$%ٽf`ra֙29ev[XYrzQ,wgٜHbUFaE͟IVO4 wz1Iie rw.ܥHjAE2D<޶:H1zi|"˦e`QkeAޖ]eeU=l>pvP~UIVxEs:U_on* 8}d# 8uG$p$ jdmx}Y ]G22݁;Ȝ:'{jW|4w(kQ7X YJ,E xC &![BX֨>GrnH>l^tzd(l[!폶پayX&-L詆3ID1J8'xY`WoH(V ,[(5(&WPD weKY |Dc %ixnl-)k[_Z L=~K6j8gp]mfI Ձy5$+ǸQ R št&%p#t%rKzA!̌vF u q@`2'P !P^g}hĈ_x4B}!N4m': k1}45β0݉mؒAS ]SǼgd%ـ;eaβm6 %J| | E!0PSs5srK:e@߂5ܭMeך t N}m?+-]+y3v|ѯd@x$ih`7J@o߿V'[`Kz^`u J} @bs:Γb'1!(6/ ;FV#C5g}pl Ɉn'A!z8 gV4r)h)`'sb`jVJW<96joJ3nQ޿vNw J eTe-jh- itg`sDQ2$Ύ7!d,<Q&'t|96,&f熿إ0WϮ6ȹu= O5QNj}{!z]L?RpCk=c/湓?#H҇ot:7`#{X$p:{¿L\t&˕/ /Kު]o]Dz'@5ϻohy*Dy`\8FQk`{.Dk̈́ڡIJFKe6,+)` -wл]yjvmN'x;DŴO4,JbnCU%-q7F{r5?+$'xDz:Y(9#;WDja {͙mh竉\.9J[y+hz%%w^3_ vቧS))'b+ҫ"aDI8 `]@ɝ+Kiw~; h&h,kYY_q#nu8Ѧn, =R1/ȷs<]AF C11* ؗj%pIP2Pٸ:djW /Iѽ<Zꡕ6uDS/k*e}* ia:7eiͿf 2x_|:|W`Q`Z=Ա]6avYK4~6 KJfo6x:~(yyB>O1:T=5TISJxS3/> endobj 972 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 974 0 R >> endobj 977 0 obj << /Filter[/FlateDecode] /Length 2547 >> stream x]‡&\KE{r{-Z{+Ė I޽-;-Ѳsi/y9rI/>. rHbJHn! w7_}OENdqs߯勛3?/W\QYu7KG f~+rm4&]bPnj[[rmdJRf:º IT!M)v{(:-fUm~q4 ͢;j?n%K=L"42ɤ 2T:RƂIHbW.VzDd$_(%4KZLR9QU{_7;û<-ΐ1=+ G]{ E |W7U%qw=. nv%اIE- i03ep-ʺuՄ2}X-[ *\pgNݪ5[D󔙲ڨO \,v̍hKAF5r툧9hmW7(ha LOo{@ Vz1ptTm s0kpDM_'h>׊2!z(7WY2R dP!,W"psϾ3]ǧU3ߢ?O(= X~8q3FCFިЌQ6<̴q]X8ZqF`Sk;Fqϕ[p Lfcx ^2IRVthLJlħE|'?E"V{cZ.кU"%;GHt9)4 Q_('qfl'P1,&&$b=`ca’yLHgHR8Z €HAʚ, %zaӳjj۪nʚGzP J g@f\[8I{Py 2锋,T\PQ`\n,eC:~HcE9L3_] %'pH*'G \{|M݊^AT{&[U}1ec6st'eScC%Ge)qN v9XR!{Se@JUB-d!#s{Ff3Q@e袃?)O!H >~jydIpT׳:gaSXۃG8aMh= b M)6%Xm`GgO1f#e*NS__3ǩ8AL싁h8- yRIJ6}XP7{@]:T ̧=zhOxaǙ3@~B6KMľ:hȫvN񶢷ˠ`G`S;Y{/@у=$@4С,l*9L0AO{8M8>2Y:AK*<iZGɓ~fM 2?=[ 8 k#3NW/3 oP FfK]As/Ɠ8|Z`` vG^ʖt\/lGoZUwYƂqQ[qP"-j,/N2 \& ƌ M j 9ܝs9~lIf5ђ}@z[&ڇ3b'"IuƵHNIiӮ管l=9fKdσ]71@9.eus{9Kg]7wg "m\B{Q/KM{L{T ## "߻^LNk\>G|,G㩁[_nC E0 &wןj[hq<@}s-SH9108y=`Lnaj=tF^H4-`kO$Fqui0>SudD"sdimsڔSvkaQZʶz]$yPRL叇uԓ? "c1>lpnz0V  ˌF)Kkoߪ,^ȏnDm84na`åwYcݍZ>)T7 x3"ysfMN3R_;éO_D}).|iij^}_$&q罷aM#L& RxEqS^PĶuf7pkSmeijYM=y$%g4T ޓZ3ǮP95NgqM]_%ɻ_yvfٮ~}l,?~]<Ձ(K436W?PqD MLL_d6LLZj>6}STi^Un =Ws> endobj 976 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 978 0 R >> endobj 981 0 obj << /Filter[/FlateDecode] /Length 1855 >> stream xZ[o6~߯P;I&@$E{h@IF-t@xiV˖`O(\C|uD5Ĺrbx>r&N >vç:Q=;ýO. CAD"75(8;KODf;X1::I 5Pp9"'c>2)&%$( ~bLзˁGP!n+BCUV9 &bӢ?\xJ-jLSFj. ͋j &2Je  0%tөTn^"`de60Jl1@)Tҡ:,=bgw9l5?邼uM,k%L 722&{Xp=d m"su0i[Ev|k;G7Cm#C[8)mw=,D"%Lu'&<+7\`n;<8_n@52^T֬~m6`YӪ^d鰌W"I^8 [C#.%˿ʷj%Y"^Y٘ÈNӲ̊|_L۲y6*Yciʵ}QL&I~>3;ˉJ61D-b 䴔Dnšw$'rn*4=c1Jy(>3Q*`PrV0R>[= ݤ,9{IתP_ը4ͳiOsVr)K>OI!qOdgŬ㨘ՅsoLRy 4KNǢHa$jxVOEnְo.̟ۇH,hSjMӳ՘N(Xx#]eu q |YcQ|3 dKy/cu:vp/r3Ebgl)_(wշQD]6MUЭ`=wҾ%n5Ye}dueVckϢ2Apu]XV&k7+ظ=%n Pߞ%(>d9rzDvFIwy"u> endobj 980 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 982 0 R >> endobj 985 0 obj << /Filter[/FlateDecode] /Length 2395 >> stream xZY~ϯl^d/Mҋ 6Hml2AC떻ؒ!}["iR˞$'IūΏEf1<~=̒rPb){=iKr'ϙgWga:/m;qT_W~{RgLYlTbla ꮜ/T <]_55~YѳDzl}mm EM}oJ ~by EUf#AYj{fC5uIsw}їGMW:a T9˵/M["*ڶ<dߙF\}6{QuayoB MYO4T]t]ugxZaadC7=*UJkHEuۺKh;X"OfI8FYcِVp rEh3ʎ| ȴQ(qa"0&Ǫ/po+mCT֧z'eyA>B>\0)}{2iۍP?]A+{Sk\1=" s"Piد9K]`};zev]'s;ԃrbq?!vzaእnxt(b)sC3'԰ )ߵme}ߡ3*)'Umہ"ci" qrMh$6PU rx`Z~v_m1{=aOojW7` QBx&0Vp@PU)'B5ͽZ/Bnݞ! *?n9 ?^7x4<7~s/c/Qtr2*d:.'c!b:XhOpVT2pF;$\>U^EkcI«c.XP3yljOa~z$C:}qlij$+,ݟEП'LegC'KSs/F8\HAj#8 IZ~i"ɏ2IMWfj2)i֓0շ1)Ya82b &RDAuvpZfي+R՜Ԃ)%]Y yk\Ahv\AbkwuٶS ~}qS(OЩy U[AM\OJ/q( ,r'Ļ2uz:bf]t?DbNr8a鐛=>[+ItW\ :&&3=}ȑ>̆;C~4o@۹;!w?4rA)3U!fxMHAvy&c%譳zY?n5×0Ǟڑ5M#o \@=}٤pnMS Th2S=ʐoBKB#H%(wv#bkBd6(ݏOz{b\2sd# 4E){SerV]$/m[|(A$B^P(?q8QuYE7&hOz6[珏=7d.̛Ҏyhˏv0{=P X~oq OI".-cc{5sqN"tYl)Ϛ|[2I`SS9"͌>x8$so-I4VX̖Dz L(RsUC%bfJgqVƉೳN ܜaʧ.L;t`dft\5֏Ffjbg 7^!%r5\SBe;ǑB)'/1>E櫮ƭwm#2:e {6EB2yo4.is'e$8>敍1^1Ev\Rp#`Os 0ۮ5o{=h$ UYRECq: r>ߤ {[߱d7}s1~m,Oa"ɣ3M.4$L(4It=)t!\]it/ѝA߱v1<8IP.W.F0\5ŜkV~՞īi/5aq b n"q P"Plr4Ar8R/po咤#`4 }B3/{ghqҔP2DH=AP/ȫU>m$EnpdS\r8mGi(^mJ>LG!Sܔ%>5~Rm;A9s`EXũP{E*ќ Im]oǼ~\Tٔ0 &w t2qĀ[2ɮfs?|"(:+W|~{&R!F`&2 M[JI2k5wfħCW0M݃{|f٩W=bc5Q=gEQP)5zgҽ5wy"F4sVρ+e*LݲAfJYt?5f=XKIh^ۃ@Ċ$%.ND*KmM_X&P*C`F\,wX,j}OF]HyQ-tl°8>=S\̮‡cY/ZW =uyM+H]R)}4J4T4NA@'!` @#?&U/JCU ķ!wCj83)n#0J_[}}]aOթ?x4,+nR) (&^ ֵ;OwzZXeݞO5 H8:}wƟrF(#-uRm^PA@ ǪK[G̠'_[8׎^px(Z>IzyyT6TeUl^zD1l!\d&wV(qq[jqCln#[eSB,x+c3@lf?^.i^ k2'`J*l^*:5ùd w?W9}=%@\i5WxׅnG~y\8n}3> endobj 988 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 990 0 R >> endobj 993 0 obj << /Filter[/FlateDecode] /Length 1266 >> stream xڥWo6_e/2R16R/I3lN8Cal dr;HY4QB7={%Pʽ~/Q/%iM[~FxM:!c?/jU*dBhsR{<$L4GӅyB,/p-fu^|Яё:c;o$N̩N "x/sSgUA}5 R=x%-Q\#gB§l;%G079!-DXc6|wD\`w#sa8vaLdڵ^R.Ϻ4=B@ZeZ )% :1HwD}5SRI@nj_tCڣT5JX5-*!#\xƏfĂ$u=@DãqN5" EN6SV: `QA Fj؝FFQg+C|PY7j LJN֏Fxln gUMǝv[PmjCV ?feU+x=\7\V# l XlD^#}:de{p?ث7AgB]ݫʴXnD^NK8{UurIunY]j1##2R&?x xƋ!q}<6tzjrs0T-N ӞX!uٶP/,A.y'h R:,.m'+$qiD#g|ld3.~QS endstream endobj 994 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R >> endobj 992 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 994 0 R >> endobj 997 0 obj << /Filter[/FlateDecode] /Length 2493 >> stream x}ْ=_U%qy6u9U^9y$fy(5G>}83OFz洡gX&>|M7'qD >w4(gut݇a$D>IrB Uך_|?rz ~s3QiNs+|*C,s>)S\:K"h%S2Tmը2ˀ_|e$Nwe'P@!s d=2;><0KË@}^gцvI 5MhigNgLP H?5(Zw$BbR >\/S6stonE{B#s1ULVNm/+S5<_ ='-{$# X@V(ع+Kשg `^BI)FߖEn;Fѽ9!{Fj33Ǔd"WMzBGlvds6̲:J7*/7WA ޭ~-ӉaEw,5i!^^ Z- ƈ\j Ǡ]\D % yn~ b_V&KEFQ=(: [臯/Q&P˯} <_ı:) .jSkT+k+dlSG5Ī8B)DQCWvn:8 خ!d H`feafowӱ9}9@yAy I?@u4*8\a̠ZVq$gF?R%?+O A)i4 Q61h1j QHB/}iw")%psV}ɿ1 `uJ2Mn|"h.7B7p̆@΁tF\޵oׇ2"'e#7 &CLC$HrtXGGU3'ںj5+,68w UcAḄf,Gqj >S 2N8V9S$gеAfH¢.wYyH 2|ijAz{̈ϛ\Qυ:7RzzxmvC%'9*9g L*)Q:^׾e=>)Eq+osCib1h3R_1ll^g}[$deƔYڈ:;/= NED!VA^#XzE$; |hi.c:Z 9:ba> endobj 996 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 998 0 R >> endobj 1001 0 obj << /Length 51 /Filter/FlateDecode /Name/Im25 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -151 -175] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1002 0 R >> >> >> stream x33T0A(UƆ`Q#CSC an 2Sp Bd endstream endobj 1002 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 15112 /ColorSpace/DeviceRGB /Width 310 /Height 442 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 310 /Colors 3 >> >> stream x}h$י@ E2^A| 81MFC"6pg;d!3NxwfQEVc63-td;A{=Z?V9;7nwTuW~㻮sXΆyG[~76ef}(E#햷ʥg\CrRzT~,ooxhȱ!YrNWO>y9ڦRJu& vun`;OwܸMrMŚxul980?ہ[6n5:kJ3c|6n滼f:?<|رS=54bu gF\Vʃ}pa(w'rxκU”[_]T+믯[:`7{V=O|DTV'*hG{`HSRGK+>w*5&lU{ս޹o1#B fۡf/%Y#=`vъd`4B}X9z]X}mѪwD~ݯe;A(е#l)~#FN|vL[Fu:h$y77+V&2q/]S=ooJI ReW;qVJU('RH?%Re%;XM?$J&.B_<'ߣ?j5`~tK|ߨT׋}nq"T||^:zGFByˑV[z4&wfIϤ͸ulug4kdQݞTixۼ^2K&jtvƺRqG[Y[Qd;Rm;jb[mey?qw#(-S)!K}PcEq!عۧ O*^퓭Hw,,gsB)o]w3c)~u~ζ;Z:*!YђeEkoq}8z|?/GYe妿jTRwwQY/>&CDGR[zm%)QHrYꓪ/=3a$Knnnҿ?>ӧ_$mjx[nڱ)V<ޮ]-Yᔿ2,ZZ䓪uG:ͤ`7 ZzmC+_z:SzڢC{M;ρ)^o t|0?#StsTuI ê7_9|l$lSuTE'N95a'[*ǍNe!=GkQ=v*#C#kJȇٽr|W7Q雗uRLyW\ljJDU'GK ROuӾ"~iNR2ygjaRV GUy >5.ߠ{skS>GCVq[gr)[t'1O>59<-m{mNu?&{Lf>C/7cH~'u<ў/N[K7RQDKJy" 3#[s:+KŜ/mE,USZ6\[no DF\?oΰjُn;7Q=FYDR\3sxP٣- rі{pINܸݕJMg"Љɐiq/rX^]JIH_$*ză.K}=T$笑cC$SGvww5o12<'~V?y] TOQ&MwBmRg)oR~yl8=!H9͗or%| uY?xfgeӹx yɋs>53g.uF/8 M-_8Wf^cw3n<_T4u $ȉo./-vyjzfɋT& e?u?1yӮp,5z͞xS4F[E[۰4?znJhGQVEGGMlr/˶ q[ʯ5h)fǾjo>Xq#4u(5Dw*c&H&bͼ?9mY'Ïs:jEmWW7nol#mSɉjHKYB#*:J`s;/GV풓2lJweW#_~dS>TFOf̵L`;Z^c'ﹺyL!>s̐qWŪ;nד1J~lSL_gEQ*/hh*lOR?h듹f4?K(呯=^|酗2?ː}*Yz+?YײEUY}= v5/iW)~Fݧ.cI~rp[=NH.Ujwd{+Ӣ(m/j4ǤRSsՇ{S-n5cJleyJ9UoozmN}q"'/ԏ:/5ɯeOq]y|sDOcK)?&}޸~:뀥ɮ>DR䬁O=z;K)?q:|\#%\wΐKl*֙ jwϒoM^"<]f T_X轣|%Ŝ>3Op҉LЖ4x;3u5}IwQW.S]=*(8uns<Դ)M3o);ouJJs.')xvR陯cE~͑ ,ֹuKo%Z^|;OkBғOJ}UB֕VȮ/߸?}-~gN~n?[ݱN^ʈ%Z*s+zw7*)lisOñLMMyFJ}mո%I5k!a%hzl}BǶwfvIѮ)}":܎qoP3lxo.Y:sl3V5Ô^o[Gm}q kz~d4@JP MRW/$K@,pthiRfu[y[fjT. ꗑE{P5Iז^^"%sr\_[^|]R_~m+0rFĵOf KARϛwkDw $['j_] vvTUв<י6pE'9C֛%W{]d$/pzʩuUQzM[E.xh O߸,oڐhjǯ̹ r/1aʳJpG/^g8%~rdU? yltl1>Yȧ/=MgM 9Vae1awΑ}ӫȻFT ub[>ҧwr}t1WW]+J'Y (E(py_zD{^_gg8.m/H*|cL+b5P:z|!Ƽ&sNO 6!Tu"sj3W܈xk q%ȡl㭖^Q(WjvQMuo]k4:-Z7)Zo2cMz!#4ZXeWOօ8ʎ$: Oߧ>^:PK,6rlč?5(G>5B}]'@N&㦭u'gM :̓OqQ>y,,V@B! >;iV{F77_ӆ&_VVvy̧5cVhj{> 09t~\%uVgCTƛYkϑN%qfoԛeç]u&bܗL q;'+*$oS;aW?E9FJl1:۫oę3- |rfYQfP^< T>14!y/YK R)u;Vg_9sd`(Z1kugYYbkEVnNARY|'>{bˏx\Y_}ϝ$vcsÝzOnX"L>znm( rSznf;vI(%=!a7Tf:-G#Y>1M7.{u 37 =Ov\#zd?]-dHTd]jY=v/GGGdШ6R+eǫNL|eӞwş\G?YgQ{ kjоu/믭[k;52bH"|\OY.Vm"n+%;boQ;$3k:L(_oHv}mMHY=ڮ7utO:2Zvp8 bJ{Us/Eϑ޾/4Z9}YRr*WaMZ"|mȾubyH=g8I_Z#S:éSa^QU^*J5 }~oAqWz1n*gbTywzH㢫LݪQN_ 싎*ח,/-[I+JUiށLJľMgNzlx͏fxc-wmw|ݩ-l;5QmnCXJ-_m?&o7<+%tkSwkR{XO5ս&Ԛ#>0vB|vP_EEDw xM6|&^izkղ$BA{w}7@y-Gl":g҄Jb8@w$[l>;܊VZѥ,EYRK|ŚsJx}Cm(|.LA|SJ:u})HGrf>cM]]tt Zx:9J{\TƎ$ ѡyG(nE܊@%|( ov$ GS_(3DaE#[Ukk|1}% +j –(`>3+dEq+hPNq,]ȥg (>MV{f,0`Es[,erU/H4*tkEX,0E_\o]]fBQv"-H4P%ʒTB>;;ϗjV_t^4"H7 Q'm!Q] [+o|a%"8#f@Ɋf0.% A좹e_4R@Cp.-E J7$hu{v^WDM4n:/ Aϸhe.—.QM$3{Ŷ\EH4yguH4yДP,EMtM|&}ŶE#DW}kOgg;rBڡ+Ui[х\zKHNV4! Q.,܊V*2v (&:Ew+Ic02$W hhV: TesP_od6rZ:2%Z۩eYH4*/ `EZ1Cmзug%{\y8M{ho7ZtID[UJ5 %^}ꋮ/]2Ziڛ6_">(M'hХm1MCm`kM| J6YY;/ ѕ$I4^D=˶\|M4}/׈p.^/D$<ɣ+X%z@Ƀ#2rBˊ&+/@ɃqwqQ Ah幋IehDk婮2dU$ AKG7κ< h4DMVReŠFH4yкJ(hdDyt1.9 ADod2kE.]"$+x895(I̷t[H<./gwhT.d/|auZX(LNM2HI[ }kXlT׎D'(Hi\aEqQeѽԣDs9yVp\8l`/4LDE6z{Ej(!k6-Ehvzj5]%d)_6VGt'& C`M- +ziZCDG6&Vn,+.(cJIVt,}]vl#r}]J1Hl` 7\$g +.fD/]`E6$EYKXQ0 ߋ5.2 lɦy\茜]%] Q-[Xst"Hl` ʦWȺkŸ(M/s_(Mͥ8ytIWiJXA`[6V/j + 6&ѵWײy)^WD6ގnֲۻ|A`[6]Y])U&+Eh& lvt*{_Q2R@ lemE GߑݠVT lf<.* +)hƴ`[|4q=hEDBʇi+}bz.T,\=2H+B&3G7+,*H4alDtdQA cF.&{QE&-{v݄v'WqF'*B6B2Gr3RUl&>a ͊/*m.9oOҥ#: LfCD6E&-+# ѶD֞MDi"`&M$06yts,E9 Qlz}kE2=G73;(wQgݱpUyG11-oOB%tW+90l[{p^$(ڳi+|V;@`kZ-aE%j[6ݳvYGD&QcB^_Ҋ Kb6zE'lzt )ͤ=.B.5ٶS?&i(3jY?lZMfVT-.,^?lE[8(YѝJn>Р،hrnUMfl6stтGtMfl6-|fz]nٌʚ.mW{rN=Dfl6V-0.6c+nݭjɩ]|%(b ͊<ћ0ٺ'M<\TV4 h?4nlc+Q*X=iEEM)Q H4^lz;Ԥ=.heX u /%nE: Lf]\(NʈnV+ s@clESb}ѭrb-*HA,d`2f_Za)ĸhH.h?4nlVa e.YNUƎfř؊?lz?F}[9.Z10lVa iv 8[q-4_.Ɲ؊?l}Ѭѭ9]s6 ?la. 5- lh|*N]d6cs[8VtWbC6z)yvѿ̰}5n36O]܊N>iʠG7YT&?7QrI{.t&qQ-L3؊?ld7pv$š]ij.Bs@=[qMh2w%D{c3[H+E3/ fl6KEKs@=[q-<2~َ+"1XdP|CXM{VuE6zl. C^4$l>B&iɫ!.2S= ]a:.QXXfƋ-Oe]:)$ 6la&ƋMZf>;;7;eV5HsB&3NE23fD۲fDW'ǂM"9l]FkND֞-`E;$ l|/* y\s@=[qMo+25nL~n`aMTl@fD57YT&?70&/bE7YT&?7Ņ%;mQ a"be4+.VF[|%77rVG&^3Hlq`"*ó{VF+WaW."ze('u[AJo[,AEc~ ;n0r>Š-Vl Q%(2uS/KPba ʙ5> 5zX[?H/U~%lu;j{̔DbvA];P رD b\ݪ=(15$z㢝?}YhA#[W!l}!|FdFZź g5$VWҡ[QMfA|v|&}=.z>]0( ֞O$niR_A`[?HtUΥlf׋ [AK,IUJ+Z.䳳 [!K/{6%vy$ 8C6\fzQDt7y XQŅ/;_l]٪d\v[H$4f_z|QMn<63ѓDolooW{Y1E*Q';ɯlc33Jf.x]SD-lfIUrV+ -lfJtmuMF]* yHlfD^Yg׋vDw>d $ )ѕ9-Ed(LRGWVVZyXQ#}uMK̿t) Q#]#r\tGtFt#]IT&`-iVvtW2/JV4_u\TDM>E0}Q]59GtaE֯lfJTEE PZ}[Kl.𠋮&N%lŠf4jn[Fge.ۥ&N%lK"wutmQ И^t-&lzW?04:.*"\+-lzW?03ȣ|sae4Ņh[A^_t3h_'ؒu.Esћ:<=GW~R D_]"2N AŅ?$.Ґ D7^(.gr}rlK|I;Nl iV`ӛʤ,Jt}Ckѝˊuecmo Zq~&-t JwL%&?>V4b[QEZv6H6H5E3zt4+F6H6H5DtyH"̒ Ƃ e^st|/z`@OuZae`2$|\>b[|Ѯź g320-J:R]ȥg’5~hllaO}]r+[٪"Ł/3.] s\[^9f[H4+Y]XQň$J]Բ,$ DaEc6ޝ]t]6~`f6zKHlqcӻ7rΗ.WF(THiElژAM`K.EY>[j%fօKNq_'ؒǦ͊ste6|4bAaőMߟE;2%.$ 锨X#ɺ{ m +L`'ٷ5qMHle&Օ\вEt!QőM_}5fMHleZ+QG.&Amax l:}Q YQz.CV^L7zfr5)zZ:|vò[XX6o!Z6VRe዆bE#5l񕁱lzBl}Q{}(CGlwMytg\[c Q譕\x(Eu]M#pQl`ǦnY lBX_tW-bG ؂i əŠn! 6a KXQMq|֒hVl`Ħ5¢E@- $:EɊE[bХ'^N_xh+f"Qy0>ɣ;Yv9ݏv[Ey.K<; V4%^鮣 ^4_Z_T Q ګtÊ XWײy)^W@/|ݬeEw˛%W1Jteu Tyd$ ѕ}e4fgl##1Vߋr+)(")b(st/:_LϹV Q VZ*OV"^!-md& BfVeg.?vEh7#_}$^JFd7d'CZz:XZ (GvSPUZ7eoS)jZ>խR{qawg=C~ ͽ[WެZVOyٚ>7=&WB)2x"մ޶TgwE"|YmJlG߷]SU c,NE~\*3mR)o@8_:v`_l`fnuߞ WafE}Qh _-+kxn[~EQsn^*](oy1VMU'n| J߅wtuWi|&k/SC/^_+-v[ѻᄏ jO, endstream endobj 1003 0 obj << /Filter[/FlateDecode] /Length 1051 >> stream xڭVK6 W=N Pl=xl%#R"xͶbSE~|Ky _g؉e)/Kd{Z떽g?nEJ^fl]%ۼ3uv<؈wڼ̗3y~aCT KJk 7^g &ʜ%g9&}t`c8׃OA ʹV"T&JB-gPQ4NCUOڃH}&ms!:':5ѡ&OeQcvDgܞ1BՇNT wK HkZ',zBQ5`G1 m<:UWEPe_*@_zvg@Nl2 hC8=ǎB}E~vSBXC$зju#YGą,һ?H.staSTRװ'ґ0E$̘uXE!dz9XT(nG""$/5|[+퉁 ok9plttQbk laO,0.|} VXbwQl\ N·]Fp ϗP5 B! `#yn` Yp/ )Jq(K(|dk.n';2[G7hh#PVZWʫQ2X,P7z4[jaJ4 &^KݿG`0[-e0 A@<|K'}'ϓ.p1Y@WמSwؤ݄M;qrmdX / Nymi\e.hh'Ǘ¬ Ʌwvu6!rx[Rx\c\V&z'*?FSPD.h۷-qhG}|Rq endstream endobj 1004 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F16 138 0 R /F6 35 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 1005 0 obj << /Im25 1001 0 R >> endobj 1000 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1004 0 R /XObject 1005 0 R >> endobj 1008 0 obj << /Filter[/FlateDecode] /Length 2396 >> stream xYKW*P) &>YN=U>dR)I)Rʓ_n4F&\FwZ2͟IWURIb:DzO_|JVf_Ι(WOkbI'utJ) $zfY}}N U=}ŷ'`?Ґ{ә0[*z>t9W#ƭqw{֐Ck/"ҍstRegZ-w=׏J>+Q?[SޯVӪY˂SP[}6!=OePy%ktU3,,sLq\Q2ɽ[s'Mddyrqcƨ}uMqIټNr项3-}`|ܤw YgƀVu#{vo9/; C +Y50 vn,|Ψ:s>#ԥk:oWA[B,ɖZB1EcٹۑCDwְܝaF)]6vQl\fEҮzŒ}Kcd=$xֵ ivh( C'M^2,ɜ"EyH4 Y@,r~PlTފ+Y.$TBLbBmgn> Fd =E(K9˳P$/b`q5G?^$Gkr0[2DZe: -FT=W=g/k0}\G=9zZH_*y&0}qn=PzAag1ˋ3>(ч/b6Ցۯ}6XtKvK~@Tv{j~۰/!0L`- ڴ&7csl)=_=i:19`,f{TXGf5U+{&_koohe80 Ѽa!ѩ: !xvnD`3Û_LcXv8=Rߜ!q+ _xrE}+cVn]I8Za;X^>S@l5р g:VCAl-5B(oπૌvSK^T9 ;<+lSJbFN^=v ΰ Ish͈h[]Ln~+Θ ĀQnb .l!=g0E=qk-"_w3T@rA@8_%GƐlŞx%gz]&Zؖ5}<Ғ+c7$+imuzRpZp6pܰ >D.vc:{˰ S>mg&*totJ'|)\Rm+^TmW 'zk&8ܬ/_+WvtRwx?T=m;OSd)[1'H9NCAu%7(c}<2)(lhť2Q)ȺL2w^S$/8 6@4xhI0,ةmoSaWՇ1UwZݓZ|:6lt{0;W'FXJt^4!D2|措04!dsh5H\튮#I>6< ~)m (:)~|X +u# wwh~ǧ|1()N)69COFo$-{_&wyܨ$w-B8w<*όzIw' $d Q0;F/dh$ r} ?>jyMIma)GmNYJX}7Ŋ endstream endobj 1009 0 obj << /F7 38 0 R /F6 35 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F16 138 0 R /F8 41 0 R /F15 112 0 R >> endobj 1007 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1009 0 R >> endobj 1012 0 obj << /Filter[/FlateDecode] /Length 2873 >> stream x]۶BdXBN֝N|OtL(|w DRO"~AП'^<,T6b-cq!#ɤrņmzs&6뿼UTy/cY|~Hܚ(NY/B='Wn|m&d<ҳn4-u`tc6e]q̇'Zid\Ѡn4OU-ױ-~`_Y7O%l%: ɻSSI.'hIp:.E..&,|[W 4M +`mИm3iq4@d$# +nb_1P!2!"AIu FHfO yu>bb"[E-ޚ8>I2DIIn?88bm;+Ȋ6zH.*Bqi#({X}m8GB 5}F+BT9~B#zS?%l; v_ \`z\0dCpz3/U[_] /H*&| ڟ!tLc.XvvuNjG䀻eB3BZD szXsAh@Ed)!"gWcV"9+S42lk  4#!% D,btfΊ}ё14u.s{tL)k7A I=lu#>_m䫻/& p7 w{`"|'YEv[+>,)YE*ƸU{jtJy EnA` q֎R #mi%-ojF׈җSЬh SH?Z1NJvL95W I$Hen!҉!,Rï/5_iɴ+Z էMDE3=2$ &{XlV;9CjgId81x tl"EiFh ' ~k>  /K+Lmm7ќֵ8$}i UU_õddd,ed[bFI^R B9_?i, S8!DaOpt1ߞ{4"K~PbQ%q*~IsO,"J%8r1n\R2Ed^P]RϏĻ/  c3a|.e D(5dQ0 8PɭA Nqp'jvJ~ ge}n-!(<sKn0,WsIHKhģb? L': 3@W/Ĵ>!s$jrI)v*E4Wn˜ hAr)Ɣu M"뾢]ἔšh{*.?mq,LGv! `]oPۀt(R$9-h%5}z@5 }B5FDÕCoq"Q!2y8U䈞T xw } !&r.QjĽj6Q_+q+^NnnZg95wF,U>e f8zD02 vd|ЗCTG6^ h b):FhMsE !"u"EX[LK_rZG5b^g˶[qL۸3rHl릢,Q lR-L6<>X2ҊTb+M6o^(eܲlq1ҊqjT~ */'kr1Ws YHux"I;훐@~z׳\pVۺlG-X//\~h?L"H&.վ8\(}{v[奀gope7n"i<&> t5# e*۰Ԫlq ~VgtWkg)]=~qҿ?,4yjSsXYv^\ G32y35q|B]k4 7,ٸJ]"RR&Rx[g82aur3JY~KV[[ }{:|ק6_W?I endstream endobj 1013 0 obj << /F10 53 0 R /F7 38 0 R /F6 35 0 R /F8 41 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R >> endobj 1011 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1013 0 R >> endobj 1016 0 obj << /Filter[/FlateDecode] /Length 2593 >> stream xZK۸W( `A[fSI*-Jq4kRǓ_nG7n|HI. g /V"#)[ "[c񇛷?EA 锰bq?yƒ?>S.WDH-WRqWS}hnH U%۪Z=09l2vuw릺7j;4#au[M0/ۧ%S ,Urgfm['3FTMl ej+3d'^;t,.)fu3X_%XAxdiWia,7,q#Ù(OgƾST 7Vk`x ~yL+x %Eu zheLkdsnZI6+68T?f@'M Y@$BzqU,{ %]U]6z4;|06h>][WLOtUn;ͨ"FF i&S( ?J&T`p0tJx;CDz[SB3CEuX97#rE` ۋ*DUBFgEYJT6R-+)r@:xW(3}.bO(twIq[q3لa!^//%i1ܵ9磆35КY$r1m%wK3t-gLj~{)]#/wϡ cU,9gr,k؜"T5A[e,Kv׆ܡC$,)0+̤kKNS8" B|.#('jQ&j7Z27ȘEwXiNX12;lv_i㵡p?іmiݘ"!@,԰8! a j z? yAW@hϾ)H`8(o^yE7 6 9dB]N"*[EFcPkH <,-"OƄ"8Lk/ zb"G)=ʱ(ᴆ-f1SNև2ưT$<F Og*$p91'O.c_aB+q0l,,ނ]zb˴-?^%/~Q<$k?K~H- ^F#  i♰.$"l7UΝ/0+O 4Hc%/Og& ,&j8vA皋0}Iee,at,TU\>yU[68a1a Fd$KOZᇘ81dNǬBjVxMC>W7ZNv.B|@\{!s=X:~hˏ/ Pa9Ž-=뺞WyjW}Z*g3ןƧy1paۡiB5Oܧt2hu9}duDVw0* oh>=TEʕnG"@QLazx4R-/w..xYxh9QnXasEY}6,\]2K"Yf8Gp RDǶgluUH^be(VHyQɧ l1(,.9AF#@o83{6Y6cE1>^Wrg /ĠߩY3M5ϫ-M'=~0?mVZ/ X+ ?.! '(x}\ .]ܚ;9rl}P x2`^b`.>TX^H|]ee/3H{&=07ǓIJ4ؙ .4y?6}3ds-{aH vBtCXIp>@[wQ$(񁙥N^>.󂘅5NX :_WfcF/;){30."=,kDHƸxHt0'|7?E0#B2ط}TaZk/*Vru1}%]`\vkgUrRIiq8ܟ7 endstream endobj 1017 0 obj << /F7 38 0 R /F8 41 0 R /F21 215 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F16 138 0 R >> endobj 1015 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1017 0 R >> endobj 1020 0 obj << /Filter[/FlateDecode] /Length 2894 >> stream xZݏ۸_C_Ś?DIpWrh ! Z/֝-/$9-w3hKvnS\!pGY"dv???O:{Dz0Hl;3fo^ɬݬRbvVkU*mśߣ\,e.q~`B!uuPZw]Ⳝ Mtk:-fZY z|Gkuy:Mչj_/jWӭVR p׏7 Y5:-l*mbD*36&TE"3(QZ玵DgI)ZBk=o\oPPϦjV:Ĵ(?K\Z <Ґ\_$].P(0'G*P8 "5Mj'Iy!oC^)aG؆A˚ EfѓGA5JARP\A` ?zGuw68d<=tN`3.> 3Z=_;NʣU2L\qen!"dxSD%e m0~Va}f<Kikp[ömmb9-؃ @5X37 >._t7fu%> ЉhwTz` 8: _rCϧozk= JᓙY㋩&leEHyM1RDe$n(hJ01TLe(ȊǙY|e!8D)rH֕MK|*a!K.yv :x޳AFbCycrarj9c_xOtWRa4壈mGq{pTobcn[矈sƏ:,鼏y8PfQM\XdϸBJ$ ?I{BpK*XrH|t~{G_#a2ĠkniD&ۍS;ys]!kfzN!pDLP!&Il_b,RQBA"Q! BBV5#Z"o;g4v$i*7#T5wv{S&0qH#ߞQӅF(9=E7V*5҉N|o3uo @g߶/o0grEToտv;N5~" gXr" eiW ]g Ux;Lv3ƷgZco`@}' |K-):GHF^df61T 9 5H`:>gRu@-^2^05l\Bd:/ڟ[A oN\gkqcM;3TAD%ƀ-eBxQw* IćCH߰JeuTDe( H؉d5Ř՜,Qӯ/Q͉U]^"1=-cyG%ؓ5g"OQ.R JV&i2M1H\OyV${&ϊfڳ%HvQ R9"GHј2=MXq1?nzґ&\/ŞKM\dJ’f/uLaD'jX?">P7*Ž"N/*X>C~"z1(*o`S1'xM~xH ٠qځDK;zUҪGa%[ c! ;> endstream endobj 1021 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F21 215 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 1019 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1021 0 R >> endobj 1024 0 obj << /Filter[/FlateDecode] /Length 3138 >> stream x˒PU.=_a;sT#Qk)Rcg&_nt(xݔ+`h E+dJcQ$"Vh#oo}*Dt)TzLwG{nZG)6&zޤi?jkm?WHHp_,`ARD*ɺ?Rhp84`x*.vx}>*u&4l;l˺ 0TڤPȶR!DhvC8Fc d4}Y.$HO*E.xf -1=GA93}(ݡk*tGa:9Π[PY#b!')E-HTdc;+gLUOGۗ;CE i\_jNv(yqK.j"ju?6ہh/[3OU=2i:'xcb2AqJX/,(|-gѾϷcE~gcSqvd?TMӲ| K9Pg?@xrs|w.ӓ|\}~WC7nj/uauN4ڎQe7lgf$ E؁s:ɢ;;ES70Цpޙ:B]ĨqaNC/h[݀$D+mMsUW@#1cG#s :xzr@mSxz82Bߛ7cxa4@{9&4Imnz/k΅B8[j\l<; 0B`=&64;0P6cMy]M6zaO+l0Ykb)cw2g4KogJ UO+L1qbHHut3۷|s]/`%4ld a|"8yEtlM.e M$D2@_ΣKȁe@gg_Щ#<MF"bm[wݽ呼# 9iF;Vw ƙ7> ;=X!QBP8:K ޝesN4XMnȉ&dIۉc8wCEAY²Z )՜ԐEYZthjsEkYi'fM)a8hZۏ> Iv0dm7ة,1ܺ(9i;hu9ngyL%ԔcX}ov䭃!!'7th|A3|'FYnBu*!y d.cIeDIK0vM|_yaWm4?vUZ Il>lY{tv)dg!PJͮ'HPKCMQ\$k8ҕg賜I%>a]ʙV{[.aFe=Ԙ.ΐ0Y&aBr3L NU#`U);<'rfsF/G\;\;%Ô\zGۥg#nLivaLEH7%=YYb((wL<`EPL%8 TT"g*m]F ,45Way|i^rKFz i&Q2`ܰ!_t"|Hϯi .8K.!ks1x6B߮72"FfA]/PG| 2Pi袂SpU.ltɚHV&Ԩps07w^GeYd~pW-^]T72-c N+)b¾:\Q2E5ۖZ/&Q'h2T/-ik9Wy"R`,AM`toJ-PĊp>N_[?K$yUL?UėӓR:E+YZckWo|Xa+RˑY:xTĽy(UP* ԅuW l; %]Cq$G^<(8g &2Rhv]iI!ߕY?XJ4(&cV/!X%]_s4sgodٻ~Y< [V׸(J,2(# mOP|{~6pgLQ8Vҥ">KuFp}ZGSNuuP]8\Q52"̠1@b (JL|:>_&aF S'٬-䟦6W_AO8 ?|I g [v|jO7:E!;^=CN6Q6=QqZ{d̒~^𑆹`%I-P OoA4ÿ|\/FsY1ӡb:3U:r{PilzqPB][㾶KHr$nZ ]v0z&vK>2 r ނø,T x oS#qr 0A ,V+ٓw(˹^JV@u2FyGlxhVz3'dfun դk _H_ Ր>_?grL> endstream endobj 1025 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 1023 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1025 0 R >> endobj 1028 0 obj << /Length 2224 /Filter/FlateDecode /Name/Im26 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 1029 0 R >> >> >> stream x6~ /'e;@3m: t6}x('AiJhIմmc>?-rm,άbWj믋][bZ˚A'bg%\签.Wȋ':>}0+I]m5BcFhݔ}HԻ[ˎ-kG`Y;L@UX>YG탯I}}T TS7JsBmZLKmM1w|@-[ݎ<Ff8d^8ܝDGtd&r'>o䝜J!ֺC=ocm>l D-mj02$9D|&”;\ΛTw'$?o䭜*ޯΣxj_s"+ZQʒc"LH"WnQNu#KSOħrp/PbF/D2r)̩!II˩FɩH!8 ]DMo@$jGrukďP,^4 ʩp|80(#0"i6z* >K R ^$re-.PJ !Zè֎%rfkrj]z&>:܋&I~B$c=uII[O6~\I2r9ʂo+8BIzZyRkN1?Srj\ut:ԋ& t\NuH`PbF`z6rSlڙ% bq359ϔ+)TmF KVcI1јzri~w_"`$zN}a&%e8Jz(sab CZ㩫r߹>0__?()*(juJPY5)J,hSH (HҬI~^AcRȲjm{E$N b+e1I{&%q/aP)QүF/a+hAU(XqPqA')Rc%) 81{qy"X ÄELc2f B:GpM%0Kht'P=U5&Ij$3IDhDS)hL <|jtRdJXIᆙ0b NARBM!5xV?ӻ NŹ͏QƙTu˦UjXۏ ox_ZNË.B̫JQ[ ͖b켦]z">OG{pk9KeySIGi> endobj 1030 0 obj << /Filter[/FlateDecode] /Length 1032 >> stream xM4 qs(QZ=P!8E^ =--q25bn\,j1`;01ĬN\.${dZiP޳O=渳lKǖ~>Mu8U Y%|_GDh% 5hZ+XقJnJ~dw??J>W@[^& {_j]df}s:[L*{\ Nuif'jnN#ii%R_Jn57g^0q_F 8EPA`X&QaOɽq\C_wNT".2kc6zGɔN'vd[W9˕踁Y!"*lKlU6FrPsq+mfSLÑ8X0PhDEP1֟Dqڙh:n+Ё"'̽or~!PTD R%-Dh:^bcqpBE^}aPtxK*uGT&*߾> endobj 1032 0 obj << /Im26 1028 0 R >> endobj 1027 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1031 0 R /XObject 1032 0 R >> endobj 1035 0 obj << /Filter[/FlateDecode] /Length 3238 >> stream x[[~ϯp*/xw@6٪͞T5SLj &0tK3Vw%3l;ӏ?C3哈B3*g~|9E$RM]͞wb`"ia{ |~x}U\E~/>SUϠB"a2؞܌$V:U=į;I_bf iA)U"Wxj#[i.κaBLp0#DDÜK @nmW\^en$^:ͷXT^YSj᱌ʼ[24DVC_|m{%5^9sL>&̋ =_—/s gĶ@NY jwi*pXmU5I#*3(VeDi_`T^5d L䑘^>C` i~,|/(S Q5w>N4Si>$_ T7Iɼ'Cgٛ!1.w4c@Ӵ>E44O4TJuqih4H).}SbzM3NW{;:^efG(uZp%jlRwv^"^'>i)`/VvH*‚ vf.q>,^f`f}Y_~g=?S,)kOVIX9})`RXkA îf[`l O?fgؙ_ }o>e{c+":$ǯ=aG^ÀАSF| f. rw`w vg3"b >Ƃ<+ݢ]Ҷ2aHpl?cjCg=`t<}=&'O₵>ž.`Ů:\+\\0!8L(F1Pa\! U捲Z8S.Zq\6sr:HҼ,?!dӦZt#l {/l5X%tEGպx^mtWdpPQBIw>{CjUo|%cƚ/fYf:au{1RWq}'U(ѪmjvdY&>mex0`BΛ2>"TvPðg{ :  h}{2h}m/ZԜ *|H:Šs{`n.{Is{`nvaނK\D܊ous۳ )նڶz.^ KB-+&Ńܩ~4"~bˋ3yCrD)H8T9L>;3$m=-XvV+ :btD" 4#GAEs*ibCArB:| ֤wq*,]0Kcأ!btW\VO-@NqhFo:8ȶv`}ME?u5ʯ*a*}؈ rm\BG\JhmprCVŨ5 F#m-.QoOxGsWcMp!rBSf=0A@'=, I SP9C%\[t8$yQ.JMEx@ LCeCY(uX#zwŅGl3{o9QpGnvvVwA._XB־KDTpI"꜈ZBDTS'= Db>;@jj#W7s" cJm$ u\$S{ !(pL0WEBdp3P8MJC} BuDi( B< b,U){3\XCpj 颀saH_{ڇ1YU2ԡsq`_n݊:nnvv{PRؔ^z@*[܊`k`>wqU\QI="470*;Ic0aa^6X";X*L]yp)Qi'RS s$FEt؜ұH,);>W/1i9MS:v8tE(7C7 G+m_^k/oh/S=Qs4:۽w@rGQ ;83u3r^ɭ8&s!Y?ȭO&ei}k9f= GuVO'ڼD!xNBξTɠX]雦kۀuh*a,=T^T&♓ב鱌D> endobj 1034 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1036 0 R >> endobj 1039 0 obj << /Filter[/FlateDecode] /Length 2171 >> stream xZݓ40%Dl}0707$sH_3,/ZVy #< cq#΢-N>/'eb[`aH1z2=S[M;^oNHT!zI{cD+gr&zۼ)p<ӏ}]ogn.4iR?[4k ~'BYFSķօ?80U@SzGaAg: vՓĴkMm״Z=mZ g+&8#*y%4T5x!+GuAF=a]\gl@ s4zY3pnda fpĢd@Ha ? `;HG ҁړ)240_`EyRy2AdK&0%+i\K2Քԅ !ہ긵k8cDջ!'Gu\Dc|cRH/rD'zV=>I5cvc+/";,/I K^*ـ1G7vE%dۃ 'I߆ | =ț߄tkwuۙ藘 я) FmABgR0D$yA$eŀG-=>2637\1ΌOL]AyW҂ Ԫ1c6s|B1,Wu$*bTA!:.9b" ,PL)]/EQ4Po+3DXe V鳛WŁwzǰ3I|q%JTQqIUƄ Z˵i@ڐv3[5SM#7λSi+ӜxyT,l!lJ* xTϟ$/M*xtjӼ }WFc@x/ͺ[>ы$1O㢔!HB$MYoiTVQPx̣Zf'Eb,Sc*s?uy9|{b9*ysObH'0X~ z/HZc#1D7bQE%:+?1M2~{V#::NlAu=6v*oAB endstream endobj 1040 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F6 35 0 R >> endobj 1038 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1040 0 R >> endobj 1043 0 obj << /Filter[/FlateDecode] /Length 2398 >> stream xZQs~Pp$@5L3Ls[%3HIN|v -y">,.B_\<,TRXE|qXD"b.|w/RvO uQ"볕b-ߟRo:kl}w\, `R-BcftS/ݾ6A4~ F87/3o%NۧVyՈ?:ZMVa0x^!KUt |_gwźaMb."p0LEU0 c"=ϹBӮW Krc׹L)_=Wa2 yZS )=4p_(,P0zz(6Q\-PxˇՇ3SOy[3Mc16Z+4 Oz6Zo>G=-(&B.]w8J-*Ck1R2}?e{u45Y=]rSA[UnV_ӵ('ea$_}5i֧3L:GZuQ(9C~>Ed<^VZ?e%)}̿hU16Ui$7z{k?3ٓyN4yZbHBL<ԅyfK[]~#XG S 4}ZVs`8Psʮst|u65M?$36|X[^:LU1iAlfvfY|nntֿ›Bgb"G۝SZ}>Nw叾 G)@㡢dM"+0B |Ɱ1{,\EzXVj(s8hIpX^-=M>aSuWJk]ӎCUC. MYif2))ʱX"׼N }y%k^ L5H y-VC,+Xy=(.`l)X@SyzrSPVjUS;LO 7x)˕l5e)el7ѻ}Y׹u~VoSNl/uBm tZ>62õ`G]c\^C% /7$yЂ6#-(/v8gI7*!Dwsuuܔ?}COh<tn"9s;B% 7$B>aC}nfZ[5" ~+5]䇻FCy%"5 yr4[WAc QĔrJ5(,BqbAï#,Gjc>8Qgp ;żIY&'A9īFOq^Z$m}bj<L6rPٓez[m3BEʑN)!= 5ΔTcE H ]y"=LDJCJ%ȍFt*u0u!Rg#}d~- ެ# ֑@l#GFXbk1#5Xϊ/!ύ/ܖ_=>VxF#v_zn[,TC(Gj$ $5&1n,I6-Q ՘bo&~AL]p஦uuZ4%5.fLMtx15-0)DQAáocqC6$3$d_>p$M&I{Z$pf}pߵn1XE-O'T=}v(bgEIS)YP6jb )ddȏ?zvPj4x%ĻI>^\x- ?[QV9rP)2<;L !n@]V\JBh,]64.X]YC7d O+`c&,ܔ.R#xXSJ2nf5SFxjUigܴo[мS`-AIBhT.zw#I"S.$ʴ.#e95Gev]٘n?Rg$D&_+a9fYeOx[.dN~VHU4$Njˣku84毲LHbO?-9wiE"ahJݗP^Յ#2R?h ^47>u?e?u7>-R\@SeID#opj[v}wZz" ʊ uFHI)4G#bOOq,msirIwy^Տ?w!p 9S|41H^ve|{#Xp2t(=AdM妨NT}o5yPq3vq>@;]m_l2"[|U5ɑ`"jt?5&$:J=aߨyYgd*`+d! \"#tLXjsl8-^Rm8"yŒO̷m搪 %@'& 8HDJ NP4#3#yu Ohm4\܋ (h"GbZY4;8bnr~xyTltIp򨆓1X{[ &)Il9qBzZ 0O,y4˧H|6p8c6׫;gt{Ġ7C?Ks-fùʈZ<3pra ׆i hBνj`€n<5M@>+ᇼ1 +C=VpgzF{5)];XDt6R"ih>9sFUs8pϮY]VWИ؞klj# ^ı-ݢ8%oQ$i~Fz^ltOj~>3`-^`%_l2x)kr %Ȥ7~đS*:VϟLJ٪|YNzeWG#?ϦLXxMS(i Uott׿YvWJ;9qM))FNNm,\GJq~"fu).oO/ZV(=(Ķy* Ue}sh Op:ܔێh@әvT*ȇr"7Y~Qgivɾ&]Qjջiˆ[]D*q eq؉)&P̻4xhg3%Ζ'%a{}F@1]!HMec ")%ԵTˣ/8L]J"_˂N!^Fb90Ϭ 2xyTlsy>tWrs;mF*SLA0W75y$&4g|l;g ?4wBػ4q-S_g-)S%5&x.;GpC`" }2v[@FPQ&џ2UT/aQWf,MGGR5p.q] Z3U2gru1Lh,b$h>Ոomg.DTs".5^\PB](\Z r<4i,ٱ&}kZexٴM,n9-yibNJճOxI|^ěG5/ʁ/b~:Yh.re04EZ'QV)EmF/: v|Q6&&BxH϶,ࠢ&~:.Է+ZZ wکjz.삸d=QwڠnU "WhLiG_tw~]23MUBO㱿?~K endstream endobj 1048 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F6 35 0 R /F30 286 0 R >> endobj 1046 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1048 0 R >> endobj 1051 0 obj << /Filter[/FlateDecode] /Length 2779 >> stream xk{ݮz{% hnhmy--9|-3!)ws_,rHg$=ͮg=|iz,0O3_4,aI8霉dvy/1vZ,?bWiEŏ_|k 0.?Ұv &cY,xy 0?9 0`g20}b,Uv2baw<г 6N5*=lUF}@Cok2R.3a$q9G}}rM]B P'̋z:@Mh `&,iM^4Dqt6d iIxKEG\7*@xR47ܐ%Zc'n5z"Azxf$Ax(ˉggs<EE"*ĤĄ$ &#!$$iK $B* Sެ(tWgA`''bb =A@iʋ&v)Qm )E/`4>r=`d +t1IR* "ozv BG/&䷱ Γكd4B-&ovY&yLB;nPaq2"~L`l A*nwiI mܑ)[+Tƅi2klJdsKөb9&pSϋLM@zMUjJu`7/UYlk-^Zy9h˶pOи&_Y؟7۴V0Ow;.]h~K#`{iRҳi3(ekZyJLw.E]+UdP+Fq AMI}X[=Qӣ^uگ>ֺ7[h%i,vtC{hk,l]%zё$Nlh޻Ʈ@ɪt7GFMѨ$~'FL@d_ H;Qg QxRC mBP~)Bѧ bSBf#8W|ݏk;Go\=S_a٦ŵ0 }%IV%m>K\2դgJeR7o\}M"Y>B{ш_cLX$Q~9}ݻ ]gRa&ybeO_Q@*P5J's|v»Hk}ʊYHűȋC{IW9z AfSeRi,x#nX荱^l镞=Ҽ@G(CǪґFbSV{a%XfZURXN{ڒ$禎 ۓ1-J f4}Aߜ(uܫ4raԐMFmz0G91]z:6ˑLDǘHq@LN N +MpfH&#wխ^ 4n ,驹!jeHZ='pEڇ614iq<@fF)PoZ_KRTkUQ8M`ޛgg Ry(dc ?#_BJfX8}N-OK^9]R_h&OVw-W]jqUPĶonRD-B(5=ҝ5hu6`\+ )mש`U`CI%4 nI!~X@L7O 8&&DmI<g4hm% tݷ^Gn4ZٺLb2%4c';6990o 2!rn"o= +Wz]fK ˮ([u)nb`T%^e>fp#}By t+%QF,QP}P⺙!QLL:zc3'r7O@3o-rtBS) kH_2tn .$j 08;7cw/~k^/wߑeG84>sh~4/ꒃf]e'_##; ג\r4B)EH2ܟ7`>^:ggzAչcz^衦>[0쌕uDg& :[ q >>6+&^92m~mZS6yKMx><-,avsdS쩪nMsI ;(GK4e/AOۣO.O.Kp>{@BG"Ix2)uq}9x3;j\ M endstream endobj 1052 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F6 35 0 R /F8 41 0 R >> endobj 1050 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1052 0 R >> endobj 1055 0 obj << /Filter[/FlateDecode] /Length 2468 >> stream xڵY[~pч/!hE=@ eW9CIѤm6/4G3 ߐ$b?_.|)I\}ǯɢ$exܹ)a[_*Kyֿ|C iH(9¼<'\ 9ט{FMG=.h yiSX|0bR4$= #[S.FNqFJtn'a9wX;wXI^xwќ1TV-z_"YACEC\F;(}<d4ʒvrvɩ,|X \4'T5nsT>iPԀ<u=]?lN8Ǯ:MO.= _Цy.]9 j=q*tK<)] Bݝc@ ] (<l|V5- ^Q[~Ύ= P2”'^[ DEާ"pUf7|)I%VuxV 1~LUz"sZia# FYj[cqv*Su]/C{T{kWKAzn⌁#u}Pzsl2A2`j6COX`j@XeuyAGј͍ KAgJ B@v+-ǖfԖchF}w?{5~*gRV@<זAЗ*)NN^as ᙯujFftjxUQ7Xbu'4I)(G {lS1"I6K)brX+a;FR,_:4 @_c-5*5lSm/TCD&@sIwq`KJf'B V$=L͓m6o1I=LJiX,"4JX5hg L<+[l$NfPMN&M>NXLkE f$u 'bfiB _H yRΫԠ ėC^v&hH>()T `*hFۗot`''w{LƷ]ϱ*W ls#Ҵ[[c엕/q琼ge ..DJ\<)X/s'pBD=LW_ - ]v"lq^]JǏrFH(4W`" CmO"PR|K56O/` ܥJ{ޟ4׳,tBW*3)HH~|x|.{6;w&>?-/lg_U(,i9S[_[܋LIƷŖx_-΀! endstream endobj 1056 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F8 41 0 R /F6 35 0 R /F30 286 0 R >> endobj 1054 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1056 0 R >> endobj 1059 0 obj << /Filter[/FlateDecode] /Length 2645 >> stream xZo_!G 4i-Z5Ї88-NI>"uIw3YOr> Ir6.`acIIJ5iS~h_L($˾6z{1g }9wyW?:wRH蛑ns *X9 ^~ '?'\&JqiE" wA[BZO265"+&,Ҙ0% T~5[ ʫ/9`"^S `I)65#M85f4)i~%>@Eu<\2KKӄxŤsGnvZs r{mƩe0DPbn1Ơk(ߛ/VF"acD7k#9 4;㠳DaqU" GCH{Blvۍg׀M 9yur|N]E%W/y'.D!vRA~4ׄc.0@fJRPtEq<)$`fqIFeywCP[xTLi!1-m(:4uDvy7#m`1װ_}iIxJug(0}QuLp ş);!88"zr_Kóy!bٺn~94.qi#+(xAc0 i{Bx!1c/&Iy|9#{<)(XPңQIֺtjm>=Ydkƽ{; qxB?5mc08!=^X{Nܫ~?i_nhW0VyԔ/tE튶D yrψ\itЫĘGf۬i'/ -RGU. v>d'HK)Ipv5VP&*!W鶙^ibf.agrNOd&T`yTi{<̳St0EKhKGGetyz 9XO·_B]m]}ɹm38$!ȅAʝS:B{ QUCZg[bUm Z3Mn 7D܎$4h߮f6ټS޾:b-)&$lp=ӻ(=8 {Ti`)0hZm/vT)S &y Ȥ{Y1O=Q0Ջ_7;NqBT 峏./ e6?_@ʑ ¤"GڷpP0W(7n3\Go>Q] /&B%h"鉹#}`Ř;XamY۶p 59#AD9ҳ5WvF(On&9 m3ͧG Ǚޚ ʰU9o Uv*9 n]J%UW*O1$:7~81"3Ig'7D{ endstream endobj 1060 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F6 35 0 R /F8 41 0 R /F10 53 0 R >> endobj 1058 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1060 0 R >> endobj 1063 0 obj << /Filter[/FlateDecode] /Length 2375 >> stream xZK6p%JI:n,v- ԶVƖ<,o!$Ҳg{fbX/q$Y#fɷ>!w\7e7~aqX֜9ܿS˲^lYmVk)O[o˓ xbYΆ8[6; wzO@rcGACyLŞ vR$D " 0 (#H ΘI"1/)% xAeOF)UkD"[ԓ裟$HTMJv6]=ke5i +&ؙG43 -:±LQ-9.-ڰID9$LԜG %R78fS@e ^N L-FDjuԼcNuRzO:*}l n0:# HLMQ5NdE$ؽYL[ӧ5kN4$ Tɀ:kL vcr9NbJ`'@cfטFM}w:īMsaMwo疻|bF+vǤ#w/벍!peJpcO.UߛFF*""&WO=Hncڝn"ž%5XFcV!>Ҵ%|mG; {ɦo>Gk{NvBHC29ӥvq՘dpk6@G8>#9I "@w0n@ƛ5&PCh׼_r@r/2_.JjwP/TYJvk۞҄bj53PCvf6bW|C73E+_ O2yȚp̋X`F*Az-`Ҝp^)@o.6a-<5&]h[> ZLhD|8ZKO>|oɶa~o#&MMr$_smocU &{^OGqɍm6Mk|]vh{QKu̯_NCj轿?K'i]GjS2^]Q _]9Xy}W[3a䗛*P !MZf.sI"04">s$/|Q4O['_PL)un. _]3U`?̨쀁; Ա}6 2ClCbUC%Q&|!1Vk=@ҹ,x`aK"m" yupO1|uO UUe H8B ę_ endstream endobj 1064 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F30 286 0 R /F8 41 0 R >> endobj 1062 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1064 0 R >> endobj 1067 0 obj << /Filter[/FlateDecode] /Length 682 >> stream xmTr0(}s;3i\] Pd볋ENo=ɒ8I؉yL.J6gȋV_SwP6n+im)3yKWh 1UUY^8e&>9K8/4a ?˥WVi|R7(튬bND`55\笴#(-։ B|ى]ëzcBQSXajDY/0Πm!X^R.X&Odz\3:밝)ޫZDHO@&t|[{30(Cތ#k5ɘ7IP mP6,d5dOэWg'$ySpjwE?“eީ [1jkX )^DB"B+q`çĭgqS_&SśuieZElU~MJc_A`)8hG:cٵO~ yf#YaI͘p57~7qT9 3kk?1K_D{.:$-𧬬qlA+kWSV9m#!\O@hw-DbrA9}3EZI? #M}UMPD.Mz5rÛҺɵ^$_h endstream endobj 1068 0 obj << /F7 38 0 R /F13 66 0 R >> endobj 1066 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1068 0 R >> endobj 1071 0 obj << /Filter[/FlateDecode] /Length 148 >> stream x-0 Dwd6iVT@01dC P ,=5XF B@@XHm?U6:=}]\$ Fr)0Np/64~D>> HT'-+ +' endstream endobj 1072 0 obj << /F2 14 0 R /F7 38 0 R >> endobj 1070 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1072 0 R >> endobj 1075 0 obj << /Filter[/FlateDecode] /Length 20 >> stream xS030PHWS\  endstream endobj 1074 0 obj << /ProcSet[/PDF/Text/ImageC] >> endobj 1078 0 obj << /Filter[/FlateDecode] /Length 2400 >> stream xڍ˲8qWdOՉv8Vpab1gPl%׏ KbcKݭVOm8I6 ~_쓸my'٦<γ0o7l妊*OڼQZ/zf+qQlY;B尋vˢFښ6;0h <N׭t#xyLk]F="ϔ۱ r=kUYfqtdMӸ*Hxo>*. iF̊oO=PWCw57vcF3E]pcrիJV1jHoD7F7D,x/U=Ir_9ӽfI@0{QD ;fTN"N.ֵr zvFY̍:^[WZNi2?vD=޼~)Ҩπ`J8 ~-\@uPpB>Tqn(Nӯ6߃;wYX#"{Pͻ]YdqZlKho|5Gpjux]a,[ն,\^Z!D-!m, ٵՌj7:ı^׼gzjcgI΂HN <}h0 B\Hb+И0#9ٌr$Hkz㺵ƁNq jf<}Qgm?3QHCt8$ؽ,GGފ)`Ykka LL%i5q 6,OҞ;}NYt񣧋eH̚-SQ ei+c^Bm;`2Zrp|#t$r^_-_ ~4:Z> H2$31̖3->[I=eY"[|-5">x"$"7a(!ϓ >,V0 dpo:ͤ3UE%@0ʮ}N&  ,4gs; B`)H.:͗' HopYA$R*;\K2ء @0gm@eH@Yթ O[^yq4б.I0_i>EO~RD zAΎ.!s|\9 D͍''2rWyNֈ?0ó_ly,ЀjΊ1_j1i)$¨ŤUtiQp\bM2IR9OR`X.eWۥ{SưlBp{# 3h _Qѹ>Sꇝ~?nju$_-ɂȄ1%cO_ԓPl}x.B|"L`m ?s"j[3[soq@ N{(n~e j?0D*X1!J7K+a"q#㼆2RnLE?8?q%d}z´)؎!pI) $` X,!\S80'LwJp-8a Aբ!\3IJ 8uTP‚tv*5&T[,s'.d |)y`$: hv4)Ou Ŋ }~,1CQ]+݃cxϲ2r2Ri fhO >),0}D+hz!F=I>BByPKP*\f_`Z%9^w#X=]lë(ΰ"Pa铭٥= W漏^ ]ٱu5Lb 7Jf%`de1r 1vX>z `5Na%5R(&V׭֊ݑ76:mFyJp=hmLPNH',Xniv_+Z Ъ+Z)jFq+> ƹ>WEY/:RQ>| vWJw)H.IOC'˒L+wo>>)PYI*A)O7B^Oi SP 0)I3E$o7ȅ*7ʫ&䕇{ݐe,hшfhWKz%8Fۙ4\HlS*- ewېeRfjQHT͂`"Y_Z@l<> endobj 1077 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1079 0 R >> endobj 1082 0 obj << /Filter[/FlateDecode] /Length 1835 >> stream x}XK6 WVv-(P GIZfK3$K$%NGq~!NAGU,m n4X|8iirmDU.YviTi}{$li}ec7΋UFLÏ#8[=*44l~{æ wr݅۞J>eLLLva&Ӊv(WQolcG~fo/Hjk^Ezu4]+p]@6v~ô֜=ʣXYΌ-'/;ȶ93 : bBڿl\Obl=bFF3<“3דJ nɫm5͆8-[~>83b!f.l q~jI1 x~E$_?U_ QxeMtJ"LJW%AIAHŲ2$Yes'ò"n:+BωE0Om}2}j4G<{kfVgi<޴6KtZ++&yj.t~ :3ResCr>W 9:B)⒈X£ &e;E<; NІ9`k/sJ) UsUI#wR0|/qFOCǡɣm)μ ʐ-6PuaQe R[4O,Xx],O՞y^:&o;w܂掄9"$CÿO&HDETeeYF ^C1}3Côo)Wc1g@*Jn^ 勌CD1nh7.IƔa*B t>rBM  MV?%$oFLHdSD%q aW$);eޡ(_ *_h,DZVw|J5'mlµ:`KYK~?ռƙ endstream endobj 1083 0 obj << /F7 38 0 R /F8 41 0 R /F12 59 0 R /F17 145 0 R /F32 356 0 R >> endobj 1081 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1083 0 R >> endobj 1086 0 obj << /Filter[/FlateDecode] /Length 1833 >> stream xڍXK6 W=Z%?v]MOMGةlL}Iv8 %(JHf0DŦ(D6*El(P~Xd9rQ*Em,@Gx=]*Wnq)f@"磯A?M=L7dBN@\&Hi׶2~7Xȼ펴Z2ƣv g[ߍq̽tjGd'7Sô5kkzr/ QMH|/3؇3d8s~*x6CE\,iXvm?8PdI| )(T ~y h< E3$tY@T,},VbLُaXze9?; 1ڼ%ySc։y%Ο޵zi%dLIՠt;(Nd4/O# /FK|JUFж_[~HR_ ;L\2PouOߦn?c,=s*P|A`EiJر#.h'tڮ8MD\\ N6D7/x2o<"bRUJ0fW?4$%w|N bӾLRc 9"]{1,<}^Z ?CP1R&ϼǿv1ZwS/zU y&,l}alg߅=Hȷ3u)1 t(=#\\*f\R BgALBge9)O;"8-D<_N`ɭ70=.OG<K#<"`p3]#{cOgp i&V ᅊ n9TqGRWqqyc~ʓ 4)054!#<kOlʳiXa8µa^O I7$J P/.tOTTSb<+1!?mjW61ݝtn7IHҍ^Hd+iKS!_:fuylU.jy @.ই'Ngks s2]\ejK —6+ba'Ө80A˔ayvqX۩/NNĝPݯʧ _sʎÅ Q/(IL\':N~#N$iWpUW $1vHb| ?wU*ӌ!pmu}FZCfJU 8u{ #58EdTx s9@]7ѺFP 2Tx8ZD?!X&Fp+Ya8n:, [nBOox, endstream endobj 1087 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F10 53 0 R /F4 29 0 R >> endobj 1085 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1087 0 R >> endobj 1090 0 obj << /Filter[/FlateDecode] /Length 2112 >> stream xڕXKW*j oM9ĩZݲ>`(Ą<;@AM""7 Ao^7U.b+fܔE)J>՛VfK/dlwn~8bvWEVPfowUUe?t`mUn1/[do_,6J|S$~^Ua<ڈBW]@`yE:{ϋ'|F5x83y6>f*<3`}-*xqOB SwTѢ vRBVBjV:Y"5+7TB.8ٜug7ou@eܼ7i NeBAZT1YhYM&hCcQJ4%`T.XINc<{WI}u}q60ִ7VEYmDV( p]JlJQd@FBE2BJ [_ 0iN~/ >~/i d[!3#1fڀ[&MO4L#5Mz!F\5Ye7^8wRg}LC`rEc*y_mq6 !K(Zl0Nf% b d$ܕ3S2 &ˉ+&Þ"Z!/h/(T:k 緉-@swN*&t.(`cq ;Ü|\lM\/0hxi8\XV>Hqlh *W$y1!3I{c a5AmBึ!IDz >y-nTBJ99X\lGCQbÈ!{h6R"Jh{qL?n0yoM@)\!nv7Є:}`k]5<ߑ*C:@]gAc)dA3|r,cdafTk-28.$mSE+ڿαd_~e+ŸC U6nrW[wyX,ؑb4K8Q].u endstream endobj 1091 0 obj << /F7 38 0 R /F13 66 0 R /F6 35 0 R /F8 41 0 R /F10 53 0 R /F22 218 0 R >> endobj 1089 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1091 0 R >> endobj 1094 0 obj << /Filter[/FlateDecode] /Length 2105 >> stream xڽXMWC8U+ A6ͺڇC*;>PFBB2AyvݍIRjsEMC$cYydQ9DrJTL$bU<>O*y|94\3>;mSk~}zzY'W'x0 FdLs@#dG4ΘV7u"4gUl.@)%NXn0[B* 4@KW8J3lJ#|!u[7:⫘fۊA˜IzF Ydl8ryD-ʙR8\iɴHDC\ ż_p9g\xhw=|otY 5FRaO$` }sU2LTuDJy220+2caɑLsT #SaF*EIɰ =!  qZ0. u۶)܂GyG#Uu2QW+ϒ+!ÒGa=iMOPKL2{ tw0WIDhńeOhJˊ /<E2xBɷVG&jj`p 4 \eX%GD,-dXp>Vg.ujJmC~q͘W _ݭQ Wp$qɿ0j%ҟ1?рpq2הKo ȧ1 Ln[Jt}vЁ dxwÞꭥ5ak R J `3D_<SF.TE;9Ӓd-)MZ>|PJTv 2>h\l`n=ϦP8%;S6bFyNWp ̡篶×zqO7g R{ԯ@]lwȌeWTtY|qtL2H;>/8Sw S^L gYYtb{HEVmbO}/jCoS@wG_ a.7Ϋ>l]`cJr1X.oRB 4{R5%YY[> endobj 1093 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1095 0 R >> endobj 1098 0 obj << /Length 1016 /Filter/FlateDecode /Name/Im27 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R7 1099 0 R /R6 1100 0 R >> >> >> stream xWK5ԍ*.Hd BFfwA;d A{mwOIϮWW^;8c}>o:kl#FﺇΙCC0vّ%9u;10b+,.'rX5< 3#{zbA!K.{$ s>sZ%;/8m:][k4/I\NχzݓE@eFn[ǥPM"y4̵71Prm+t?OjI!H@ƑM>6+WV8okX.Y2]}$<3 An8;ur_ .D3vb1I{zɣGr}K$ôٻN߼P|+׻?UΓN_8kwg:m3ME}ԝnbo02UM),.X ,WPLbr?BD8V_Y~改QUbtRg,bc懝yE.j* endstream endobj 1100 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1099 0 obj << /Type/Font /Name/R7 /Subtype/Type1 /BaseFont/Symbol >> endobj 1101 0 obj << /Length 328 /Filter/FlateDecode /Name/Im28 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 1102 0 R >> >> >> stream xR=o0+nL\+u%R,86q%A]*ݻ{ wɅ|j,${gbE8TqUUE,xT,YJ?v) F9 UVOx5`j5 T8q"A=X BoqҎVf[ }c[Iw0Y4 ^ji\`V|D8n8LܳXp S9핏G <LJ (}.F?e@{A;-I7{=( endstream endobj 1102 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1103 0 obj << /Filter[/FlateDecode] /Length 424 >> stream xڕ=o04y-NS d%faȿ/%2H>qKgL& p$PGh@I=|b3P<.킡A3*Hֻ>RJbGEF1d6!}ВaN^3|,WDTy.OP8T >5h: "7#=N\M3ɻ\RSV#η5;C0&bAH@]Z ꈐdFǺWԌpӗWG6 J!)Yݦ endstream endobj 1104 0 obj << /F7 38 0 R /F6 35 0 R /F15 112 0 R >> endobj 1105 0 obj << /Im27 1098 0 R /Im28 1101 0 R >> endobj 1097 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1104 0 R /XObject 1105 0 R >> endobj 1108 0 obj << /Filter[/FlateDecode] /Length 1939 >> stream xڕXAw&W(֦ݾAI֒t; l9^ 1eYg(TU2e"i\,SϒU:yx xD<{z߷XMmxç"`*RIp"ԯC"g\:ԣR V^ɗVJ>j@x`-O?nU*u#tjP.=boLь<͹nh3qfJqLVS0KrPJ6 ת U2bRmMf|EJ''[.RC@Z6"n@RzGyame4ؖ-RRk}wFX> U<A/=NfY12k4-Ȱ:0F<_}m:jw>kB_yl˼se^K&`GŕbVhjN#E;8s2 \Xwِ`N(NMw$74P썵iYb8۟ `jL2Վe)F>zt*j5#88ȝc/V\^L>pGj~IZ5NBows={s. .T[TTg ÿL DJ@ m*Ή2&wa,;^=.<Z+6LU qzqѾ<am'dKVXDP{(<";u-pnGs՟l}f˃߿Jpmm2$Qk1m$XC/NB.1ut9#Mq8_^8R~xWczg>s sGJPw+*C<"4R_}ɟX=?U|Q~GoV̫k4p殽S NBwU1)c)߻_*Ŋ~i/n_^{Vޙ)/+X+qޏ->gDRɘ( ך&??5-z&0d2L1@bee-K2~, U Zl9QiioTYM`aφ^[~c-izjCOreo0&@;%n'_$>EWhvHoV[ʹ|wX;QPf˧(pTM gkcJ9)DKW֟8) !o$<<GM5{>z]Z/4-c yeIv['K%u ? <ѝǚT֜V =ƿ Wc endstream endobj 1109 0 obj << /F10 53 0 R /F7 38 0 R /F6 35 0 R /F16 138 0 R /F13 66 0 R /F8 41 0 R >> endobj 1107 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1109 0 R >> endobj 1112 0 obj << /Filter[/FlateDecode] /Length 1917 >> stream xڭXK6WZB%N**T0ffH0t ݪ@S&daywH6/,DB9mD,v\o~|s.d,G'1m+!4KxCmwq)>DnRt|چHrnyݐ\.7`]͎ 3j`.uc]U&CgO*= Rȵ A$/mifx62 (hiX=DYF0jqp#/.?e1Tpe~$QZE~ڧ.?Lzg*L8AI ؍LXb/3g c>h >2k V팠u>BWT/ kb\{+"q~xΡ Zyӹ.Mk9f1IX)̔DJI#׎ӣ`TWBKL3STBLmK?T.e3OhH1H <AӓnTF?{vxy}mYA" /v-!c4Dr4 <:PA7OI3]  Ao/eCoj t{66U0|_QK*,ҴO.@noV[5jOOEbtqb^ 2ƭ"bѡ 8]%@ re?ܿ. endstream endobj 1113 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R /F22 218 0 R >> endobj 1111 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1113 0 R >> endobj 1116 0 obj << /Filter[/FlateDecode] /Length 2461 >> stream xڽZ[~ϯ8ip d7%R҉"e@snv83>U`c.=IiL._b(\']xDrv D^o>%'9<>/1?P7\|-,$M/=%S“KR2%YlQB&D~"j.4H8Xwe=5dMk(/r^iEO$$NYD,|¢qAMbU$,;geXx/gez~{w<4X!1"|eq.HDm:49Oܷذ[#pl°X)Ck㛺^4FocJ"fT͌#ADګL5'*T:?̖?{bA8s9јpjNFAyuǮrO5YP7tR̯uu^WծFwFknISg֙z߱7)%LhH cL,klWۦQZWZOP c ||VOCS\!*ܴخOMO&ߪ)$/*ylH܁Cm<#l)8a{%:5mvy0mÉ,+e$0:jXX%[;ƕ'R뽛z@f)3MnWpr m6xof)ISo<ƚuYwC\\zׇ=<z0Xm8&fahA_beau:H.Fk5NJyɶ$PVa{֮ئ> _ۭ2p?HyKU9.ՃϊC3AM޳w \{s1^p7Ƹ؞#RjPiC I/r$fTӜwt7kW` endstream endobj 1117 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R >> endobj 1115 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1117 0 R >> endobj 1120 0 obj << /Length 59 /Filter/FlateDecode /Name/Im29 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -188 -330] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1121 0 R >> >> >> stream x33T0A(UFƦz`!C#K=K#C cc" 3|@   endstream endobj 1121 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 1985 /ColorSpace/DeviceRGB /Width 235 /Height 143 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 235 /Colors 3 >> >> stream xkr6 Fy=y+L lqi`UFB$RD+C$~K%x|/]"26qRe]_?k2(Ͽ~(?'g*?" &=Q0IwDLz#f/Gb*͇ۗ;}~Q*c;<& ~bteӨۏǍQo޽ o?4n̶?so'y<#߬_|X1pQvVħ/?& ?D<27(x>ǯ(QzGJᇈ'c& (azGC?1ζ"PC!1w`wqL:Oo*(|GD;" &=/ O1͈z)Ć:8arUvwtB_7{_')VC+ j34[ٓnJEt5g"N1PM⫮f_Sp({cu%U±"!=iweHof f=ӓ;%:xp(~ ߠ`*\Fc]B-vp) ZK ݂77=o +oP0?Q#X endstream endobj 1122 0 obj << /Filter[/FlateDecode] /Length 1625 >> stream xڕXK6WpU$fRn&Z29h& ίO=[ɭV_8E|?id(TT28Zi尮͛<(DQ<6eWQ ͱ*RJ9~eYkǪ6}67͖~YtTPGUSWam ǫ~jgEPݮ|rke0I$'AVJ**5$D5W5C>Zh+E4%~|Fe$ШOw4)7~>f̨%C_cgƾwgaD?ݍn~jʱ)۝\=*<kjgܳVnU h8C<0S2 kn\nEAȯx9qs + [-Wf/܂WVQ~Bvܴޚmz D-WO"zSӑ/mXUcn?kMǭujỘz€ kzZ\oCdy!~3LgCx4%|P4)rpI#@7d#puƊwo#Rda"j endstream endobj 1123 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F10 53 0 R /F6 35 0 R /F15 112 0 R >> endobj 1124 0 obj << /Im29 1120 0 R >> endobj 1119 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1123 0 R /XObject 1124 0 R >> endobj 1127 0 obj << /Filter[/FlateDecode] /Length 2811 >> stream xڭZs 3 W(Q=}LͭAD[Yr%ٿd'^"H@IDl7'xdM2L$jsT v,M)|pRrǿoO禭vxMBd&z*ɨ]EQU G]ms[ݥj[PۿT _o}RVn'^>l{UV<;o6z$kcԴO;Pktv }GAEɤG+R([2(_L. LnkmH0+I0dဗ;4jq ^kzیiGu5jzA@{0ϤЀvqcΣL[܃s7wX0EAb$~'M^dQۮ硧'ppKgAQãQQk5 *<PcmdjǑ1@x/ʥc:JMmcCePL)KoUG/OveD"3Ht}SUNdס9Z{CT\_*23UCM/0|qFHm%GGבxv6 D]UYx\9CSp#(78s3/uàM5wGwT,%k)1VDל۬(ʏYX VB_m=_N 4OqmZ]iaK^- V 8!f@i(-IS8*NZqdN!*&ƽD$ܟwY,>C$/>eXȏtS-#3 gG&^aSZ\#&x&:-M*L{O~b=#J H⥾xQG邨ۼpC;̨qf,ZT$^/XS)@ă ܦI݀A, L[CL#Ѭ/x\a?@YAm8-@ j[|d%-`u-p k0n\%m[s#/\gREpކ$.AQ8ޓ`Cu[N. g7TC> ŷx?q-U&ҥZY]֟Ie i~(u}qn;^PW:*yힻDž8էu+‹uفֺ+VTWVq:q-ꬾ J9g*2wt0rVn1s?;}[ [zRoFz%rZifKKkіoT$r'*R8.ဝ(qGXܡߪFᦌєWc(XrERRp˓u[ 34cgWORidNz)aݵK`ww;-0r57{ÿ2 ҅S\wRw m]C~29𕲡{"|2 c~p+RXqzC?[etN_{=4L 9P8hJ=á-{2wϰ'D:Mpk?cMPq9V6_~z8}}iX݆hy1 ΂2phpO< ? <p:(L> endobj 1126 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1128 0 R >> endobj 1131 0 obj << /Length 54 /Filter/FlateDecode /Name/Im30 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -188 -327] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1132 0 R >> >> >> stream x33T0A(UFF@3mhaa` )2Sp B)  endstream endobj 1132 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 1920 /ColorSpace/DeviceRGB /Width 227 /Height 146 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 227 /Colors 3 >> >> stream x[n6@9Z$qJ ]PQhqtn\r˗+ah8J:C/J) eRH,ݿuEO/S8sU#1tԈFL%<5xC0ߌ-e߾|KIf[FLLxk7/n>Oov~gޘe}2e~y_ w15LR_^XmyަΚ۶6Q{;(NTM֙YSSb=_b8TMES2f1u`ָ}ćZKcָ&~DLUOMDZcMxTQ#1tԈY]d=3jS%0T0tES&1ãT*=zfL]^/*,smɲ Wɦ,zGML=3n:oKR@rƕ7=MHWc$/3^]H.! b= 2zS[ U>HLMWֻc]g͊)_̓^ՐޥgI7x$OD藓@ndO3ui15ɓ|*{IV7&3mYW$ĢJgT]\AصTtvOS{GY425qVJ%7]{K*Gؼ VQTf:K2%9ba_Z,v}N 4M5k~w`B)EQ8De' ٥v,Zv?;~t6va*@=0 >CMw^3A-S :h557w65F$Ĥ~ SYRtLN)S :(65-SG3(]L>0:` SA :T9<% endstream endobj 1133 0 obj << /Filter[/FlateDecode] /Length 1502 >> stream xڍWKs6 W(ͬ!Jʱf'=tvi[]=\=6_dYNX ,C>Y.JX,:PR1%& >??=Ari]R)~TxmC[ eмiv{$ҰRV,g5;@OM$y0 ?t%)/%-J¢kd.ɪ"ma ZLJUtVA0ZH4iܕxnr&_e9|t!pY&yr4H*\)-Dɪ|LYij&=UkveX}A74{HzdC1njpBQUz/`r!n*!xLAW`fW󱅈$IuDomHi}*nTºڕJm4g]RLEHR.HzZW8*ہvi yl{sp9u_:PZԞmSH Hd5,DbH1=덩YP[&FݰRcuwO l}I}۟neQS mw^R_V)Paz2L,_-^92K=sf5"B|Q|OZ3,@_>A«xn5;,S{L+Z8W<ŬEK =Mҏ`RjPJarjO]ȭ62e7JvLO4]| K P9GX+)mU;uUT1zYEߨL"eYLD $H/τ)pפv9}?K7Owc}몫} n\<~v1>1A\ד].2kpD`BEk>믫@O9?qp$XOP*SW^AK |C mfDoq v]A4μ> endobj 1135 0 obj << /Im30 1131 0 R >> endobj 1130 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1134 0 R /XObject 1135 0 R >> endobj 1138 0 obj << /Filter[/FlateDecode] /Length 1608 >> stream xڕWݏ4Tl8! ,BnQx$M]Ox>33ϰ%#o ^Q};5w9 s?wwljZG"zT+ŹRuټNȘ^`AJJV%qT+h*ι1/e7uDBQ!;ޞG9֭͞eD^P1JI3p7p>ҦȐ ?"ïG1'>t`DC͢堰[J;A#YN}>w4.ބ"!+"3kn-uv]Vӝ JB=Գ;c&2IG}cYp4yb&U\ЀI8kUIK&5* k77E&caY&Ȗo#(}swwD4($]ZKm Jc<,K~].^Fl~M AC Bl)8RRoA+w̕691v&NV"n` e[4o.+gVb艂%=4q{:*IAj:-gJ2ʀ[羹 !}nH3i!&ly½DegTY(@A҆-LLzG,gOUU'`ۉyѿIìvA "F-Uq7;e##MS6uNlNqUBAe3,@=8Ն~ykᴕ=u$;iK~0S endstream endobj 1139 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R >> endobj 1137 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1139 0 R >> endobj 1142 0 obj << /Filter[/FlateDecode] /Length 1985 >> stream xڍXI6ϯmd䙑YI'`n`@۴GF-{bV iEo0* 6^mTzU8Qo櫭f)]=@zTon8qH֛,˂7m})+ӗmtZAfWVeE_߾U+j3i:֓ G __tKQ~

~}rب#ʪb>7g}'ҧ$}MzMÙl*ILZdW6{=mS iGW[`ڃgbpnH-@>c6Qq" {NSdmзE-ut73װ @I nz1x{b X١`9IwU6/k[ a|i^YVԋR-9S_*u{oܳ Lk.k+U0`\O30t 6Z0,jg*Ϧ#b)w)|H3ݩf,&Q+cF0 2lNXڞlc;C0$v j3U3aȱl^X'y#IB:SV*1yiBP KGhO󉘸gxv VPP%!;IXâif_L׳0p׆\~Dá"ZKa ՎHA\0i b6W5 ұLOՉbrmuy5ԫ^M3-usXCPc܇[B?䡀T\XɱMGQJzǽW g8S[^c;v hx/0" CLEp8> \T@ Rg\=!*x(;8o"\"l7G*5$ qd ܐߘs_oq8M73T^'G'=f&G謘T>i$Ã]-,~bOLV (8  u;LZT Atxo6KQxABSZ6G0#{0Zmf*`mC ?3t?t |Qacxj|p'!9Rxr1=0KԦQsmFbet_\eAxު$TeѴ-Al2=:5!$슐F5]Ԅa:X6~GCHmě[P#[93 2ऱ_z^h~P~ö"M1T\z-LF-c4mIT9]͉޶i6ҏmUqrbBLg,"%>,Q LM&xjm~_"- :zM/ WQvѧ-a+A%#JLe<ģ^elN]ί=}3 :,$+:-ۊc y.0JVXThDeC7nU -U?*&I')^œQ>mO~A =ZVAޗ񲗠D/k@S4.}F} c(*TxW6L!o> endobj 1141 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1143 0 R >> endobj 1146 0 obj << /Filter[/FlateDecode] /Length 1752 >> stream xڍWK6WЃWsjn9u+kz ۋ P$|(ei=En,:Gy*mTR)M9}wӨbUm3=g?b$qmG3&n}w_2+YEiE弿dimb\:`/M&TG8b>$ pΪlu3v!~HɆLk]ߙ/V`%ٳ=;$)e\6z}G vQQiDy4ϴtL'cZMMlz4f^2iop26b+vM7/# ouiv@(ݩS"Z 2iҏvRh`eTT_`\83/dbbvO+/$2LUk{'> >ݦC'{jqQ2Ki@8'3yNdUArU0xAFA"0eJ2e9^7L@ ro\T%GTu25HOYgդ(M[Ņ&o]xYRUlQb/J\xn~ÿ JZ;Xbi%T 'vkf,8a҂KBPq2 f<лS#K xt' 01 dŤ@ea{OJgf= e4s ߀G`Q sӮx馡92'[C N0ZҺwAdLW{ʝ!'D9rmzz%_B^. jEy z4󧁪i.ZWTf-욁Qf a x V)&U,|- I0wlsM ۟' @:s SEBX_AԓnG[O 3 {(WɻyN;4@{MHаZ]Mu Rx!D +qK<膿+lOG-Bw43:\&tyLuEՂ~zJU-&,rWmEÓfG{}ZC,4nC?!_ >"c" {,3$w Lǝ _$5;yF9wQ! -pэyAcn p H*@28} {Ϟۛ:pmͳnn( F|pXvbD)1YW~pa[& x<DV, >ntg2` #$~,@4.dbՓИ7~4wth՝-.~ 1apG(< endstream endobj 1147 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R >> endobj 1145 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1147 0 R >> endobj 1150 0 obj << /Filter[/FlateDecode] /Length 2371 >> stream xڍXK W(WzKKjɤfjvqe2,y^JcRX | XWUxIR?VU'~9^}]i|j2~]O/ &̷i=izDZ'~\o,㩪P Cu{m7窮uTxvWj%~&{D뺈zE[sOvCU26%6{i;{ڬj^AU?>$vv{uFDhzgSWL۽MFoW0nz:gou<چO=`fh[;ݭ_[m3ֶMyOlrg 9 s㺈<]fO{k1[!s﷑n"ʼoX>$~eEUʘ"בm?|,苃22sƙ$|94ӌNA*fąGS5-!q'(k{mdX~ CWqݱ38 E{?cV{coO}ڱ~-oQiTWRBeKL/*`o#hJN,-ΧȬ`'J Y)2l|}gG3`m#˶ vjv36)0l:s2RMNƤvWО$[hACP;( "ϥǐ~u >u$pj_;s x8gAፊc?żC\KV _DۑMl]9WOl^&joFBT Z\ʗq gPqکX2b /_tk^;)6mf% y{o@"i0ǟ;j̀3Qe#o*SGC%F L{2!0zbܛ}gPe{7NJM]#żz*MUʽZK#-SpuR%ВLTW@ܙPR~ޮ8[@+S!#|Q(/ᲖUzHl13nfKM%p+)\I.Y:W/ά)YW84`j#bަ 1{_짶;EٲM(^*])9#8pO_?%D63+DKw%&w!{8;ȹLeT?}C/*]!=C7*JnR#I5`$3A'a K&_Cv`P!LYY{LXTnt|h{E\m"Ox "ʚޢ8t 0u*e;xn9d"G_E}1%.kii06[n|&9Nn"3Qe˃N,ɁԌ̖78'w!If>YT0 ]0`{r(P\xڽ:z^# KJѷ&vFA;?}|X<\,:QqGE,"OS!I:m.JhG_K3벜`RzԛܕUXO'FEUYyVsEZ"ƒKXe~YA#~*/ۆ`f>ӗfqX撟WlL{ b=7$ŜM8%+Z|Z2DQ D%}e<0Kg겐&׊o0UP!O'% O5-*oU6ρ rhI,5ze"wM&UxVX| ~Wo|ND+qƐd>^SB=yFF^pv> endobj 1149 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1151 0 R >> endobj 1154 0 obj << /Filter[/FlateDecode] /Length 2105 >> stream x}XKW2H{sl3C H&Dᥤԋ<6r"YU$Ū (S@_Ƀ: Y*,͢Lq=KuT'ßve}]f'v>:i^EyĴJ{U%)~nqگHw ?8=.C3qi'ڌv 3L7Biqfq8/=i${7g8n`' w,~ 30Vzhr mf8د™_  {*u:Y6y\ZcYb9H̏(`n3MPw]'au +-BMN4Ce,fpx1ơ qⅮ88jbGX.d4X7aܬWH* :o]\o<_p7n G,^4V 'iUR$6Z ]h\-0n>XǻpHT x֝ef/*ʵ`,*)`y5{^#7,7Fъ ϭ 6@0+ g /Wq2Y4e>!E/`L+2^;; :K  <] ApII0Wܗep۾X@!Cq*QT)JR~A;-pAK0V7-f3,O23|W4|``r`v9jMjy@8gy{kpa+91TAE@RTE镀M`*?ZEUBqOwJh>:$۽Vu[AR[ Z,MJ=˛:-2؋^OzbYԖY$|Σ8$ZPgk PKХGC@ro>',oryDyE>'xC?jx[WsfԸaUҏDf{1 endstream endobj 1155 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F16 138 0 R >> endobj 1153 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1155 0 R >> endobj 1158 0 obj << /Filter[/FlateDecode] /Length 148 >> stream x=ʹ 1ާ8eR$fn2t\J0XD1ķw8J*lޔx*5)"40HC]cE Iay`T%|Z39ZVuMS׎lcX7xJuʜfɏaU7) endstream endobj 1159 0 obj << /F7 38 0 R >> endobj 1157 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1159 0 R >> endobj 1162 0 obj << /Filter[/FlateDecode] /Length 1618 >> stream x}WK4+rt6^n,RTA\(b{/$;l'ڭukQ9lۧjSUayz٨ lc`߃r 6I%JrrT$cvcbSi% #N0a/ۤZ)>RAmN4xƣg}[3 >ɆGk+l;9[rx7V]w]RAx"ˢ0` mҰ,D0dOx~RU_QnO`pE#a^v`ҁ_=sN&,ݵvSFZ&&G)`4X?j'(JnG>do2 O}|מգיvn@/2eۤ Q_#$H$R9dogX@ pxx*K$eO"FٱY -蛋]t7$4l.Wɧ t0ى7*C!? >qՀl9DzxI@lsKCD`aKlg&g@2\`Cp}A MQw8ٳ-}3Β,[X߰ bi2ul=>*Fӵ(z2177e)+SgXgm%aA?d\-ysUE,dY4S1a%R:1.Ngk፳EDTrL;ޗC9lz Vp 6XsmsKF:+$ K{ogC/H$7-E}ӚZ_|,W9zd^Wv] /ND!HugR ,`=Z~,^b*D'qOV*!,fy,ϩer7^<.S}/?]w ^btaTqyKE~8 LS9r#zQŕ }qohf3$?&{r29\W t8T:tUɣ =Xf[ظ^z(BHuWG>${NŜdv?{Phd$_ \Nr5Zտ endstream endobj 1163 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F10 53 0 R /F8 41 0 R >> endobj 1161 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1163 0 R >> endobj 1166 0 obj << /Filter[/FlateDecode] /Length 2321 >> stream xڥXK W訮js$zmNnfkRNS9s`[DQn)ɒgjrHW5"Q`R%)PR q| 1JQfv?.ER_&z$ʓ>H)Â??=m,R}>2s1 `6(d24ukxˤn(bcm6*S +cw} qq1ZafTu{/?4 {P>5LU@y cRҝ`L#"t3NWbMTLjܛX'܉3L26/Жdb4b$:٥ dְan%0pBȔ KTv=~G G屓׷2pYPD0-^~IҼb;jywtAISBp 2=n݄oxGhٙ0BV#= Eru{t_XޡLEV:oofui&rNXui`NQW2Ag\f25GqtEš 4 I}wA{ࢺ]nt[,uLenPcͻ>~}So4<և#RZvW[w#ix҃qYG)|ukAN7͍ᕮ9e) ŐsAI!$ZٻPΔ0@%|ν4QwIRğl{ #;2tg8Ʊݹa anL׭s)>`{ ӕ_pVst Lk_8g>tJҐA곗WfBʄܘϺO|Wb97$YE%<TzN_vå7KڰSQaZ yٛ̉Utf0^?¹1s̚cps>c{V77[{vbF [\I 3ܡ.g[`ZO`j)C>絋ҼJ9ͻHi"Igy g˄.קK'LJl a9v oȡk@20 VL7s 6e.x^:UF7XXlji:sWi+ݛЀ`"[x ˗_ 3fЂyn[R6ע(rr.YFIߑSR Ǟm_FX`X^xy^p؁y)>@8D˹-d]Sf_7.`dq/FW`kb*a~tnNJ);<ᢴP?\Zo iḌ"<]?*NX Gcޭu3ƍ)|VRaU1rb*gnK0R\n0.4[G=m5޴ *XWE*m72\qF7ٵތpJSA$sRg~ҁ̧@|g%r %U׽Y %ʲS'M6G荱b3;P|wa1?SyAFĥXbDZ $w(ʻU pzY$k9wIyb͏)삹.gs#iCOnO v\(8f}"#]ծM fJQb80C4<{]KgDxQ`I'"wrnXK;Ts\Sp:vwo0eͩscU UKH<~Ajc\m "+ I!b - 2q5Wfm8+t|'( ܢDJvgӮD<J\.ΒG>;'@PqS;, j[sPÄ{;Ȧo/?5̖{Y *^P+n:A5gz=hpE)pc4]{J2TfkO1Sp WK\HtAߺ$ۺQO8ps\5}> endobj 1165 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1167 0 R >> endobj 1170 0 obj << /Filter[/FlateDecode] /Length 2818 >> stream xڥYK6T&>}ɮRzj/q LZ?Ix{ UWdJCU&NTWUlbk]>~(\HWa~t[kNU 6v٭7&ɂ_ ^ֿ$JUH,txdVQt362eSi -}n !,U̧?8χq*#'ەO3IY1Qi`ypx49|v O^sYULyXa'<:^Yهl?oN{ZĎ_mDe(Ù?ezm_>15Ot6',v;e]590}ԊG?2$N$;ܞDs" Ux3Gg~*7t/Ѕڦ>0not5Aedz}ttLh e]0m+dٿcKHA-N8CǪSp \bnSZ(֛8΃-w{".EvH+@Cbʺ%b17xW,>)X%ǯi>w]o ܶv 5.(Tewb>`݃ / | Z.C@YPy:x$obQ;bqi=QUQ\. ] JήZ}Cºu'!sQs&Lt>b|`<@Q-̦Ln[ǝ֖ 'P n{&ѧO_E<|‰tfQǦu>bmS;qxT`N#-$w\XF๕8 ,>BJ}Ȧ84$bL\qQ 8a"x+D(T~V\\gF*j! lICdo=``AgK*Fq ^Q!3d~L_k)|)*!ʭ`LBk뺑Yp#p*HOŻ4OS=؎dpf.ߦ&pyKRyJei>w_YULP4&-Eq 0;4js GLuh @8ck1 upYms\8e*1gds$JRӁ0XH_$1pJ  Nj K>:);|>8"R-(R%* "ϔI`Ks1NY:bZbNv+uB<8Kd'k$,`,/{_TbXr+S  5c||ҹftkitJEূ.-gR:GHCS_V`D0[ܤ9\!80zfFnI;*a&͢wxk;w'vKzOl_]W@~_bW̫* ޖJޕXH(ȖZ <"h#~Dqxǽk}>munHh(b\(H31֗rM[+/+uxBe=?"5շ{bf"Ϫ.LOD Q$PMQqyj0!on50.8|/<[Is3>ifa3 b,m=k-`(r%jINO&Nm)IOk_JΌCqM*yza|;0_ɟG˱<˿Qy_3VX]!&s O&?(>MoϮ3=M ;3y!©D!T鹁¹4ڞ^(BٟOZ)dC endstream endobj 1171 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F6 35 0 R /F16 138 0 R /F22 218 0 R /F21 215 0 R >> endobj 1169 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1171 0 R >> endobj 1174 0 obj << /Filter[/FlateDecode] /Length 2167 >> stream xڥX[~$kFnC/'H$@}+ѶY2Hi}_Pl b^Lqf>|3d( k& .A2 i&"4IE* OEP2^X2xB盭B;f$Iy"y34n 2q6x9jZj4jWTN"4ּ6hߏGء骁FgeJ|)}bKϫ%-o+@mi`s5(0pX"0Hh*wa=ھn`{j/ǦBÎ4<*oKP5ZWBD2K [VZQfN*BhG@j8lq|'d#ldN{Cj8sR@\$="^ nOm,*/#HH"@94}<\C0g~SW5p[&KfMZ?+XndRfw@ R:i暎Z8e1;A ؏xqHN넶N+8;vyGj 9̩ቪ?РLxK;:5OF;)NkmIgNBA“g*tQ7$k1[FTUVxq" Jcty2<~<[l7a^5w?4WD_ơiye#zPg)`I܌.Ўpxv?qBn(IvygC09_vН6-z]-q'SN->=J&w"I9m$[ ;/d'esaZPҹ'Z%|]8^,g&b-*uׯٻ}}߬l)}tHiNPIotv1CEzR\g8iV-ȳ,vՁLêWL?Émi["I5N&ĵ(s+YaQdT#@LIV$%acizVʇ7jGfH|?4v"arb{=b#$Ը0S#Gj?Vz`jA[L[ɂX l,j\>9K=t1,ZZPoX w}3-`dW~93Q>1םrv_07 :U4#gǝ  ?omcz dD`p>|C#e(ҸPNWu?\n$&%)  ܸқ .-tߣ)J4O3p՝]sҔo05 F51Kr0z^ "̫9"=* wX.:|$SYyмOW0簂Nʵae>BҠ>iN'u}G|[v[ͯSqPbv!=?vn 0,W'UZcFd)d͍xDGM 8X 9öY$#{\FGSd K+&y%O;MBa,Þ QTQ&h|EVuLc<`ɪ97R< Uyyö=m!&eŰ;{0X Rm ^&p\io es_$m] xTL,JuVU'9[Nbq ~drr| lB"5谨JGpsD/A$$Q)bHTYZ_X"o z[2jD[ Vcu2.x)9x[x</}UQ Yg0Y7Ϭj+D@.N~G˦V2BGcߋ"q\"]_S5/ZϦsD07qu endstream endobj 1175 0 obj << /F7 38 0 R /F6 35 0 R /F16 138 0 R /F13 66 0 R /F8 41 0 R /F10 53 0 R /F22 218 0 R /F15 112 0 R >> endobj 1173 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1175 0 R >> endobj 1178 0 obj << /Filter[/FlateDecode] /Length 1948 >> stream xڽˎ8_ ،HQiLfEH9@˴-D<*)˒6s2U*֋ϯyO^,V2b %}gxbqs&2ϝYD.Va |j=CSQʢ 㒅1Ve%$8},* 4߫Nsלv|H`}ːI/Z}GXWrTq|u&ãƑ.~Y(,4oLl׋08Ә\mqLmCȰ#TUΥ2C r<詹$?$I$J$$4j[:̓CVu ~<=𽢨2աnu7%?7(ůo / 0HH:BKK%+jHbxFG@͖ Ԧ,ڃЪe*˲F2bS3qb #lwUB꼫vZROů(K [MݜPr{P/xV,rtGY3$}h,|(91XuO}֋=|}=n#?: wS|@(xI ^AuB+F-)s-|YM9lp֡QrtBD 2^/^Oð-_q+=80Г)'ua{e56e⣶g8-jso-Æe<ͅ,WǢ^&Tbt8ǣֻseIevO~4K`h:jHDG B_tZ\( 0`?TFj z9[QR%pD7ZިEJ:5飡 , X> LMvƃ}AW@CZ,",)qъ>*~T П%Hvd7wwxي:a^dA[_Tk20`4aV7Nݩ5jWA~D~e!A n惕y Yn) *:f>zo:C+r*X 3-T1 \ozߨޗO3 0mEg'n ܙ@~S Mw92ء9ɱd.q}yj$2IUy7R]?  H*yh-=u*{9Lm{[b^W۾TEg=EZO7cúv!/cfLS^ }#jF#V+zh_?>u endstream endobj 1179 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F6 35 0 R /F8 41 0 R /F22 218 0 R /F16 138 0 R >> endobj 1177 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1179 0 R >> endobj 1182 0 obj << /Filter[/FlateDecode] /Length 2091 >> stream xڭXKWhsEJ  E) !Am赒P= d"bզm>#~g8C8_N'm8ħB.u@e2  ttVC֙XL@+,q7}̭A@9L]dR= a 'EN0D#H]tBcHjfNaϷycU?Xidik {r|B5R(4ѦEٸ%ffVslYu0 5㲘ds@iڳ*?#n&rƉBKa6OpZkԒi5s ,UBE-;iv1G$ҷܲ9meKf[8"%pW8 ̰qT+}ĥ`78uE?$ +jp~K w5[B8kr|Sە}K:4cN}$ˀ猃.2n+p ;u ;vńAe;y-#tA]11A(e >qRVg6"Db_׵  ք`~EF@γN}d])w'ȗصf3< ][G- J.]-Eh㶖ݥJ/Ym?uy& vV오t๗kLR *`yw;\4 Eݮ,׎T)ve6xsΒĝ$J0r<1ׂsR3XclV] 8>{U- Cg,H_A5 :Um28L-衔bJa]>A^Lօg*wu?e.p a.N|d~tĮ(e$gMbyL L2^(y)?8b$!3.:>/T\tqJL1Xχ+Fz7$XLU_-q d®4PlEN?gG[xPQGr]0`xl lĄv(e!}[iB[/0"2D:p=&I5Ki 0\9lY2H!|ǁ/JgR4oN/8Ef8+:¤^[ bB Gbut  (#}os]#SiOžM,ݸ{v#MwRO8`<JpOoܧe]qL"^,Rk i#M Wʔ496Ki0)93km9) 5yV740XAU$J~,NڔyW`$z +KҡIHxGrCCډ*'+zSYT4aoZ(W]ʵItyf䙧q' a0 }齐ܛ߮y ;?Njn5!|vC/P؀kwdǨܾw`H3ÜUZJ޶jW^ݳH]G7dzm}P_wS{kW/g{ endstream endobj 1183 0 obj << /F7 38 0 R /F6 35 0 R /F8 41 0 R /F13 66 0 R /F21 215 0 R /F22 218 0 R /F16 138 0 R >> endobj 1181 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1183 0 R >> endobj 1186 0 obj << /Filter[/FlateDecode] /Length 2199 >> stream xko{{r-jw@(Pȶ #d܏ %ʎ%ys8QB~f=4%-"v3)$<_f#zvs3‹ϟ_r.sŲ|!T^fy?w2D+9+p~E/LJ"Z/B1BU#/=uA ` 1lDք3mVDŽ/NL&9iH3?{+ U=OaMQ" `„a/@d#H.{"JLʇ)[AB U8=V#Ngr/2;5Wh(ҳ%Ϸ$#cs#vS9mUOuqo}͡5wp*/dw,|Y_&%,k^foE9X0Q߭R/u(gJm޸ pLlh%qF+jsZt DA]*qyVaWem)m@,V0N_cVa] !i.I]_6KUnpj{koB2A~vIGnj=#NgFgkv-!A(Cej"s:Q׮T<ǘa1 4V<1+:@(:hGk[ݵIGumx<\5716uct.t Hy:iͷUkۨxZAu#,sJmöl)1\ 6l>'{Dzt 2Mxk7eޚ&quuWڊs'iS**uEkj"("I̫5Tg۶QPJl(ӍE@l-{q,s,OgȒI)̆OSu&YFI)y܏Ƽ&GSUMtYq0 D~T'm>E6Qp`3ސ>:+d^BcP88;`nqq$_X~lUdYYR3_*;)Ƙ VƦ=%/zۂJ_ͿN endstream endobj 1187 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F30 286 0 R >> endobj 1185 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1187 0 R >> endobj 1190 0 obj << /Filter[/FlateDecode] /Length 2049 >> stream xYK6Wvfć()A{#E=tvұ Yv$y7 9hIv)$zDfdzE>~G<{erT1l;SR1%rg/Y2=\w9򧫹b)JMoMXJ){<_,N毊: ,n.{RDFPKlXBXj;;yx &=51k|&|hZޕwk%@5QX8X!1Q[i$)9}*q]]"2aZ#>ڳ (>|au3>-$ewzɓhRȘ,K-4%OOAOIOuG" " ܂G͇8bISSs_?8lX9O;$OsAO/S/`ﻶXH]I:o7y{]EpWjbWƼrOݷ ߹NN\ mt<*sh뼤Cٚ v%!\~s՝L,~g1E*ƭEͅ{UTlŅU'H*Wyc.Nys(qd # }/ bLtvkK'u"z$>OJ1}1`B&<)| עw̎Ĵe=ĴU[ Fݮ%O%8"0[禶RM l9_Za#;w_ &u|>nl$q͆2>k$qyTspqvז}qgT3OX=MYFj WhkkP@4@i)0J lJ qw x9tα d>fw骇or}8,wOxs ^ԉzqQᑦ\xBa@wA(cH9%6bR,=dD TfS3IH:1kN(ͤ[v̠.Ah7X${L@;!t E Hs-A4& 5m/zh=V,桒4qBJ01uW>$^/3DL۾%&tIRPCsx%155~]y*^Lٔ`IX H[@M'dަS4'H:Op9Xi_BRZ7e)cSSV@eaXiOʨ1&}+_l+vBT/@1 ÑJePKQrlKy2h ŴjaGuDwJ»OoM%qYc=ě(AMSeLEWto]ɷʛa3U? #Y-mZw  żM' vRI"r$~i7b0ABxχnd 9z;cR:|+҆6c5S*:RECŏfe/> endobj 1189 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1191 0 R >> endobj 1194 0 obj << /Filter[/FlateDecode] /Length 2565 >> stream xZݏ_@k1CCMͥ)!Bk!-i}w9$gVz9+j4buZ)aܬ~\_Wwq}D?PuzCͿ"0W",[`Wlbt~G24vb7H?v8V psf@\3gd c8%6i6[-w,UV?ՌOPo5g+&ș􄳄90cF&ޛLAAMYɗ$n$+{9/EHa#XOT#6czw!"?t;ÿLi ͔0Lf+[T5$ ?yCEڶ!GZX:竩5z;"ӭ־&z/ыsO28pf`W&Ya4H+omfmSϓf=ټh7mPܶz Fܦ3l-bc| bh;ي2P:bFuuވb*Ǚe]]k9U,u ?"ZQ4$MusR4<Ǜ4HZP7x>>V=u WS^C z.$Cw9b jCy/GT`v~Λ,v/*{&AU߷,ircyR2:ΒK(U L_r’"1A؞8u G.0R'3Z 1U8k .+ zώ+4~Cה$p촭 w{:ݎ.|XM@yNQQET;IC#?~ZL/%ơ| IBKm=C0wF9P%Qˀ>Fہ *Ȁ3L ։l@z)V{MP5)2r|CLssiLDH(\bpSK\j?L2k`75NUne,NdKDdzn<QDb m =M"Bd'?:k 8ZՁ^_eG2t42~ i{{B}>"# fL s ua7(_ƈN`\Zu"*@CJ>*i^PfN@,~=ΓP"xB?D="Y`w"g)Έ'`Z+DaBLN)1>PnRRT@sp2m&=FP?_TBJ}!BqvzAqT ?ϞV ,_|.$P_ǗpCde|7e`u)v_cǁ&?7P=lfOO:c:h޽] Ơa-&jOxڇ:l w^| KqWm0傞 (Uk1edD[dy|r| O_s]Rap>kqE*Ysu~CG!c܋=Ÿ7wd!wp endstream endobj 1195 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F16 138 0 R >> endobj 1193 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1195 0 R >> endobj 1198 0 obj << /Filter[/FlateDecode] /Length 2065 >> stream xYKW20fė$,-f%GS")J{g,'⣪>79~uSNiybJi滇fsTL !;QRdhNJUq+2y뾱W8+`5YauYOz P MP3:n`1^0.hrɝGbHRoN 8}~GM;A7m3-GKdAex׼#Cp~h~ڊ2d '^n5+*8o'eZ,O<|mcӵ4ױ裦݊"k٣Hdñu\+1iG5[; t`. ;Q~f{4u<moxil_Ov+X%KXLLJ5pK zIBY/S.Tx&Kt9vfTj7M{*D` Y {$Ǧ[ my>7@K\/п0{{p==G\u 7 <>r(qH?wqW@8Ds=zBtS+&'}^!k)״(5t:/|߯cnī@j % ^8W@կëY 7ϼʽWHaMEUH+6Mm*k}CVjQ|TH*[Ӿe  O;f(33%) P,(Urþo`!HJ%OxͱG L(੬iIեm_"A gU" 0EUE PF=)Jr@"."UԳ\}QQT`>ҫhf$vHR)k0\@Mxw)+~{(Qp}ڥU,rBͧ48 [kq9DWLwivW]{wb 0p҉*%(E^\|IyHoK..?CgqQ0~a'̤vvB9gobק_/?~9"L ЬL =x+87Gzϛ4gw<|O8tѫj㻒?7#^i ܑkY aTT 9mI9 =t[/2imF(8 &])URGJ ,y(t#6 1/PYQ7J>=>bG,>8},twܭR* UӼ@SZ>) b齲[7s]ψszpu gNr*Ŀ*)0eBh߮5}Veiu3"3׍{> endobj 1197 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1199 0 R >> endobj 1202 0 obj << /Filter[/FlateDecode] /Length 2220 >> stream xڵYY6~_< Dx$Mf] @m: Զ-Ė0?~X$MKW2Zi=KI!$~YRXkj%L*7GY<8-XTvDS_mgVU[*-hS˄ݽꦟZ(:zWMOM2OM=F.Y0j#OOml_ww֤NmOttt$*TH4wZɆ>͓ RF~qCe_q*ڸ:kmE늎ͪz`fimTpˢ18VdF/eզ->Y.ٱɑW΄n1GŸoԌ<,C 2#4/NVn t(Q>fZFc%u9QJWZ\׳Wנ&Y =9\=swII&9KF˗.?t%nwftXg3bv79O$y8s#Nrt_:0NL|Aփ /#c NyEy8.H0kOWB]ҏB\p?VI{DO@)Y7zw_j%3CX}>d' `DcaS.QPQi}]">qWR@5oͪ{kڎxr9<*4Aԝ& "@]%k* D,[)v}Y7.D: 6/=#I<1gھLe`8ֈc%:n .@9~Ȁ' EǥzKӏ0tnx/PtKnjG9ഗuH2忏ixz k1zzD ɥ.X@iS01mR~y %ʍM/~gͻ¥r!Nfs?pFt X67NGb~pR'De|% PdP.:ItNlH+W| &}a ҅IDoɱܢk)O=#})\QQwX.+binD Pfݡʵ8uԛ¦m'k&벣u;TJf;4Il)ʫD*F_bԌȌ ۃ۵' 3-65𘹣Gr"S7`OvlJs7@:X$ItW.MM۴%)#iWOvXCuXD77YSh է$`CoBN QݶU{G TvB*e-O?!Ĺ0y9-O_箢rܵ+v4M%;;oVldqcLcp'˶[C7t x> endobj 1201 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1203 0 R >> endobj 1206 0 obj << /Filter[/FlateDecode] /Length 2811 >> stream xڝZ[~0,*njxmH٠EyYȖ!{ɯ (QnIh8f861q?#|ܤ1+fybJv߿6+s&?P۝HU&w:v'rz4~n^oaoN(ƥ$d3~\H.3SM߫[GnMw+ܩ̳Ѯ}s dS\hwOvrqSއeDAs WmU]5eI@'bNS|VRÜe(Xݧ/+̪O*T~%ҡk5b{Z]ʣI:5}Sc=' >GF$|SxPF6ós$SDР/Ѫ4jjLhg:uT{>|$ ͗wK`ʹ~ y}H!%SofCXǺGR(qm `JX0eC~i,˼RHm$U8GF!]r24S-Wp/"p IO|j#Ya!aS*ނ6sxAU++5D}(¸'F@>uCik945m)r[0f9u LXOAIveY\D)> ` lǙT0$̫ViaM2mu$] El鹊|盞:FUubP&>H)d>ųfl[lozC,#:j JQz)GeLeWIEȠc}{=f:hT iټͦx%S9\I˨P2Y& L(RrF\Kt,̲ןPӲy(\8Y8dAgG~/3(cع6}(㭯C挓?>H(g g,V peρѾPfr@"S?pkǹ0%+|a6GOGF\,3Vpf+%̰wrA `$f|d]LA]@ vrowPs"g0>@m9OԺ]Ddus$|l!BiX\xZE,0oo= Z\i s+C+&sTQ>DPqΔP>U,P³|#d;akxdz{2C@E UE%2҃B(A^=hsN%.^D# V$pK'ד$r$kɔeÈ:1.y`?{MZ;甙#Ijݜ@kK7M:v!IRob:i?s1m,ߊOwt~ .RwPb0SLi"0J15!C%Sr9F3./w;ѓ5uL _W$ho勐_j29i2w0&jfrGlm'׿ +Npyė:;^txn2ۥCrp';l BXFqirs×Zi4av99˔B$;;o./ ?Qթ\.v_Ҍ//d*.X꯶1c\8(9wSqwG~SNls;KNpT7ЪZ $"d̅ azA8WgF_QQTxz'*0j{9%?iJRT`HE9]j0|9?n8} gٌk㉆xjdCe o"'dQCe yLz<+a7#/Z:BY[#gDJDﷹ=b|JUM`h]V MpuIJ: s-T) `ٳD N\HhC *1.+R8q-='IZkIItjƗk%l0!9L4_yF\"<$)j 7S?t!qL6y?Uu,wcZmsnƧ M '$cTÓh UB>@K}@z 'sk[F2f7}TW"H_ӵ.ؕCT,sx"F$W|?pkCD򵭉LeN i^Ez`*06YctBc~Yڈ|?HugLV]_𒥦/x> endobj 1205 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1207 0 R >> endobj 1210 0 obj << /Filter[/FlateDecode] /Length 1819 >> stream xڽXݏ6 _ap Ԫeݮ6 {({kwq`;mo?R;./(J$H:AB$ 2 y IḽW4 rjৄ/w}Q/64\F1*emWj8eF }1ii:0Ė#fPΣ< U_5uTVlYܮ}S؈XSMPv֬cZ0>!^gTصPvr]jT/R1Ajek[} VaD%_JqnP/L5FI(>)jpk6 ;%Ҡi_)<6D1l4DI#sЉ)`Q.W.Q, 7f\/f:b*ٗ3BۺvSc/DO=a14'Is=w3TrLqfvČrNc_4LGZ=|=VX4߃3'ݍ1*տǢ!G+Ο7^bU9Ce6-b8d]׷9)aɱFqv˻oX6FúzxDG'mmזGG UϘn×$DKcUw}Y,͢-mkIr cS/6'e{酛tͪ\~)pљB}baaڼvAf!M|?$XM(5,8S9p,/gK[~naNLNŒ*]NCSEad)s bE!w푙i "QF>l)ˑRK ])T4AĊN3:NhlPY(e?enbg䨷iKVŢ&'EC=BE'XNs@\&2;S6&cQk@ P6>J:ʹOn ظטKFrܢZ+Tp n2zX k%+, FyWv;ΰ=T@J]HE^vf^+QAUlF|_8Y4i^dWxcJSB"A(ɁBǣ*/$$Xk}9 GCˁX!H3Y9(HD]~Llfr^j▬xW (tW53."L,KfT0jW'SStÐ?Vcgics!TJ$l^l?YjA+y_U(HZY>[͵z Vպqcr2u@LӏP3|.+Wܑ =Гeƫ,r?JSh"WۓvG'121gPsqi%z|0"ݍ9KS4J`,lj0*ŠZfLV'o){ Sz endstream endobj 1211 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F8 41 0 R /F22 218 0 R /F11 56 0 R /F20 182 0 R /F23 221 0 R >> endobj 1209 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1211 0 R >> endobj 1214 0 obj << /Filter[/FlateDecode] /Length 2027 >> stream xY[o~NXEmOEhޚ`$:VNN}g8D[s RR1%q#wh6Yt9Vk\?tgMZK)゚:/C <&:٦pzۈb+c3 OCv!U3]It džk~™zλD\N XЯ:tv%<^ڕb8ߋ\<~jDk.MTХبx}>74z8ZqmP4nyZoҭlWF+@4)7,Ͻhᅡ8`v*SW~H^+؍塯ZyJV@,2o"ޔ㰄s}<`I<*4~pj NM(8tǁQ FRL\pS9w^ZcD\WLr?^ 3Ie.V;KZ[S԰|Og/l)5 .rT6|(@Yo}zQx1v4_ t{5Ni/>^ @pÁ,eŘ>=ǺoIeTn80 $od O 15oVksh53K^D~ˆE=Jn[HkZwNh؜O|kc H֧)Y`g|(9d =`nRΏH0i¼xU7LgVK %I;qdKZ1Bbx^KMT8Iƒ~3ߋ4Kw\)s5`syCǁFW!DJ s.I"{5,& ]3/8$ 1B7Յ|\Paozc@ָ%Tzi~4N_Gy`rzMS<#!A)Ȓ>)Az/K">B#⤜X0 ݯ  endstream endobj 1215 0 obj << /F7 38 0 R /F16 138 0 R /F22 218 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R >> endobj 1213 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1215 0 R >> endobj 1218 0 obj << /Filter[/FlateDecode] /Length 2656 >> stream xڵk8;Y}=ieAH 1Hw{z"(I?*W9q:ݓtrٮýEo>&E7$ڜ6ND|26#ܾ[q(C]2:\muGv0{|:5"M7۩TNJnE[F٪DHWG 5QBi> εsKx[D9wHg=쇪mhʣ+e" =ax2ٕvQ?tUsN=eiTK|vzO-3L$?,msqFF`m'h;ўORi5tnҟ@Ƚ&hG uRL*iar^=!drAs& 2< %udI`FGb r$nՌY{'\ݷ zv家5ʧWtcLPE zFȼ k6PN0,jKgP?$=}ˆ'-V=#dO; CIND4*p:ɁL^{[B'pN8Xwu)$ļ"v$ݻ6 ծ#'HV0 A8= :\]z$f}>t-UDOW"shX'L-<8c(> l}yZugf!ۺz +,MT=p' 8@d?C>)㦖vvja9(*4X@+]@Q`}[FG\,dvS{?KgHRęj}Dz% ԣ% G@.i:&r ?|@d ,Y"- L4ٮJ+#BMhد4E+Iv/z1 Cshn':ƂJg|UǶ;_mǾB<a|~ayΛypŅy*/}3З*se20'MR_D@xW@3.&M)񼫫=6=j҅TTGg _4kst W_H<򭋃8n9XhJ0V:3=|_& ܁2)ɡu1p :4ޓso+*M2O3W.k :B? Pdz6#4J ,ʊɛ=L-ǫe@\y|LȱBHDO"3#i< v&@Z8E 1_XQȃZɧT%[BzK sXLVQ `E() ‚xW'> { G.1Fc 2C8yaMu t:RZVōTNGSΗ((@=t͊ڴ3/Rz4J¿8V=+^Em+@^_2s@4so/2ts ܗfyH.'C;к'*Mz {5CṔmsewW!NٝKj=W5'a~l廞^v{]KDٺ Op:y{w]3zW l\n ;{5{Iwl"r4c^ZP&L'B5᲌vٯ%an0/oW]5w/zM'~#nڛ[^؞%Y:=/It- X}٣ K BQGE~DB2񿿅0KSI_q9W endstream endobj 1219 0 obj << /F10 53 0 R /F7 38 0 R /F21 215 0 R /F13 66 0 R /F8 41 0 R /F15 112 0 R /F29 279 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R >> endobj 1217 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1219 0 R >> endobj 1222 0 obj << /Filter[/FlateDecode] /Length 2593 >> stream xڥYێ}W50-KA`8lAIęV-%̎OUZ}a/MX,ɺb{a޳'_IW/ 2I{IqߝWr ̻ZأɒUَm~6rqN$A%,Vs; ̓Iَ7&gy38<^i]70桷X̟ Szj VU߸DQPyǫ$xIC-U9Nɰzcgqs@)b_IK?t_;c~M;Jou歯mMn{c $'@7vgas>'7!6Xi{ 利lg8">}5; : Xɯ>DW){  G& b% 4 \=VS[jimG\F& 2Vl٦&aOMG4|xMF284ynO|ͿON؋ N0<Ldn9  &|e>9xj(}˵-őESLv|٤d4,L>i;< PI?A_l}t C^|Z BՁD4]Ehsu%F$!0ilg;XAsףK @sFW%"NS)X:/,^v퐧@fq]%W@EqeҕDqS&R)rJq!uNOvn흽V'Z\KRSQrCa{[>:=X"zLK+|h'*@c()g2?_;ᝏ,giKiUhcpMxh16(1bO~GaNkVd_dә%XR-bhU]y{]K{g/}|Ly cImI\G]Ҙ cc5̥ j]?V]XdRM>O.5]ƃStE0(ra:6&R}`>vs+;+( Uo/" |l{0A }xrDL&'uZ^ƜvHP0ĎavC[ +3SP@i."$^Yu }[r vE*FATc4BS Hn;?wO-mJ҅ ԧR VF"Ĵ_ / V$T:,&/EW/'8 {I2 "bIş7Tc4:ҖЫJX:N;xAA㡵P; paʂ9sOOqvArpj͞kȕmFQɯ2iWqHP>pC^se; AYEЌ;$^n+nZ(V?á%Y(濁`5Bj$.oqz ˆAEE$$ĆkFBC2],8;avHAӾꍠQi\l.#nP㬀| w@K57b/<:#sD ΢'d@Y0EA5a ؙ{LF>r|:!dR[;|#Yݎ+FWf0IP{eB6&RZJ|JIw{Lzmw Ψѵ4: G CDΰ>z>s6@_:Wӱy69lV~}ae:ׇIr I 뢶S̑tD h_qz*2]f.#ł%2ZOf 0GM7pM&PQM"hBz*9 QIM]k>.#PvV`b9NZ` A]۟p0F7U_pff5NN9䜳齃>ԥB}olr anoyv4]Fi@\HYt$Hd"i7J &@T"<9)\v$eT0Z ._;Yqb=6|/f(2dg LGhYS<7A R>Z'J`$N%>G0ŅHRg&f?qm]?KQqP[,9Iy:;*$Lգ pT/ӞmoNJP& rAr' Lg0BpDsa +M?[~ΔjBO`c=rRTt=~@u endstream endobj 1223 0 obj << /F7 38 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F10 53 0 R /F12 59 0 R /F6 35 0 R /F16 138 0 R /F17 145 0 R /F32 356 0 R >> endobj 1221 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1223 0 R >> endobj 1226 0 obj << /Filter[/FlateDecode] /Length 2665 >> stream xڝY[~[e`H$uKMS$hWmȒAIgE|g8Ci(+M,xsܸФMRmv:ܜ7Zi﷛6_?!7(aO,7)a4mDNyqd~!giL)KR"K7;EPmmoNydFj_jn͹,N5}տV'zKO4L;mQ 5nqhnZLDI׭"3`G8>uG/8FüXv+YymBo$Ûy8lSά̮{rRswSS"9]mXNN՟/M['\/w#5@ Z\Z  8?) ahM7Dӌǹ?6`! @E t`<*ekM>< :b{w BS]!Gp+ `C&f_~ߠ3}2z"ê2ksx JEgәcMN&X|!`3C *88uu<'ڋ 7Q*30a[wՌX-hlhlxr"hN%F3h;gzwm's46k-)&s2t1/Љ!s^B1ǹw9!ݒ`)=:o98ÅldzN\FKRI}3TƢc :<q&쳃 oF0En<P_Z0zxċ:+s3]DN;Ikr3фe "QzazǷ.-Ɵ1ozl&)VdD4b'~cH̀֎]<7F[\!!ScR麺~.#*TP 7׉f5~P'9n?ss?2j1;E'亚c8X TeKLK:B"ɮl\ܖP;+ xf~2) - 'zR"ENO@l.*OLu m'*oVw^LK]Or7˸d4kTze"27_HEr`RY4=Hgpϋq]Od'(_k9PsL8 T!6QEx^LT uM hVկyR;_>UR]~+rsB7כ3RI=vG}_H{;1"R[;t HA~#Beb> endobj 1225 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1227 0 R >> endobj 1230 0 obj << /Filter[/FlateDecode] /Length 2762 >> stream xڵZY6~_>D"\&UԖ'%Έ EjIo7c챝}LJAm$}yI^iybJry&gy 9//B&HoOe(^Jnfc+w?mlB1.Es_f۶mH3t@NMTՕőުf6D?tUsϨ|]QuՔKNmQ_-prmp(\r}iW^R{Qf'GA_]3ImG2jn֚U]&C"YnfFRl UQ8* ;L3gXȷmS*Z+akyHB]u¶Ҿkg~U$.75l%<xYA*5P}&sJ2c3U `2FX[n^ˤ]mjsj5{f-w7  6.6O9H*WɂM/-E_t*ŢbI=ZϧtT?/zI?鋟LG z) +8zŒa FqkK꣣?r5z#jt]H_aI^/{?Paȇb̠$K,qbu;NVh"w\{@BjE u6̲$prd~;?őt k(ܢjꇲ -@OZ0KtjRUs,>W 95#ؖAo>j4u}^MM2i#)vXQ?,ɣ% 8 Œbӗo0otw;N| e3Ů %_1$ ofEbrx,vtr÷"KamqK'Ǒ^ۦoC[Ca ce]{Z3=ө*!1@%Rd8qBN8 ;b-cݟk}Mn?԰|YlD"W Wg"w,: a0wQ#0(Awڂ&"AI/gB$t4)߻;0驐4sF ij'r)+Li04!2֦-26#X)7.Z5a dzǜ1i R4-% z)[qL AaqWZC{* g -hM㡏΅)2cRx0-xH21kN|UzsrCg^u'9K(b H:ZZ.(8tswmtr m,On_ҕ5c{^g=Pv].7ػ-:׸}6w9`1UC-|m41x8MSdp-HI܅Xy߶LϨf7yC.&8 )g[Dt4.אdôݫ^E5,3Y=VKvum:`% ސۥH!ͳ t=f;,.ZBK)qbuS~ ϡ=_u*;GìKwŹg1<$F0S Nuܖ k 7RSnrMqIzqƖZL0ZTT%6 ^.sk[sZ^,=j8 ݕHK-("W\-m?v^H /Om^{F W"\&Cnj\/^Β |R LǰQh2=ׂT,]{8NkK:M40 <> 7yĢȜ]+^o6(9mت\6>Om~!^@ߗ.~zS@b-KN,e8St類y?#i:q V x*KL &D?tTzv_9_(rYҔXauFfSMɳ{0|ȭ/~HEm=]b,uK|iq鄬-J{m[7bi^NR Qw<$}L&ʏKQ/#9=x=_f. c<KFR"Y-vyY{=,߻빽mU)=hA* rHr,G+ߊגcHcf#1#ٌR"lWb`7VF:q~,Cf'* ܣTN5uxBaC>'/Ѡ׾絩I@#^yRIQ76f3%%b aORr;R$u8ɖꑾsw b+|:D@A`H > HGa@L6uX>X3*ſHF4H&ߌxFUUWu_!f<#/j0WĚ%9rOtSڱ/y]і j1u ,ūv10'10* f\7 endstream endobj 1231 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F16 138 0 R /F10 53 0 R /F8 41 0 R /F13 66 0 R >> endobj 1229 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1231 0 R >> endobj 1234 0 obj << /Filter[/FlateDecode] /Length 2484 >> stream xZYo~ϯ*qvnրdN`8e % 2CHdTuu3]?z`WwEQpoGOAL[Y$SbJc!yd,n>g" c}^o4I^07?][-uʴ"RHNB2a< B1.ʹciSJag~s;n8C i:%<bk&b񉰕alPaP R><sy"eBv:;IMN221;=KA_Q/͆Y:??v UǪ&HΟi1=N-w",&vǪ)Fp`fc<=QVcFM2~;eJgS sUgGEF{iGpyTA])'O9.D渜q,-HB ױ19KpLzT!L _uL^\#(zN{Y _S]y}]ؚn3 8z\!*(]R5?+td?]ʯ,wqWEPA-@aŐuˉsaTb;2Lpf$6`fט}9ʹ׷ [kLnD>7bOǍwyYVv-LÂ^N04*R Ql%>LKNX[v`T rG>P((K]<@:\|56E 5=͟0LŚ1\9T"! Ў?WNzrv`r纂FfRND~I՚t1̨ (eo({:ߞ V~L̖C@i%c<9i)HsK> nڜx5~4w!W+ƚ޻f)͸kh«)8>yJk:GCu0MǮMjN$y7kf۱? g8<͵잴' &ne v)TG:녎=%o:ɣzcJþ#g^sݹt~'u]l≐؊|~dmǙ[ p0&=G{^C!wdP3 #]A()E#=#cX> endobj 1233 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1235 0 R >> endobj 1238 0 obj << /Filter[/FlateDecode] /Length 2641 >> stream xڽَ=_! /jcDY; tQK ~|XZW&F^DHu, G4?Ay8dPxGOIŔp?&^Ʋػ9w׷bX%ARJ?vqxhtS߼ b\w|"Jw6"kw-ts[fDy "74|cMU.rw CJb!≅*|̷$ۃ8h=8:#)XNSl8NO"GF>NLIB}_m)m<5+i >Qcʜ{ ?Ӱ0LVK0.ٟ5>%`x[)]@Md "0ow09r\[}Y.`F?SJ94:Zz,$GA{k@{%f !xC'4%Zta!ǕٺC" RShw,ZxN#E<034"Żx4#}B J-= &C1<^q)EM]0%{6 [׊.5 `c룺Qbl B7j PI_5Ղ+j W1^TEY1\&[B7з~B^Ρͻٟ3v.nRZԺOc>W~2HZf *ݯ%%JFjUb+2dP©\(EB_UdHEtNuN}(Tc XrX]?;9@m%)pU`˒sl-HP'!lTo tww4k$f/PD_$&u!:C3<,&ťg A$7l/Gy*6`ٻI, Х3XYO/0OUihj0[Cel55pԴEkbDP]7ԝaa;.nE˴S⿇{f5tZ N9j4$1M~~mT`كe.4k|W0ِy%|;{uwKJpS> endobj 1237 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1239 0 R >> endobj 1242 0 obj << /Filter[/FlateDecode] /Length 2160 >> stream xYK6P"ڌH5M&od[Vֶ\ HztOeO$A10ͷШCD,2V,>ebƻ]goyd,K?g" %|M^/7Ti}ay>{vL 2+8,-u)bƥTwKl滱8b,j:ܜvhp7Qaiaͼ?mY[Do?,zOk@-#lyp`9˔tS4\iX #lATD+1o-ZZ`^0<)0LBa;4,+sk4nOfneۘ~sȊYX〫 EXp[䛢IdLs3Ɠ@=j'm[tj&UY*lyh౽axT왁]1W@|dT1SjjQ9#ĒIq.q[e c:>aR͘JJgh[ mF&E@G-=1 `&wنvJ,chL"L^]q?T\ߺEt$M? /gcfgq_*SSW&|Oq gGekZ}+qv;V"5 KPz:%Jn]S,׾|ܚz 9eAT~4S49⍎zh϶n6?l&6`-y«Fd`DDTP$su )zZ?jWǂ#<,J*#w`vKo -@eK7C)W2WԮ@Ҫv 4לA "݊+'ұ~ZUx(w1r{Lkؒ@dúhOʩWj_m&B0%:g CNf%'}d^roL]idqNjqV\u,|* Sbזj6wD<#:`7st/AN,dm:Eʈ*@:<5c1[ )F س^+vrJmc ZO9⮆EˍRCN.|uг#a~&&YS\'>9bԌ"ݺ:gܧ{+E&ȃ?Yϱ endstream endobj 1243 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F31 329 0 R /F13 66 0 R >> endobj 1241 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1243 0 R >> endobj 1246 0 obj << /Filter[/FlateDecode] /Length 1777 >> stream xڵXY4~WDabMaWYB;c1*v'U.W}uv{! Co[$ޣ@$,^X0{޼μw# >&ȸu7A~NW M3]u˾R͏/9^b- ~y 1P"QĊD|ig87j5 B#U7(e -2eX,LsLK*gIŘ땾A\>572 J8}#$ k6v S E%Q4{k0WSf pDjJTP8oa8c x]x$_,zk B0bj@́E9BqƯ5 \e&>K A0~OG}eRIR}MwSfn4E0q}ʱZ pk auF2?iwׄT9YA7g}{٘PO*T(: H6_1¢Ky7%Tg0<Z&h%UWFMe9zSյ6t M7-JMmkN}UV+*0 &{X5Ƨ0֪;UjZW]I ཮4s(^nt4g"=T]a#c!`x!,<&u{R㧡>~KZg@ FP˺v^Oס-OR=ЭࡠeP.f.D">>bK` LmUF~ ZBX<&Fz f,ҡm^.Q%4$o9(#  bOE{ *pàD]hmt?9#ƈOD,=V!~.D"UZ0ܠ&u۵ۺ~˓6ɂ,?@S/,cz.w? TXT'nv#sc[KRƈFaR'x iw!EK˶00La9 LƮ`M5 6or c~ ʾ2ucvu(5ar^Wp%qVJ5GsJ653}rA#*FE1Rd.(U̝ &yݦc7R#=F>:4v\Oز#z9L+ pԘRa(i*TniAo1qq$f :%56Ly n5U> endobj 1245 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1247 0 R >> endobj 1250 0 obj << /Filter[/FlateDecode] /Length 1826 >> stream xڽXK6Wp֢TRáU6xf'>' hZkZ%*Xopk!Jic82Hx>H1cDtqߛ}nQ.76XnmdMBI(7_ ,#ss7XrKt`)Ox`ѿ> R:fnߞ)3E޻wė!k5q 08p7bs;(al4UpDnޞ; ˣ<(4KDs3Z2QM1٢j[M]YܵD|V.v tcj@Gx#Tҁ,.bi|S&%ݵTKJSRL"1lƏD7os&Q9p6~.J ARY0K(sN\Üh.Ϳ K/K#r`%8 >f*j< %ssׇ~.0&暃#ǩ0" ~/k1*%z>MʬB)i GCTQ E 0Ma몷I~ewa2t'[hZ7G Z `Q+١!u09^Dl}%5`g콥NSfn4%"j1MH)es6w?R-(Aq(lD lefW3͋ѨBF;p Z/^_v!W]^Gβ,>뢎]EJ`$7${CXQ\(Z}9޺ڥ$.%o/ݹKs \nIth@h[-\fVU406xB7ȵUA_r |,W %a7&BaqpqC3'ŒA)eү$qUA4(t\A~_# 1rK `|0tE,U8l e#iDtIi+QیX] X=&qȮ/Qg-kS@!phH99jHIyBa#yEC&>O=|_g{sdP'SNn䊀:kb!sӎ~] g*dwlK<< 8Z9,tNg@n'jqKYcW0'ctC̦@$LMuBˮh4x$Vl5w _CgbNLdMT%a= $f=O#kMY;MֳܺBϋ k†ӷA#`Zb><(=ElqL0 e p|-Y5|jյhB4yBpE[Ӎ&Pj$PY\X7d8@J8p8ϕlE({je:ECx" I8}jtC/a*RgF%ş*9CT2.IɚJUntheDC," !.~~2\QSXBfWѐa&+)Uפ8NyzJg|i0Dr#WRpţ-l}ز124_^BzpZ!is(1[wcxa)a5ayl>Y endstream endobj 1251 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F22 218 0 R >> endobj 1249 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1251 0 R >> endobj 1254 0 obj << /Filter[/FlateDecode] /Length 2459 >> stream xڽٮ۸_1G$fdEƦȒ!E?%92i}*fqzX'*Y)W$eXWLX"^YWde9%^ԩz# 7YGvYOx 4͊289S>tHzu2Uo~д߫vvȢǪyuNHa~]yDO7L-ݪ^R2mv"$xb`ə,8Q "qs!ȏMwսHsmV~4i݆U-]{s>h.M.QDfd5cB,-:3 a44;ɶ=UE]5zvިvQLִ [w9N腌,i@EMTe;2=^f,+rE9Gp3u1.Pw IBSBruJ`DP".X`9Ka'`* r4pvŌ7\paov.l8 Nvm wk0G>H0r@ɲԣ)،csg;/h!r<$LXɣݹJ4ƉjaC1+u)x+H!k[; ՘=m0+ )ľQ,EʣmrJ!@\H{vubTg4.]JU, aVmeՄ8Jэ;*k (W[a[rZ II'@hCo]7ݱvxuR[`˿qBg+4=p+ ղkIm,0қSA =XRrGNBk ٺv$$P7L<ŐLVwg;[=ԹngIwiLMU`dF8#&Ѵ-C[σ6ذq(`sb/&67{[Z+=wOQq b'73 -CW!ILl}!:8Nkh0'{޺3g 1pmғ %Ccau#d&*E@ j.'7JNwhPsiq&Y E_pTgch!btOM;66M,$@F^U_5aP<,AMPrߞiKE^4S{< #4ؖzx=>k/ W&E `0۶VJwYa-!D r>- ~yO# w~ /;4 &ZǢW \HdAtrb&}(SM\6JuX#`!8BCle`hJ ;yCÿ誣ihENpH3ٺkGoړ#@vHҰ-!.G͒;`v(Ch>1@@2 ;'3 ?w? 2XJL C(YϠ2ԩ;6<-1M> 9!ֵ tmC&alɜE2-.'hsۏ.j8|֏߱-owc7ety\>}KGdЀGPW8ZAqEvdoObO_[p⟿m[Gs.'vl")Tw`n!ގYlIul> endobj 1253 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1255 0 R >> endobj 1258 0 obj << /Filter[/FlateDecode] /Length 2413 >> stream xɎp:ȃ2GܴFf̡6J]Bے!])ђ29$'R7j8^lv zHbJi~bJ?xmgRRս% Ā\ۻ2Lf-8q~XxaJL݊Du};;gw0׮z]Cu3vm'4$ $n%!ԇ#i? H~8/E^n+H;E@^@3r996Annʹe sw;KEώwFV)rR !Q(-(@X( 'seoZz]eӶ;G5zQD86di׌njmpPam`EKrtNW)?=@S¸Ԍg(zI>(Pt(fB:$lLkh,&yUT h(D7_+԰NwVt]c͡޸hu:a'[FY_1: v3yX f/$Zl'$`9XILYmVU{#z^ZWaW U't+X ]v;S; y3w1m۴̭a3e9Me|3q)Õo74Zg(y7G\K1Fʣ}HPBS2q4q6[DPVĥQӸk/c|LmUYBiFa5]HpLKT:3+YҾ>bn\>4j2b}ҡԐ9`i4_Jspq>nhz7d m\8m2Z඗};WKs!mֶge^7Z^]50F A^&LOwWɨ;9g*(1{|'GvКi7꟫qxsץ %Ȁ2ꊽ>Gw~wәe_0-X#TnLO`%I+I{j&@UT ^h .ީs&BV c˱څL 8B>6n Wp2;.%1JPu#а0^*wt'rYiо*Ԩ J+;ۜלӐ6H;w;0G-zQݸQXҝQ\j6qKFLwtEYϲ<Ռ5k &~RjzP_Z)ZW20|v? f,鴣m#NXXDFFͨv; Q'cq?#-Ƿ`Wklfg\>' ZNogx$Õ)%R` .xT vߋ毰$ jZP 0U;@Im^nQH>vL{"p|)!1~[L;q5S|6Y"T|?ҏX5/(7^cP\輠N7vlrזӹYTgf9  ?ž^5 q ʗ{SqވpZd.&.@4"X zxY%9j2GuvӺy;L*H7$jU5a|oPS@ lM6MV Gtx9"U6?=Pi xntpn|HUC^ T*K ?.6{3=oq֯wL $ endstream endobj 1259 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F22 218 0 R /F8 41 0 R /F6 35 0 R /F23 221 0 R /F30 286 0 R >> endobj 1257 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1259 0 R >> endobj 1262 0 obj << /Filter[/FlateDecode] /Length 2520 >> stream xˎp2J$;@;AۃmӶz<=b%;s!9,z?Mxs_m7yJ٥٤"e)z~(6%+qܞ0^nKąx͊-~laɤ?uh{z0&2gqa"K?'@/ilwϢqEr{/k"Cݑ -o@Dy]NemO$O?5d$/0BV_Ȑ,uhY?O?L}kLm:MK⁙9e0F\{IZjmڏ\p!AKo:hGI`:lo:!jC{F3≋b&@ Z:r5xi:NV!E2aQ@/9WybTNiFTv@.bz ؂3%c`bq@x슬Jd32b\d 'v@̀?N2lQj ȔEV YWd]SNlDJ=vtl@@(Ȑ**5K1B"ϼտ˱ +R5/yBAV; #  f)bA!-KH7\_ߺ xN_"T֜1tE KJrPXM)5K;/9; 2z`voIYpV(yP-Bj 2Kyf96Jn0wެFm/\r<7,vw ݧ*'³r-I fnl,Cs PEOT(,iW0Tj( @2α|M)~=~N* l$XCfI9!lѠ lp GX0KST*H|X)I¬ BUWTra,߁Ftzn)\LMU;YG-]$@^\Cp /L~nK.7jk-񢺧Y+tMʩu 2vqR'FGZU$hSJMa⒳2udLsrMsjR6cđ+ϊ*c,Qe[F|l|~础i(p~b-tuL;Zfs_ -}u mw'bnO_VJcoÓKT1܉<,& I}iJKEӇ"vg8.2 $0KKP9giTL6LLu7w _La d(i*X endstream endobj 1263 0 obj << /F7 38 0 R /F20 182 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F30 286 0 R /F8 41 0 R /F13 66 0 R /F21 215 0 R /F12 59 0 R /F10 53 0 R /F17 145 0 R >> endobj 1261 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1263 0 R >> endobj 1266 0 obj << /Filter[/FlateDecode] /Length 2214 >> stream xڥXIWȅZe.31$vwC"Q[Dyb6zHDQp =Lp H*k#"4VZh9/>Q< ^ydnLzv;lKU,CXǡŇt 1ADty?^j+:unJA+oW:|4Tm{d8Ȧ˘>EnڟTX 5Ue?+b#d Q|W8,i@ܜfVHDW}*jQ.ƶxvK~el5[H!өѹ ϻBjm[mdD[SB‡$<芆'oN7&3:,O4{dyؖy=[`ȏWA O&BW))v`J oL6v,<$mud~p1(`&-ȓhjOhcq5l] mS L:ضRd-l c+u O*[A 3Sp>^(f;7ϵMH&$ktD0֞p*]E|l_"Cq.SaOkdY8lߪ(7MJE H p#"]I)ò㰁B&2\ܽ"m?*ڃ\WH8{?xRͼE]59):3g+Bד٣u=0pDsscVQ.˲,o;{v"O|Y$R$?X@am-m+ƭɠA@DԌ&Jce(Φ&"5?hXWlW811Ld#u%p<Ve%+ =6(#B6"R5G70OOSaohD lWJ %zM\tSaZG knYzWAyvcRkwS| FG^6d"Ɛ!E'0Z_Gy.-:W?F%'P,Kk|>_TTsX! 03|\uTk| .BO<w`gX`2_W/;MAyNPXX^bP WŤ7Wq3Rr hen pÓ5kɬ&9ؽ ze)D9Dϔ8,ʒ܇ZyѡP"4XFs}m- vbw ɩڢJg`ym%#6k%tdlεmVEU0tHOnUqH8`._XT| ;^̵aq6Lj}`gnKݬY/bƿch[x ̻B ޙS'NWHW虂GdIhFՕ h%(& ,SZBsa_ `} 9{M|Q&R=`x |/`  38H`ㅺ;^B$QZ8MDFTG6JDn'[!!cDl~@rOq Bף!ꫥJ㡕x%^Eʂ>˕%tAb:^XtYP$jGH{\.%p֚sH?@V\_5=0EUtx3ۗc? ?a:6(oee51wG+kxݎSV8VXL'3Ԅ1q d<)7Q`5eZte x!]-k|h~:vS3K\0$KU\ \h TDZTɼVN֔r-5uu&3§oyaEp0|Y2A. D{ endstream endobj 1267 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F6 35 0 R /F16 138 0 R >> endobj 1265 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1267 0 R >> endobj 1270 0 obj << /Filter[/FlateDecode] /Length 2479 >> stream x]ܶbhoRnIE}赆nO]$m.3CRWm|ulJCr8_/JWoWn# jҔdbJE(_mWRH"y^~X}{yʬ2.g%]\KÓh֩"~xN6_&oʢ]+J$L8eW~qTKݚC A&)ywW4-ʤk0s?>h_X ɔ#{_|XQ~0,<̿Έda{l7axv*9:fS|[u%^7DUj3;(zvוGoGnŻ|{_N~ PƇ >FPNC[m{V_׾m}F3HR哲)#J7m]H@v ߏPɟS[ TP'Q,&nOxz ;>ٜwwgk<ɾu} I7a}-"| U[k׷Nioi.qΧC~ !q(h^ε^(`\n GȘf`~W %eoJ̸Eʝo܄kY7D,+՜J,4yQ/M2]fâ8OTbrR-._I/F_D1p`GP:4,\YoA;rHSP`]V, (֓[OJ}'%gM'79s (Bk)F1l63MPi<[M4\IèFA"6& f߇i'Փ{& * 5C(@])-BV *Z_%`у\nqS 5u6lYi*ʼGu6fWr,$K꾅jT}S,ǻr<" _a=(I@_9=ܦhC8Uo]-wyW7-u疉!×%[ >(xS D8mPߴ.dH6M=Ls9Tsi40O]z#hH}$O߫pjJh6Ap[&D)Ogj`/ endstream endobj 1271 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F15 112 0 R /F29 279 0 R /F38 688 0 R /F36 578 0 R >> endobj 1269 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1271 0 R >> endobj 1274 0 obj << /Filter[/FlateDecode] /Length 1891 >> stream xڭXK6 WhC5#m3&6PߺZL$ѣG6/@@lv7\,AvL'( i=_WeV2"ZlpwC?-r./Q"|_xF$n 4|iQ&-\׋摠զԷZ+Et=C~ٛeݷF%^ 2n`O0:5:Zoc$}HhO7F΀(w|,BȊfPU}!ismkӐ!PԺaѺ\ 5!ě5(\t qA\bU`rn! sMjcgQB, Ӗֻ8 ~Τ@h*;[B,np }U_d-[tRǣ1h Fة ^8E1X59άԡ^a ZO1hֽ sL 'ZU(%3^}*6I#ע`Zf\^z( ju5OEc "'p"Pe=A:!|,ЏRs'L)YHKsesis&@dYS!8 8oϧpfyS>gOѓމ@ j lN(ȇ`$M&XR"z3Ia"sIu 9iH}^~&cEF⤇:"~W 4Aܷ5懼g, GާhH҇#bkದ^aQg_ (ʸBh74h0S:f'v8L>ۮl1t!欬6(ٔ#?||ܝgEc(arpEi~+P-HT]bV.&=?ph;t DR]V*Sdw嶵uJ;k: ]f{bm}~/F!ZW\1&85;9OaK~hl]{VC(ʐbcHvqzj:x/ @"\6AZ5 ~9LB;7D7͂׶[D"މֳiQ0&8ͧSXj rF1ax'`pK-ԸP)DwFI#ꦕ5Lsj>L|es=9h`_(' [F g)} 8WoMUŻ)dIܨ[dT S[9ݪr(xG$r~b?0~?u4-Uc%[69RJyAof;rU85~i[1Er}p}hsWDW_TAyB4; Ho\!oq]VHdn+@lVg— LK'g$#IѳԌ $;pkmK ^#HO#t' y&(Z悪ϗȄR`AI<3xy*G&վ'>'zTapeK&pŪT#LÊv$SʢןŅ$ljQ{:uE#YJ{ H]-htGs@tk4sTMvc=6xër4L7mU n| endstream endobj 1275 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F22 218 0 R /F23 221 0 R /F13 66 0 R /F8 41 0 R /F6 35 0 R >> endobj 1273 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1275 0 R >> endobj 1280 0 obj << /Type/Font /Subtype/Type1 /Name/F39 /FontDescriptor 1279 0 R /BaseFont/DMVWWS+CMSY6 /FirstChar 33 /LastChar 196 /Widths[1222.2 638.9 638.9 1222.2 1222.2 1222.2 963 1222.2 1222.2 768.5 768.5 1222.2 1222.2 1222.2 963 365.7 1222.2 833.3 833.3 1092.6 1092.6 0 0 703.7 703.7 833.3 638.9 898.1 898.1 963 963 768.5 989.9 813.3 678.4 961.2 671.3 879.9 746.7 1059.3 709.3 846.3 938.8 854.5 1427.2 1005.7 973 878.4 1008.3 1061.4 762 711.3 774.4 785.2 1222.7 883.7 823.9 884 833.3 833.3 833.3 833.3 833.3 768.5 768.5 574.1 574.1 574.1 574.1 638.9 638.9 509.3 509.3 379.6 638.9 638.9 768.5 638.9 379.6 1000 924.1 1027.8 541.7 833.3 833.3 963 963 574.1 574.1 574.1 768.5 963 963 963 963 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 963 379.6 963 638.9 963 638.9 963 963 963 963 0 0 963 963 963 1222.2 638.9 638.9 963 963 963 963 963 963 963 963 963 963 963 963 1222.2 1222.2 963 963 1222.2 963] >> endobj 1281 0 obj << /Filter[/FlateDecode] /Length 2427 >> stream xڵYKW-u{P7m3ά* Pkg#(e%E>%q^vgxݷIBJlj]vJX }]fti,?# DmAJ?EmC94u_v TJ44 pY44hUXEcך Jsu|-rDDR3ߜ>XU-}+2nwAͲ4Nh#J/-Sy M-~8lZfI￘B,5>0M)+?Yʟ 3dfBJTL2ܔ09g 9e+ٍ4NSB`>IQa3 @ ]N JX5.c@{#ܜȴc]xiE*=aVyeF4&4#qX r1qB0X_y0sܥ ~i+LA81v{| 4Z'Xcc"3Aq^0oS /6ad_~oʌpVZ/yC`HAvY0xC1pMeSȌHQDNS\N@?,anm GMnSRfC%x;dDxΚK-b"|{k3σ:q˜BK'ufZDD,q[ E"Yé 5R;.Qn=%M 2\y &!upEG 5+_Cǜyٓ0AL/M8w5'bVq섯xY=Ӂ0K%˱,U",NLZ{mm 6>¹]O!D(QX9" " e1hBBdHujsY.K܋ m!]?{'mB DbaҏA&WsEɐ4Wm( T$pzB!_)YRLؘC %N}RR={؋"2T,4a%$:- [ 1Wro<| =D:&pʰ=:Ru9j$AL\Ͻi"1>bײ-ZTP3,PyAB4Mov79 f+ᒁҔmӐ.]$,:d%H f'KO [Ո菘d$:Z$QJ͹=*$,5X6\X3O"㊅/:l~X`0]dzP!(DzҴZŴvUlbtb;0۬m^O:f򉆟[U7(^޵MeS$plHضE]>ETC?XO\,eVUMME̕D6\Ҩk̵hYz~[ȄC]+uMH`WsReOuXszmh^^q諲IW5i\8zzݲ^XPW|{qݞFd41D O x_ez[*S4+tRtNRςP c endstream endobj 1282 0 obj << /F7 38 0 R /F8 41 0 R /F16 138 0 R /F13 66 0 R /F22 218 0 R /F38 688 0 R /F21 215 0 R /F39 1280 0 R /F17 145 0 R /F28 268 0 R >> endobj 1277 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1282 0 R >> endobj 1285 0 obj << /Filter[/FlateDecode] /Length 1809 >> stream xYKo6W(X xi%Q)ChѢ8yŵj>×D=vizDÙof! BQ<qR,G  1]GA8Xm4Xy>z< +¬.dcJ"AMѴuqo]&d@yiQ]tߗv0wXIQq-bKʒ0r l I)oډnc.HKh|B Ed  ?VSݏvNf,wjXg"Zb5#Dј1jSHuzJ 0ͺf]\dԅ%uI1qlXʱQ0zj!h#$|V 1BlXXlk^,j7vδEY U-eB]hL[8RJ})3RnIL! 3>g,vwYQ5NvKxR6j] Yj-Qt?Q*ʰY1V#B.%G);w9$WPI> ҤN榳 iХ$/XSČa;1XfwfIO37h);DM{K&m|#֨]4r;L:cr,V h[N\řk3<*\c?Jva Bˡ_mxӣ~İ應pe|Qi%+( j *^#(PQ{7LM!YSF_LPԧZ z\>d. 3ӳtpl=@};s$D' ;) gq3A%E R60#$x D+GXձ16|JQRTJ%d{y=hqBZ h!EmH{ >;W&Yxa hsA?e>66 #/gP1Ώ8F)QQ}R*." GaF{֟^=IN.]aa]y7أܩ=n;K K+D)d߇:){D祯KF%>TKRxx馨XO ώX̛%8^-m_+#,3`/8s=^딦oW}8ӃH`뮌u;Ѩ*8'ғGc1e+#X=S*zpzrzZ Mq:S!a vs.mwǍJWz hWٚXfMkOVΜPD v9{`zZ@4`SL[HMBEU0d55sQ2GBf,χ #!'quJƔӿ& B֛l- 09`޾SOYS.M}] ٮ嵯(1KU5^݆̙l^r)sjfx{6+I"rzZfrM@_b6{ϳ|O/t@\ɭ&Ha}y~0{zRy5- TF8aɡ%jSMs1YV95Ru(LӬWPfOxkʦ,O;L v'n7GW|֣ C͵UdOf P~ (5< endstream endobj 1286 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F22 218 0 R /F13 66 0 R /F6 35 0 R /F23 221 0 R /F11 56 0 R /F20 182 0 R /F30 286 0 R >> endobj 1284 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1286 0 R >> endobj 1289 0 obj << /Filter[/FlateDecode] /Length 1591 >> stream xɎDWԎԩe;0=hЀāPT^"=^W@\N$v_%^"U-U.ʔP21z~*Qz;BVTj*e^F3,Y+Z.EQƟӠkwQ*Fzci\aD>7nmMaS7z`'LK9ME{k4_xa5(Kʢý.@VOa vW`TU"OeYç g22+==ہ&K=H1 Ta::hHb쌺oO[V{o`y96ƽ4F.$^,smS>nExo!Rn6[h SMz^;tsW#=Ŧ7_L}ټ!. &\$jx|_T"7x#=b>pscsOO3f6{dccb%9#o1D_%2lWτc_s3ڗVUq9ͩ (|YUKʗ{MOL5<&\CX7;7F8G?*z=04~vK HBPQ&]*1EvD:"" +Hu~ {SWΒM@2/L=RHs-ǽ@YqMT!Gnx0۞i<"hFL[El\;{OOn(r{B~^89z~Z3w9o7`ܾe1uF?92ui`M渣J!YGE_VJV +ZI= Df.!~ôĻ载kOor4~nݳ 2p6# Cm0^\;=ww$yO8 Y:`0P%i j]V9j@pJd*8L5'!X?n4U[cR0c: :*Um= +7+su1OӜWݸFB^aڶ-U0 }6>,>/tJ.-߁{ãp;^ӪOʕ9En6ɗ⨅[MC/"RL耬gL"ï*\ U3KBDj1+ak%E:d  @4a`M_87^.;<|\ TJy! )|5 85.2 LgBW<t@kJA X [_{miv=DԾ#49:"&t\uHƠW8,*u~ŀU<7C{B"B4=,|bm$STzC֌{NMx`Xk^#,;T'!'8{ endstream endobj 1290 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R >> endobj 1288 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1290 0 R >> endobj 1293 0 obj << /Filter[/FlateDecode] /Length 2613 >> stream xڭYK۸WHUǍnkU{oϟ>aujMB+8 uMR;k˝ڭΣo[]DnKS=w G&DJUMWއcvYtm*~c~_;kɝQ!3$ >ñϑ mu>0+~܅׃LY+~\fԌCyȳLצ=YDL[e@a߷E44 wآ(eҾs&4pNK3ק(7~nKZ$ pL|==3ʋr$q]u~q+?H㴔"(}͡gk"Y3J܋zY.=i\FY(m۝TRJť%<ثшl tr m}K#-~ s#u] :fm=sCݡ/t=SD%t\ ֤hFc#d$ ExOW loC£3 7a%>0]5=:~`#s}b, «s *c@?wK3 b ^=s}$2pl,0"Ez .°`C$JN@(`Hg?zq+0OqLW<_ELAt`Ħ!fr7SV$фrfvܞe5TG1F !עVYrߞNhP#;Mt^"Ftu`BqFv&d9E3lK7& т*P*,~*]UBZ,NL5# }PtDί(82KSmw-}`UK=sRx녓t {`_**>zEA+T]WpQ9 ӵ֏s}ӔO568o$ad!vy1 Ίx4%M 7jf9Xf2QqD ^Wy l_yHZ{4r߽G+ձ>m>xL*W,<5heѭ?FA#Xp/X1սUX:3i}0h+Oƒ%I 9DY*x, ozT,rVSg)J'm><qɜ衺Gx(08CҸ[\^;/ Q"_bմ_Mfۥqc{2DT uʭ0%*d y!o->["˪2cx#V/NqzOdXjˍ džu?kŏKոaoa sėct[O%R"G e$*K* B'*ўtpJ_e=;䢉\LHRqM4wcqyC'%+Q:=@򝞼sdZUHE':4ɦTm$_ -ՋF(5(JII~gG>wg&&[i5MoGL2Nx컖\bPFiף{o44njsX_,$= l)ʉt"q$af8 7>@*]p{olDP>X,]Q,(ܫqI'.GLcU1ܸ&&Oַf,"J7t&XWBAu•U~Uh(otc=Y%e6\A3T :% Y6Eq: `jCWtH9)MvD6/jlAp<$\sŏ%);JkC]{/>Rg߉9 NGY(8)cBO5q=0 S.MXp޳2i<7ߚOT?fNvij[V1޷R1]}mUxrwl{` k'Э2f|lJfcBBr:_jg^UX8 N{+E*(pw3L1c|Ay8ȟG,2a4ﵖ"AjU_(?o>o'{ |ON|<)UAyˏus2쑥W鯽Ҡ0_f7li"6iW#^ TKqEы endstream endobj 1294 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R /F5 32 0 R /F22 218 0 R >> endobj 1292 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1294 0 R >> endobj 1297 0 obj << /Filter[/FlateDecode] /Length 2142 >> stream xڵrPTsL&]IŮa4X%dPa/F|IR9h4nbǫ}tuYb)\I!a_Vz],W=a<_=qGOܙfBD}>7Jmƚvo_J'H1^mxԗn k8('l| <pk5"C]7jD͚ ֯tڝ -vSeS!~GXYoR;pO. V ƴ9x9܏$ Gz#EH;iȣ֖кA W_ q좺վ.϶Н+ݚ!@s Rwע"H6XDl 8xVd"a"`wIbֶtl.zB_3r&+dRyLI@[߼;NopLbɒ{zo쒔l`GLtuX=@]ޜxK*VuohKݮTMAfb"ܷ1`:m s`tsAȾo ͝7eM1 ?ķLS)uWd.c\pQA$p'$l)S}ltY: {<9wÄ-q{/mÍ- "Bxť22)Eʅ 8TԲ9N, @yGF7.En=Cr,HyR})$:`<z0516TP($G/ F~M]'axG_נ.TfLBHVuw, /; %Z!t'WL`}ird%"ǚsc93}hF[~)m)}8NkoE0'쬿 ydh @Z. )If.kublBC!#g d1SCcDTG_RbcT6Hs_{4md'\ O22ɁeBsX> 6=V䓇倄mJ; &p̯ztRaãE#Lp[h'CMkrQ))SD`Rk rG<+۶4(d:; 1.{t\H%6?# PE|ϗe$JVسbooey?P9ETME'meŐhAښVPiVnyvy C[2$}AJ*HLQZ`s#4|HFw-,ǒp=H^QMɉvv?}F/`hmg>vR2vZιD1t=*V.=Ï}Ql<,&?եSyP͆+jᅌ] O8l̏b9oTJM!pFCO<\ؽCޝ /gpLs@|Pxݶۑ0WN-a:%bAuccLǻ;9O NwrW[ޅ$҃oAavŒӰe)yח}{:I/yݳhEWW0{v_:VCp["xfK$ԛV0B3z$:CA[r6O;Ppx0luU|2(?(&|R |m7.{2w _\2 x)qq''PrPpEpxY%J$UtB X3Բ(*?PYD1DsKd]Ko O/FuTbeVȅcaLwl;&LBl AxsLX[*aX`i endstream endobj 1298 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R /F16 138 0 R >> endobj 1296 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1298 0 R >> endobj 1301 0 obj << /Filter[/FlateDecode] /Length 1896 >> stream xڍXK4+rt2xU{pD5${w_O8@qՒZ]'yG~q7M\4p~=j>B.+A-⬁oIJbRz7yMY^\ڸxie{(8ih W%inE@Yz@:ܑɳw^2g+sؠ:<9u8>0wr=i{VeWx]ے4̧=hgU(4#>8yt3A{y%Sr$L x* ɸ"䂜s1ᝲ9YYy7Y]TVq]ZZG_٩aRgu-d4ٔ>'cu$\),,li_f6:AF!Z*<$ &zgUʓ21S?9T^^0@)չ0PB)3A6Jl2N /[r(eͤ o% <+DOPPwvh⅒b.0q;?3+”&LmE_c;w^&hyn@y&%ATك17jkנU^}qȎƣΪ%|-DR xOuJ6Y޲jH[b8z-y385rٳ7iOӂgI+gOSGubjyV5q.K qlYڒjM7t4yi1 lI,٥, "ed(t3@'?O >W/+ɺNIa"A4UP;Vy.Ч y||i\$?5C$\d[{pلӝE7ld:7oRq9VPH 6'uG8Y]nTӓnL{ >:$b)} _W4,h~ p"+w}A+1xժ-U)[*#Ka&b"R\H^[ YrǫRԴt FX@LjwR0O_1$SȗnPYhBBwG6r]N{,y͐ 0N'CuTb0#[.Ӻ? i7*3<^6FÑaUk "= /"vd ˤ@o8`5`n# :% :iA%- Uw%blVT,T puZ 6CxYVUV."PJ`=)` *VNriaq}#v80/pU@vHS(ؿʽσNutFtBkn}Wq8[?C*iacM{(nh6eϷT\O  a4\pvӀrkgi1MՋ ёs^݅Meu>֏[_EܔkgR F5Uc7Y[ B^5O/'́Fi0[paH s W'x3Lzݻ¬ ̓oVKh_Ty9^A>p&͑r?Yqy13XիI-8/gh)m {gh& < ޷pbKh1:RpYxb'HiYQn^v0'~}e> endobj 1300 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1302 0 R >> endobj 1305 0 obj << /Filter[/FlateDecode] /Length 2271 >> stream xڭXKWj团o;N\NUʇ( I!Ύ}$5R| @h(ic'ۦ*qT$~K*dzRowiivWe$8I~l.8T#vE@Y+ML-{iB4^[BS`$M,/)f0 aqd_puw ,{kyy`&0jAϚM G'eH&iDyzH+({ rwXpj-8ɂ9A7NxK6rבሜ|6St(?Mdw!06a]H89lY̼hKN؂9g$94"tw'G&ջ܍PlӈQҗ5k> )e09ٴ %^|R=,%>FG.w!dEbVV8W"b̵Hv]_9FyR8$!hxyv7id }ZU 5Z28@TGpgyV Tyo%@R"ru+v*[IL(zڦB3>E Ԝl}=ae(}&Յsnxd_aγRP+C{&,yĮrv1 q#%='!GVi}.r&>va5W|;uZ\{N-+0_Nq<9#/9RFw+LFbk|q"t&DM>8ý+ vȢJG͖+ubl,%ҳ5Mz+Oq!tP4dm,^]헧_5T LƤ9[ˢtxJZI0j.u^5RzQm7bzD;hEc"`=xPO C(FI7뻍ԕ\F=QbZ!. oU~TO&wrozqŠ}>?plTVjPA19 %v.;OH5_*o IW,Uk=07'V^3C Kɒ.}ytiG]>MfbG[#,L2i! .Q+mCZfwwjp(}rn>C~Fe߀6pлr1|1W[em/|/)[_&^~bK%W[l"tʂ_u‘qpud߿Qd&U/@٨Hr_gl]Wk1 mU }w0mBL94Bç;q=U7|xe endstream endobj 1306 0 obj << /F7 38 0 R /F10 53 0 R /F13 66 0 R /F8 41 0 R /F6 35 0 R >> endobj 1304 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1306 0 R >> endobj 1309 0 obj << /Length 1608 /Filter/FlateDecode /Name/Im31 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 1310 0 R >> >> >> stream xXndE W]EH|0X(AHcv:@-4te8 y#Ww1h=HSzLaoql؜6}}0:ܿ;!Ǟĭ?ug{j-`]r}:Q?Q>;y3:{,e_ۃ%xϺ@9acdf(RhT߷̈]\eO~}\gHXO${nx/X#ǒ7;s%7(n{] j"+iNcPt#L/7$g?㗁-1 CrsP£Ru<f0Zc™ k&~r^zգÖ9ܿk2~NJR-g[ZomI@I#p>Qnxz50̑lg~ik#y8|MPzqoI-fh‡ ~Gf3֨!= Q׊7*!8f-4吺F__7_5B0ExC# @se&Nĩ6g==07$)W5@w\$ qRmC<ӻzJJ#2쾅'͠ۊ6-Zǰ5 2`9% ќ#3:#B{h;9|G"{p:xDk|OtF i) 1䖱h#/X!t"X"A- )C(L)'<<9U@S5r,dRzB>v^^/K'` G#zoLs$fQnOZRk# Cq"g:,9N hF+ U~fy:m*XQ |~F(dm[3i!+,QR T)kHWt.tF{>UËDP+T2r*p RIPm/CugeF5:w K f`;G)CDcT h#M?1zٜrS}!䔛j#2ג@ Ngf0-gui5"3Ym-*1my2R)8QWcJs9gjHjkf`q+ 5ZBnps!GPfը9s(Fmg& ˉšQn#q5#m=$^"Z~" ,ȦRsz Vm`olVWΊX*-N˶F6(U7-W T (>deeMJ`)ۡ3ƈw1jFȿ5TnBGdԞÉXܭկAӨj"YէϊŽ pD)~>mC}j8н wW.wW?7Zy3ļuWw!S ;`^b6(v׆hۛ_w# endstream endobj 1310 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1311 0 obj << /Filter[/FlateDecode] /Length 1511 >> stream xڍWKs6WpCS !N2h8-6||q~}w %KJ&],}SQ8,:zҘ*Z&2D%<ӹ>F^qTpɺW4 IK@H4eaQR/tY{ʇC\*ieѝe#Q{()B)v%3ki£D.r [x68hk#KP=m@77.yS>i9ە hk|l3|6x䤅s+&‘b4 uCip=ruTQ=Qʪ[!?݁v76.k aR=zJMA̰>SO:ݡVx[Xmq|5 v1& O~v@%FfԖ!3:lhiL)RrB3؍L)'g>׿^Cj7cӵҗφA:Q5:Ca8wBW(kCLDi^R2X4 -NI=4I0&^=y4P0ws.9oVqnķ T dע^oo s!o` /iw=|d8U'Ǒy "6 QH23~z g)$ͳ`^RllG;l{M'p+N6A>?1J endstream endobj 1312 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F6 35 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 1313 0 obj << /Im31 1309 0 R >> endobj 1308 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1312 0 R /XObject 1313 0 R >> endobj 1316 0 obj << /Filter[/FlateDecode] /Length 2739 >> stream xڝY[s~Pf$xLfdt2iu<$lH @yt?mB o7I(rG渉t$"~7ltt<>LۥPO^\á8$ /0C4|>J,pWX:MژѮy=Ov5VsI͊geky( pO7_n~n4NDo]n>DoR5OoivB&pX=S,C4hڭJzs`Lŋ[/5݃%7[o}FBWÎŝ]nqqH:2d bf$ px~7t!j+iot:qkg ^{+xzhp3? }A/(UU JL?,@{Hs2!nP=g*-|Wåa$tP38B?Yw+Bԭe|pLE;H1:W,6Rr؟qO8 l$$f3es\]GA_Q4dՓgixv7_f=tEU j1{ P.))KLhf>ݔ7uJ]̱JH7>uڼjXw-:9)乘{I@ѲB!F0r!`>6@#b$!$FDA{q:0~]b'0=` cq:K(p|iiHPbsΖƬ4[E,m8 y,ʐᴘ ?0b$z 4$CJ2tl E^ъSf fd"ܖ6C,(,59+yy\Q'"lˎi^kiֻśkT! /mGl:&PpXj! ĵ~{ff9+ıHU> 7]Jd86^kQ3BXd@!h]bz2@2|j겦j),_IPgshW$nC#WmHB#o7:w>5g7JluJ!x~Gk-T26+b '|geY-<dyFg]Y2:q۔#|se\nF+g J\,zc)OM/:FDF0> endobj 1315 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1317 0 R >> endobj 1320 0 obj << /Filter[/FlateDecode] /Length 1944 >> stream xڥXK60$b=l˝C`vYl.پm@KmI4(i:_zl`\,U,UыM, mćlη&]4< عcO'^y!(\_:]DDYN#%0tӲ3{/eiKƁ 5.mlC-uM t*y~Bs<`|bŤXX_ g OƩE) }E? BsW WhZXNN)܊ F 抇,8@H[ e. &pJg Z.*eh ' /ADC(ʍp0ROHxنN52bb 襇%t2(A)i1xrr{'acgp"UUl5,pjds(,|]o{-wjpFqLtU`3|ޱ[KŰyR=)> ; Y܍$ܶ5WQ5y"bM@숌%%H.˴wJ<+ ޛ޹v`귂D-wmV$ξ19ilG;q*Vw)ozbMJRjOާ V>&U;Fcvoj> endobj 1319 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1321 0 R >> endobj 1324 0 obj << /Length 376 /Filter/FlateDecode /Name/Im32 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 1325 0 R >> >> >> stream xTMO0 WqXp| i\8ulCUgvܠ6َAmBG| &V2zSr÷2,>׋Z*Vb1SD 3j#]`i|#nXO͢b)DAlxo?u*|m(X _+[$x$M9._ WKgΡNY5X:2'pj7!'#yğ~9wLq^Pjʭ5;hv\Јc+qlwj,_qpylDea5]nJ WU:RYDa֭CE+y=iYfes/úG -$ŘW{7R9 endstream endobj 1325 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1326 0 obj << /Filter[/FlateDecode] /Length 2041 >> stream xڍXK66G $L`@{Hl[z8ܝ^o(Qd7dz|Ut( }OyIO3 $Uv6|C!XC?B^LJ$ߟyn$ixOy^ߛ9Z^{?fw|8 BG^*$e:37+OHy+dUE*e~/ê5<JuWX™=c3~px6ep=k0=\_Su.$Ɏ@w]jvTTrbӶ̶ 8w?tL4D ~er2p>Tq:NIwи2F,F\: "gRVCg?v~\&d|{յd}a;O8kϻ}Zdq!ٌsy40 <$唸UZ8FH0ޱ|: @w-/ \55[K>눒<]^,8c,0N6DC : tMkF1\PӲPTRl8I%O>䠤T,I@b-*|vyZ/,->,5x Þ45KY4"2rQ U5[@?dt+ B:Qh`弿ڠR;6}&I废f:o9L6!2?S}IJ6Wlc( @ }S ;x[/9^3ES=S*&<]XD -}ȸ@]IV!!b=gs2Ba]EǙ~1:+jt"/2y!Wt ^51yX[U.Zśĕo\ 1Rgtc ^ɜ\Ïi\6;Nٛs4(O5!$ե@m+A;$Y%ڛf#<$tq#fY|fX&R09Aun @yLRer;8t0<^y⹠+0l& TKO^LdPaym3K'KZ.˕S#-sm[ MMqU\ʽToo MmVg=H.0T릅*lNGhxqΕ8(]c* L k g:>A˜*϶B=ԹkXHgv#PvwpHf+]n#D:h H "ƑJ(޳{lu¸ѵ.L%H6Y'^gWR*bҚwW.sd~ #()r*vX}pHCK+]oȍcdD*+ ~|; yb3%zlG?->S-!]pIiDe(r7X }SSUG!@ 'GDIݳ= #-ZpF R&|'H!\T endstream endobj 1327 0 obj << /F7 38 0 R /F13 66 0 R /F6 35 0 R /F10 53 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 1328 0 obj << /Im32 1324 0 R >> endobj 1323 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1327 0 R /XObject 1328 0 R >> endobj 1331 0 obj << /Length 1771 /Filter/FlateDecode /Name/Im33 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 1332 0 R >> >> >> stream xXn%5 }.b'qW$>`J<bQؕ̽]mrvδl˧qh' Yr~[Xh,&CT*F̲~e66ǥwZ0WԔFR'oфEݠOtڙtk:mݎ{ `WiJzABi慑;sn󫎽<~،X$}\җ ysC%}8KI߱> }!>uczрG7wB8VP2!qE7O+Jކαyj_4a갢R.^ʗU,,fZC\PO. IпVף7d[\/G!Y"We8iA^(ʥ+5ސ9ϑ >3h6 W>we_5`!!Q Au"4/E!$8Nv;i=Tq/7 ~l씓0i=$pM`~z)I4eq7D2-6L;uw;?}O҃wS#;Ԝl^N>G'j>y:7TZ<@JUH1kǦ7AôX,oV*M)7[1]!a:7f^q;m Z5;P: S2ղ7aV0 R}ܡ2 Hĸ>J0^ATNn:v,tKD)Oin$zA\I" ona`bPqg,ZĤj6|JtzᭉUSTAf8vXpk"d|'狈Lx"r >{HXgPNqz5*rH5rgk9]%G@:@1<= Lڸ]hq^~ "V-SpR|1 μ[ Q<ݯ׆X`*Z;d"D^nAqS=H'ȍe&(霱i;hA",#L>mEОEG2vA>{E1Zle =ۡ=;MJOwM7lׂcՆ'|;Ф(*05圫hښ0`iٜaH;'TѪu UBkg>uSY{;ҴlTz6ކ1NWc|Ҍst4] 8io:fVK{OШnf]I,0>s&'@PCrS[ )dt(|C LgE4iގ032B bWgw MI+*X%IOLSJɔ'?,.,㠔sNv1:mx \iYgJN d6XYfeTJ:3L+vf M{F{3GӽwH;#!tV8]*ǧ~bj55Գ ITljr$O|X>̟`$~]y:!;7w/'g]5[+DX1ñ|xz7nyku,x3✱[ -k oRa?G endstream endobj 1332 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1333 0 obj << /Filter[/FlateDecode] /Length 1780 >> stream xڍWK6WTZI= R(,CWm5zg;/M{pH 3|q.{)0:IXm$J7WTQyt<=|ixNuZ4i+fEViSw?z.tiMW&PId\%kDuO+CƄo]?Ӿ_Z/ҹdОq'E=u_M?LN{`{)K7S#kR {{g~+EgykK[Bod $˟M,k I]U`X0Dܮ:<M] NK6nntW.OKa2QdKqǺqXW,8~;u*=: bj1I+"u 냔~Llw<x~L{;$[L<.:73:TSlǗ-"vu\|A<᩟Ď#H)ٺ O5sk|.yy=Vz*:oíŹy,:bIё*dHW5I{󉩦 / jKY`ۆF]ґ.8/ ,DWjAL5Ds{{`w= U\蔠a /ΪoY8="#6 C}R˳lI{kNF@xшܘGX2 eggbZC$ RXsiTCG%>r>V[S@*?괷~ *R_WU|-\9ϔC窸rhI+8tYN?0\Z? endstream endobj 1334 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F6 35 0 R >> endobj 1335 0 obj << /Im33 1331 0 R >> endobj 1330 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1334 0 R /XObject 1335 0 R >> endobj 1338 0 obj << /Length 1797 /Filter/FlateDecode /Name/Im34 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 1339 0 R >> >> >> stream xXKe5_qv,x aY.ϑ׏?;[ET]*I}]ikoz&>OҶSXpBۏ 9}S35&Զ>pձ\Cf~.ݥ.+H|].'A\f&}\!FN_Cyy,؞os}j(HxmC(AN1Z| 0fi-9Crg 1Ǎi7x8\.@U<_ `۔g!L"4QGP=P2oofvZn:YĜ}WXGހTzkYf\}m/sYiX/rհpq*Ep< Lpw.W嫔t%l"oeRrC4RMӵ'*/'јHmq}-=lr][jcFƓ)Np䠑$/NZ+*Au[i Jk!sIW5i,_f:RXs,TEFi6(Ϭ;(כCO23^pg $GTih4.Ƴ$AdxƝ꤃7Xʟ8\'32x|@ A7ԭ 4,%xx)n\sPFU.eǹ8V7QLO\A}mSLU#s"aFNNרU  Z*ʹq<}3B@ڑ;k"c8~cfy1>DƩE2g2d|c96)Ct0[Iv)Cf~重& P876Pet N }TotJx9XW3ÃC,O1 ό QFvX ; ٪ٞ^;QUepӄ-Y+:"dvnV GA0exdV`Zꋌ'I%!j@ShU,Y(T!`I/bGT%1Z.Öx m# Q&\k*heeI|AȰR& uzK], ,s=4[F;A~c4댫_0C.3;x+Z|uXCߧsjp]fڻrjp[s?tŰ+ _% 6AZ5R ۚ+_#\p κW,Ϊ:S 9L!zAѨ(0p,9+'>j&͊zeiq{ŕа eDQl5H+kYi^E`wRNmjkH{qG1jfy5$>h А B :27ck,#5[>Y=2s&Κ.sThY u(UHG)*`'mP=Wb2{r+;}3}%T<1j.īz[j~ \-[ߡ@Pvf.N;L<)_b;%"< ~7 EI?ʇxAs߾|yvJ9mch787)Rk܋xK>?EJ;n󶽍σ:n/o#l~q%h[_?MqCM endstream endobj 1339 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1340 0 obj << /Filter[/FlateDecode] /Length 1683 >> stream xڵXK6W=2fDe 4i-ZE|@۴-,%9zt i@[9q4$ EEH5۱럢'SUU(K&J(#Wm7ME7@9~@S'U"%8G=28 > "(J2DPl iہxmY;\X;(HXK;^ew]]"3a$0A}s>ǎCW"Kaet3ȬTU܁ IЧ+:Wt Pe\4~:GXބk+t~rn =oouuKnVӻ|4 y@lC@ 7@TK}Y;JUDxår= |L6tk ~%Bap쐡5K +r|۱5*GxCN6)1<šYE-7&mYXB(Hh Cv^[lU b8SKnknaZ74ݛ k{0Mt:Y+f]ĸc鮦5f3l>:.^y;7)`Aۇ ,~ J/J(/v%cr۬h܏j12tLX0-+A?R\Wy܀X*Ԫj #Ϟ{s>nCُAҾ4ON7ćP9 ,t:<,BFuK2q'ʋ yV S 21, ufZX4pZz.}7Rݐetf>/'Jd'],$̯#M%=HNhP߄s\lB\_996iz&e&:M=QAul÷E.d~p>g>g# 3-qlό_EJ,B(/o蜍>w4;A+$3 a=ʔH2{1gG-KpkJF(qz]KXrUw0 & -*GLctZc9mYƏ0 ځ!}byYG Ay >ciwu.{LxBKw_#A{?v. Z$HD*0YBH;T*<^r ѵІ3Y9|K> endobj 1342 0 obj << /Im34 1338 0 R >> endobj 1337 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1341 0 R /XObject 1342 0 R >> endobj 1345 0 obj << /Length 1999 /Filter/FlateDecode /Name/Im35 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 1346 0 R >> >> >> stream xX[o\ ~_qޜvxc cS sȱAAˏ=m@!gxn9V/?ݝ~=}o~yg2c hxs~?~nNu>icu ifbvR]̗|]Ӓr[GzU}\ϗEC}>Oὸ /w)(rfDu!#n=w$bUVQ7e"GXz/ƬS'"/EeE×<,_%OsѠ7F ,<yXB4 92P<) m7GcEK*bp\ xoOc66w X]E^ rw"$X /3<—嫤Ӝ>TݽfⶇϹ.Fj:x7sC,`·e3%p%¼séu (&gF@Vj Qm, prnoknO<GWpn\lp9:ULd3hjszjFdW#U9Iua{)=u)ݺ880+Yu-L2;]*QNI֚Z(0Ygtj,תufku 9CۑֈY L+)0`x!-Yg)8( cg R8(øu</=n?6CI(dYßaxZ8ŴZy31nuhiaZd]L٩i򷦢vD/"HR!1`āHi@ Ækz''K)Y!Q EK[g}VǞCB|vv_eio- `'`tVE@ wF-ϴ*[XƅT@ڲP4 ;`H7`lFX51Yd JÍ(Y"CBtyэpd3AX팆v~x RBexgЪmK`ZW{dGˉjK.Z"V2JQB/6,gsƂ xC. FN`A"'vF-10w (4̓y"X޽R-I=qQ,]t-HoOPC_Pc2 R*ɜ^J^_[Dg=J(%“pe7]cBXuW\T g!h5FٻҾk:__OfI6;e111H|>9`$'#&m'^;^[ 3;Z8h挗BmĄPF3UGl4+bd垷VBs+K8܌ ztawV-D %j̱s 7\=T̑E!ȈBb$&,9lL*MĮ؍3 Z]#:mxiڄhFt"Tȥ^F!ra*rA@ :Ič[{xVh0>cu;QUlx Ѩƅ!X8K<Y缄#^1ʴ #< (=%(nE%uLRF>%n`2`&N^JFt91li{˚ $ZCuJ+ḛ@=k:@V(hW oF*<ʽ=j7_ӟخߟx$Zlv7o޽?߼!H=ɻ :?Y> endobj 1347 0 obj << /Length 6614 /Filter/FlateDecode /Name/Im36 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageB/Text] /Font<< /R15 1348 0 R /A 1349 0 R >> >> >> stream x[eQ ΧoHI<(=d OO]{̱-WwZtϷ_>>7{>yt{PJOC!1}NՒ,zI>|2ļzwTW{;6&?[6`mZ-Uv]wjj/u\#Z:3IA<k?gJOva3cQg$(RTDu~\˽۬N0M 1+&ф pnʼnMM8hMTnMDk"#Z i+61`&D&@&fC*+pF4%؈X6u Dmbt@hM@0##Z " MM(*ԪxL:K۞IR>j~ MIi%{:uIRUF,Qa!^ՠgĬvۈUqm"jm jk!Z[њȈD F&ԉD&@&jC*+pF4%PوDYDf2JC&*5QmhM$@y#jy!jy~o(opImK^7Д|7}CG%(oZŠ*8bFpeŊ551%kb!ZQ 0kdl@m:%b&6A КV`"Ze ވdIB46APЍJКY<hM,@0d@m&@4 r[o:gD-oYv7f L>}(i}CߐVhkǫH/|o|`6]=0{Dwuľ}XvBه[v&`anݧ`iX}Fۧ3`Mvw)Or\t^`ww ݵFl NwȽv_ 8hM$i Fm!nw|LSeQJ*x`~/P]T{pK#y6ae|8 8@̊QW49UMNŌ RujE, Qթ+DSg"IB4u6V괌&iQiQMzĖ$m :QM$m":=#Iz̪Nj::I@4u&/DSg5HȈjQUQ$!: thLD5 V@Sg#IxTTـjـz,`;MDGɈPyL2_}: `O"^q8יz5#fwwW]`u7Xe&w@#nhD5I͈N,nDU,D5ID5I鈶6&oDS I8IjOGTu$PM"a ͑r<$wDU$hD5: 0I!# $r1NQM:C֭&ዔNATMDS'!I:#Ņ'n\*>(2U'`TX X/lv75nޅ˻p '\ۙ 8Ǚd癐יm[X Xx8ˌ8{hhD,Hh.!ڒmLD;>,D[6m-іhC^#1h{CB묖ArLІ'/9- }׆}h:1D18OL2 ø= Ji6{IlI"{yS$%h9A^Lw:杓@f+U)f1`2꒒婱ҽIڍ΂EK%kyѠBi=gyyz*| #5IUfK͝6zQij.Cz"I19ѻe8kȝ>k2m%@QnU]!.'eDRfLG$Iҹ-t*mC^$ %y+#;Mdu!\dxqھq{Pf",4POsx>2]&ɒƝ{zrLnHFImGHH+'in恵n'gXgvl]!z$=wɀj4[LFvU?*QG*K>J%.,h`ÝsJ$M }iP5]5KKA+qI%C$c^RLW-2eME<[ֻ Zf.s ozp=T)Pƫ^$>qțl~I X y_r6K!"W]RMI6K-֣H&;9}nP%CGXGr>sTRngiw$YF"S/Yņ*ȝ-zj3'[xN҆2V93/L5*bɮv#:-=p3KdЩW$>q=u,[ímA]-;ޕ$_ehrWrL$/EI[7zR/݊iZvUKn. *D{Qei!8.gs ƫW@fzUL /6b T%I׀;YzÀd/@}!mĜMh$P\-t@jWyZVfCPU5 ω8e@ xE\>E"ZȈ'7@>NePpZBs:"SYz؏yju2 hh} :#Q{CTuzE4SDS'V5IONۈjU6$m :#IZ*DS'#IZBTuFTЖPթ&dNjUZ$Oɀ6%8#$WjUB5"pz0_/t"cQ#mpqgElc#3}LqWzbk#nCg2D,g'Yy&dv&o|&k݁Vw6SDj$ /^$__4]sqv{YO@ 8Rtݒ0ަFI`3E2d$45x:5%J+'dLKaZL"QmM "m"1k5%!$K4kkTGN,iK“hh$אɰ2#m{YK̪y=$'u w$O mD"n JfN*_T{[uY[fE$iZC/"x^/fv`ڣCA1&K+- 'qэ\,KFEy$offJ3ӃSͩ-3#:]$4WJz> ԚkuLE"Iu^ZOZl n:sHU¥#<]1sU)嶒]me}D8dN?ՠ%E°H"(ɿK~A[gS:}qyRUq렏Z|iݣ~sܕ(K+X=h&` l@4z'ġ(VGdZS"JJip[Oy͈::Q?#b5P h䎨;+9*UVȈDQ pf@=P'O9M4bSl TD,Nɋ&~S`#I8Q/Xh&`T*?(i85b=/z*>F h/;Y؈ -Aqqk SP2666)Ot@mbDmSvNs њj>Mc|=&"NmR\v/%nI"7}$O>䖷zl>2N=[6)|T/h"]cDmM?mvD_?_WEhre>Gp]p|kElǛDnkkvXyd pRȦ"z[yJne/fn"$@,? On>u9}@$Hs+e#r^$3C%@qkvs|5FH﨤{2bnYrYLOg/ӹGe^nly /ÝY +K_7frM("*,Lg׍&Y[nzk\Pڜ P6tv/z ~=5\G zzi8x9^f>uڛd)1^yʘ _94M`}^`Y,ZdNbk:mZ6Vh62WN.TEϭɋf$M^Qș$2x[=M#)穝eUh={2E.k7v}`M"W쵝_dgytqtM7yɇs/hy%M٠E}ݞ&=Q,I}D%ZJ};G#[K> H  Q7]T&P@Fg!T, HHtCxH1HH HHHH2h;@=t.M ў3DDsV}h'@@[7DD#Id$6hs&h/bskϑHCiJ` ?z"Zd}B$e"TPjBH@ @gw^ Pqo(W \Զ{@˽Y^/z sgm@ z FB ]I8HW $ڷ +qـt-@H⑖|qHsh_h_ h_3h_6h_^ ^<+|s:UR>otQ9ָH{!ioUH{?(@C}t^: P:Pl $sl$hJýE&rH@gH=REzW{Ԓs-p{@- @Գf (y^#{, ~L h( g@dz:5tv/ h3)5eD^D@Yw ;M$ڷ 43D[RҀDRTt@Cf^%ý?pKNAnZYR=;5{pK`q(J?%!ǖo8x·Hm% |2S闟Oj>B*S!Qe*_@<L6;>]e.9u7/O,Iͻ?%k|fox_?ef_볊zh6*ϩ{5{w,mnw_~,{6?᫷wo}}{/~ /$ endstream endobj 1349 0 obj << /Type/Font /Name/A /Subtype/Type3 /Encoding 1350 0 R /FirstChar 0 /LastChar 6 /CharProcs<< /a6 1351 0 R /a5 1352 0 R /a4 1353 0 R /a2 1354 0 R /a1 1355 0 R /a0 1356 0 R /a3 1357 0 R >> /FontBBox[0 -146 146 146] /FontMatrix[1 0 0 1 0 0] /Widths[0 0 0 144 0 0 0] >> endobj 1356 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1355 0 obj << /Length 704 >> stream 0 0 0 146 144 290 d1 144 0 0 144 0 146 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1354 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1357 0 obj << /Length 17 >> stream 144 0 0 0 0 0 d1 endstream endobj 1353 0 obj << /Length 700 >> stream 0 0 0 0 144 144 d1 144 0 0 144 0 0 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1352 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1351 0 obj << /Length 700 >> stream 0 0 0 0 144 144 d1 144 0 0 144 0 0 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1348 0 obj << /Type/Font /Name/R15 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1350 0 obj << /Type/Encoding /Differences[0/a0/a1/a2/a3/a4/a5/a6/a7/a8/a9/a10/a11/a12/a13/a14/a15/a16/a17/a18/a19/a20/a21/a22/a23/a24/a25/a26/a27/a28/a29/a30/a31/a32/a33/a34/a35/a36/a37/a38/a39/a40/a41/a42/a43/a44/a45/a46/a47/a48/a49/a50/a51/a52/a53/a54/a55/a56/a57/a58/a59/a60/a61/a62/a63/a64/a65/a66/a67/a68/a69/a70/a71/a72/a73/a74/a75/a76/a77/a78/a79/a80/a81/a82/a83/a84/a85/a86/a87/a88/a89/a90/a91/a92/a93/a94/a95/a96/a97/a98/a99/a100/a101/a102/a103/a104/a105/a106/a107/a108/a109/a110/a111/a112/a113/a114/a115/a116/a117/a118/a119/a120/a121/a122/a123/a124/a125/a126/a127/a128/a129/a130/a131/a132/a133/a134/a135/a136/a137/a138/a139/a140/a141/a142/a143/a144/a145/a146/a147/a148/a149/a150/a151/a152/a153/a154/a155/a156/a157/a158/a159/a160/a161/a162/a163/a164/a165/a166/a167/a168/a169/a170/a171/a172/a173/a174/a175/a176/a177/a178/a179/a180/a181/a182/a183/a184/a185/a186/a187/a188/a189/a190/a191/a192/a193/a194/a195/a196/a197/a198/a199/a200/a201/a202/a203/a204/a205/a206/a207/a208/a209/a210/a211/a212/a213/a214/a215/a216/a217/a218/a219/a220/a221/a222/a223/a224/a225/a226/a227/a228/a229/a230/a231/a232/a233/a234/a235/a236/a237/a238/a239/a240/a241/a242/a243/a244/a245/a246/a247/a248/a249/a250/a251/a252/a253/a254/a255] >> endobj 1358 0 obj << /Filter[/FlateDecode] /Length 1085 >> stream xڥVKo6WJ@HGz6-CߚHK릿~RVmЋ!IJӔ[>"ș)spASFsN9 rOO,%5 e5|=3&=4j$e1OȢf'z4j'_nn.**IM&'?MYL`2-"5zƼ8i\cVDR$i/}/ѿםi샗UQ+;QNy)NƓiLF~i;VJ,p 뤨%PWQGc(~V&c:]˛1jZzW6/)Y*xgPWSo\FgP_1kRey(ף [WGg&[Ŀ5H^`ǽ>+tSA&\:ո"e!ㄐuuՁ mÚ{5G&JH5wW`eȚLЯ'.-L2;8bE &8x׎\(iy7qȭ6kXh0.}9'QSyCT6$HeM 8ayI2|7O$jrP eg(#~{  'uf܏bDԝGn# T 孑nWD[}'sj'$:l/>h@tʢKkkH杶(6~v;la1-GV,}w9_v_5iuTdDƘ\DŁ2ax:nKl]{m: GT>⹖1Yf9.sW1ή3򵁷4> endobj 1360 0 obj << /Im35 1345 0 R /Im36 1347 0 R >> endobj 1344 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1359 0 R /XObject 1360 0 R >> endobj 1363 0 obj << /Length 2013 /Filter/FlateDecode /Name/Im37 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 1364 0 R >> >> >> stream xYnd Wn& uHki3VF2@vH`T,Vˡ?~}9yUK-۝,)_iWݭvG({X̲rC9=w,[Y[Gs}\_+x3Zծ]Ϯ(pQ_IZBNx$4v~Ch; &T Xo׺xwA\u[ TnA6jkފ @pK>_x^{o@fsٳ˞z DCFL J>_ 2/ Ԋ0]vUnetmTSo2f'dE4(|yoADJmhޏ2|(zD|]xd^=a5zuxyz+ 2cӉ@ SSccƒʌgkY9@l.bugozoOww>dn{Bӗ Z;{v1Tg-a7"eXizWj:5{^e'@ꢳg=m o LgWF@5'^ATk 5[>H~M$բJBKihΔ\:ݱ_;z7xL=0%]+Mu)tnQVc9|Cq=s[ wۈ\YtԷڣ;H`(} yDAäsr=2#nLF$ѹ3':Ղ ~,cFqz ֈf2FE}YEYچN/WS'`,S3"-BZ4Eq̢&͘u6>nlvN_]oAPׂ~{/`0g(dhO' SNvBh( $9V]‡0O ܝ!N z&'^@DQ!A"%dבJC.rbQPf?Dwh5oVqτJwKAֈdht+R>&C ¬ƂTX@Fx7 nֽ柰EuG5MB{`Y"W}@aK7c9g1vw:d<8BG17AkB?2a 2?7N&(H" &MXiFAY:& R}ԕH HsFQ1BrXK~CqN !Z9:P{JRs‡$3c ݘ"'Zʄo,w"@Ɠֲ V"u,c+|4KT((%(Ytax؆2Hi{?ּ}9_^~'mp]πG*̄.>~wC㇫ iw{@{C|#^uǑ )x_(×_~L_Oc1na<~ұC P?vGs7HtJW1W_nۛkjPfw endstream endobj 1364 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1365 0 obj << /Filter[/FlateDecode] /Length 1603 >> stream xڵˎ6ޯ3eY~$d-EAkkfؖG/)6)$IM-2҈"%EqHX:?{+`X9`0fB椺QIX!]f[ߩ&܍k]ƶ?<{.ң`' '6NcbX.$WfHhkGR|T FayX|Qť彮RQRb(jQb'eDa=4f[ݫ~覫uɸz]N`>#k:L8T[0tY!+͔Q*5E32SwFUڏ>}zRy#+ 5ѕZ4mh) Dl9+0Te=PxtG߁sB܇ǫ3IOp7 "g IAXE'p VӴe֚Q{FEoӻ 4#!Ʃo/`3n3A^Iݣk=u)/O'0uFNEn!`ђoLErHD_峄V m/b+Q$=\ y#!Fk^3Щ VےPZL\+i.s$4-!-*N Co06 VUSpHf]'zI CYbDx|) _/%tZL)?a st̵S뜅t.#":2bȟT|G$CIGCl-ȵoEƄ8ys4&>@X0. DFg!W3t[@0&T" b!H[~ܡb}jx>K.%Jf#ˀ'L$i rjOFcCx&C$e X1u ]Q1 Sl`VebIѮ9 ^ڠKV!\O[\ )W/-4<MUtL\qX:.Bɔ. 92^Fe>J{K[p_K 6pލb<+Δ=MQ-HWiJ V@hZNxM}LOYʿ9U8AWd/4RD>IKve@IDz" ͈-xmˮ??_{ RE۰0\йX@%I-[WꡥnZ:M|9ݱ_.%1Ԗ B7,Z\P^v^B )A=b;)jt݂@M-Sk endstream endobj 1366 0 obj << /F7 38 0 R /F6 35 0 R /F10 53 0 R /F5 32 0 R /F13 66 0 R >> endobj 1367 0 obj << /Im37 1363 0 R >> endobj 1362 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1366 0 R /XObject 1367 0 R >> endobj 1370 0 obj << /Length 6388 /Filter/FlateDecode /Name/Im38 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageB/Text] /Font<< /R11 1371 0 R /A 1372 0 R >> >> >> stream xKӥQ Wb_*U, *)cc(1q~=}{Cm-U#K]%#K])a"s]4,SiD7tc8YꁥO1yfdk1q3S`w=s]-wdX21 "=N%| *Pme>=KcN3\RYs;j{[K+p蹜xΏ]Ss:5pm|OF␖c0`5]i>X(/j;_~̔2O|r#SE>˲kv].vz>s?.vO Cu0Dg e,`khX륙Uj%⑘*ް4kx1,U-KګXK7RaLGe9b- ,.H DTK-KKGZ#ስLK<mR:T,qojGDm(QvBK. OhX%\` ӱV΂Ӡ^_XGih|jG{CMOٷA9C-^^8zxq9mOk3G%rcٺ" GF!6bÊ,6lJن# d[52۰ZdabÈ,6b Ćm)2۰sdaȓFfv {<" 3ذ" ;2S,Yi#pa.s<[# #pfdaEvd!lCΑن\m-r5e,[~ms\MYnO;1،1Ia+1899999xyyyx<蹜鹞빝칟y8KdY jdY;gtȲFZ߳{m5ڱYsiX5%Zl$eO2"s :Gqu:.ƞFc+EY{Ì7I,81oY)rsqs2>po@ۏ IOڞR_FO@'=;mH9oyר d Qn=޾~GE~ ?/c"J~˟ZIJX^6F93ȕyEn; )dΑ7s <6J;.-2FtǝyDfdm;d_Q*1Ft+J;f(}5ZdRRŶ}5VdmGf_m92R7Ʃt+N[f(}ESbی̾Ԭcm{T}+EfV̾Z%2۶jdjٶJ,쫵"m;2jlΑW.l="}[jn]mt<:޷,tԱKÞ ǙS\wn O,r:FR^jvҧ}DG5E>5V"z,Ymk+2ȲF#NV#m%2b[̾8ft+~i:"j̶}UkdDf_Ŷx6e|2F+p̾8fc'b[̾*9ؖ"6XcoY`yD^z,:6ٴr1m\,7[q}xݱx1fY4&;;;G;;;g3=<C\k%r>s9s=󍥞o}XbHoKŶ9iy;|r<tl' emYȒ:%`)-d.)s)3\5E2[Z7*I~>7I%d**@(O-m'ֳ](II#"=fjWERudXex!=O|胟*|ҧ#?Й|BRfܼ%#<# JɇeFśu$=IE+Ir=k+_͟ʙdAP2LC(gv~,*u \˔k5wkx~KV$([.;G)8H0*(Z/x!DEvJGް$@U YԴ=sMJ)s"~5k9uԸ ~(/IK( A*g.WuՃI[ F9DbOILNbIUI,)K,.!Ia7bʴ %\'?Vz_&IS4Dz֖$Rlt衶1̴\$kJE8Kh$g&X/j3`TWLZ,MR?6.mׄ)m4zīUw d=/# ,:4N ~7*ڊ=CrҊ R*?89Ǖsip`uKiNet` yT`'ɖ2Mf_]%Sơ cj.0K)IJ0uW5:誄'MF.z 0\ff\A3. `KҎ][XVؖ['s-֤^CO1%^eQ%rђATMh GhYLi;戬=%/Gd}ߎYPͪukRcQUo!PObOK^0DM=h5WGK{`QёcGv4t";K[Gɣ;: Q@Y:S lieeH3Kg! i.e]pZ"XD^1osy ХI'pkfUIkKʩZjc%8PWoK8 ZCEg.եԨbk1T5J.!G숣|qѶZgE4Ěbjڄ?{&KpI<0MiNedw K$2LJQwcs’pp0-D# [91" zփG%5DNQ2ER{3(o)F=[Fo`nyKnb$nIrsHF)fh_UJT3IR$=|QϦe汥]&0$]HJ0Flã.~ՅgwI1YzB $Fh.uabDŒZ*W35kK5W"o-OLJl؟,t_(iK5H896?tVgi4ʙf`Y_msfP$5jm 3jgu+&R*٬'Us]D$WszJTV_m!DzNy~4aIfkvƎ"ưkI%EÀjrITDQ;tJƠ)s.^M,C%6J-2*XX2J ߃x3kǻ3%sBNȬG^,Ң_ځ ۆبN7E8n~O`aF7/97]I/?`r_ׇ1)k_~_Ƿ_w_3>qo endstream endobj 1372 0 obj << /Type/Font /Name/A /Subtype/Type3 /Encoding 1373 0 R /FirstChar 0 /LastChar 2 /CharProcs<< /a2 1374 0 R /a0 1375 0 R /a1 1376 0 R >> /FontBBox[0 0 146 146] /FontMatrix[1 0 0 1 0 0] /Widths[0 144 0] >> endobj 1375 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1376 0 obj << /Length 17 >> stream 144 0 0 0 0 0 d1 endstream endobj 1374 0 obj << /Length 700 >> stream 0 0 0 0 144 144 d1 144 0 0 144 0 0 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1371 0 obj << /Type/Font /Name/R11 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1373 0 obj << /Type/Encoding /Differences[0/a0/a1/a2/a3/a4/a5/a6/a7/a8/a9/a10/a11/a12/a13/a14/a15/a16/a17/a18/a19/a20/a21/a22/a23/a24/a25/a26/a27/a28/a29/a30/a31/a32/a33/a34/a35/a36/a37/a38/a39/a40/a41/a42/a43/a44/a45/a46/a47/a48/a49/a50/a51/a52/a53/a54/a55/a56/a57/a58/a59/a60/a61/a62/a63/a64/a65/a66/a67/a68/a69/a70/a71/a72/a73/a74/a75/a76/a77/a78/a79/a80/a81/a82/a83/a84/a85/a86/a87/a88/a89/a90/a91/a92/a93/a94/a95/a96/a97/a98/a99/a100/a101/a102/a103/a104/a105/a106/a107/a108/a109/a110/a111/a112/a113/a114/a115/a116/a117/a118/a119/a120/a121/a122/a123/a124/a125/a126/a127/a128/a129/a130/a131/a132/a133/a134/a135/a136/a137/a138/a139/a140/a141/a142/a143/a144/a145/a146/a147/a148/a149/a150/a151/a152/a153/a154/a155/a156/a157/a158/a159/a160/a161/a162/a163/a164/a165/a166/a167/a168/a169/a170/a171/a172/a173/a174/a175/a176/a177/a178/a179/a180/a181/a182/a183/a184/a185/a186/a187/a188/a189/a190/a191/a192/a193/a194/a195/a196/a197/a198/a199/a200/a201/a202/a203/a204/a205/a206/a207/a208/a209/a210/a211/a212/a213/a214/a215/a216/a217/a218/a219/a220/a221/a222/a223/a224/a225/a226/a227/a228/a229/a230/a231/a232/a233/a234/a235/a236/a237/a238/a239/a240/a241/a242/a243/a244/a245/a246/a247/a248/a249/a250/a251/a252/a253/a254/a255] >> endobj 1377 0 obj << /Length 6798 /Filter/FlateDecode /Name/Im39 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageB/Text] /Font<< /R13 1378 0 R /A 1379 0 R >> >> >> stream xKQ1au>9,E2T%Słbaq 'ӣI\l'Snԏ'~ǧ߭ߘC =)>xT|ks]:sÛ/ʣL*S ?_VL|J[gXU֫%VLtZ:B.S{M]o%ވ)ϡoR.m.qFZVjܮ#/#rәo y*o&϶yso],ZrgՕnV]f.N>(jYtvfoVb!ެ6e,ʸYt~EWkr7toV]bEW7.G,ٞl[:gSgع~[BeI"jYS :cb\(S_6Ux~)\\p Zbѕͪ^UWYu7y)rYt\eĜo]ܬZ߬2a,6p%y,h)N z)!doV]bcEW ,(N{sӹY#Ɣ,;c$UyvIM.XfYqd$% *6:che1fJLg9NV8[#ijh9{q͏ͧ?r9x\w|B;9>6ěKdmc7WOiڧ\mXu^.V]bqެyZ\}c$ho?aB}2mq ck5];iY))v쒳2W#?. YOm<뮕WYɐwC;];!r#hwxGwCNg|p<D;OC>8vyGwxG;rv;!.#hwxGO~ nRKymGJ> vNgZ v`wRU2{>Y o'{e#J+i+]>K}Pe7?Xf)b ۓ4ӳ}΋R[}(Oizt(3EʔX:9j" $RH鞺H@"e=2S)HTOS4 ҁD)ٽh'b'KS)HT ҀDJD):CmK9TC٬rzGZyW>ơa^s̻b(izl8 6Rb)<RDJ)H$GڃkI 6XJ[b) $R'-$R"H xKY{O,v R'yG,pԬE7l=(iO{:J<uʴhiGdvε5"E,{@DlemS#׵s e zM8|TXܣ%zrB^{+ݾ}ǾLI]lNT]0^hx$%G_ײ(2ǣdO۩naV4atDU<DSi](欭G>I,Lxz@qTKFPw@DԵKkKRD)!K֠(j*:z*UDc9"KL(sAz&qQ2]Ra953# m7<>;h=cwwz,{x{8IFccۃܡ88Ǟdgcٓc` `,N Lc -8 ( EqqFDn|Y^l &DvjY= bsv0@YQ }fF9##F( Q4:,>T@uDe<i( Q43bAQeVDilǬh Jh31٠A6#u{M@ۡq Cc`<;! Gl,d}&J³i 6G1mn\2*e3yOSzȻ൧mjj%fccf {:z+{,r?~::`UaQMDFI"Ve:EJxaZ ]5DU@TaPopˡtCƉ3"Rl +*"ºǡ֐Ta%ND:PDME0#*ϸCQ# y7qǰPo͆8u,`Am&\Cc"===<=q<=<=!==y==@<l<<<<QI>JSyTs\Ŝ65QJѣ5D1l]:%=m͝Fɧݚ1sG}MY\1]ccFz< %90Y j QED<Qo DJz; PZUXATa}{PDV&+nwDUXETaEeDUXDTa[ӀcxLv={b[<0 D==ꖕŠG;H.Zmܑ~Fky[aɔ{έ8?.KY~=e>t}7>LNѦMAܷ<I&[|Ǽo{ྻoy4qhw&⾱y[s qM=}Op8=}C<}7c-:_-AvI([zks[qҳ:8q$5saǩ \ǡk9s0O5u2 GU5Mj2_?9TQSВ2`3s9 Pq Q0D'&f'&@|w>Wӵ P+jrjgDU5#J{@f)m J u,}&UQөުVqn#r Xv=(c;ա&|ifBq֜yj%}Aҹ~huKW9ߛKHWoY<~ϑ?a_?oo;b+51_>[=?XϿ[lA*b}v];Vs?L ?^+4aǻ+I!]2bQߜExܘҞpy VI,zsn7mfU7m. md}YlkfU+7m^nV&|!bۈ7FYlf(7mk,svo_ېնyj6j6YnYfv܏o#9>vc8D.{ #=۞Sg"=c=o;!뎥y^c5rرXum7Qn>f-,f-\u7me,*f]Ro=@8JYmK7JYm 7Csb|ƳJdsbxn7rYlfU7mf|dz.+bgM0Y|%_$,W#`之Jj[Y|_lV+"__r<,3ޮ?/Vb9{Wz1Ϻ'1 Ng3G˙SAv,psy였=~0!3GY7M&RR5)$9 b%kTIIZ$ .uCF KY|]~y#/t.1eM%;kf~*Lk)%a/Sa?T֧hT.V]ǥ$5wi]SikdՃiS^v5E#z|8N(VĞ$|yghOE\Z.!?s2I;suړB(W(jϚMڵVGўš\FxZL"{omnJ(sT]'3ͯj%]ǡ&68f$$z,"ڗVRNqRkЖ!clU;$dώQ)/oҧhyy4pG˥Y.  i|yj˲FȡYIY#v<_x`ɹjIU)P$Z++ol%Zc!XutbA묙%uir:/%`-8ɾVh}h۶ul|mbQ( Pfj@iѮԘPg*@M3{kg֯M' ~i=ukbtIv$*P5 4EH@ټ[yґx{]=#{*փDrH@F#a٨sĒ 쩵"IIVLW]H )=+V+,rhp/[AZ#^2bbc-_(P]1HbdmD^$6@?-6ן9D4UtNV2LD?kTN=So+J@Wo~՟˯uv5`ݟ?,9%E(GN:ׂ޼yQӻ'*3%׵^OW;~d}> /FontBBox[0 0 146 146] /FontMatrix[1 0 0 1 0 0] /Widths[0 144 0 0 0] >> endobj 1384 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1385 0 obj << /Length 17 >> stream 144 0 0 0 0 0 d1 endstream endobj 1383 0 obj << /Length 700 >> stream 0 0 0 0 144 144 d1 144 0 0 144 0 0 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1382 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1381 0 obj << /Length 700 >> stream 0 0 0 0 144 144 d1 144 0 0 144 0 0 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1378 0 obj << /Type/Font /Name/R13 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1380 0 obj << /Type/Encoding /Differences[0/a0/a1/a2/a3/a4/a5/a6/a7/a8/a9/a10/a11/a12/a13/a14/a15/a16/a17/a18/a19/a20/a21/a22/a23/a24/a25/a26/a27/a28/a29/a30/a31/a32/a33/a34/a35/a36/a37/a38/a39/a40/a41/a42/a43/a44/a45/a46/a47/a48/a49/a50/a51/a52/a53/a54/a55/a56/a57/a58/a59/a60/a61/a62/a63/a64/a65/a66/a67/a68/a69/a70/a71/a72/a73/a74/a75/a76/a77/a78/a79/a80/a81/a82/a83/a84/a85/a86/a87/a88/a89/a90/a91/a92/a93/a94/a95/a96/a97/a98/a99/a100/a101/a102/a103/a104/a105/a106/a107/a108/a109/a110/a111/a112/a113/a114/a115/a116/a117/a118/a119/a120/a121/a122/a123/a124/a125/a126/a127/a128/a129/a130/a131/a132/a133/a134/a135/a136/a137/a138/a139/a140/a141/a142/a143/a144/a145/a146/a147/a148/a149/a150/a151/a152/a153/a154/a155/a156/a157/a158/a159/a160/a161/a162/a163/a164/a165/a166/a167/a168/a169/a170/a171/a172/a173/a174/a175/a176/a177/a178/a179/a180/a181/a182/a183/a184/a185/a186/a187/a188/a189/a190/a191/a192/a193/a194/a195/a196/a197/a198/a199/a200/a201/a202/a203/a204/a205/a206/a207/a208/a209/a210/a211/a212/a213/a214/a215/a216/a217/a218/a219/a220/a221/a222/a223/a224/a225/a226/a227/a228/a229/a230/a231/a232/a233/a234/a235/a236/a237/a238/a239/a240/a241/a242/a243/a244/a245/a246/a247/a248/a249/a250/a251/a252/a253/a254/a255] >> endobj 1386 0 obj << /Filter[/FlateDecode] /Length 749 >> stream xڍTMs0+tgbՒ-P)f܀j+4_ߕVIhZf8i]{zi5 IӶ Y)hɖEIK~ yf9ii[X(ok&"9nҬur#]*X" 1`aTȓ zJ/?\\Ǿh$=}__alKei$%<'ݖ\n\i 0扔K7(6}V{3aZi"H3M2fvNOd穪s>eKyw Sg}yNyq:ctC?@fqړ8mK2WTLfIxrƊ5 F:NPڔ6H:icW8ڍ4  )fapUҦdQH:s$¨u$xܩ,06p"yr4J:։ ggg vWSk.X(&頥{l҃!IRxZQƒUz~a o++?8X%3X.NNwsiV=;Ly> c`\QhƋ 6W2^ԀlУc5p* k 4P) |J'jatONp!M| qyP`f9e ߞDpz!yl $+(Ё18<_/i{q?{5>ZԂct:S<@w&qO_u^=" endstream endobj 1387 0 obj << /F10 53 0 R /F7 38 0 R /F6 35 0 R >> endobj 1388 0 obj << /Im38 1370 0 R /Im39 1377 0 R >> endobj 1369 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1387 0 R /XObject 1388 0 R >> endobj 1391 0 obj << /Length 7612 /Filter/FlateDecode /Name/Im40 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageB/Text] /Font<< /R15 1392 0 R /A 1393 0 R >> >> >> stream xIql}s/G0cC%JbY?}X FV1̣jU,.%~'ߗw!R?o~<ʜ=S?|GKK==|9c{T&T1%T&Tc d{i+j Mz[RT%-YG][Sm Ug\ FbYѫp9 <[Yx\*,JYlzqQ[f/j+ݬj+\Vb+fb+5MYuw͚pb+VkvJbTnV[f.6j+\m8ΛV7-G\GDv?ޒUҏfDg}m_ ܮ~:ȏ$@̓}iU1&R*sH=Wy3k"k7wtq*\nV[⮶j_<ָYm͛V Ob+&UNfjݬiͪsx<̛%)\>R,{[kSZ<~kYlpaiww%GbL2381fK\5ӡ%5]Βc$f% 'f2Pfcv}4Mj"\dJNl2H=[1|Yn1ڤ: {=G#SAyZ\{CjǴ3VjF[x-Hsڛ ӉjJ'V{IM>hT,˥Ʊ>ILiv #o{|yO RYp<ş|[Iѯ?z_e?l?%b{k󰅢M蓍=^ڒ6 T鱪)3EG)9b-kDKq$Z#Ep$Z#2#ֲ@kY#HT)Z#HL EKkYM䈵,@QG:-͑hHI Ge:"-si9TڡsvO<4]j@m.PݵTvm%U[ Pح kC:b)j@ {9w/7k@yGPQИ;ڎf@uG= f!q(bq ēъ#PK$4"iҌ[390*R!M(T@%ckP޹ ;.IԐ.::ڐHG%;- HkpMDKp5X#R/i ZJs$Z#RH$G%:-9$Cb- ܐdAZPYi]@y PҚFJ:2w> DVIt& |9 s@"*{,S\/i `Y0[ .IЛb7CyD0k5:^-ԓS vr/U^l3,q8UI|𪐬BY?4%pPkQbK?O _ 2$7S·YiM(&+l}[z8b}W݈EzTeͣ**U(hր((v6QF3DQFsDUV<갫QulxTeӡk,P|PPpQUyTeq9Teӣ(6(9(h(hʊGUVDHc?$`19x 1Xjqrv:8̻8Lq9!FЦa [g@qu2d1ZE ǰxb`X,0!f bbݿ݂.f'yyf'xyd hΌ(WG#ͦcfZ<7gan"ja]Ԉ9Y`1X-!İGTǺ aPeZP%yTeѣ* QRRzAԷ͡_zTeţ*[bޛ;Te(Kӣ(K>=QU8]GU&)G"z `yUla${8"4VC9iS]áIJPL(\]PLClbbyPMTj9j{TPӣXҡ$X全P f2WjyTݡ P+bDbbVts(&ӈڸJ&G5,J}%Po8o8A1μñG~(<[Uɇ'X~⑛L'n)pQOj:v[EO˧QMLz+"8v'¡ވHF&GM=p.KpNPLQod]IDuEQMtjb8vCȡ65#r(&V$,|f( >v#B?Rwt{ީXxGm7nN~e<RI7fgY^gdGٶs/W{6lFX7nY7Eͺ.֥g7f}6߬j۱nٶd= f݀7wx[ɛr|q)+pګǩk6ӖVӶwאpt}pܬ}j,>p8L73,>r8l8ncQ. 9"n$\n . @a ϛGX|aG'X| G Y|f1,/yF#2u4oNSG/r6<xژtڤrڰch󎡏8O!k>8>븜>ฟxc(4@LXd1q>11̅k͚p1αf0rl/ֱ\<pl//Üımv\b:w7ktuܬ/Jެ/:8ݬ/Bc7~ԽX_Ŕt7wHl!7ǚvy}aNY9uow*s#Xk(76k1I}tkl(.o1ܬc\𑝒>y*w[owMxzVZt &Z(zGxW}~'imޚ_ϯ>oB?Sל=v_~y}om %]OqS5ZIj"Sr*:RcJ:Sq44Q`@ܯĹ_ s>GBjOf V՝+ ќѡ"K=ZyCj$;\ @\LʮA ќ]hn9@¦[k^m i{Kkn@\3#SDsDq2 ּfyHz(k^5 )"qK-dG\jkĥ"W4SE*LQnւdvع'/. K퐴*]kᐴX-kPݚe{PH w)P9P%wI]@s̡w ]@yr*SwԘTMđHZvt$9 8c:\3uGqNW,e'y$) ✎HrqIDqNi97$>A8W+H\=;sڣ#ip9m $>oCutWڡl:-FZBQߥTw+CjP2F vY+v;$=Z'Pۭv *c2{#Pٽ( dQP;ZU>@yG)١4whI$'Gm3RH4WG64>Q>!班}2_Wm)$XYǃ{I]ӬSi|:'$Oka (]?I֏D󠏫$O3$ESBS=!de%cXOa$˄HjKlT:BJEl:U|͍J<yץ$#S#>:KZէFWI*Y^Sins IjS?VCUZk~Dּ:ƢBiX*̒^lSU~DME zFST,k1]S>5gU2}D%Ze!&[VV%Ds^DK#͢ mBg:9,T_$)ck֖aIi:]O9눤X~jH5)M%y*]ʖP$Ii[2EOz0VoԜ;;Ñie1&)IdJnf2#4j" n<$|IcEG ? ݊TcK )[*;]Z"mKh+in3YN3HT4>љgHH"o^WN}ە [o$SWZk*S[ޒ*zhA57yL2^"ɚvmH?yz>(qM5Z`ѓS$MiKIUhNJ55HT[RċT=?)zV5?SK5Ʊ%ZbaUdߤĺ%B-MpOޛȝvd>KU<;M6_4粿F5G;9SYRP ?]z4O]R+({hnV y/IтY!iHVe=4j$KQX%$Ize}HJO8w~VlaIھq*Ӝ\4w9YR۶9= ?4R9~dd~ʚv\UЯ:Ez_M%dSF{jS9GM}bdI?ee%{ !b{͓ +?Bk|i_|t)t Utpz/>_0nc+4n[WW˻_~浪?|~6)rያyE ˖Gwj~/~w_ET }"|_?#%`Ak??V 3ȉ~?Y)YQW;ѯR endstream endobj 1393 0 obj << /Type/Font /Name/A /Subtype/Type3 /Encoding 1394 0 R /FirstChar 0 /LastChar 6 /CharProcs<< /a6 1395 0 R /a5 1396 0 R /a4 1397 0 R /a3 1398 0 R /a2 1399 0 R /a0 1400 0 R /a1 1401 0 R >> /FontBBox[0 0 146 146] /FontMatrix[1 0 0 1 0 0] /Widths[0 144 0 0 0 0 0] >> endobj 1400 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1401 0 obj << /Length 17 >> stream 144 0 0 0 0 0 d1 endstream endobj 1399 0 obj << /Length 700 >> stream 0 0 0 0 144 144 d1 144 0 0 144 0 0 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1398 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1397 0 obj << /Length 700 >> stream 0 0 0 0 144 144 d1 144 0 0 144 0 0 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1396 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1395 0 obj << /Length 700 >> stream 0 0 0 0 144 144 d1 144 0 0 144 0 0 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1392 0 obj << /Type/Font /Name/R15 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1394 0 obj << /Type/Encoding /Differences[0/a0/a1/a2/a3/a4/a5/a6/a7/a8/a9/a10/a11/a12/a13/a14/a15/a16/a17/a18/a19/a20/a21/a22/a23/a24/a25/a26/a27/a28/a29/a30/a31/a32/a33/a34/a35/a36/a37/a38/a39/a40/a41/a42/a43/a44/a45/a46/a47/a48/a49/a50/a51/a52/a53/a54/a55/a56/a57/a58/a59/a60/a61/a62/a63/a64/a65/a66/a67/a68/a69/a70/a71/a72/a73/a74/a75/a76/a77/a78/a79/a80/a81/a82/a83/a84/a85/a86/a87/a88/a89/a90/a91/a92/a93/a94/a95/a96/a97/a98/a99/a100/a101/a102/a103/a104/a105/a106/a107/a108/a109/a110/a111/a112/a113/a114/a115/a116/a117/a118/a119/a120/a121/a122/a123/a124/a125/a126/a127/a128/a129/a130/a131/a132/a133/a134/a135/a136/a137/a138/a139/a140/a141/a142/a143/a144/a145/a146/a147/a148/a149/a150/a151/a152/a153/a154/a155/a156/a157/a158/a159/a160/a161/a162/a163/a164/a165/a166/a167/a168/a169/a170/a171/a172/a173/a174/a175/a176/a177/a178/a179/a180/a181/a182/a183/a184/a185/a186/a187/a188/a189/a190/a191/a192/a193/a194/a195/a196/a197/a198/a199/a200/a201/a202/a203/a204/a205/a206/a207/a208/a209/a210/a211/a212/a213/a214/a215/a216/a217/a218/a219/a220/a221/a222/a223/a224/a225/a226/a227/a228/a229/a230/a231/a232/a233/a234/a235/a236/a237/a238/a239/a240/a241/a242/a243/a244/a245/a246/a247/a248/a249/a250/a251/a252/a253/a254/a255] >> endobj 1402 0 obj << /Length 4589 /Filter/FlateDecode /Name/Im41 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageB/Text] /Font<< /R15 1403 0 R /A 1404 0 R >> >> >> stream x[GO%\K%hˮ걫{fV+_Uv].3 x ~_?}|4{!b<ʽ9bl^-!{K燯w{R[bJ\HhKm)5H0gdCʅ;?oo[bԟ2*'J"Y,$8#2@7qy 'Gp88n'g88Np?J;J=8rʞT[dOV[[Z=Vwddd-OVՆx9}LdJdJNT9MdOV[`]c)<8xX=wr)gx>^1u~Dk}^ni_JKe^c";8t莬sϓe\p38`);jV;Ym'y2lppOnɰjeɪs0OFS8X3l> [똳:<~cr8X0]ϰ~, `)󂏩c$W$q Kx;gs$J,Am u-hhlр2AHpJ.3)e=dN-y"kskq֘פ8\q=9=hYٳ C8r!{@}PM~ޤ}&ԔZC듅1_icAJ ,rwe77ȜTݢ=_?g?+!ǩt;h1O~x5_eS"5oR(>|̚<5g=)Ñْg^XO}4 {UK`ەEwM|N6KAgKUT;jmGPɉ᎞,;9ܑż&;jrc&iGLwd1h᎕Hbq%lsHwpGwtpFqE㎊ᎈ,9ѐżc!;r eGAw d &qP-"do{%~*Bd{„hXnܾJ3۝q55lJL%4YWBU'VSEJ%),IH4ZjLKVa+$YڒL3~X%A,&uf]6Gb3;1Rd&d-4G A Y$fE ?ߝzRɤ1&IY$3X#ϑc!%GP$t",I~%y6H5$yN)E2Vzc'a^ru?=7H9+:УK_ &#m Im-!?^)8:99w J=ʣOᤢL3z,Q}0y#:4Î!:4GPWX<6`؁ӣtJ&yaqxU~#:ď!~EtHY C;D ‰EtA,CaMf ~a<=t0Ctatא8PXj1CB]eMC]ݣ.CGNaE=Z11xM6<ꆬѡ=C!xT0<7$(PZT'6> /FontBBox[0 0 146 146] /FontMatrix[1 0 0 1 0 0] /Widths[0 144 0 0 0 0 0] >> endobj 1411 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1412 0 obj << /Length 17 >> stream 144 0 0 0 0 0 d1 endstream endobj 1410 0 obj << /Length 700 >> stream 0 0 0 0 144 144 d1 144 0 0 144 0 0 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1409 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1408 0 obj << /Length 700 >> stream 0 0 0 0 144 144 d1 144 0 0 144 0 0 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1407 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1406 0 obj << /Length 700 >> stream 0 0 0 0 144 144 d1 144 0 0 144 0 0 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1403 0 obj << /Type/Font /Name/R15 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1405 0 obj << /Type/Encoding /Differences[0/a0/a1/a2/a3/a4/a5/a6/a7/a8/a9/a10/a11/a12/a13/a14/a15/a16/a17/a18/a19/a20/a21/a22/a23/a24/a25/a26/a27/a28/a29/a30/a31/a32/a33/a34/a35/a36/a37/a38/a39/a40/a41/a42/a43/a44/a45/a46/a47/a48/a49/a50/a51/a52/a53/a54/a55/a56/a57/a58/a59/a60/a61/a62/a63/a64/a65/a66/a67/a68/a69/a70/a71/a72/a73/a74/a75/a76/a77/a78/a79/a80/a81/a82/a83/a84/a85/a86/a87/a88/a89/a90/a91/a92/a93/a94/a95/a96/a97/a98/a99/a100/a101/a102/a103/a104/a105/a106/a107/a108/a109/a110/a111/a112/a113/a114/a115/a116/a117/a118/a119/a120/a121/a122/a123/a124/a125/a126/a127/a128/a129/a130/a131/a132/a133/a134/a135/a136/a137/a138/a139/a140/a141/a142/a143/a144/a145/a146/a147/a148/a149/a150/a151/a152/a153/a154/a155/a156/a157/a158/a159/a160/a161/a162/a163/a164/a165/a166/a167/a168/a169/a170/a171/a172/a173/a174/a175/a176/a177/a178/a179/a180/a181/a182/a183/a184/a185/a186/a187/a188/a189/a190/a191/a192/a193/a194/a195/a196/a197/a198/a199/a200/a201/a202/a203/a204/a205/a206/a207/a208/a209/a210/a211/a212/a213/a214/a215/a216/a217/a218/a219/a220/a221/a222/a223/a224/a225/a226/a227/a228/a229/a230/a231/a232/a233/a234/a235/a236/a237/a238/a239/a240/a241/a242/a243/a244/a245/a246/a247/a248/a249/a250/a251/a252/a253/a254/a255] >> endobj 1413 0 obj << /Filter[/FlateDecode] /Length 565 >> stream xڭTMo W2̇IoM[]Kxb6i i>5R.`f <w \``p~>/h%,n3 KD[ՌꆢFi8ғiqUKй Wfeىwv|YDkjjf00Qɯ8&_BҘ㼼XoV"Zb޵ tSFEHJC-EDZۊIdØp#⺄sWٚǙљ<DVBDo[2r؃&cn1gq7y- vcb VB ;sgdfJs[ł_Kߒ|y"TTθO)tGChAW븮L0\J)Rq^Od8r ]{&2:Pr@BP Piak3g V8=Qz&Yb_"7a6[AK8 ۥT&M.ә'Cil2{!~0[ endstream endobj 1414 0 obj << /F7 38 0 R /F6 35 0 R /F10 53 0 R >> endobj 1415 0 obj << /Im40 1391 0 R /Im41 1402 0 R >> endobj 1390 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1414 0 R /XObject 1415 0 R >> endobj 1418 0 obj << /Filter[/FlateDecode] /Length 2592 >> stream xڍYI6ϯi bQ{ntch!az|2m3őA~F^$Xܪڨ!>6 }>'߼mC\}6Yřv/}L:iObUo^~OVN;li^F͓H > vlC8;nWqokOMi{a٫,:}q3UJ{~ЁZӸa9tE?~Y4ZE9lU]zlh89ь7jX*Rq#9CYsZ;y4B3ܻ@n.x'CfM%,zEq^KPD_z^a+y;V(%*ND E~ INFF:+P)> vpʊn@λ]lsad$`]lĊۡ?1^҃5:|OkoM ]r?HT@C@We>'av F >,#2[3ɬ+=GSZ'΅qD̺QwHPa^b]QyAWɾJiyiP`R Mh+>MQyB<:}=nI>1W9U-ƇO:Vޥ|?DPrJjPe$񑘊Y~2YKrQLͣ'G`e?8YYr?=o~|$ZGpрv /`>+ TsD6xg,/(sPtYўBlo_!VNC+@ _r3I?wBÍbaН,M%7lQ ޛJÔcę<(1`\4]2*NV,12-je441bTuz"{'u4_nX"x(I%)TP9$s #BIS7%+A (zG*Ux3X/;7ғr}Y1TJ8Aw-L" ?N>M&"gߤ{F%8U aTnJy3*N+~[i"qӫw)@H __1s~&E:PE݈/w3Tw endstream endobj 1419 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F12 59 0 R /F6 35 0 R /F17 145 0 R >> endobj 1417 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1419 0 R >> endobj 1422 0 obj << /Filter[/FlateDecode] /Length 2617 >> stream xڍYK Wh)3V~Nv,2Nc3Wd%zt&i#*z4XVIGcTeR?DG{&꒮^7,ɻ:=+񢮋cQq"p&EuL{AÿYeYU(?ye?Γۡ.co"QD[Mlw$Ixg▋qت+쾍g3&^xX ί_3- ~? .G)gՎE]v(N32.ܗM^ /_ )pͶ_vǯv&q'ѷCU'_g?\"Sd:ѡ+Y.ܺ*FXw8+haX':qxg>QqZNyz *-3]e߯W.I3gKr`l MQt QQ%YIWa踍'lOlaWƊc ;E:V}:({>IpߝK|ǢKj 6!s7hi}"@/U ;Un`i!hxgV;ʫ%H*I 8J+xmpμGqwuZ)D*< 3')={^m:])fE6a($%lL!h~g "3Y!ݢzC@;3,BƕHZ^˨2" e6ϮlAk#wq2f4ZdλP=H`B89ȻqkJ d!- _Bڇ"Dė~d`0(G +t4!K+l&57xZOhuf+$[f fK삭;z-Y_N4tl90AG=IOǬL%E W\Xl)y&!rwwwwpVJQޮ@ԉ㑖u֡xoX7\r@_;y-G<4b{u[tsx#:0p(Gpm̻lpu`h2XlܦHkho9cʷcfqUj)j[4JWݮLdRL3: RV~.doKqW/Ÿ{dǀW#&zbeP.>Sb"J iah+% !)J(a,HX}<]md<БD)o80ĸOZ%B8Ọe*'/Hf3n3`W" I{B9S ~\JF$ E91ɰ`g$ۄXvK3prB``BJuOUy\ BqϓfTE:Upy,! ,o8Ep^!*c=E.26I/R{$%XD'w,M=1x.SqN-hf 餩B,0kuLdÂ:8!CQF)ZEr o^.ʎ!r(: g塰$ Ո puͶݳRj]ǧ'ljtAciZ y5f+j/Kjil-[ IZi'fێ>텩ӛFCOu܇s^gN, b(g"Sp&x5gAo5V={uTx`c&I(Gk2G<5P,^a@|Q_Oĝ+&\೰!e~9 @IӇ"KVಷ X1g7)6RcbzF'`+|#:L Qm|( S2_݇ ?uʛ,# B_ECVh(,2LhQT[|d{/ L6 9AFƚ -t4fvD4UAY65̕G'AX:k& W7nQSO/.%v]<T?G-Kry2ċ}+wARՉ#Eŋe? Y e;‰dI$I# Д %,+Ѩ(,SOyj5okq fm s!ٞm<0 j$V `7('w SYUTR* :b$I$;AZef4RH 7塘SLյIDcgiR95Q#O2N$c+`5TRKIʷ*U +xU-G5!$f;fgRlzms^!u9g.j?C(cY%Y> UE|HDrD endstream endobj 1423 0 obj << /F7 38 0 R /F13 66 0 R /F12 59 0 R /F6 35 0 R /F10 53 0 R /F17 145 0 R >> endobj 1421 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1423 0 R >> endobj 1426 0 obj << /Filter[/FlateDecode] /Length 2445 >> stream xڵYKWV޽X& =L9Lρ-mR{>U"-Y&\,HU_=H{aދg>?'N^em4zI{7QAy{7? D=Npl4NVu# Lߟ?MX*$em6ݛAz+ǽ-S" c]ɦ(ǃ†+M+@$~nv4~A&uį껍q"Hv<5Mlv굮xK=]xD5mejNlJFVmJbTwf\c~49q؀8cwHb90Af;-;Ug9写h$DFCXInG;<vitb3$~UGD=vtݽjFUc? F]5 $ӈFCNl7B\ܡq5 qM[ gw3σҤ@G &*qꐬ8"4a$"`ĂOj2&kW#ˋ(&~[xbY5r5xI`=S&SOGOOU6jxz/0Lix ?@&)8ؤd@(H~-Qb0Lʙi3icOhuC}K zciVR!]0)Lf22N :s<P}ҥEdꗑZLάL\j~Ё-kN ;rVL,!ɺf=Y| %:Cа <]wY8PЃE n;DiaӰp$.Q,"(⥔"tefV"4IV[Al&AccUZm^qxҜrVS[?=5~u-ZG-3&6A/ߨF# gj zM@uijq;I>v OJ3,{c9dG=t5EMwJWCm_hnu `Qޚ+FDB?|ԥ*7rWdQs]i[Slc8Dz+;`R|eij_% &a[26iqwd]>`6S>.ZgPg՝1i6C64LJ!<.ykM% |kD6WF,.*;^*O`\* O)O3jſ:i,i3 Aa<$zU##:/Uwڕ! \ZT@oPV$ 8oQVz$">?ɓH,^+«,nݶVr]1RK ٚ *:~ Au G(X6lMAEg{"AIFl,ЀͻԌ 比ڙ-";[{4K?u ^&M?`0!HLQL*u8+w^h"DMd;ne0B.E r]u~VV?DW㸸Q^y}ݿ6Qa endstream endobj 1427 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R >> endobj 1425 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1427 0 R >> endobj 1430 0 obj << /Length 6205 /Filter/FlateDecode /Name/Im42 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageB/Text] /Font<< /R13 1431 0 R /A 1432 0 R >> >> >> stream x[mOOqޒvF$әm4gזR.LIDxb q ,\,$=#?_|OΥxRH1}K~=ۜze>̏ffȽoY9fԓc5^.Vob<.ָoV]7.tMWLWtMWXyqfxv7[eClN=yjf7ۘv7rX7tp7:nV]_SʝcdOHdy}嘭eQ|mуs#"(ΈQD{Ub 8TEUEiM#q&S1"QU;7Ph(r^a&2aA sŰ/rzq]5.5m.kE.{J^\JJ.x.+ME.;]W lXͰs%mJmLK"r*^.6]b u~g|xg g|gB,ԁY#:Y#:YPG> u`ΛB,ԁ,ԑB:Y#:8 uPg|gz^?|ɢZO`kk⽮ǼG.%wC<#Y/B%ԀU/,#]ۅIpD_ سۅ 8.\.foK0 2DZ;طb<}vDz1`9DZcwpyGqi. oD4XҗJ(R*Ioiu׆~}{#w;6on7?}{ *ZN/ ȏ߾ j~_'oQ^׏%_'e'r?dwc`9n] V\f~{2bzAo/rngX3rqcU⌈RusĩX5jĢ"9A#9=FD3gTs0GT``.QA8֯fN.̙%<=9=GT09!%!KTP4szDuI͜K(9{ .%3jDuh^<fN.x ḧ.yTPzljm%K(vsöUxmG7ox<88neǺcہv({=Qc%ܡm\2F h91a98l}a: a k0扃cށXw{l{2x=qJ-3']$e3I: F(WѣjS,=m$uR%*cB< :$mPϕ%P?"<ZOn#=s?".$DSo}]Ju\G^]2JA^WD rw N ?HhOg_$e EPIϱC1OKBi4TSʢpD\ M6ҽ%Ѐd2NPd(z]mrE`R O#Q%{DR|FpK(pER@$m~uHx DS%u* R}6"|2Vu҈dko4E3KjH-_6DiC1iڠ ɒ-jB*6y5y($i/YIh,60N?HR.ZjWf7T]:MQ`Y9>UG-AŠhkZZ טZϴU4媶b(sGg(sX }V<vUIݴ4^|P&)QScX񩘋f뎥{ld yjd~6bL{.D9n9Ro{f[9Ymb짍gӧgcsNMr \ =Vq/l#mnVv6f-_\W=ݬY}f +ě6W.fͶ| j̛W}md 6oVnV_AYmzJ_ l-WK6~ɳ}dKkXj:=gwXԛ0ytȳŤivb p>1cdzb8yy gN.gzNuv\wNv l{7c#sc/PG4ݞѮV)UJ. Gj[VMhMM\H7A XUJd*AJɕC@d>&ɵzc ҳ$6Jfr?j?IUY;vuժvXBNj'SZ6qdZ?i̫5I̫MˏU%YUSgEbހԥj{?2dUPWfV?RxQ\V k/ jz\gZsGwWW?$ўa$` Rb .GzτJ}8Ԫ]cl\Xΐ&-_4eIXc<9ѫ# ʈmT?Ub6&H㤿 Ngrm_$fEjVX4?L)nrb6 %¶Kh\S Y*lKkIefhgtfRCf-O`=NZ?6MR6PeژK~{CntJaW$#=Q>Y_}ɯK=Z+TtUF~AOvX>_ᲔGk'uF.IUELJDU?PG[%b=&L8#ZϿ7PU@*x7UDh* ̦bD43WU~v*UAAZPU`h* bD43!sD ^j![D8 Vo#=8qǼnura8 8iMAWZ ++]L+y="PGxk<= 8Vds(c'Be9|\!/rUHowrIz KBs\]Ϣ2J$PhR|J*p ʥ=4G%U\yZ?@5AT"X*>լJRI|HQ%RGd \s6| D2VDULAa%fHlCvȎz iъ)⚎gRTBik&UJcoRydIX&G[, R'V}e JAk̥6YL9G?584RZ >*ZiLÉf69uk\UL,c+z+]Yu:AϑSc^rFsvIٝ_\!W;L>=' >O  I<Ї;"i 蹭u)c9o˖pid-џG-,49%9ƅ%z6:p)wrY;0I%b":Ih:6M$ gzOg= ؒE8T:8dtɓRiAWm3?O9ghDZ)}Px~?EqGz+m3_Ͼq_9}#{^e4=T"|Мr6WbAdjߧͱ:<^er8=7o~E;k uW$wow7߿aɫƫi[z^~曏O)Ͽ?Qܝ4wܐG!)__ZZoĨ~7}7_F^P endstream endobj 1432 0 obj << /Type/Font /Name/A /Subtype/Type3 /Encoding 1433 0 R /FirstChar 0 /LastChar 4 /CharProcs<< /a4 1434 0 R /a2 1435 0 R /a1 1436 0 R /a0 1437 0 R /a3 1438 0 R >> /FontBBox[0 -146 146 146] /FontMatrix[1 0 0 1 0 0] /Widths[0 0 0 144 0] >> endobj 1437 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1436 0 obj << /Length 704 >> stream 0 0 0 146 144 290 d1 144 0 0 144 0 146 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1435 0 obj << /Length 144 >> stream 0 0 0 0 146 146 d1 146 0 0 146 0 0 cm BI /IM true/W 146/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1438 0 obj << /Length 17 >> stream 144 0 0 0 0 0 d1 endstream endobj 1434 0 obj << /Length 700 >> stream 0 0 0 0 144 144 d1 144 0 0 144 0 0 cm BI /IM true/W 144/H 144/BPC 1/F/CCF/DP<> ID 8 '4@4 @4 jpNi&i 8A?恖hfhH&i 8A?pNieYe 4 ӟ2Ni 94 @4 @A4 ӟ4p,2,2ɫi 8A?ȘA4YeY A4 Fi 8A?恖hfh$| ӄpN 8A42,2y ӄpNpNIi4 @4 @&A4"a ӄshfhfDa ӄsA4YeY Ni 9C/4 ӟ@4 @4 pNi 9Ii8 '2,2, ӄspNieYe pNia ӄshfhfH7 8A4 ӄpN,2,7i 8A4 EI endstream endobj 1431 0 obj << /Type/Font /Name/R13 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1433 0 obj << /Type/Encoding /Differences[0/a0/a1/a2/a3/a4/a5/a6/a7/a8/a9/a10/a11/a12/a13/a14/a15/a16/a17/a18/a19/a20/a21/a22/a23/a24/a25/a26/a27/a28/a29/a30/a31/a32/a33/a34/a35/a36/a37/a38/a39/a40/a41/a42/a43/a44/a45/a46/a47/a48/a49/a50/a51/a52/a53/a54/a55/a56/a57/a58/a59/a60/a61/a62/a63/a64/a65/a66/a67/a68/a69/a70/a71/a72/a73/a74/a75/a76/a77/a78/a79/a80/a81/a82/a83/a84/a85/a86/a87/a88/a89/a90/a91/a92/a93/a94/a95/a96/a97/a98/a99/a100/a101/a102/a103/a104/a105/a106/a107/a108/a109/a110/a111/a112/a113/a114/a115/a116/a117/a118/a119/a120/a121/a122/a123/a124/a125/a126/a127/a128/a129/a130/a131/a132/a133/a134/a135/a136/a137/a138/a139/a140/a141/a142/a143/a144/a145/a146/a147/a148/a149/a150/a151/a152/a153/a154/a155/a156/a157/a158/a159/a160/a161/a162/a163/a164/a165/a166/a167/a168/a169/a170/a171/a172/a173/a174/a175/a176/a177/a178/a179/a180/a181/a182/a183/a184/a185/a186/a187/a188/a189/a190/a191/a192/a193/a194/a195/a196/a197/a198/a199/a200/a201/a202/a203/a204/a205/a206/a207/a208/a209/a210/a211/a212/a213/a214/a215/a216/a217/a218/a219/a220/a221/a222/a223/a224/a225/a226/a227/a228/a229/a230/a231/a232/a233/a234/a235/a236/a237/a238/a239/a240/a241/a242/a243/a244/a245/a246/a247/a248/a249/a250/a251/a252/a253/a254/a255] >> endobj 1439 0 obj << /Filter[/FlateDecode] /Length 1754 >> stream xڵXKo6W(Ph/#񡇍ڤ.R@!΁rwhĵᐒrEN$<%,I]_`PC%Z*I!aDn^^Q,َSg|i)^Wk!d\ :!iLe]}5& ʔiY$Z'*OX pO|{.R%`?,)>{A%\MӁE\i'7nwooF&C^^[.9`HY:1YqldڊCZ4g W]kMkW8/)gB8 /nWUZᕔ]+-mdW!m|/X!~kfpk/ԘZ`JM*Y~f7K$"vaܺHX@=gl._-}42̄V4O1e]:56;zȜڞ6R$pf0Y\uiW<})]Aj&BHzsltebC7;'V^nKYo -떶ujM*8a 2d&w.}29wEDδIINĝCv=vX!<&Lxl'\Iz&@1GtNqY߯9*ҹҖ[JB1(i[::mh[}.rH{Y-E6}wXjLU& Rd(eFiH99`vYwB8Ia]{Gf_4 nRM@wqU@ 60톎یs8ZHˏL5Ϩyt7os]&*oч2D)Sw.o -ΚߧXIdJ_0*o^Baq;gyhJ1 )rq)a&g؃5YVUo\u PEQFSXTg>bl/y($P$.*g7t$$8Q@#07Kw7Dzs w0 R^-XOjeOԂ>z`X'J}ygx 9Rͮk@x[#zSFJsUD=&*ޖX*BͰ#Gvb9 9 ʻ/Aj}085'9xOypq} P LO yOglBUx M@|Ͻiii8KX:[.(T KvɎS4]PG_١  BxUÞ*kއ,6V7"R[$TtF&9n|h%YPYPVi8ڇ5NxtIZ77R;'m7fS~w~1v;/`>pmINJ`Ar@**D8R[lLa/J&ëuw|\2c* ڳ < vhy[F\ye}[4w<@UéTo; /awqYBE8Q֭s%kt J6pr/evY g!C̥ǹZ_qշ֣R׍w}N:3u2}_ u^F2}_A` endstream endobj 1440 0 obj << /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F8 41 0 R /F10 53 0 R >> endobj 1441 0 obj << /Im42 1430 0 R >> endobj 1429 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1440 0 R /XObject 1441 0 R >> endobj 1444 0 obj << /Filter[/FlateDecode] /Length 2260 >> stream xڥYKW(cćdgAi tMZȒ!S*JT,'{8ޜ7W1MBmvڈXnכo×$H7Oq}"dy[D{m{_TobMgӬTHxZ 7;eraf'HM.ɤlsRQ{bqkv4q.}yC[0TmCCm; )6$qGbϺit*8zNCK`o4 2Veӂ N:ѓ1x-N$S.L:J`;E2˄>W(FO`:^0%hvu{Y8_} Dq-%2BOm)2{@lYmycumW|~ZC.]9\Eά֐+\SF(JK\O;Қe5. eeV\DB+E_ۈLF1*^ M-{JE ^[\~U(LIWo cFa(mJ\f $L2ϖ2ԏ4v 1>R|( p9\''9w+UO=O\RGVoysx@tN46α y=C[3 t*JH0o4b/gQYЫ3)nÑȵ~8?9x{ _-aLbX\{P(ƜI(滖+$R[pnTőE/Cz6aGSoP "*-~7A_ELb绞KLJ n2?@ª W%@PZjfa>P͋AWjU{%g~$Z `6WѽᖃZ಩nMXxcYjXSRD^4U3{DZ 藧D endstream endobj 1445 0 obj << /F10 53 0 R /F22 218 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R /F21 215 0 R /F6 35 0 R >> endobj 1443 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1445 0 R >> endobj 1448 0 obj << /Filter[/FlateDecode] /Length 2002 >> stream xXKW8E\)d$ r6C:Xgea,S"%JݳH.9Q|Ջ+j狗o>CcP6vfZ~q>E  &_I+) %uz˕R:+Qre>򥦱iiw}=a*]ì[@ S*іau`9,]X7YXT-X֐%QT=<6^w2&0QٹEPE;X4EI0˱9k~ovբ]@fbdƱAFF1Af{Ԗ`4gȴ88ȩnW7$7O_gdM 7v_Rp4+LD%ul丷w6%!v*vK /b:m7;l1hYEBf*NKfcDo|$'+Qz>6%x Z.~dW-3k1Q 8Ò>pdbrײ%ܶB^Z5Ѭ=K/P2NVÕ5,|<L}3ݺ=u!3XQiySxg'xH8 ^|jW Q|jG0+,7S>csH̽m{´haJrRx IE_IGWGrY5L"s7j}m.T4&:Enk$ juN㞾f.N:=-GF[  n0fx*Zj1yw?PbJS)>C5zm_ L78Y:'%<Ȗ#o^X!˜*ĒX;O$V c.y~HD4B$5"1J$7V D{HLKwH [ 65itiK0E`㖆=)A:g_7hR q+Swޱ}P@_|OO^aʛ9o(̅Ģ}Zo:juKllKm ݿ 1g&4bߑN Do_6uCq8 ?S_`OwzY":Pu?]}?vJ"cڐޒBM5yR/6 T-e^#UθHofJO XT3f[ /`> endobj 1447 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1449 0 R >> endobj 1452 0 obj << /Filter[/FlateDecode] /Length 1822 >> stream xKDί0Pm$XhU¡8PT:cGc"~rZiV e$4/(Exk0"2VD~Ta(6Gj'^uWU`kcqNoyu%(ۦ7m*)E|5]]n-T:m%(L|ZYpvD)~e{8 *q. 7/lFy͌I17㚭ski*!GRH?r%3TT n9LE! ^U}bgkl<ֵVu[^oߞZ^bG#Co۽VǷ}hJ0,1ԡ8bY7usm{fHZy7,Ɂ /y8bFbj6Z\9 [I >NI =aA{`7e[׺Q*cϫf['q;a f6t$Mq6~0>P$Fj n_BQ[kae<Ǻo::E"> D̽{42d~[qYJުOa(΀~:Y$/{&;LQatY`Wq2As|DM8'keH=)G" ?WPUI{8U{p:udie, Ȍ9W# Gr#򄸚) ΦDC.ϪXR K+raê+g腟d쟡Q T襾ju_C5Ntb+zɑʙThCLȨwzڃMi/Av0 *,E?( +aZBsjf98h`n wJQLj feҰ.U׷O$4adhma84YU` P% |̷s0]iUwS )\䀶U6*[mN0"h `i{^dMu-T$qwYf6_fn kF ) . 0?DZ55tϬ@FxGY{Lzx\DqkGID)K 7Ll dU.9$\"u>y4/X# c?K3h$ؿr9CN~ BtB0m{eSr٥#s"D 'lG%<:],Uvhq!-%k;h- rZk3$J\e_(Sq7^CGv~=܈rb=nhJeS+Ǿ*D_)PW2YBTbY: vA.AFO ǖMwT~4 ΪX- #k5>X/9<"QG|vY Lc%/,~x k>b7vpϔ(#"p3vCcO8 x}E&t4\NS Jqa+|\g( !+H@8"-od⟅UJ*\bR ş^Q%!]ġ󍓜)I0wLIIYLWFX 8pw C[F M@d̒_)6UUi?Y4 endstream endobj 1453 0 obj << /F10 53 0 R /F22 218 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F8 41 0 R /F13 66 0 R /F21 215 0 R /F15 112 0 R >> endobj 1451 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1453 0 R >> endobj 1456 0 obj << /Filter[/FlateDecode] /Length 2574 >> stream xڝYK۸W*դF xqv]NUN;U>ĩFfXHlt|HI@I4IӼmKozǵWc٘tL+8i2ڎ@q 0r5zg@[@f#r4r7ݨwImdWGkhdMľ/N#fAnδugbm8:sLoD6h'-٢nњ_챃eT5zrdȕ~ts`R>-dN|qЛ]J״~;6oi:MD62?pw X w-0r"6豪;ǝ.`婦t:ù<κ&!B竻N[+6!8ࠊʭ&x +t #D7D" V!ʲy ^X h[wHUOs84L -ZlO$uz=t֕`;^Ltv'Ja ]JXǮ=ш쪜m1{;gc* [ g]sXoV,c+҄$4gӠKԯD'0hZE9Wjn0&Ә `1hr:_0EGYb_"ǡuvc0HѬbmn 3P#(yj}J=W'tqrq1 4;!89+|%:%k n+9M-%Yk{JB re'pC~ZܻLO.Oh|8&РbN c!D^(OuxS0ē=]bǴ(ڴ۴vOڐЕ:{U_Qb# WQ4\$^ju! lv ZgBأ# BPZ"K7 #p -?!nρ:ԶZ#OԦ[ %Ug'>x4)9b}X)M;Vyk<%N!!fasVɲM1f1U<,C _7o`;œR,MJHǣ  |Q]N\aM;Q14 7U(2_,ޕڮ@}YHm`Ip~0qq.;cw`˘sX lnd]Y!&#TsK%XQWE{nb}3>Љe5nX&ҫ 5S%K݇+ eŢEH]i\߯n웨ǻ[׆dX\c,Z{wYyq7i\om7,^&6{v.}g؀/}.8#^Io۟[CGJ Qƹ4tŤhԷ 5y]@.Ō Kߎ,q)e\Ô]`QN2Q7 x@5U83*dRT>ƻ -UTwG3ח+ ӡԱ E uL"@IO@4$_}rm&tܷTT-6+eգg[QB(?gI|;z*ܗ1Aw"*]4 e[TaYPv$F剚vMWa5ў0`xRASh8O.&)]M9ضeqR-oR:EV7r֊_S:?b̝n -rjaݿ߫|_RZ PN3>%$u&/Լx ΔMS9b¨m~l0&Ymb!,#UfF5;nE63Ȥ,>n 4)>&`K.r> endobj 1455 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1457 0 R >> endobj 1460 0 obj << /Filter[/FlateDecode] /Length 2105 >> stream xZY~ϯP#Fisy:v!b$/@@cmmcwJMŔyVu|Ud]e(Ũ`*%,A uџnLpT"n}h_KN^\/}+Jn 픡4L;!Qn;ъ&0vw"N]OEߞt"' .N9jIX)7GMԕ_%+NV^ϱ]zFHqJTe/PB&+P_c @Lf!)wC% Nx#*O>EAzAy2C(;K^+EI67'sf}s8AYfb6Rs g|27 vpR췗352 sn=b m9P¯3f3D^7U_x65=Q <<GQwAPxWz=G4Yz&Ls lpOFroSY/|>U}7þ4/sÒ!FҊEoʍ;8n4kd>UMmyIeEwj.͜jLg0NY[էlIUuS$0H63\,nƖݮj%Mc5AQv-IjP*ݨIYOڤwXpA]]@)lz}n{Bl>(xېx,vպEfcYl ׺BH(eSOuq C_!+d!aZZOqUoC߀*cϤOQz,C]Or8|)! Ϳ"A6 m(&">~ I+Y<96BCMFww ?ϗf43 3~9Y+֥x);|b$i'R]i9Z당(w,Xo ^2z9:k~*پA;>WҕF"@TJk-H(U6\p;{cg q$Gĵ,U2Lʕ<+\Zyq-P»[1Sf*r /@eypxgAScތ& '[F' X-;ws91d <&@ :dYy=U\٤VaTE5r@ʩ\i&$ڇ&Hi=վS~*64c?m\xnv^ cG.;aL,C!ų!uӄ3ڦ<ѽab &^]hf; ]yZm~oqLzg=.6_|fݹEOѷNp?C_vh6A\08wtِy:|>o'2%zUp{}AD"qʈ 5b|ufF3yV9y97.d^߮ɝ{Gޯ90>Xv$"K ˂gCmttznY}65*oG+o%QNa2iWq \kSz,\NJһ׼+I]>MVӦn a-j;=ʡ*z-vp T Mwevf@}k,Q/3>\rXYNZI.?35۲ګKpMھ8צj<\6WÏ[xOߗ];ښ]½,:SJY@Ud:4}50z1)/ endstream endobj 1461 0 obj << /F10 53 0 R /F22 218 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R >> endobj 1459 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1461 0 R >> endobj 1464 0 obj << /Filter[/FlateDecode] /Length 2232 >> stream xڽYK۸W0 U @1lU]*JvhKBR3߷ ȑ]&ƣ 8<*xR Db\ K.Ȃiܸ1EۛP"q!]ynI>dx4 _݇r[vVC6wD"a- ['¡l==v+7iXSovv!n4iUcW7:ͩBݢTO_qA8f~GBX0$WxriƑ<_@ v ƕ&D靆UBWb[Ċu7:*$ئ^O8,$ 80v nqVI7 8C#XR" p  G9v0<9 0 -Y=^3Tt/Fl,m\r5h_[T̸3+YBQr׮"ss5z4׭I2ِ]M+h֐ "ňbݩi ~0@EIߍon"d! AL/UuhP.t+%Z3L zH41Y?V@~f g(G*A7QιNoI2[mSJ޶E[OGz%[0z$cH%*Ji QSnog .Ij V +Mnc `[@MNSJiѱ삵 ,zū%4ZiiHi8z7i=mXI2TTx-Wxv )Z^]6´Rl{]JvW7W]mkM4'< Oª1r+&A G=xT'ʙ=-!)CQÌ^ӌѶW0-=t1p)܋x;AosAšѤv39> endobj 1463 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1465 0 R >> endobj 1468 0 obj << /Filter[/FlateDecode] /Length 2125 >> stream x}XY6~ϯK50-Ku4i.bd7xLtt(io]T_c?,HU4IgsT&ֺHRuuU,6ɦ?K&[q:_Vjˋ:)(%Unp=Z ut崤::8yэ<:MǓǕ7ޚϛPǟUQVVmed}rO+U5 'k26a֙Nr"(OR$ oR]r7pgw< kVyvUbJR% ƍgНYBbKLLL=]P*p{0h͉Ͳd !{xGߊgR+/{ v󜭄닠8#M]'$ܡn֏Cǣy4kd qӪVp;a@PV4W Un?@W(H`6]3,? 2sa?LBpN^ɛiގg)bB8~By7^yH}CN\/eRVl~É,)2a,a;m4 C 󊗦#64mLƼ:Xge5Q)}kSgaxziZm=JDUQƦMy{3k5LY4DD8'ƝMOt{#ixmaQT/F;Ӷ?,?&?;, ƒcS56R NVu [z4LY 0jWW dt"uWŌx3] G]iDB"xB=s,'"t! y!޺ U5\xf[|0?(3+Lgg)Q{&)a!$]F )4 8i8C璷uS(9ȤvSZ̔e¿*(^pw7p'75bP'x -QanATX 1M$]{ s묮L"Jg"~X(T T39m XB8ڴWB5d[<X)2#S&tZ;_L_!%fxM ڹʲ<$s;%S/w^j[: Њfϊ8OӔGz˨ѭpM@G) -0+Cj,TK킩ť,AEYQ5ȵ&JpQ1Wr@`V2ێפ57dZMZGI+O^jdCQdy:w\+E&h5:Iq`nÀw2,, Z Oǁ*cXGŒtØ_ cnLIp oZU7gttCqJu;|JTm'&q@G@P^X^ҹPyR٘0˿{r.MHsq)>ZJ}|@tWۥʅ|AMH`vǃL@l=/.U8qv}7BEξz< 4o)Ut:;l)Og\#fO-Za5ht~v \lK )*?Lؼ܌;=γ  %g$vXx7޴/#TEJ.HuSU\Ɏ:. ssC׮!p2B$1IWR L-;48ˮϔ tj](\4Γ,'kY'yui:CTci`dE8Ɋth4Xsc57)a8 3keg.<"N3=7 ׶bƎ{  q?p;x_jSnpؓ~=K )jbw燥:A5@{'~e8HpsZ# \SZ]1-ǿn4;@r+i=S &ٵy#CU/"~BB M a=͹קG Ci> endobj 1467 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1469 0 R >> endobj 1472 0 obj << /Filter[/FlateDecode] /Length 1476 >> stream xڵWK6 WyKEI4әv&!́i[Y=\J^gso/@PۻdzIB >@A8nC" db4L$b\u> V`9E~9YHxx]Ȱ! _nސ쾫kSU׮?qp'$ p]s`ھz\,4$fgiKªcך7`Mv@}"H㏕P>ոJ?W~4t5>B|[Wߓoo?ZcuPRnq[=VLnGP =i;ФjIc>  s5EgrV1L栆_;ޭ!%x^0Yq -$*r51Bv,νpb\,3c0‚{NҮwk6*rS}1I@KŸR\N[d RL\e1KB+ǴXP\ך'e '?x)'7x~z^@O.ŭWnVix dy0QzG5Do*MC`GWq bElr b\|ܴBθXWn{U뾯J&ؼ |7]sAduLtP4G WlIJm}<ǜq7p\),.IJ(.2/9K2.ɡN۱wP֔ԮQK䩙ʗ"D17-y嫊͗؂m ۱\^"u l;_Pv?~l)|w8떖}`o p?@ k(&.k&'S9%WI+U~=|q(X*b\JqUۆC!$F{vlǦ?| gĘiXb_(Gvđx&.cw4K_ofp3.q N~AS^75i3 nn3䮘ܰ3&OСҮ endstream endobj 1473 0 obj << /F7 38 0 R /F16 138 0 R /F8 41 0 R >> endobj 1471 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1473 0 R >> endobj 1476 0 obj << /Filter[/FlateDecode] /Length 1611 >> stream x}Wɒ6 +|풏$\&U9s%fZ[RwI$ hQ:O_j ~fiM#)?P*]%YXV$Ә29iQzVisړՖN lIaUzZQ_mؤQl*LE(wY0d5Ͱn@<-tyR$dөIH+<\ځ嫚 ?+"/7k*k*=@S5(fnu+Ȇՙ4 P ̰_tY8ېT-e0 =zBe QӺXD2!KNj_EDM,js FqwnpYD|IxY7Y\3i=ȶ3).K wҤDrF~ Id};q}3gk5ba$Yto-Z^WDJB.!FsUG1d9ēCbH)~- %)"Nw# PKݼ`(˜-V{.|tpŠm!@GvkԵa06PVm{q'|InQ`/?]Z/uW&HK "a# 4#2WeiD(}K|z */n|<2fP?!Ɓ?QMMqG5N|5N`4੤ sWiGIUTjSWo"x(UQv3mIp]FSF*rI jX+WFe\r}R-"c[}> endobj 1475 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1477 0 R >> endobj 1480 0 obj << /Filter[/FlateDecode] /Length 1791 >> stream xڅXݏ6_VbpQҤTj[ݥKGxl`EO]"fo~^d<4nEw!{ESDCmº'XJT7f}kDJݽ!QϙYM{J]zm]7]/'_v?42i><2pug7M<; ziڡeh'!WLpL8,Ɖ!>:q")阎tD R ɨ١ҴRWމG>XZ15QzZΘN]:waCb%*apڶ,rO~{ IFCs/5 @Q Fqnv@pC+]ϬB? *(* ?['iWT D3y`co,D3NO P~vMW@YBnu7TmQZ47ulqmAMy[u>t=&V e.r q$6z @YcEŁ˔n`/ DJX )TS8T_Y ʪb̅%0eOX< Ki211ps[ `@`Kf`텈' @R̅ $o Lx1K2gI,憏/wJsQJ2X-Rm-ctũp)5-%ټ:9 ʐuD"bZP=8r+Ӻ|NlȔAl 9;-vFl97 Er.pьH c]JVR.R&΃ing($~hQrlʒKq}|觢n^]v~[qz],s%q 4qYh6ГIx$B]1KRxZe3b]p5'" F`F[;Ao/LMCXYx!`eguV]=R'lPJ;yku{. }d&L~Hf^vH8#-+Yd>WYZyz> endobj 1479 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1481 0 R >> endobj 1484 0 obj << /Filter[/FlateDecode] /Length 2217 >> stream xڅXK Wjs+cDzL[$k:Ll%$OwwD ~A$(w%H#Qh$*uO?~E#}@to3r.rZU5`p)Uj]2RyR}^C <2)Z&S$ƪ0f 3-is<rruu9\}GN5] מ\=j)4G~H'|Tğo 4]ݻ;N"C>zC^~Vӆ9mh~e)+7h'\^qBPq&d̮RpM?N{:yn!D'C3*uʩKq%BlXͦ4*]+2@ $rs!H fEteh7uD͖h4U#uհ4DC_iDu@R ,IѸߪxC{-u5)wA#$=/#&R-@2WX&>%أHdP"Uc{[Ƣ7n$&SIUM/g5g z̍~x\$zٸ3m'Ԗg |αMC bIF^(\](^~fP::f.m7 ѩ(R!3<"}yܸX9 vth֥#⿛Q( 8(q qQd[DfT2>3/) ˯U΍ws,?&(X r=ؙ%DP3 ޤx'馍)]bH;袊T% ɯ 9C50nC{Y 3/}|0y_5\|*Ge6Yhc(,;vWp4&K{ZYy9@K[вoI|~tt׻fǤUɔ' jw lYp,@81O{ T_oyw^Ix+9oOA$ ?i<JӃEjLS%Xsn8u>]A25xqfa:w; w_(hELHX*EY`4W=\i2g⒩#ab)pfr)v MrgsHAw#8bzsgw@Ts+ mjAd?[4G{'}&[}0p#8r 2%"H0?[99ga/&/((V;({]죮sigxOCKa;>i\3ƦvEMb'f,J7W鹉q< M3K?,LĬH"j;ULmih6" D&KgmAmχcY :R~75|&S5 ??/7F` 5QxhF2$xgw?ch endstream endobj 1485 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R /F21 215 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 1483 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1485 0 R >> endobj 1488 0 obj << /Filter[/FlateDecode] /Length 1581 >> stream xXo6_!`/R"Ϭ-mmao0(6m Uԧvx GWc5]h=`& -k%!(<Ĝ9}|BY\˭]A3>֟_K_!쒥c0|5rEǒ}>I0}Qmu؁Dli&c#i H),YYl,hOv f]FASRˣ3g9-e>eVZm Q KlI8`tǺ#IP}*j@hט 4 Jrf Ih5D[{$ US$æZå78oP' #n-Z|g֍Ty<̃`L`'It{qn\4K*N)?|aJ"֪|Baq3I=|-}Uv-Gc {:sD}&Dܥc$LHH/T E>5yL*ެff3x-C P3&HM5ӘXFЦzX%9ʏ&A!r헗8k]~kK$ (ߙRԁ^ h\7h"S;,tPj+=er~E<v/7b/qu" ɊI9!OFrwyfR΍`bL0I 4Ea,Χ usR@bP ^*sG>;u$j`2x^wJ+7MSOO^*[bG"r3"8ꩀq.l˹8 hZ?ciYoDLT>mO m#SapbW[:dbb>Ј*#8#"q26X|*8J׼8w:^&Z}$&{>ΗMߌotLfq0?-}B> endobj 1487 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1489 0 R >> endobj 1492 0 obj << /Filter[/FlateDecode] /Length 1516 >> stream xXK6W(E*F$Ňv-(z:\ۀ,m,yeZ)4rg&9d=d4 rl N~O~?D%Oa?FLߥn\7YFH3vpoE`L"ƒ0`=5#,YF8OHow͢_(DA@O+Fm4&oھBۇEL2m xշC1EbFDrZﬞ:o-j,qZAN]Eg" #aJf;EF)M?F} 8&m] roCGIFmV,CeB2&JKj6 d |"dU! ܷՁHcENpأާ#M $ܢvjĨ}~w}\rhqSH4'~5pE8=E_nL'+C>OQ#3@s~}2ZdS2ڔ1g$V4EyBSw a)hGp&pk̡^O#oSJR׳ QL(a[ړNfc&٫Vxmk;{i:YtGP OΑ PxC Oa qFQLrS#L@ uT 2kՙDwkDcdKDS$( {90ˢ9yќќbN!y:kPK[;5x:iu})IY-$R Mc\NaV̵Ibssw2(2zD-eTՙMsk5`(爕ԵU[ޙllw[KZTu'TT&o \wbAA}?VUt~R]nro ha |KJOGOLI?jwVVqҿQo `HD0&8 ̋, hsT{3fx`p}}턹4}77ʤ& 3 t w(fv=F'0MO6RBzeBQ'H#*tAjmTcx!AcvvF 2>hةj-cW! ʻ۠JrnTcW]'|^F9js3I#Ynԇ~V=C_Ϡ,qevgq?c,ꝲZLz\HIKkGiV>mbǝp4O8܇)!ʂM7 ]mXk딃T . a H{ԣEU+z=4)Pnmt睃;Xm>>x0fpx8xѻ ;> endobj 1491 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1493 0 R >> endobj 1496 0 obj << /Filter[/FlateDecode] /Length 1510 >> stream xˎ6ޯPDЃ$@&E{ 6BksmHnwCʲxIQS{1`WX.Hce"J*WwYǸΙPd,Z*.ig*12Eif+;gY$DNbDB1.kS5o͢DeG^CU5qk̭B,=b]=E ͮ~=RH~̷g .i*zuH4.@ˁ`RXo0uvyԸa>δHj揮1fc:b6Œկ /^wm}3ƽSf͵;[ض&+;ͶEיuk$4P(\M(0T0=q50,#wQ% ukp7( ʢ@KP[qs#b>LpA7c٢HRͮ^%ϓDLI!ϴ؁i.Y>GHg"ؚvU]AUZ`ɌYu]ꄉlPaA51Pw T^Q* % E-Wk41vUOG׻STz.@äYA)͠ܖmezCV0F%֨Q1hl`E!Qf,ME+xp1`FOm1THJc8QF bBu^쌪䫩lafR2޿",{VOvU756$j,9)bꭌIOi %.l^ÅyR-b:>[F|OH Gdy^=£#J/BB3$\C5r$ dX&4M ( G|p<=uA( PXɡ#eCC@Hs᥀6hfytK~,XyHicPs4=,U>GTLI\ʩFI? ?ɄtҳÁOf}\`]k1~"ȮwZV;T;RLR{Q na-F laLz9 9ԏ#9K?;b߄GdmT4dP_,GKuv 8bY˭Fh47Χ9lƲ.5CYQSiz}Q|QTuOvq(Gs\JͼOSn{?䦺4H2|7I8G!Ajh)VTހ%M^MOںw~|;K|Ѵ0lpY4^Դ/ΏDi'-bk˿|0vB93v*w (iJ2uv0@Ц^_2;Cfa:^isז}o/(}W館}۪\cx8?(2 endstream endobj 1497 0 obj << /F7 38 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F13 66 0 R >> endobj 1495 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1497 0 R >> endobj 1500 0 obj << /Filter[/FlateDecode] /Length 2238 >> stream xYOS(ȡr̈Dm66ݢEnE ɶ} )SMѢ顀Cr8g~3$>_K&,Zi)_G?Dz Ois&雷7W~O+JջƌΘQb cpIN`2sZkܺb( K8n9hGvuhm Õs>'$Jcy -Ⓐecr6~?gc1l EƄ_^[R \ Po,J 6-U,YGwEݗ#AZ8G 3e{|\Ul?[Jc`kg  Vl\qԇL{(GdHW-BۡEZ,'IǏv_W0ٴmM_jwnZ)/\$2:_ :&$;j#P 1FOqcͤϙ#a`»'"˱Nj@59}x%%X(<: b4pwWwhv7kаs\ɵ[5oiW~λ__vs69iMb8l LJ'Xz>(:!ű=% !E)h¶h0uNВ.ۦu&NahwmWUWCpzGFJ\婧JgiveוԛG4vS\n뢳NJ$PTW@"ܶSUX:i%<.>ue_)`I~{ wy]`&'+^Wg\i: ={wݪvWlXM~;%\7 B@Up2K u ERIpmwņ6>x>n*SfF 񤄠xVO`N!1"]ک8' ;ʼn>y>9gGU)?M%K&<ɘl_CNB O1C@Q z˦ M<8Fꋪ&q զ?Qm륝pH*H>ٗMU%f~Y17r"o h(wBsS-,k ȝ𹐙M%g}Ԫ1N*GV LJڮw*"fCYbnM9G7> endobj 1499 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1501 0 R >> endobj 1504 0 obj << /Filter[/FlateDecode] /Length 2248 >> stream xڕXK W(7ue_zˇumʓGVz)[SΉ"x|@E Kt +dW%"j"%S">dQ4z<윉"z|{,|TE\FR'8nY;![S@].nY|ˈsVh<2B1qFrw-Bǽ)Dmq`I92%SؔdfGIt_j3!4N^D#r0q׈k>v9 vQ@@y4ضD,?8֤*ZŤ ”VjrnijDL{ :Y5OU]9_Qùۉ Xv|ic=U/9M_UPa5CB?1,4}מ0H-̫r}4} bc6޹IƵaj<YiO_:I&5bɯ 4aa[^q5yW"EΊ3T jI!GMg1jT) l?`hr x  \B4]crB聉2 =t3p ?pIHLe\j޼9KGĥ >E y!kp%ڋBp(li9Z7 tqA /Út:jaт)Vˮ/40x:EW J4֐?(BH$O\nq%O8u BT4g#kDjT"D w8QtՓ%ǻk'J[@plajqHY&gƲi+ E``\nAp5]UyƋ$jb`?JJE^B+N%ֶ !AP&`LXX,{c@:_肅^[/W&ݭ 8YJ^ N{Xb-V.$+X 9@ilOSE,X̂oje{ c_AXwv;LsmAB&;›5O,IJL'봜ࢸ.J{ԗz1!|Y3qv Kb&9#^$3a{xgo͡2c^z]@}ܫ ce[&Ls*~k/fz!/4|-*ß ߴjWuM"LYS"趛#2vq{,{%B3ŪU,zmѧxbٍ5z-8o6/v#w8\Im۷%7~si'O\ק&ʡ5R4qY(x/ K04 33mJM)6,wky "=< )GUZ~MI|34;/HPS?Tŕ1ٸRҘ_>D&|EnN׽rⷻI-2oR[u2= 3uA| $@*qǂS9k&挰䄕rWRJ7;Oty`5lwPzUoPz׷˟a?] endstream endobj 1505 0 obj << /F7 38 0 R /F13 66 0 R /F5 32 0 R /F8 41 0 R /F10 53 0 R /F16 138 0 R /F22 218 0 R /F23 221 0 R /F20 182 0 R /F11 56 0 R /F6 35 0 R >> endobj 1503 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1505 0 R >> endobj 1508 0 obj << /Filter[/FlateDecode] /Length 1730 >> stream x}XI6WЃ <356[E1C_LJ eۼ^JI_ $Ο7XF~[*phK;<u1VuŋZ .>`"Idg<]ol?p^G??"Єiଔ"೟zY7L>3l F 'I$q#0Bw7/C *9*fIkzH쮈UqVML.HLunK0PoJ7Q#|S%7lPߟ[U o[`м*L-'j$+5*'a"#9gJɅcu"]?&夊[+Hkp;[NqRvEEҐa6nG _g2n8eE㲸\ N؆6Zw #oA;^cǽr^Y .˼LFPWe,WGDH.8 9f|&uN w! 0*+h#w⹇K{M܁祕0bP0nRSͮQLЮ ?{ާtm5W$yCݙ ZsA/;\.|ϡ9jdvi:~ _+fT"aiHjfR=Y,\Ul9ԃ r@~`@^ZOe;?Й1ԳQ% 4[".p[`#Ԍ'W+R0Y~K$U&;3u,8՟ ƬʰI,d:^+0"a˜ @뭪3kW!@(\2.2w?Gz endstream endobj 1509 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F11 56 0 R /F8 41 0 R /F13 66 0 R /F15 112 0 R >> endobj 1507 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1509 0 R >> endobj 1512 0 obj << /Filter[/FlateDecode] /Length 1301 >> stream xWݏ4ov>8=E !!],BnѦIm6MK{~8>}gyr"; X y|u;)K#g9WW$ R;,<)({(vjm4wW[\$­ݪh-p|z nV rk ܪohj_=ۊ6UcU&Eg:[5Z=NV9F\%BOg}F!|y M)lϺ*[}u/|'EK(n /5oQnD#Qg$2H`W4Q<&n yWWWY6C_^k;]ϷዯK"E|<qum?HA$fU`/\rX}&\S|"wUq|2ABE|ss#aS1z1%9K3զ`ulPJEuK^E)O,H.\B^J97+ͦ> endobj 1511 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1513 0 R >> endobj 1516 0 obj << /Filter[/FlateDecode] /Length 1774 >> stream xڕXɒ6 +tTWM>NJ.IUɁ3*K.-O>AJT/c$A%,I>~G=GYJUu)mwGD%+s&⿆]Aywc˴`i%VWϙਰw{1.m LO͸K!E-e۶~qg8hjkz|٥*Cc(R3T#٫ceb4h`jxGXsG]έ2s #^\ Mgܢ˓[=0+\FxV 8 s5̓-烝P7hG]kj}=iA7IũBOfI5|a 5p15#fi<LIܸ5 - #_z޸mx"~ a] Ϥi0d#2? )Oq'C>"~pNNit{zK,8IvYR?-s\йS T%S zzNd`m&}>ʌ^f;59["j7G?/!Oc["zGzaƃ9FF~j8ѿLt g\'e7\ɼn1 [A[@! 'Sj'vH]'J7$c _( }hT]F"\bp!;mjKh?$l=F,Z@2Ϩ36Ά ]DZل &l`RM5Jtlށєt)]dsQ U„r4vDSySi` ̦lcL=.`8G$-_ {Y̚:LXֿR%"=9E$<ŎZ;g_;-3!ft8/ `jP7IF\1.ɔD&0g,Ra26p}ql~#zo0W?w{B0$ZiDIpR'@ 4 T IqZLmL^B}>dϠd_²5U ާb'~l|J?OezR= 42[*N!؟%gEvğ&u~K9f%",?F> endobj 1515 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1517 0 R >> endobj 1520 0 obj << /Filter[/FlateDecode] /Length 1771 >> stream xˎ6ޯPor1CmSiE{*P9dwHG;!%JI'Cp31>˯$)HcV R EJ*7,(Xu3Q_ޅ"RP*12ZDi6%O]<=햋\E$p2IOA$>7eZ8+2q^?< yxW! ޴ۺ^5  oUV;.״tY؊Ѯ6pF$YSwhI^lOuӼ0(x}]y/7 GsI:կU+[G &)S Pg*ȮݱNU,^-,@w-Ѻ[ovmiBa si#. i5HemF[=us#hAM2Tne⴫yEMlPT hyd5gm3.8)vC!ܜFti*`?Y뢿.3&X݁]6t% 2(qr3?/>UTn@=TwA6y.w߽yOsgf_y,9y#e5Ε'ZƤe&+MM1%P(}Tٷ7 (9ûAj8,r,OA%:P=퐋]12LDzst. ,)(@(q}bTFPAh±f?cX‰{%0(th 1Cc$`:S:a809iD>gӁ?BB|D` T=Tb#-| "gB߉h( ?>eS(#+f.zVB)#bLN}8ƿ4.5>,տ endstream endobj 1521 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R >> endobj 1519 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1521 0 R >> endobj 1524 0 obj << /Filter[/FlateDecode] /Length 1442 >> stream xڝWK6 WЃƌDQMCn&==řLۚ!캿A=,n: ,g0ރ,Y`ߕ{ys^GCoo~}sQ8+˳q\0Rm!811'lis³u<Ȋ̞t/+uwpCCtU^U%5Ԣ Dh# YBh&*>eS[z(PUQ:jj噿sۅ52m ?8iCw0GVeB)K(یhO8ŘAO.Qؔv_XAR:xm7n_g 7W澊e@,(e>q4]DLpGԅz!$Bwl>>j0A::4c4 \'K~c.,X:x|@4-=K$d:EFj<-ɜC6 86w"Dz$܎ pյh;, }[qͮ')#J\n<àkeڷQOf<HHH $$tMB/2@@E8;KOK TeSx>KTY=={Ёe#iS s`B1 80qؕ(m^#pxiz"D0ox`#ep* J8A52pڮhH?1Jwvi:+ iCI)acm8^3c8 o:]Ƒ^KKڃ[U hZ" דerޜY^jy3|5NT76*$YŁ-VVYlQN9ȧ4SrM ƾNx2% g?bc_[WphG3Q'Č2NVgSeWZI3u7a&q[x)gQ4%d9e,D[l`` 8BtW&)ˇF Eyn=|.ܜ0X'>FBL=@}^#&ApZIK&Q zt>pnI!+PCvƗ/XYxV:`JQ֧J GqYx٬T R϶*LNRkUoٳk g"= _xDgm/ԿIQƙ3KOG!Te7%BOlpؿ߮޾tL> endobj 1523 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1525 0 R >> endobj 1528 0 obj << /Filter[/FlateDecode] /Length 2176 >> stream xˎCm._z†g'@AwNcUkё;3?U,RK=FtX,*n8|so0ěMY6;3.7F+ʹz/&gy?tdHfzyɜDzґ8nK4g{ϭ_j~ɨduyo2wX Ḯ'v8}GC y9rf'Wu>|%TCIݥ=|Ow8'ɜp{w /(nK=~˂πfE,j-|cB,\ixJSwZ{>d8TڴȢN܂=LxY4O3 SڌU9tBbyR&28ϻKvftTIT5i|tF2L+kzdc'e2P@ XCiL2J1!Ҳ%")@-ƥXp2yHrgG(sUW}@.e:Cw⯰/39W̡Nå]@Lo5Fl6MZ"ǡzq"O"*g"!͓S~M[ xlԚ(Qqt6#8bKƌ^GZNY0fi0#DZ}w9hN͛st 3ohggǚ<X f:'um>z"_B>)FFLlVl:(h7kNU8MIw4ʲKr PH"wMHkA_L^jlxmK/;f,|n%kY.}FZe2]aN9Ks6&:2 !g3TΪq`JMiZI3y١b"*x=sw:kM$ XRTk'D1D!X3JoFXiJ3\o+!P:Y'h'vA0=zR^lkOp!^v "(rUr()aNHQRtw.v']M \}86YB? }~臮9Wt޺]%t@٤yt,>YrUSȪbeMpqȫ5a n A'sB(]e]Oج ^roHk6~JSol4@PcWה6}l}Q^Q„w;}T;w"՜b]Bꗿ]mΧ{^+"qLqV-AM Dʒx^|Q*KD(bIH_!/ =U iĔVgL;3Z24_R&F:tTxS-_0Wԑ-8~+ JЭۥ1hhfepu*b8-$,IժsLj&0򦃎W%9!>[:E6kR.ySkBÞf\yMfۻe7ci,RfR+nSW4ί73PX:}5/40oؿ%j2329S$T R[> ådzL\Gyi]:_uf|sڳLzꘙZ dNBQWCO.Ω O͏qPū RY( +ڽi;Aw1kP:CQK$6CsY\@动^9G$Gys=R?cgeTs1 Ш I+vB~cuˍ%twդY,OĢ Iyz&2@Ψ߳{rK݁R"9XX)NAz.Ȍ!4VIdzF@+9l/DZI܈s|bs'F(p֙T`zk,&\q:Z`'gs%&C7 @@ GfIw6bƺ4H?ʤyJq& D)zb$oߕH9wY|ڸU]yBw7ʼnp k֏Umow qĴ|:*.}TZT+r1R4woVsn=q9Cx\:Kq endstream endobj 1529 0 obj << /F7 38 0 R /F20 182 0 R /F8 41 0 R /F21 215 0 R /F10 53 0 R /F13 66 0 R /F23 221 0 R /F22 218 0 R /F11 56 0 R >> endobj 1527 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1529 0 R >> endobj 1532 0 obj << /Filter[/FlateDecode] /Length 2113 >> stream xڵXߏ~_'&-E -B%ːmpHwt_L p曏d,˒Cb~<%yƌLJL$]bJ6Ww)#]BNKtSDynM^kv]=ZZaK_ .dBDvJGycg,] 1[&S&]Vr ֮L/NJLSc#R.(Ta>=#4 =)%[3P?Y/Ƿ&yAa&yL"*VI=GCՍx;UMKB`ȗalvd~žRm/[/ZJH{Di #$0w;ʊ2!\.VA=Vihآ/qxSH-g>jǙ)l;E'1*1EyDbUܻo\_{u40E=(/|L|⦵孁\*m.Lȥ%BEkxuD+3ʥspzA4O- H]X+3=H/{D/N.)!1"<+mb8QS5]K n/v9ێ[4U[|a}e:'n0@XQZ-V[t#14#3-0aKuTg!Wܒޭ7j|Pӥ7~PQ("sA/SCC7oiqxi?<}oIĂ,<Չ6Y6XLs HpՅ&. endstream endobj 1533 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F8 41 0 R /F13 66 0 R /F22 218 0 R /F11 56 0 R /F6 35 0 R >> endobj 1531 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1533 0 R >> endobj 1536 0 obj << /Filter[/FlateDecode] /Length 2277 >> stream xڥX˲+ Uv%/?DI;^YDߜe4v'x_ʢݩ ɾju:؁z<.ɢ?>ņg,d%)+2܇itJZWP'-8- Nқ(M"g Uc*0Da\nܝR& 3`_D6 n8¾[)^w0=IR,@^]jC>8%2CMZWL} 'j遾/ Q@E5~mײ!m`Lv  ~+@@r% @iK_rW}p`/9֓AH %#S0`Bo*Nuw5 +Sx%+iv6 )x5 ܵ^p>H4 pz4tjйsqjynfx ;+!XP=@Volמ$SQ Je8/G^ ve| t \0 f'= `1:!!B*L Nӭ}#yP?-\MIjB4!s7G9:%(뙇B4C]_Ijݞ& Z` +hEְ|P;xCY"OY"H]hRFKI&"p_?KmDbUUyg@4ʩ֋jJPS9Ne/ endstream endobj 1537 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R /F10 53 0 R /F16 138 0 R /F12 59 0 R >> endobj 1535 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1537 0 R >> endobj 1540 0 obj << /Filter[/FlateDecode] /Length 1844 >> stream xڍWK S(ϬUlI)&vus%DRe=@ě$Ny?7u|ؼ6yI٥)Qnx7MVeEd49®Lcu[qh=*NMo?gqnvYW_5W}̵8]"q}Y9@SazRfed66II֠Vr˃8e'< Bu,mwÍMVF( !!ଷ5DUVDQ3yNN쵱^/;vtsti:|s#+΍m4%Wy9xW4:/|l:($zR4Ygſ_F}O.ջ`M 1CDżS8о::jt9L Dk=$)8~l9yauڏRav$Svf3pu*|\W"!4|#vZWC)0ÜU#yif H$/~]8՟ ^/ ?]z8y;g0Ȝ-j 50 v.\Ey d}Cff h Fmzŀ){GTF|4w5],X_??~<2&u͓ "47I\g%\}R,WBw[vHrN~?@Ů~R'e\=_-Z3KIoYq endstream endobj 1541 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F8 41 0 R /F10 53 0 R >> endobj 1539 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1541 0 R >> endobj 1544 0 obj << /Filter[/FlateDecode] /Length 2232 >> stream xڥXKܶWL%pDr`+I!w;ÈCNRMǻ_ 9Yˉ.$Fw6aCn6Yx&ID%ͷw_7*D)7wG`b8ͱ:߆qZ? ^o,˂man<$]4T惘Gj]|o4wu{O_Fh(ڄ&Sڐ4Eq7EP +pwĽL-H@гqgNG^9aY/20žvC_?Cݵ^Qtndh:)1M.3~[>1؈K7u޶ǵZ9"BFyWiWYpz{;T5TQ(ط`bo~m-S~wvp#+Ag52>hL̜%6!q}1#SNrh4 ϔ6@Th"Ogwq&,&]~Gfs Y-@'g/ xc2/.K1@ɵ\aKP &YIb$­A$SɑA$0/Q57*Zq`B̡N'8</#9A<],ǥmwJlO2&ZcbX,Pe w_j\hLd~ tx fJQeS8_;{ƒV(x[n<=qyNs2f:h;e 9UW*_=W54Rүr+4Vű&ЬZv!|{SLf|JPGݱCy{Hx6i1u'n(dgY,4kgj}!iaãyM2jJ bN)56YsBv-rZ;9x}U7<')7tFVD3E1rXTQO*_yQi9k)*U|bw{+1Ptϳe-bE=- /2m; )/9U&_xJ_-1!)vE. 5\&^h8]^afKr hOP`s㋰L@)#v?2<U¤9PEK=|Z=d=k!%zĹH_ #M57TBDf?n+8̰?s9DQt HCUt$bC/5dS닱IWPuǏPPO3ŐF~2Ẓb"_ iLV$o;Bzec U2 z)(=ү哼9]a`-Sxg] ߽":z{ YRM LFi$:dHNPVAU~ӵiz.+ӕ x1OP,B+A 39.Kx_ -"8h0%L@2)U_PίPև0HUiB,zӀ/4s 5r:8R^#^_b` n~UK:CO>*pzW'Jyrw *tz#h,Aﻻ ;+ endstream endobj 1545 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R /F10 53 0 R /F22 218 0 R /F21 215 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R >> endobj 1543 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1545 0 R >> endobj 1548 0 obj << /Filter[/FlateDecode] /Length 2182 >> stream xڵk۶{~NZʶ`E}3qsݦN&V&L'NH IN3łI.gk .}b n<;A{w^Xz3Y bJ߹{3y/fqͯ؛7~z-q,RصĈf:qiQt&ӦI>b㯲qXUVjW`R^9@@bP3 -X$a#?)Q++:6NgeTvh4f&tm (n1etr*"_1 z]MDsk{NQ6 rӻY,jש>/Z^]E d kY-ҭl֙[)~Vp}Á\cV>AQ8#Y/"=i MTes|vXrt%JEZ<.YK`5ƿE{)ޙjUvle]VN=5̒ dJMzA Ǭ QDaEUyS% Rwkb2'+ӭsSyؘi`2K#9ʒkt$NJRdAf ob[aU:`lRRa}V$YK`Țsg⮁Z91l5٧). wv1JU C35k,!'F; j*:,Ox],;uV;k9#:LE J7H+%a0K9jE@Erf"K/[k[wޥ6m:Y\h*`a0uPiVEG<.T}:8 EX([y6ioq]R9ig phndFg>^W׻(`J!,]Ưz74>c|\jNHf2շ0g:@*?_u0ڲ|RGtMQ ZY6YYu}{[]C1!j_"(Nf'Pl~;?6g\gnݓiv3.5zNO.-hA{!" ~z}ۆd.-s5.6od X{;S 5Y1.@Dx(Q hKTRf2b=b\R .{>0&xt8f3θQp(WP {2=$<t GF5zz{AzNt_? =R<';X0(RL)+tI?mѿX"4ixF<= x, m{CZ@F=چ6C˄%} uMt]&4tOkn=R]=;B'n)+OݛqQ nM#ҩ ڤu 1H[cNJgR#MHOI(&8 4RJm0=,;v :lslS#gi9/nz6$ .Nkp֚њKlp` 3 !& =h_!6bsb3Xh 6=SŦk{[qa^>Hƒked!DWφQ A-јW -A!EcqgI| =}x(r8ZDu`ZA.&7)Ax(H_1 endstream endobj 1549 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F23 221 0 R /F13 66 0 R /F12 59 0 R /F17 145 0 R >> endobj 1547 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1549 0 R >> endobj 1552 0 obj << /Filter[/FlateDecode] /Length 1729 >> stream xYKs6WAcg6!-ʁ`3!)+.(ӨLJ7ۼ#U =}\b5lFFx |=*b"2jڑ,% 4-wRG̹dxRphd7cy̲plYўM%Rpgܞ;f%X D ^#W;|MI>JW$-27#ԼٔorO8 T}( /Glar/ Mo q"3il6Tr/\8?raoυ;?{<U1|1_5fxN>^(Xdq,NEpoN0 A4Asv:М4ʼn%bo,N^,isl|c?hK+Ccp>bf~0dl9/xs{Jr5˪Ftt NMUM82\3# j7{i{.{Πu ,\q>f[ںw gQt5rSD ORks?Hוk7$ S{=6_264`-Rl".5d|:OX{#|(7;;1$_ Tu$;Ǖ|^S`L#&܋ïpzُp\RS妸E#}22~')%gu]KjwޔYW,'||J,\WOM)U?*C*CiӮjnh&nj:_th7 .[tAYk bBp \%-p' ^ʆc<&N8pSO2 *Fהe~+*c9Kw8dܻ덮 *V;F&hfoVb=@VFM^ }_-47zb"Uu5Q.3t-?&R>O&wQwYAn 4 7LUkϘSg߫vp;]i֚5\F/0 jQut.`ˇ jO̪U[lӄeA{zwmy@4+KpO@O:I!VD"xb {բK-U6EVuF[#[㎖4fR {D;#sPg T-.6ŒܥE# Z4Xۙ&B' qev '3yzUC /5n$}g]nWC_yM.TTEQ!-q4uZ%uьnO endstream endobj 1553 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F13 66 0 R /F12 59 0 R /F17 145 0 R /F32 356 0 R >> endobj 1551 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1553 0 R >> endobj 1556 0 obj << /Filter[/FlateDecode] /Length 2803 >> stream xڝKs_[DB@ \2fmgrZ-6z=?ARv=O~/y% V^m3#ZVDuwR*EnTruɧIKñ=6צƬ$Ue竭ݪL> tiʹLCL%*9M>msS?mp&{s36[]DNZp=ҰJwG0w<kB/~.<5cR(,K?%Cs_~ Zxq H ,(;y'R6x[;qjPZ] ٳΘ}6`8Zxu@0o@@bN'X_a0 %&BAt8A;&=xq^N.QA,װ)cǁ9DI [j0/$k]z}4^P k^dF6 11x0yˋQΣ)d HU ~ȫr6\[ cG ) =¾ XGCOc2'W"ߋOv;A¼XX2-lO };&4}J2# {>SV )4nn!}n!> 5;dLH픂*'~X*aCydj5'D>5yf( CDMz,u$A!JEXdb$y|n,0ʰL\ʸg"Al؈Ꮅ ծK+Z ZT{&[#N -{{:`y}UD.gb,H+QY c%󱮨hfe.FtЂBz?{U8O삱O /0xAaQ &'xH__eGnǢMTF/ k0Unoy28ռxw}ݱ0)}(TT :3ZX3P*ЍvvTǾ#esu.*jaR):t w ij!Z(p^bĉKn˥ &d/% y<4>t9t4XǞ Ifd{-=*+d<Ib4Ko {1{8L jIoE=,F6K`U[W4q vW)݀k!]Ko.\樍8כ.(vBu 9o$4:BgN'%gOXU/B2gkƌ֧z+ kB_WNeebvMO8Kd3(~On8dʍڍs7ڍ4 Ҏ/}HQ[z<["Y/e.AqbkQY(G7E,saԴ9*̾y83\1 s/H:yCB|h1++Ti z,!I~&K61N_RHN'CXSz/T7לALHmZ]S晤,ĭJlG\*lV*BOP\SG\\e^bLP>NpD ;rA>dQP>\`,ӽCb Ǿ9Py(FΕ_ g endstream endobj 1557 0 obj << /F10 53 0 R /F7 38 0 R /F12 59 0 R /F16 138 0 R /F15 112 0 R /F37 677 0 R /F8 41 0 R /F6 35 0 R /F17 145 0 R /F26 242 0 R >> endobj 1555 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1557 0 R >> endobj 1560 0 obj << /Filter[/FlateDecode] /Length 2542 >> stream xڽYKW r抢{ YlL dz`lYr$y;ɏO( $Ū,v1ilcV!XλT,Ms7]Ū|w9K?EI\么,/!D9ѷCL~HoG"yikS˖fe'۷ӑh.r|];$)B w QD4⠈8 h|L3dz:AG- >)D3v}dQ3ˢA&eD|lPk+&2qlY8gU5/EP 9y*w wUMd7rRfPg -LC]3UKR4ngKz;?<+g@٬.I0!L8y0Qь4Y#5Od_f0qԲ[kyԟ# Ǚo^ |RkvU(  ZC=D\hZK1Fh8gI1t VK=۴L s*c~y2a`mx,zL0 a#knz&Y9Չ 6n.f;_9la^`ȵݪ np;L>lβ ,fj8% #_Wz)(14pwib0g땘 [,ImC|K?'\[:,;_IGw~!^J[pԴ1t|y}TC3Jt#:BJ]chdwj(_iQE&GDt\2M&3O\bHoK2na{U(q\kbVdc뇓ѶXM MKɲr7Z(ʶcmV+} 깓WmL)Py5. T^RO T?W%Z|ruWXuoNW[DsƶTeas%,qGOCA NF}|3W a4=$.6GO%}5/} W$ԥ;"М>_x'm6o endstream endobj 1561 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F21 215 0 R /F22 218 0 R /F15 112 0 R /F16 138 0 R /F10 53 0 R >> endobj 1559 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1561 0 R >> endobj 1564 0 obj << /Filter[/FlateDecode] /Length 2359 >> stream xڭYKW(T11ē*dSqm%ON;-DiSLR3.t|hvڹh~~Ьbǫ~21*RbuZ)~^~\}wǫefus&ޮl4_O-~jI_i^IZ|  ŸD7qdꘔEŰT}STGܲh@K.@lfB9jwń*KcT7zsǽp;;8*v)H"es͸dLq=dny;A8fN nfv4axvKG}~exhֱLbO77?x͛cYO_P,_2 =Z鉡~uE+.7;v (D< C]Ô u {Ovͱ:и</KLHfSG&LZ`IJKR013Q@$F䚙)Ǩ"ǮaMq(3$Q6wg<(8Oџ sbT8CSX8d@aq5/]Wo 􂚐 3=P5(`` (?q,6A "ofuj9G%LYk`ojyEpЫdz;u%ۄVLNOf GI6J~|v|v+Ή"pm ogXwoIƆw@ԔN#Z+%0Ka+ jc B '=ځOM+OgnI+/w.s*yN߼p y0b |!(롬rb}% C)eu໌57I>p\N8Dyiu$S57 ۞,9040%;M$x[$S>S#]S_EU4yWXv4{ڿzb?l5HUd 6$⮀C vsfIw3y߁ +yu䇂}EKӜ>m}i|ia[vDC2y16ln d\#awN8^ݵn_M_Mkw): Ca3E@5=,Ayy:7EeV8;XS|W4//&|G@%\r*˝%N;l!xeb'i?W5^e a:P?&s-NyB3P&}a(eY&SzgV ֦X0Phh֡gjC} BT pEb1^v]I.l`HLcy{OU#CH.8C6Dppi { a ]K4.\}P3Z4ؖ2H!<\hΰ i/yY>o"9%8E'([;spQMxO]Q}4(o|5eS+ <1{|Ec~.?:rKϺEøT=,Q>6cxWn{#v \{"?Œ q{ހ 5"=>A] m+`X{05wq4)cc 4@1:=fZ+"\ Щb36S) <@a8䫫Fr*JG=%9yR+:"R_Ɛ ;;Z27D 0%Mz$ 1cvgxnsLjzXᠥ X AQ-M&-K IC=~5ϒyOx>٭$T΀vΗ@/״ɘž8Ýo56 F5-W(e lIKCQj6bqXpJ69b빍{T-  2kQ쿂au1P %(8H XЍϕd'7jlE ]՗eA5mUki}Z3ހ^kw \%z7&gǖvx =R,ygS endstream endobj 1565 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R /F37 677 0 R /F8 41 0 R /F6 35 0 R >> endobj 1563 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1565 0 R >> endobj 1568 0 obj << /Filter[/FlateDecode] /Length 1643 >> stream xXo6_"o+%mMk {،U In xDIǻ}h/dam< >w멐e zX$z//Ieʻ9w/hp_V׋@Jsi(E*jįʆHhJ}WV]^7EcgkuS6wxs/Ҁ޳7tt,6t" dJ[P^ֻ(ҞBČx۫F Lcll>z*l nLWc#x&M Z[rZFsCX-XjTZᄡ{[: #\GgS.:acq&u{SBF=1spS/se,f3ئ^[EڊzD&'DV,zjoA HB񄦦 r9o jMjLFoˆ_WU?( D7`o% kS.E̙yFyJDׇQub4ik:J5XgG `E5"@52oڹU%Unh4 4 ìW7yKE2;Vug;m^ .p c&õ^9&eѴ4z J& mCrC`ll]M':bj "SX'p@k):qx˸ " k^#0*X{ܪkTWrTJ&]ϯX(J˟=kqZ(Iu@u_њ&v6^Ҩ ;RfzM+kR;Ark 7I!͕Nw*ƛ)nNMeLSZVFT{P8)3kAx64vB{AAK?om|PzA}r%Du+E;L2xxIHƃ--( Y&VSzeXZdZ%]`@ :+&̶VS-`QG_P0U2K% wv`*xEl!,$9d6r'C> endobj 1567 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1569 0 R >> endobj 1572 0 obj << /Filter[/FlateDecode] /Length 2364 >> stream xYYo#~ϯ !"c=}% ֈQ` /<6lxWuu49fAzTTuU}uQl;37g(`-U1+gJ*{/ff߭,ei4[= 9lw˞V&0aa8 /Yb,e(Y3bAbX"Wt^Û{İ2)@H3w b8觼ڲB?Rӝ`}?Lxq'n1sʔ㹝@h䜥^ln۷?ۗd!Yby'Z%Ք'ȫn"D򓶰 ꦂ)_ b"r XNY ᄓ1I_`y>bГ2Yr._tFq8WE .^]Xcc |øy!F`UAX%YGM ]ִ Lؽ.%$o(wMVEu<]Nqa/D4?dzqz MˇZ'Lc1&jN31\OR! tѩF^b n:" جU$90]^}kwHNKyEu]h1vˡB.]kґ쩘UYqhRJ,lkW`# `R Op \R ¢LvYA/>@G"b/{"վ$rVX @Լ=׀B*+c7xP!tX(ȕ6CdUmFa(d$R^肨Ix(nʬے^ NM+]Q6؛$GrSIPkp -vx\e,ujc@`"y :nO% #\29T3+H.)%01o׹&@>\:)8#6 JV@nt2eFMh&#yv@־JV 4-M.k􀍯́M@mtۗ11~}5=sp~m !,EZ/1+&K!c= -y,1B*ώ `oQf[Lt^PP3 ^f Tv0rTr2 chy 4p2 -1H'aWݻ*LgE6znA3S 1U‚(w_[ rKٳ+C*,F]+j&7A5YW7K{bC®tB1\ގFS4v"uݯ(´]թwB&zyeȔo\Ey0+cwK1;> n6-mV51$S3J˽q10ƞUl#g||{s*g|?2, 8  ib_7N ' $K,HN5yvs` ~> `'L0?fvP!͠ЄˎWSv9MUH}.;€8Vfȃ6#&EF"Eτ:ަ#(QPl;A 2h eϏ*=[`/i+ݰ (ӽZӦ%j.mOԘ`GUdQd]c`C A;[Q_[TMP"8S0_,^`j tdi}a6b9~ŠS!P1M"Np`*}ƒ`*$ L |DдC?ugsk4|=#+PF&RG;(? *%&bؖs(> endobj 1571 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1573 0 R >> endobj 1576 0 obj << /Filter[/FlateDecode] /Length 2079 >> stream xYKW;'ɩ!])WZ*L #">T~|IQI&\D4R!roᡣIY!Di)Ỏ~~]0iΙ(l.y&RƜSo7M"{v4T&jٔǾמˮ?NM@N~6Bnچgn74OB3!o44 ?3He *Ax^vOL +ٙ~/ "eJEbVh7_WBK3?\I ڙNU5=8RTrJ}~L`|P\&Tv1%ޤ\L(77ꏛ\wD)؀CYz۾:4>U\fvcmpueO#ћq<`.qSѴ-hыD !EQ0ArcL8TBg$x4ܵsm2M+.ӻᱩz]{Ƴmh;"es˃#:EнN< Iwmv=*laC!}p48Sqk+fȀ̛LF/R9w/D ?\/ʕ?"x{kn:^bfrLv3W-ʇx*0%ß]x Z6CcΒQH$ޓl6OZzzp>ɵ`I n&'HޠQ*d#+z-4aZXvpGn_]vygMxP$!o (I# W3A: ˜& $\]mQ8iK7WLԳɵpͳXhtBϠTAۀ0k׻&(- κZѼKc 5X6"ۆ GˡiJȗԵ'Oǐ31pO=Kb׏^:@9f^ہfLZH'3fGR_mN;zK;{˝ML۴͢ xLdq0IeH:2V@_H ^f*v#鳳Px--JjE +`=N<w) x>@%)Qgh5XՏ$\ut6Vd7d%kuƯ!0ܸSnBr}y!a*}6@ Й x$9@|^ʁBX/2( _6, 5͢yF'I&2nL 򷜥KKBi%d,[V*M*V*}-s@ g0LRxM4eUߗ:՟R@ǩsgfSc YSHtZSu<4eo[7KQxEi t/սҽofQ)Ԋpqv /.t㐬4$qI_=?v1m-%Z(R (Ш/U| l]v4V:I7+?sZ ^ HuDۮC AeyyN: S2m IGC= ghUe=֞̎rܵ)\ʠ];{z},nFggB] /~9\,^++U>&t >NwX9u;iX{ZAj67—ыexiX#H+ eh # e> endobj 1575 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1577 0 R >> endobj 1580 0 obj << /Filter[/FlateDecode] /Length 2711 >> stream xڥYKW6jw)㬫ٜR٩Lb$1!?>E<."x55Eo>eE64ܴT"l>o>}17(y~"dOSwc>OSY!l)q|cN"Q~hpXAwc;$AF2\OGY'Kc.v"vͿ&UƳovI"3PiԘ/5xʢ_;YDHÞq.htwpmew'D+9<™GSfh;p7fF/$05͗#(cD7i5\~l85|3M3c<b馡h^>V=Z/x~- q,NX!j嚺vՍ[[if|4bɌ*ʙUz@#nQd=6 BE.`\\w|0x Ll4Y^DE/Ѷ{??U?5y&Nj3!B+`/?v|Tq[1.\w/zOh%C'g"ң!Lg 0$h=2O' wBbT?Ғ%_4;I1s2!-@ݰ0aMӡ`5 2#ڪ ;^qHt C>i2C բdA96s=CF2a*V-ࡹdT-9V:+\/r:qLiRY\E-ָ3BWR~ kh P/Ky>Zġb"tkE ďp'^3/I""RatRǷ\d{~@#!޾_LHZvd'Џ(S?U3hw׬.~JX.W)xX$Lh7 g؎-%_ ]ދ~4? ^:ZʞH ͖_YG|:ޫ \rSwX@2VL j*i+o>j$}Ȳ [ӄ1d=yZy~*k (cܓ@ő_8_ l;>EdH 9 YaŜ~q<.Ź4h12i(d> endobj 1579 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1581 0 R >> endobj 1584 0 obj << /Filter[/FlateDecode] /Length 2044 >> stream xX]4WiZ-m4w`D9[93gW+N(Lobe%糫wϷI;O, NX(ʋyo^^r-7rD-/lHf[jAE>E L":ZiZZSCWvߚ싶+ůYg3BveL^hVt&9+v$$56*˗q9:~ Hi& bڹkUk($v_i:9xDXS'cCOj3E`gn=a!"Den[ &2!LU9=Sv|pÌc?%7E3ЩggsvmiC~"()tuu0CdN}%[⇲UYl=9}}CR?٤[ĹZSl)Myh% hG1@c[ 2,FRg!dbψjy$FB0o׋IF&,XS`2q9j 1 P1fp8!.#GqհKID BL4eJ0sj@"uk?zR[ƷJ[jH"-:膾ovvUjs==ZmiۋhmD C 7ћfDyW۲+T/;p-q>[ ǾvHj\? V{.uG<ʚH2 z4܍ d7n6 ^ S@N`=aJM@/L}Uq̨ iuTbӹ@_/F.WW'%ONf,IKMZUWvA[*p%D6`vē3"L8`lE9@󐉃!Tő20XᄌQ4Aiԍ,k_w(5AN68Hٿ^/ rGdn@z!hK *F-Bm4W,U_G97D9P1=gYI 2{8Ū%j:H;P`t }Me&o\dz)BƦ"Մ҆_Gb#ϼZib0R=%M#S$C.3ۦi{R0)"͡=*ME׎d)K;eU^nrAh\Q ".}>x2+ZHq*fǤF},i [( zR}nZds6xr:.RZCfyc*çndH&[bx 8L *rM^?N57AvյH$u#g@k ]״ۨ &;FѣbPXwἺHP 1v?_.?# endstream endobj 1585 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F13 66 0 R /F10 53 0 R /F4 29 0 R /F8 41 0 R /F15 112 0 R >> endobj 1583 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1585 0 R >> endobj 1588 0 obj << /Filter[/FlateDecode] /Length 2125 >> stream xY[6~pӇ#"us!eH3>t挵+ ?BZx-@^,<<׏GEXfl?R$bVDprb>rv/CӫW|m~X&"Jقg"bIS?yEpשVEUU;y7o4z[Z7s˶Az\ o[ǢlU\Ʋ(by1 8Z$lߩTX\бr8v<ʭnWijg^'ΰm\A4w V]oj݃2N<8eG;?i!ӱdaZi'i]( eepZo, Ri<NZ# W#!b0ayoj(2ߟ,L,_#I-F}zJϚ<-=ET8柎u^>:O 㫟.Ǫy_=aO9E$Rߟp/5ֱcz¯Iu8u8Inp~ѭnw>ֱc~ $N5^mG|dqT41ƏhR{ecrͷTx|ΛK>&8&,^l bm::חgUK͟=!wrD0 Fi>L,fFo ꐽU~{)dΕ{$aV$:x"|Ix7Iˑ{dNt^~(z$ ,*c>cb*G1Eٌx^ >_e=_8@'( oT9=D`eiUGvAMt1@H[q?5Ͻ>D C~ ?)?>i?$@^qn*] *vP"An57ĄvZP#mTo)yvٝaZ:'zi$`ǕoY<{&QJӕ^=Jlc $1LQi߷n;(otkBӖݨ~שz`ZUWPsL VJaW;0LڞI$ķ,gok2֗CJ\4g@,>-˦UѮ -<֕oJUM2լyІ w OUHtEVm,(,#qG3"FkM2PHA0LrC G+՗S3a_8ͱܡ^wsA'l['36Q"F*ܓ{06.ы,C]ֶ;Yeobhb8p?Ԁ"r=|PѿvjU|NbGˀ<ݖiZ")M[w:wcg,m{0E@; a7,u1Inxy^LA /F mÄ?d)XC p ۲ LrImt}a@w.w(yËs;A$Z2bK@*Yz r;ϰeB7TG4+_Bl~P.O#0] ,)ts* {eOyppI{Q3?t i/NAxz8[t%;W;Mۄk. F9{Дp(qTȏ‚&uEo1rK)iCa`"\F]{H~`bis  Q놂1gNiC_ QTbܪuT:u 2]A(Bhu5=[u5Tb8 %1Mo)]a!svTuK{aKk{s ;Kv ѷ^LR ^ֵ"OSkB7W. endstream endobj 1589 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F13 66 0 R /F16 138 0 R >> endobj 1587 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1589 0 R >> endobj 1592 0 obj << /Filter[/FlateDecode] /Length 2217 >> stream xڵZMwۺWhXx$@{k_zv[lJR iN"1 \\ ٛyPyوlK9٤,%)5zu٦ 쟬{Bh[xK,۩<cQ3;!D4;E]=SףO-_CYlm:Ϲ쇪}/S's"͎S s^3jmb/-#儲{ՎY ("y$cC{Ea=v#yQtk]U/F1֓ 'K$X'? LIJ2f$ELks_6Mc@ءe<#uOʳWJUCյkD?PoS?lKt΢K ͱͨRg+3R (IG<@Zg7=z~o;yASv/ķS9{T De%[l^y\FUয]+ !_X]dqX14|i+!nfw9Gg899\l~Ll᱖R0dv U#cxAϖ/A[#EVCCFYN8<_‹ByqɆhV..@3xJZC՜UN] e|^`ސS$ ,(KQ4BϱG|}:-Zds!,~mDZR??Z1CRt-$=uS_ɢkR׫bz;|/W94+(=[OM-nn ˅CEs^JoWb_%'x6+wH] 6ZoV$i<aAM85P* R^~e-D6`X-ٟJ2F+*z>|w'k8=<&J0)Sݱ,&s9K[H9^Z5u#A#jetvL̈́RwŁ €gnmN֩:Y'"!1MjkI3Im[9ž4H:bջ2c۶{CZ)[ʴnV]T2&p81m{鱩)nhfŜԥ\yR>+abVǎq`q`{Î8~d/:wJI06%{ѣ*R*r:%cdN6vLL,/yTxzj^>Z/#4<},۠3}hoss >-*[#U3[Raf`hfDݘfSF\>r`5>Ld?V=o|l>74 PWh50ǹ#ZȠRwvG:('1 RpŮFDg".%0Cd̫-I$/*3g$NܙG?s[?;%2lhi^ g&&\3,qKW \O^w*j\8PHF qQ8'9 eXh^i6Tč-BЛ:<m1f.SB ?6!Ye(3\@K%"ՇG=aa6BGh1ǹ! Σ`}p¾naxDx9^h_~:aF (xzgʺ"*9k"Vd"+b.cFo0<7>4rfo`0xa> endobj 1591 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1593 0 R >> endobj 1596 0 obj << /Filter[/FlateDecode] /Length 2060 >> stream xYA6W:hZ$v[,y@}=%zw;ɼK8kg]I2EQ$EQ$cYl*ORL$DIwݷoxTcLT~I>caݏ߾)[JTY<4u`X:nkK)T^S_4%q'Nu{BOD-tlzl }vt=}4BӛUiZ"n%KǒsV) [,Vi3`ӵMoZ4Nf no%Q8Pgl-}TӢ)?/;5sF0؍۠NBN͸d-7P;3YP1+uf+i?`icꕳ+{,{~tqbکv>͇Co| 8ݓ^-~E&CqYYBVLPⷔ. _Υ SȊiO~AёS Y%-߿/NxUiT3z>5:׍Trǀ`U휐mCf[Idv hunOsi]F+;he /vC~C3w%D +*,!+`ցiq,SLc eHe(J@]~Ӂ?SCp2DΤ:pg”by sageq&"];~:ЙKKpFki W/%F_|<8\"B4l/ 2B @\vR4\WUYL9"a+0hr *5[d2u˔joG cm;Fp̎{t1 !i @TgBYweյEf5Zha\gV^:r `71V幣"1q>uBVu;nC)bwÎJpǢs8 5, ۽7},צo/J- B.F' ܋l!j\$ h;I9>Wt1]̃hq _N҅ _^\4pTOtD:Ki{ *c?'9nDH(m9 |@%8P!QIdR :l*E6D 20ލ8)ӢW0þ doi@>OnO@*R % Z^!R&s1xO7/g-2OP"+d,+mzCuñ٭Cկ'? zߧᛣ}r_ɦPo%,ިgt9`8=3 {hkj -~PtL9vUbLŷ44-}DYZ:{+.~C96}rf5+Qס;5CPѢ,}?;nҐ$7qm}g";T<隕s2\&uI.Cedh.b0jֱF/]?=Fk}Ǐ zz?X endstream endobj 1597 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F4 29 0 R /F15 112 0 R /F6 35 0 R /F8 41 0 R /F12 59 0 R /F29 279 0 R >> endobj 1595 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1597 0 R >> endobj 1600 0 obj << /Filter[/FlateDecode] /Length 2010 >> stream xYKsW T8brزYU H@+k+?>Ӄ -ew/0~`ᡃD,Ri`(w|ޯ] a VrDs(#Xri Kڬ^,!Tbi WXb)lX -jZSd_uZhZc7׆ܥuſVxwz~0,PL %50 r?4üfBl-hB䈎J~u_/hW,M4p%K[ b=x5LtG93v iK%IbYO~Z2BB[g~a.lw3m[}Zw~Oz>?r j6j]D~Sit#مLCؑS9<[P2}KWg1NmCE}X481a@L"1:@5F#K{f{P*y[^lΚF~$~]]96N$uq#"Xېh7V; ^t_ow 4rAJYhCXu;ATsag5 #R#kK9" \ 1h+nU^7-9R1:h!>RKd2)P읣oJXωcS=Cq$t7`jJ $nlOԏEG ' 5.s |y6"Ӻ,یVqF?|@'xTFÍ/mF pCmPuFmE,];UQXmU_ :z}M} &!">@5@d7dTN/qV` ~uKV۰~o段 2*_Ңqcp||!Bd lh=]`OvُcϧNwM`-oߜ)لd¸LV4<ձ p>w #8GBvV`+]MFLަkp.ʲ'"@v0rr$dqQͶ/'=##Xu3=JsmqqrV9>aլ&ǩjF7#$OF?gtYy]=Cӕc6jv8"SVtkDBJ遀WdNq .v0  '$vZ=LSs}&ƖIy^'d(%2M: ӊ01U}_|) 9qP-]o/)](@o/k^:M[nl}Ta{HJ B^NP*l{Ȅ/YᏮ:0^Q&g:`/DΈM X 2OUN:ΚYq[$gɹn$+n{d? }g~3|nқ.(*Twхi%.6GF@ÿr7^LӃc{pe.C3(lB=(&;9[bmNg8$NI>68%T-Kl(Nv2& endstream endobj 1601 0 obj << /F7 38 0 R /F4 29 0 R /F15 112 0 R /F6 35 0 R /F8 41 0 R /F12 59 0 R /F29 279 0 R >> endobj 1599 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1601 0 R >> endobj 1604 0 obj << /Filter[/FlateDecode] /Length 1735 >> stream xX͎6)d fDRԴI^ t!Vk )ђɹ'~3:X%?*yLU2Ye"'Y.¾Iׯ,XU$w#?gJ^<>_٤*RIXd&9+`!<Tm>gBwOw}|q/S DO.TjbMN? bP64Ι;8Y;3ךIlkU[k ̴anꭣWB";P#ōP=mR-t葢Ӻ`B_t1W<esh5vQxf6u"9 ݭ֢*(+Xi=O~Գtv{+::tDE[γ?g0_, Xh8ƒ>pM8ל;B1@u~W v;D @nD՛E ahP}?޶sy阫Zi-}A`sӖ5)iȥpI&BoNfO9TH U{eP!m $9U wi-ë1>EDA {(k|L 6$J{bSrPbAOzUchfkSiNsQ^*2Vq* 6~!ǺX;"RRoH]nMG)M] ppxP$*pqRVxƙRP1@Y;ߛ6K?ck鉺sh޴Ǹq`ߜw:C,:;_ށsVzҿ-W ti+4ضw,R!/c.Dh# MCc '-E7dX;Ӱy[k,݆ 3m┡8?&Sf\C :+'o ?PGܸ@-cu,8>/.D?%X"|EW4y&X(c :| 9t ܾ;p[ xPn_C1r\8#Y\\,G/9F#-8ӒX op^..X<#I3uc5dlm!kTA%saZJ?/Q\!P5ҧD[ajl_9"د/n1s|#-0MMY<7.Xc{λ Osis~_Т%V@/^8=4sCG<8etrennQ~W2T `iƁz@cCWzѼ8}grC+ЕF9>mwGO/I endstream endobj 1605 0 obj << /F10 53 0 R /F7 38 0 R /F4 29 0 R /F8 41 0 R /F13 66 0 R /F37 677 0 R /F16 138 0 R /F6 35 0 R >> endobj 1603 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1605 0 R >> endobj 1608 0 obj << /Filter[/FlateDecode] /Length 2013 >> stream xڕXK6Wb$R"f`4)Ns=\I^ eYʦ"CpVy hu\ž #U U(Bivd,^#{ dZ_(?t@űr^;SJ9A8vn*dU,۶cJ)bjթ/{;0>ypZW EHDz/+NsS;T!x0ݱEwPM/x/WLv9Ui \mSg­A nd ~9 FcHc;t ggHp4w.4"!<] eZpr_/OW>z+Bb7ACQXkX(sKr ^_[9l}91oϖJRfJ:X;| ~n R7 piͱ;ʇv%;D*dtLJu5CC#H:ۮqLmQJ@E._222bw9?5OiTQۆlS t eY&& "䊣N%Hu = uacӑ L\i=ˬ%"u-a8U~īHl䦊JFQQIWijjd>\^=KI*[]^|?JʜcWAyĴtRg_U9N~[:$#>3JGMxe7dߋȐN|ƣ^D ԎgS*QG9~m{hLXVL*`M@L!fNe#42}eWGT.<=V2af/Lɘ@sn?6&s/t[pkv !q=֏08al-ZRdɺ |56_T*(B_ `%f% ϒe #2΍ާG>'2<5oȾ1 l#~9J k2Oe  7ׯ߯}K-GqE\ aٖc?A2iȩ7rd9P}UjjĄ{ }J> endobj 1607 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1609 0 R >> endobj 1612 0 obj << /Filter[/FlateDecode] /Length 2188 >> stream xڥَ_!GDzA ly䁣fO+13S"Ք] /Y,"qOK cK(,Adi2u1v͏IN㌟0 =xiGzh(AlPXz1G!KR_]$xß"/}P"r 1dIDIvPφWK+AQ6kG Tw j yXAhV#!T+XacG]֪ׄ7\;.Gɰ qubϽty;" 훉^c}} IA8—jChKŴ,nDld ; /65&s!AT4Y άYq| ](.6]Yk=l J-H_u*ǹadǮ;s@%gF5i!7hܪ ;_`®=44g)8|jQo iaYl4SPr'i(sN&L(uGb! )r}R6) ޔRi$i zQnҥJ%+4cܝrσuaM60l۴d;~]4u-2=[bD؎j:Bb^%;J5ƎꪵFZ $l3j7I]4]{0l:A.R5EZxX;KEh =2gi|sDdV}흫V3OE 8ٌL@J=@loܰv˙'5;e9͗5 hы(/G%]Y{޴)1|",{M Ƙ=biA _G,$#g(W*G:(ߦT4L5Sh*Ys̪O!^n& [BtOm/:/Y+C7lWu!g`fhA=i TZP6|j9QGs-^K`j: mB%Ld59v9kɲԷi) `[T_ѯ{MBnв#%yX_Ńtb4(bn.(Lj+E|xaZ} ¶k7l'F=]6l-a[I9EX5Ⅎ YFA, l-+Mװ9/b٤yXO2t! ĝwK!$ek{ 1W7n4"]¼bJ@z\,n<{|tӽLW^~!3GO="$5sGux8Kd&2dSA%8YgNL=jLJ)3p՜\R)^I@U]O7IM 5!NE/WqE$8Y;+AD .u[{ :Sji:A>X\c񓪩IaTvIzpQO%8Jv K'[U4Խq;4iT*pt$Ht/EMD'>ۊ̓D.?OίY{DLECIk5g#<0ބꦡ{3ϜۇmA#K|(pYjC>)TDHWT@q$ϯ[G βT»k -/^\hlOkҿRFҾvX?b% endstream endobj 1613 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F16 138 0 R /F6 35 0 R /F22 218 0 R /F21 215 0 R /F13 66 0 R /F15 112 0 R >> endobj 1611 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1613 0 R >> endobj 1616 0 obj << /Filter[/FlateDecode] /Length 2649 >> stream xZKsܸW9U&x$kWYJTjo,'E( Gn4@bJvE6^~~ h$Y/_']|Zr%bY(Y](XWwpDH&zs.徯e,8_ZZjhE횎ZTUܖCWw˶WO8׌+H2- GӘ炥jW^+hpe hz(RV Uax2:~Lxgsm;Ex8mu+Y޷fLegO#X:/NGy]/Sp"[ 8BHa~Ag4Ӻj 8:f?b2ys'24`v^39˜J7Eoꭱ^6Kg? :Z8#Om6 le |i ˘8k`򜁁)=kaZ0=kabp) S(xb.ycTQWoөf7r^o_ΒߙsL@ȣUy*'A>hkPRQO~I2Jɔ;hWn 7b8ky;\lTw= Q[߯MhΤmwG9Ʀm°:#_1nz%"%3'M6xSv-Y-uWs"6 +:m՚me+]lw;enusCbYҚx]g uBCkh~[\ѬQ3vl$O. kYgLϮNDzuccʫաmʺ2H%pK8K}7,/" zR<ɭ^9DMZ?R7|@Eaocf|'N #{^=X6u27bjx@ ^P|R.$Cߡ%s(" ?_Aa^=EZ`r,gj#*`bzx :uB¡Q|gGBqbNUug&Joiay55:T xiaV~Uk$pXլbK\Ʋ5475))VTc_,Zʷy/NAaYwb SiOn1֝|QpX "]/yP uBb*\ZҸz[8KOs[j{ [ݳʍGn.l xPQbBhP?_ uUP<6t Hx ޑɑ|AvjqefE*Ԃb{[LD*ԷRJkG_%"&KqsxԿlUyVK8H3ODͣtvV5+N! 糰g?ϕW 1Cz{I_e  {[8:nzBD T?Մ$#rzȹ˖)XOhQ 4ÖIw.my0|=d-R@ũF 'ّ?/7}@o/g2O `BabrD<2ڧ?HhD:<::A#߾Qv endstream endobj 1617 0 obj << /F7 38 0 R /F15 112 0 R /F6 35 0 R /F38 688 0 R /F10 53 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F30 286 0 R >> endobj 1615 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1617 0 R >> endobj 1620 0 obj << /Filter[/FlateDecode] /Length 1875 >> stream xn6_(xJqb)C J[Iw!%q@'3$K|~O]{Q@REH= ;\<94yJX-^eg9cdʛU٧O2^`. xons&/ݢؿįЬeQRP be|da\7~m6KKm://dj[.5(@^ʵ~1I}iUYU.Œ8攒44/d^h8gsTer\X6=yl|VN`L@n"|ٴO-M%֜F@  b;D%5U>Z@ 4&z$%T ¸c Bo-cj2óBCw/' .0y/?u^|ƴc -L)m>LO'RV@L@?*b)$D,G5b_}y>:b 9 v*Y rbLmڼ*m,kr|Yܣ -a$y؎7vpAg3ISh# trnl-BZxo1'Ŕę(͘+tn^.$(0p+lL" zǣ4M!3]lL[oL< G4R:U%RKKK 1Yp 3`[7Z! R;5p3n tn~qs\ž XrqamwnQ<^]ს].dyEIwLmCX9ۮFO"NeؔU'j5Bf7Fza.mpc Adm]!aAK$xœjh,׹jƛ\x&$B%~UmX3] < V5͐iۼY!JJSVl U5GsF 7Ʒ=A`K0ˢvҰB* -p:-UVHsBZ&sz/ϡʬøqkRGJB"\%87)TuPy8 mFjin`_$p hɅ4p+F;HR& 1&Iq)LӴQe ʴbP }~r'[;uc N:=\~ޒY …=ܘXS;ΝxDwxS4Hl!Xt}EV3<$楧{yɥ'>b^sB^3|ŻukfໞHgF6]tL vKZXPafh|fE.kP촪3[m^SK5r%Zj=[62u+( Ng/SGpJ{-\BJ,˾߾9]L xF="’}޽r1{q;r#}tmˆ~ H/m'AIM/mn6*3ۑh*)1g1Js;wADWBڤ6k8u(cJ=p $Rb$SJT~p"8=,G%2r9x~7BF2!ͻㇰڣS9L{^Gp:踽xbg<@R xb#3x omj}7 endstream endobj 1621 0 obj << /F10 53 0 R /F7 38 0 R /F15 112 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F8 41 0 R /F37 677 0 R /F16 138 0 R /F23 221 0 R /F30 286 0 R >> endobj 1619 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1621 0 R >> endobj 1624 0 obj << /Filter[/FlateDecode] /Length 1609 >> stream xXK6W @Hۢ4IbEcnQp%&"K5pH[n\h8/9;rxQ:Xqs^._I剳\O.$7e8n,$I" fᅩTd* QWԢ:tdvT^św2PR #po /c' cw|-rJjTxlUdZ>d˒ƭDFa{THguzi2 Z4K> ^j/~7zͲ@%J|GL!DQ{"P EVJ";0E[It9XFv u[(~P=Z;Gc^0ne!KY511%H4 mOAB*ɮdoz!X榛o'svK~=Ț%;~׷sGxD>C3T ŶJpG::o>CTXz Ab&jXGmJ2ݵxpWb`雫T2#Duuٟza/)[!wk=dUT=  8\(eW^!iTM8%^t0Ϳ^h$B(8c>ΓEA>Fײm5IC03 Hi L,g'G[G;"M4ĥ)}j1F1*rW0{~&ie@aNTSknmWOM!l GaiU8O|ssp[`$, 7BB ñx.+gE?p%KƮJ'X&9r+tv+R-F m` Xa+zwn\EƺH4CK$l| ԅK*`b[_Abkߪ7{~C]M+wwSO#e`s=7ϱM=gWwka2s-1햝Q/ Uз' Å.ȲL-.d: w$h$(Ra,S7sڏq|qΓDkK2nɡDGYψ5i2Ѹca%]tÊO|S7N=i<# L`!)MRӱ7P٤,tGA(Ф V!ǥ4&gڧ3,7Nkqu$4E@ yHNeB œ%˛tz/$1 !|hp9m[ʬ%AMLNlg'FaL =E=egx|%4~05=B)'l:}|j Џ<sB怰HFkYcU6>bnw0Os&.6r|6e$g endstream endobj 1625 0 obj << /F7 38 0 R /F20 182 0 R /F30 286 0 R /F22 218 0 R /F11 56 0 R /F16 138 0 R /F8 41 0 R >> endobj 1623 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1625 0 R >> endobj 1628 0 obj << /Filter[/FlateDecode] /Length 1455 >> stream xY[o6~߯C@͊7]m@E/06%MII,Eqib9q~>YjG 1R+X \kz۴LjwuXDјrϾq"*ňc{>c7^әrqn9#uo E' aZe1'̎D.0\(?gB7QMOYQ. ›`#P| 60vEi"B*s1KhL<;-\WF.U$MƥȗQŋM OOֳ^qܦ5lrGCc1u1➦&0F)|5+TUP"*\tghWCls&8@.o.EHO@&=R*?E]Τ;uT :0D$ -)hşu*.k(giܝ̣d@ LWHL˯}i1hC(f^t!İ1jE)=2i{5..b^phI3-?noܺwZvljYtŋ[/;"^G&5:Zn -: c1|s|!@ryߓmڔOr`@4c 'Mڀ)vɅ&-$eɨ}CLn|M' %MZ/[𰿟Br?by0}afNqXiTf%5w7bӵ% 2njWMɖ4Gac$[gb/ߜ)l3:@L~;L 7`3=ubQ,t EmU{ƺܫM'c*6ֺq`Qޡ=0nV}vֳ`7O(-;Dm"#އw6vrW"dؖgΧ陞Y2:J(lNUЅūb =p?`[&@G̚!8oElQ8b1 G<'ӃSJ۞=+|> endobj 1627 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1629 0 R >> endobj 1632 0 obj << /Filter[/FlateDecode] /Length 1509 >> stream xWmo6_"w#ztٲh]C$[d˒;(YԠ[sLJɚILS[kv9&fX61,nF{8=w5Xu0_[<sj ?'˹9)OKan8/Ӌ\*4Y%dmyUiվEh *_5S3E(.j9zXa\I,T1+|6yP 7^e8>8 \%mA'w7sKܝmXN]R8vEbYc2QfAw9jNR5W6Y,kd0Jdkķe*),º8[adRC1vX,j$.a'&UK=KIz-]RpK83d8O1s 9 zT,v[@s 9oD&M f΀qI6YvZģhyآn"p4U4вc\ghPl#X|rD}I kX@`DT0+U(0Ex2#܇-4R(FR|OVP!MZUxke88CbPeEIppR;Χ)=c*V;"|OITwPdД=H!+B1KT0N" Y ^P?DkP(f5k5i !#H.mQ\?4E6l/e* A l=M"wR*MSރ@ hiXֆqbmو~Q.*no 1nկnnh]r#tpq}pljOt%v{q;Ec3[T]ѹKV @j{T\9POYS4Zo4S5PqǠ2vK r>8D`pyE:J՗0׹xm>|N);p'X>y?ȃlW#q9  WCkY|'C`~˸8>]˷KxBn>$(E9ݻHTaQLcʇ&rc`l1$,":We5GؽUWGӪ#X>yp&VIP"kx${wm O烂A>~ ^x6:cWz>}̺=@)-b=~2߻_QW` >B?V:\-&1#r~6{j-gWNÓۦ>Vc]O:l$0ufg/^.^NB. eXe̞蘊b;bQ^^Y|]s1}l?Iv endstream endobj 1633 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F37 677 0 R /F30 286 0 R /F23 221 0 R /F22 218 0 R /F11 56 0 R /F20 182 0 R >> endobj 1631 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1633 0 R >> endobj 1636 0 obj << /Filter[/FlateDecode] /Length 2078 >> stream xX[o4~WD}.؎s$EJ<Ҭ5,Iv?x%x<3"E60ᧂ X.0V,>ebuSox,Oq>g"zrJ~\hjkWo>~ʘRAdJi\DAf'2"f\iC%et^>ErZģr<83U3,)i'\cvu HD1"3ʡ>)'bM{;"at.LD˥.N#gOt`=k1o 9B8ބ#6Yrs~6t*AzX?\(jHx4/4󔋤,Q/r*f2,GmNDIZ_ƞw[' %hJβ4\ bZ+K.^:o[l%;g=t ?Q `D)[Î龯Ʈ(=3f ̙ Yꇦ>OH\(B뢣Hh4c:h_tw ],kpSM;ǨlNr#wGR]5OţM)$ԋ_wOv}`,f,/5C 5fVwm6ڎ8ֺc4} [dg o('j mZt7݉l*d5Fs=!DK?Z;CkT$p,j;SJ 7LaBc8>xhEwgϨwj׷J?C_o?oig&#=͘``+,(.}]% ! q_WKEh>y,8-mƳ`zG7*?HQ[(&phV7\GS}yƸu,A 12[@ ;K=-i]`2nIde> $`* S@VI2/S5TΉ:[A!f % Q3;fnC]& c3FƦ%H7䷵7mbƦ?wpN]Ӯ6x%rҹpWJ3ٲe3ǃI $0pwp nM9ؼ6,]qY5S;>E}ŽqVS'ʈ($ 88☃?2OH()Q*ʝq9vn= 406"B#U`f6Fc*G`|ղx.J=]lU' R,wѯ'M>Zqp0 TaVBj,*r&hW`æjb)ۂw|mv'g4"p$8%UsE%t Cj NVCG0> endobj 1635 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1637 0 R >> endobj 1640 0 obj << /Filter[/FlateDecode] /Length 2053 >> stream xڕXY~ϯw]xwl A4jδ4+ȯO]Rh/M,ŪD*a TnUdSSΌ:[o>A$ȵ2yp_f6Ilñx|[kCqt᷻}$.3aۛ4lIێijREͫESԯ}Տ<]_5_nGm*Q(؛DiC|x]Y9eСm)}jmovVU:b: r$ 0NhV pn?'$CqhɆwEW"+9Uv8LEXy ~?|e+D̢GUn``@YS m7i˽R78EG^ axXtE 7!MζΪ8]QOV岋!vm*ϭd⴩h"+Ip(}i)ȶb.8=[>n $SZ/k]8 12NfQyLADΓgR`osD%čt2+dv/H̊VT?7F\pyYuv@h1TA?Rj5˓ ͷY,Ga(7MabeT%ȤDY y*()5C׸{YG6ժt;Ujh ˠx "HM ,hJan7&wSa8LSʓF6$GO8QF˒GG~g\J\ΦnնWhO6 vb#dׂBB/y$5|wŃ̞7cɅ6(hofj ^4[v8Ӑ6d1hz-2:Ǯ} D 'Dh)oىڭqlgzY#rMeeS]XdO QɉzU #.q(h:`I8Ρ?C]E/ZA/Ƃr 1ǩS!yiAʓ>!(/I%ba'*oǟ{;~[ c~m jcWyaG*%ˆXuO8VLKM%nmPfwv'ܲM\˵#uj J/S!!Нk_iF8u)w.ΩM" pQn .;ßkSPŏ:yY[SG^9RVC 0u&a4?Ul=~M zl# ;X;Dj~ ρ3,g)ЉQjv_SD> endobj 1639 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1641 0 R >> endobj 1644 0 obj << /Filter[/FlateDecode] /Length 2079 >> stream xڽَ=_!%0!" =dKꘇٯOUW5EQL¾>+;Cz^%&/W* #IV_V=ECoS[2#ԵNK $ʜ OX;"BuZ=+TS}ӜƁElUo<&զ9DT/Ix+Jy7@@|jT0p~B܂T%sEٰ`d{$xGutGGՓ]o`,Bt;k1p&b0@Hڊqێ&g˃`a`Mln88eJKTӌi^ *J+#Wcл;n2.dN޷\j v"a ]@NAepw`?m|y^RIo?$'݀"h$Zzh*R&~npz =i`RLKA4 f2;DrgΔsoRY3[~K[2"HP#UXn@z[.k-חq ]lwhOEޟ9`s"(Rɏa$'YȲz Wl:wlJu%V c9UBR8$m\`oRaTM!L3J ǤN_G[]شߩ6̆E^8Pw`AȽ<>Y;T{nk@>":ר{j2c"x qՅ/ȒT!ctyOgxҟ@[=ۨ`An`UWw 0֚+3X^QE}U N ZƂ9!Mxơՠh3*ѭ=sjlpLmp;mk^[\,8aޙԃ-5! hx#vn')݈pF\m(VQBsy^O(R/EJ-PR=>Ž`3G-issYb^+2=S+盦vN Pw \A G2㇇> endobj 1643 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1645 0 R >> endobj 1648 0 obj << /Filter[/FlateDecode] /Length 157 >> stream x=; _qRqf M1QӛRH |};X)7`;~) gvW$eWd-[52NDԧ[kѳƞq]bS&u?fG{ 1 [l0k`'/٬0 endstream endobj 1649 0 obj << /F7 38 0 R >> endobj 1647 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1649 0 R >> endobj 1652 0 obj << /Filter[/FlateDecode] /Length 1449 >> stream xڥWKFW航V8DNRNUo*l#4]ק_N90==="`Q.hu"txXI* ya7Qo2d[ioj5kq*=ݩMP{1}@N'b~-Y~NuB+V?/k{4m "wl٩nS- _jrPClӟilL4<ۋ:+/Yrzھ:9Xi>ey wZ9:l7Iu 3܉JwDJ=Eitom\xL&opK]G۾R=~^L-Wkޜ.қ臑_կpqC˫-Vm 6;GZ#y[ڱbmH (9Q_'>a Tq[ v{~I2>/`Fg \`Ul8ady/`O Ԡ]8>0WG<" 2k:'Q DTU"[Gb.CppNgYD(-uvxrW_?th &xz|b/1)2 \{aXG]RVUM_U}M<S9޹@TU|G1ѣ5|‘7p;!/b@;`Е`I}=H׵E5_dsJ6]l! 5o~ $W : CG#rʡsAh wa-u;NOȗ0:Bq^\" \xk.HG'D)BW:'T?v4$3Xox XpS`C }g )]hyU6qJv>4jl/xv8TonHH/ RJb2RMcBKq<4pIAqXxj(@Sq ⛦|(^\T3*u2IfG~w߽Y*ۧ}7&o;^EsAqU' ]|̋8('0{zCOy}?M簸S?gyIj1H^jIS fu5i6&wNKP[$̬eJ*hL4c*®j x" Wvf &"UJ17)ZZ v~a v*PYdY0i*.C"o5Ek7N q"n-ϔHΡ$Fd]LEm1WЉ2Kx!{<@;pCL*᫡!&MnQd endstream endobj 1653 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F6 35 0 R /F4 29 0 R >> endobj 1651 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1653 0 R >> endobj 1656 0 obj << /Filter[/FlateDecode] /Length 2290 >> stream xڥXK Wv;}Tm%[:ZJR̿^Ԗ{kRH$ |!x 3-Q4*4IT~| ~zsTQO=T^u4vO4nEkSEuش͈oȩ·fݿ~s>z*v|Vbx pw]`KXLBk88 =U8yG 82Z8Tn}m, u;}\G rlzyC쀚34x+QE?f6 2X_L>l(΄yj SNͳrc _5; e .c͝eZ|K"@io_&b\ OFZ󟩱Fz5,ud8?uin6IJ!P&4O EM|?pehOhmH#dN8a?fy`Oa+:{dORAU^v $QӬ`??ZM1o|x_$4JˑYEzeIo.9Qw-sh{N ܶ:?t/'mOeiX0rjul,!#FW?6`Gqx HuBo =$:0ڸ I[Z󹕴9I -ɍ^9Dpdpmz>ВzqO$.WR2̄-^86Ua6r]&G@P.7h& |Zh8}6s ᐎU 3fjokӽcdJDUͺ$AZ"IN<'$Cu8SHq@ӽJ# c`т2xæxR,aw;^0cN#:?HYNؗl~$ziABs2T6E,Դ' WSC;2jQr)',g_9X: R @wUl[Ų1*g̅$>ks /EhIF$(Gpm2pV[jυ1[ǝk_rWZBaVގvobUҒWْT?|d#֒$qjr'\23YA?![ϱqPR6a m<~cŽ2 tHB\[[,Vkww"wBvGٳ5H9e _hdO7dp6bR}KlRn>\7+GɗG%8JR0`YT%$n84r.d;RRR=C Jcĵ- a'qI2#  K>rNJe{np_ n7E/ $3_oW R.dK "}8_TXAvmsIJ,Uζ2ǽwO⣉٢hxeU|g:tS5lz3~h>qe^`%vY2KU+lޗ\5 ?UlS*)~`UHSpI>˓H᛼Qf7;?PQΏ1>K|#ȱM/) 2ȶ4*]l5ĩ@3&Skg8A=J>,izs\̕O \5 < endstream endobj 1657 0 obj << /F7 38 0 R /F6 35 0 R /F16 138 0 R /F22 218 0 R /F12 59 0 R /F13 66 0 R /F17 145 0 R >> endobj 1655 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1657 0 R >> endobj 1660 0 obj << /Length 1976 /Filter/FlateDecode /Name/Im43 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageB/Text] /Font<< /R14 1661 0 R /A 1662 0 R >> >> >> stream xY]\ y'[tW)A!!*yY˒1b9}L gTwU?0};}= udgg9,Li_R]2FX} s]U9 Ey0G L)~YbK,3f9rdNu(kb#pTgOzv\IVLjNKKR¡vcߢ^u OW~)bLzܵ C7BKŃ0iaS(3Y`wE'gjи:Bce"}_G 0L'ßA) d0WN2h&p.$C0NDd!ݔpz;90Y'`0x$B#6;mWA1BX$L>Fpس}ZAr= ΫPSg*vPn 6ȱKcg94ba5PI}6-X6 ҾQ[+B B50"Lj(37*3>xF=TcF,̇_2[606k_ukXXldes`ݘg }U/}΄뱨 iߙSQ9si_ %ߘ$S?T<<2vwkyzd>ïoW^]=6Ӓ?_gW?"ھٛoxg.~ϯ;2qݝ5cŏzH\ǏO1O悉~30Wz0?Yo_D"Mn[W5ȍi581׻;W!gNo.Du=.J]vL n,篏`||#˙l_{?u, endstream endobj 1662 0 obj << /Type/Font /Name/A /Subtype/Type3 /Encoding 1663 0 R /FirstChar 0 /LastChar 5 /CharProcs<< /a5 1664 0 R /a3 1665 0 R /a2 1666 0 R /a0 1667 0 R /a1 1668 0 R /a4 1669 0 R >> /FontBBox[0 0 146 146] /FontMatrix[1 0 0 1 0 0] /Widths[0 72 0 0 144 0] >> endobj 1667 0 obj << /Length 140 >> stream 0 0 0 0 74 146 d1 74 0 0 146 0 0 cm BI /IM true/W 74/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1668 0 obj << /Length 16 >> stream 72 0 0 0 0 0 d1 endstream endobj 1666 0 obj << /Length 215 >> stream 0 0 0 0 48 144 d1 48 0 0 144 0 0 cm BI /IM true/W 48/H 144/BPC 1/F/CCF/DP<> ID &e EI endstream endobj 1665 0 obj << /Length 132 >> stream 0 0 0 0 146 74 d1 146 0 0 74 0 0 cm BI /IM true/W 146/H 74/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1669 0 obj << /Length 17 >> stream 144 0 0 0 0 0 d1 endstream endobj 1664 0 obj << /Length 138 >> stream 0 0 0 0 144 45 d1 144 0 0 45 0 0 cm BI /IM true/W 144/H 45/BPC 1/F/CCF/DP<> ID Pʠ@ EI endstream endobj 1661 0 obj << /Type/Font /Name/R14 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1663 0 obj << /Type/Encoding /Differences[0/a0/a1/a2/a3/a4/a5/a6/a7/a8/a9/a10/a11/a12/a13/a14/a15/a16/a17/a18/a19/a20/a21/a22/a23/a24/a25/a26/a27/a28/a29/a30/a31/a32/a33/a34/a35/a36/a37/a38/a39/a40/a41/a42/a43/a44/a45/a46/a47/a48/a49/a50/a51/a52/a53/a54/a55/a56/a57/a58/a59/a60/a61/a62/a63/a64/a65/a66/a67/a68/a69/a70/a71/a72/a73/a74/a75/a76/a77/a78/a79/a80/a81/a82/a83/a84/a85/a86/a87/a88/a89/a90/a91/a92/a93/a94/a95/a96/a97/a98/a99/a100/a101/a102/a103/a104/a105/a106/a107/a108/a109/a110/a111/a112/a113/a114/a115/a116/a117/a118/a119/a120/a121/a122/a123/a124/a125/a126/a127/a128/a129/a130/a131/a132/a133/a134/a135/a136/a137/a138/a139/a140/a141/a142/a143/a144/a145/a146/a147/a148/a149/a150/a151/a152/a153/a154/a155/a156/a157/a158/a159/a160/a161/a162/a163/a164/a165/a166/a167/a168/a169/a170/a171/a172/a173/a174/a175/a176/a177/a178/a179/a180/a181/a182/a183/a184/a185/a186/a187/a188/a189/a190/a191/a192/a193/a194/a195/a196/a197/a198/a199/a200/a201/a202/a203/a204/a205/a206/a207/a208/a209/a210/a211/a212/a213/a214/a215/a216/a217/a218/a219/a220/a221/a222/a223/a224/a225/a226/a227/a228/a229/a230/a231/a232/a233/a234/a235/a236/a237/a238/a239/a240/a241/a242/a243/a244/a245/a246/a247/a248/a249/a250/a251/a252/a253/a254/a255] >> endobj 1670 0 obj << /Filter[/FlateDecode] /Length 1555 >> stream xuWK6WpUc z9fv7<*29`#bpCxƹu%뇓LdYrH3|lL:Y+20*mW̒R.gy)Tl>>ӥkv++jjn6O+~zğqQlxM7DSg|~DIFit:HRQ.KR焓`.e &̌p brv\Ȝ,1.ݭKy2lZi|~t>\gyĢm. ~?<"EiIW5dFY?d1ޮ֪H=GߞE qj?3jXIkXf5{ݪԟ@;FOUfEI܆(E"\:Prv-~#WZ\L? _(0 _Ix< ȝ@U B ¥;j0J?$C^f+2р% 6}R%;Qӹm#A@']8įwڶgH οbϭgeZ ~<>C= ~:ךisHp?b"4[HDɷL dvlҘ9߁?Llϴv\RqGMǥE9CTePLStBT6e&#F∤c]l@P"}s@gj7uO\"ֵ"wHaI8|u=ґ! #YټZ^'YAbEź:ql5QK;'{q}MS6۬/~"~ j2t/C,%f O`TТ%U^GP#UVAvVAGsfQ }4o>*1ft'~WU`2e Haޥ+r8[`kIzIҰL>/֞DZG Z3W=+\1#G DQV^ vv OUF(-雐a1d?N@DDE.~_[-.;1,h4.hȂ d.{ X+,ږê y69pTŊ) D55kK38xBd7u^ҮUӊ? 0#LCs}JrC+ۖT{3FF.$g+< +.f?7\͐ڽ\#ڰeԬWD+݉gEi0fuH2Y_gL-"x-",yɥF\&g|A2tJ¥ ,shYsM77f9wآ]%3̘;`(2 kaB5PK!%h){ I3 Ƭ Pp[kxCpb_OF/b endstream endobj 1671 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F13 66 0 R /F12 59 0 R /F8 41 0 R /F17 145 0 R >> endobj 1672 0 obj << /Im43 1660 0 R >> endobj 1659 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1671 0 R /XObject 1672 0 R >> endobj 1675 0 obj << /Filter[/FlateDecode] /Length 2221 >> stream xڥM_ICfڙI:%3=堕Yr HMb /obǛ!ټmťUblTL 77xtcћ 3a6O?7mZѿH#m=.j@;baӿ7f/m&>9輸 /={i{ɋ9XC$WCOR"+i_sf{cYyU^)y]ӔJ<,*iE[M1m'Wik/4yG8#*yselPFsh椁^ERk5\~"Y5qط  Mx ˈp "(}8[*HW针Tngl+0FyߣeQQC`V볾T3Ě֍Id.'pk\tUt^'ڗ`'w|NQ*j_V8u>HSX#F`hxvhx(&Ө^ڠK&@3P?e<94ϻx)Xa刑Jb*Dl Ocg# Nvg V% ye -3Lh/_RX#$ڥ熢4_9,+2,Y0xI@3T3n2ƽ&;"^Ks c4X +4f7y&{ӑVCaX_[W [o-9i%Tgf1y=>Z_]~Mԑn҇\ \7#UWF n xğ4r,AFybIV-ht6`Jf;a }$ߔ)@2".ë${ amwd)Dxm@f а=Ѿyg#FKc9:<*d R}(RLn-k~l"Hv{Z3/ mn A#YWCEq@ؘw<ֻޛպrmCH;5qٳ ,2Ȩ׺uUMk@س|!EJz#u}#c玕Rr.\6=r WT sen1Ϋp (u.rO_#Oң:iqU5DZl-je6P߭v]3.摀ت4~;4I['dQʘIDF(HuTդQci y^k{-! RSrTlc''w=>ao`f so8&PÜ7vݛh endstream endobj 1676 0 obj << /F7 38 0 R /F13 66 0 R /F16 138 0 R /F21 215 0 R /F10 53 0 R /F22 218 0 R /F8 41 0 R >> endobj 1674 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1676 0 R >> endobj 1679 0 obj << /Filter[/FlateDecode] /Length 2411 >> stream xڝ]=:?c|J6w)\+K6uJ忻[-8Aj[-M( 3|)dW1Dp TL 7oOo|QL7 37{x.v1^Ix*WxM-q?R!^1֏Eɏ7y1x* 2$p=[^(ƥYy@")8B;Cax" Ǫv{vc"X_U=b9@E4Q;V_`C7Dpn 4E*[ѣ`9b#4]+& V=а"J;wwg@y |)fYFYTHumu@CZ1,F1z Uޒ[yB! UQd_jTWc•f-+aJ+;Νh%#l<Q<[xj DUnM x&caCT'a#->?R8S4p_Y '$V#5!"cC(JIh~Tb > a}$t!;L쑓="SlR&FR)eP2Z1T<'Z&ahhqqա;[0ᆠ fxB%<Q Jb# -L4&a|#l MCpM.rhjICj&&pFmV~$T7U7OETw3 I]ڃnBL↋¯;dW͒xL͑ DVȈ%˃BB4:RL@uaT$l.q~SQ4}aEFZHS+$Г0+,m (GQ{[a=O,Ph:흫Ҽk%B.D:M\6_MFK7nl+̜s2eU4"139 >* u Ee!%D|$ 9G7_/N FO`,`uT >@Q84"Θ؂ 9tƔ($9S[|˹biXLiB]Y`Wj;QO{fCL<[ ϓM$LeM`UL:ϧ cWdIj@bmIĠ=\)CWx2he ے)^ݡßrY}(*cnLt>EEeʙN|Z)-sxQ)i`UfD'2"]\`7&\bLЉܕ)8&6&D)hh U?Mc'X`>ϿE5;5tvDGM}>QJ:>{&c3 7ΑsԬbk"\cEܔv=@مﲧIڜI;"Mw3 c~_?Rcj%rCZ5߹J:`/>K^tfxjϣZ wē2\Êp!Dj)hսx͙ /KK6up2 R/ y67׺P=OE-"T/EmWj"RE|yϦ;I#e):jmќlk6_Hccx`k]I~r=]&,];]j6G]@¡o/0+"}e"BdX0u{]q\B,@7 p+w8¯Xbx1Pl=)m@x _{G~@~ohm k8penCTwōj<-9o>7iOT8BlKҪ@!zHt{<?l]5}9^ |C|4];&\ o/8BVH>$:>%XUA-ʽ_x=p.×1CVM]qGgu!0A'(3u1prm|Y3^w WVDz-;4z?NF)A=Ogv)}My$B]> endobj 1678 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1680 0 R >> endobj 1683 0 obj << /Filter[/FlateDecode] /Length 1886 >> stream xXo6_{׈)tn:t: uȲlkՇ!M}wśЊY,٪_ΙO'܉ctl8\`O'Іz;q5"/wRطy!zZ63[mWjn=>q?9}%O9Oժصvz'˵Y(Uݐpϛ}>˟WKR<˟<10E'/5.]ΧPYjnTdcR>]u^o`W᱀"6dt!(j m^).SɧIYЍlz$%Yz <!}}?}#A^&Eђ#&k3^DDv-|jGH ȯ*yv` }^&ZH]&!h+EF#eX<-C s(2ow]l(Ho8 cm p؁K& x98N# 9i(DŽܸf!lNsBA .Hx`\0<2.!gąƣȫ3/baQ=$8 1<сm- {&GG8wfo?^>y@9e:ɻqHW/r斖7{^ȑkэK3u$1Nǰ?e !L‹U/It0RcxZ4sÔq(I{;BӲ]GeCfMFtz(DTF dl>9y_~\By/ڼYHCs EHZLiQ!{&# nNwu(L He9QͤmRRaja*x2F0ș2rk&QnB5<IzPkC?p^z=^P| ,|ap2ay X=l9V{귾'&pGؗxr{# DL=.XzaI0~Ez"?vBy訞89(gG'=st9>:\Yv 85#0HQH=<^sl9v+~.Z}#1 b_"@c1"D1 3gMO}B0>p&Q\%*Z(K^ji`oj.s9MAaQ%m@1j(7mvjNј)] Ɉ@@cvh0.ٳ^\$m7 mV endstream endobj 1684 0 obj << /F7 38 0 R /F20 182 0 R /F8 41 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F11 56 0 R /F16 138 0 R /F6 35 0 R /F13 66 0 R >> endobj 1682 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1684 0 R >> endobj 1687 0 obj << /Length 1065 /Filter/FlateDecode /Name/Im44 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageB/Text] /Font<< /R15 1688 0 R /R14 1689 0 R /A 1690 0 R >> >> >> stream xKo6Ǜ6SYU7/ZH@M:VZ7~!kua9?|̈Tׂʿؼ¿??hubH5 F:`e*x,tm V]lT4 ˤ+?~Fm ֍H6]J\l|F= Pbc3 Q8ꄒOL9f Pf 1K8Y3TY"pL1Ic@_& P M1K& Cz*a`BDqL8& )$!$#>Gcce 1K9$T1I`& wsLG&(8PPjT Xj{U4UN06aؔAq6ӄVs֡,MYZCYմl+VesDŦPP3LY*̹ %+6%yT<9ٖ49'Sj%EV<%T%mI֜sZLͺ*^sr+۬՜S@yz9rv qnR#tɥuc͕.mWxvFqaG90nj?/~ Mw-ٖ֋'7\ϗq8WYai>}rżxVqLp`Ti:뇃hX!|dY`E#}w_|UFoaXi{3QF6Ü\( XbFlעV9j:*c?ěg KѶm^%qy: ;E,~,OQ6* 0IQvTӡ? +o竞*UITHe |g׏GOCoDכli*7؍8(Dd/пn> K :YmgcM؇Bd{9į'oVAUqC^>̂_O I endstream endobj 1690 0 obj << /Type/Font /Name/A /Subtype/Type3 /Encoding 1691 0 R /FirstChar 0 /LastChar 5 /CharProcs<< /a5 1692 0 R /a3 1693 0 R /a2 1694 0 R /a0 1695 0 R /a4 1696 0 R /a1 1697 0 R >> /FontBBox[0 0 146 146] /FontMatrix[1 0 0 1 0 0] /Widths[0 144 0 0 72 0] >> endobj 1695 0 obj << /Length 132 >> stream 0 0 0 0 146 74 d1 146 0 0 74 0 0 cm BI /IM true/W 146/H 74/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1697 0 obj << /Length 17 >> stream 144 0 0 0 0 0 d1 endstream endobj 1694 0 obj << /Length 138 >> stream 0 0 0 0 144 45 d1 144 0 0 45 0 0 cm BI /IM true/W 144/H 45/BPC 1/F/CCF/DP<> ID Pʠ@ EI endstream endobj 1693 0 obj << /Length 140 >> stream 0 0 0 0 74 146 d1 74 0 0 146 0 0 cm BI /IM true/W 74/H 146/BPC 1/F/CCF/DP<> ID @ EI endstream endobj 1696 0 obj << /Length 16 >> stream 72 0 0 0 0 0 d1 endstream endobj 1692 0 obj << /Length 215 >> stream 0 0 0 0 48 144 d1 48 0 0 144 0 0 cm BI /IM true/W 48/H 144/BPC 1/F/CCF/DP<> ID &e EI endstream endobj 1689 0 obj << /Type/Font /Name/R14 /Subtype/Type1 /BaseFont/Courier-Bold >> endobj 1688 0 obj << /Type/Font /Name/R15 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1691 0 obj << /Type/Encoding /Differences[0/a0/a1/a2/a3/a4/a5/a6/a7/a8/a9/a10/a11/a12/a13/a14/a15/a16/a17/a18/a19/a20/a21/a22/a23/a24/a25/a26/a27/a28/a29/a30/a31/a32/a33/a34/a35/a36/a37/a38/a39/a40/a41/a42/a43/a44/a45/a46/a47/a48/a49/a50/a51/a52/a53/a54/a55/a56/a57/a58/a59/a60/a61/a62/a63/a64/a65/a66/a67/a68/a69/a70/a71/a72/a73/a74/a75/a76/a77/a78/a79/a80/a81/a82/a83/a84/a85/a86/a87/a88/a89/a90/a91/a92/a93/a94/a95/a96/a97/a98/a99/a100/a101/a102/a103/a104/a105/a106/a107/a108/a109/a110/a111/a112/a113/a114/a115/a116/a117/a118/a119/a120/a121/a122/a123/a124/a125/a126/a127/a128/a129/a130/a131/a132/a133/a134/a135/a136/a137/a138/a139/a140/a141/a142/a143/a144/a145/a146/a147/a148/a149/a150/a151/a152/a153/a154/a155/a156/a157/a158/a159/a160/a161/a162/a163/a164/a165/a166/a167/a168/a169/a170/a171/a172/a173/a174/a175/a176/a177/a178/a179/a180/a181/a182/a183/a184/a185/a186/a187/a188/a189/a190/a191/a192/a193/a194/a195/a196/a197/a198/a199/a200/a201/a202/a203/a204/a205/a206/a207/a208/a209/a210/a211/a212/a213/a214/a215/a216/a217/a218/a219/a220/a221/a222/a223/a224/a225/a226/a227/a228/a229/a230/a231/a232/a233/a234/a235/a236/a237/a238/a239/a240/a241/a242/a243/a244/a245/a246/a247/a248/a249/a250/a251/a252/a253/a254/a255] >> endobj 1698 0 obj << /Filter[/FlateDecode] /Length 1427 >> stream xڍW͒6 )tgl"%Qui:f@\/ݼ}U;8OΉ_R$IY-]^0..er6y|xfu:9;]7Y';,Tz<5ӳA =-?J?k&ݵzPMeEpoO ϓ_ɿ *WpW*8)3j4]m!'ɊU55Sk}١ R`Gn_ide+삥s$M Wc w%c:ÁKSK a#S‰Lt!KՖB,k4ྷmpV"&'N =zC<#RQi]vheN=[$r,-mРMAGWþԆۣĜQaIj= NhA> ږZBP*Dmg좓 '7fV#Bag"@tDz[)2fHBJ!֞|N~6/j{Qf&@1#=ŔG4"G2]We|:;7J+Q"H!PK-4[@|=\/ň/ (ԙvw0XNo7x y xfBRAA.qOTC${`(8(U) ,z#z՛-1}QmOPȍSI4s2VTsZOBٰN0埥pq (dR0$#o'2<&X5/zWȭq4bY %SU+yk>_5Bo&1gY| Fǿ p9=-!#L2[kl*PHF뷤>˿+VMXPEJ&0k3ƍVM^2yW2 endstream endobj 1699 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R >> endobj 1700 0 obj << /Im44 1687 0 R >> endobj 1686 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1699 0 R /XObject 1700 0 R >> endobj 1703 0 obj << /Filter[/FlateDecode] /Length 1885 >> stream xڍXK6 WHϬ()m:tC=C2FW_ 9!?<G?쩀g«HF<n^yzo'^3ΣzE~I!v~4f2nKPdowR ىj{0]~,r@ j \O~7ECiNqDI*PpS&pr$^l-&ݢ*]޽l"% %֑+l ;A~{'"b"b~q퇶I~ j\qJiiqȋ]{+0i=4P~Ao x~Ge >5Kxumbv4&FkC^68IgE82¡qM(3ed.N綳'lhr}1(m$@tVҝ<ꭂ6 P6P֚,h=pԝLF1 ͞TQ&VXhצ hGPٹôFGb wR>UX}&ݸ=OH`f$FÜ֎Ta\e:Ҕr&oeSxRy;h$(VLr1'BH18`^6SwR2n_ª߉nB00=AFB)KHVЕw{[cI݄Y E4vp'DTlVVqVkm.BhgSp(:SAVW/4CVd@K1i8?A Sfq`܄rt+D3c&(_{l(& Js`u;S.oK芮a7砼hRR /oWjq[+4sfB|o?(#,wG^Sµ~ ]3+3J-} "^;ݥbaђy\*JRJLz.&֔ɡKC 7kZCkd+Cfl26']Tju> endobj 1702 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1704 0 R >> endobj 1707 0 obj << /Filter[/FlateDecode] /Length 1964 >> stream xڽXo6 _f/> ڒCÀ=p|w.|v}(:'IEQ"#%/`A=ݼ&8`JxK{GO $Y|b*V?d\y_~+릨|+s"d{}u [}4^(ȱ4,K.Б+) nY4B 6Ϫ)bKK/Dmuu@@M W Z@jHh:̣Ql5omM6;ɩ5Ґo{y2<j Vls\% ή.ti5ɎWtk rB Nu4 |t*҂$Bɱ H|% 8,3x)O1LԜ*M|`z RT@ G $%Z:T%rsa1M %˲A4xତXi慳Z;'1t9? '8/fJÍLEs&Atu>-g) N|% $U Hlu,֡JeΣ)_̔Zة4E+9:wpG*P\alvB=ݐ B 9׈p*˥agS㍯5*PN9e<s)BR:hhO9mC^yiӜ*NجL" H~3Ց#xx4gv}&.im2/vxHIl[K]$"Ja&>_% `(6i4GY 7BsI4Undäoslr4* ^ϩʢaљp^M,f) QF2W5yGt.ΦnPlHy- usDs/1\"[stP@29}cY,^,3:Xj,K2҄q[% Q=/JgqFFeBalƧ,[<|*k~. ^>{`9LGzXϧ@e0lV7o El_c :R$g:g*ط0HlQ7qSG/WmVuw)?8јL(e@o G#Nk̺35}JJD:yr/m'E t884R.RhYTwMb^;_=H%_J8-ƬӤ!8سo.IDN;U󤴫*_mv4+|$v8z4Ro{ǡVN1͑BWO({-ȱϚ[yqAxUo[v5k|a-*xD+*5&x&>&$cmD VKcęT+G_oUaI"K7J'k=DQ7`9D1];_TL۹kPRGuݣE{ue4|b% q>tI3Z-u33Gʫ%h9ilȣ@b th hݡnW +V\l+9Vr߂[jUL 7,Qgre4X2kБv$BE$!E{V8[r; Ʃs4i>,vW!CZ~+ endstream endobj 1708 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R >> endobj 1706 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1708 0 R >> endobj 1711 0 obj << /Filter[/FlateDecode] /Length 2443 >> stream xYKW{1[|19;Ld r[,ܽ>U,z=@ 'SdUc^, W/+gѫUT6JP+%S}_V_ƫ9P ODz# _|^o(/ &ea;JN4q翬F(ƥ1|f} u-Pu68Ip(TAVWڎ695.f(ousy'uU\hzgAupMNLpRmsVκԨ8Q 28ڝrj Fu+(cv.%J@g;Ծ]~f4PUQh0{5s %@K8WQѯqQ z[2oi,4x6v.L2(O2dMG]Afk01|R>)CvYYf'2>gGBjd%,q(0U77vd)ޓek0\$-y ”\"ƸMST,F2: {^{7Y,|z+qm AN_@wL%єSIo;e*ؖ3%=3̈k3ˇs׼e1V넅!t 0~m #+N4>@o@'NxR: vY[2#!3W>#0D$h8O"s3:e#ZoNz.:hl=g< D TP@9;pX HDhMbd&dN;*}liM.—e/9@QHt ;hWA+68nDYdVg35 X([+ݖi{hrfVbR!ě4d<5uQۏeֶjgIrJn L< X`CyW*Qb~{5uvF(jD7" s"Ɲ'gZlgid0P̴+Z噅8CyWd]@cpLPq6vs,KwFJ ú0ŭ-3UӗOˏYg{5*k'ڵ]cab祏rrC-B-Gܹ47tJPBen$= :Ї+2ryzP!wdv馫INTjB#+ۚ$qqv@>hl黰z%Qsx^{Br(4Vu $95u9i}0‹(2Dy}xO${4hOBԝ6@hô;2̩%IB v!{G0f|2P}{0w|y q{T}&Aڮሊ]]IV\!`<ɸ3Y$*18GA5q ~ 翧h(zA=tdD4,X*¨@y":ßMbN"rm2jʺzs+t<rP^xh.Yݙ;i=9?ffp{>8~ `@&yc" S;{^.! $8wb_bJb1 <;®trcv{27;yLRRS x)d$)1S\tZEP;Ыlߍ.j2\YT$R!K[Y:kj~-n(Σ Ah% STO Up]}9s>B=ߪ6MmnRk)P2UX [a(c8 :ah&o-M3=7c績n'AԾmRod]VbnT/mWqoNf̏v'* [ !3aE5 (3-Q8Mg.$'Z۩(sj.U*r 8e|'],c|ffL9zC'(z/4!YPm?H4f%V[,~DTWͅ}YPzo_F&" rk١ mjs- OoivgȘ#m̿oB/CC VB;'> endobj 1710 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1712 0 R >> endobj 1715 0 obj << /Filter[/FlateDecode] /Length 1971 >> stream xڽk{;E0Z/?v{N);E?4(NfE{IQdzIER$ŗEay; AyO^ˆ{O J֕Yea{<Ǫiz Jw蕊/Ϛ@wY;n[Dkgχ1P)/2gh PH,Jep{[HT7ekZWIaa‰G _4≯ nWw ِ~tLze;vHx*9I1KbgH(@, G\}$a.[PFsasXT/@yyU[.h܋,1G*'˜Lw'9nAGX⋴l diOEBZKF(pi GP𾱣5b2@1#7Hۗ #'('}QƼ  _d8ط-x \i$iI9Kՠk &9b4zcMNQpz7iޚK6ڌB+0&`P6Uՠ$O6266?FJjʤ;K| sFt? ;B^(d̢'l\ (:,sw.Hh]?`)mr|ʺNCo&?q$YB, U8fs}4OmMVgB2RG;$8lGC΄^E}`[nq]<.I1ͮ!oi[k;F~iNK=֔`2iY[BTvMKddipG$Ca=\2-Vg,-EWVWr-UhUfBza fh%XM%ʠgФ9u$s>xt!/ՉEY{R豯hnj7NyEKu@lCjMm6p0P6!ĹL@C~8V%`֎Ի$F岶jj[r.`B36{ZbSƶmX2%!B؂*;[aO U*\)L]5b3\!Lj}W]̺[v~pW EiWυ>A{*mқ2˦ i2s-c[]WZ6^ endstream endobj 1716 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F6 35 0 R /F12 59 0 R /F15 112 0 R /F8 41 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F17 145 0 R >> endobj 1714 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1716 0 R >> endobj 1719 0 obj << /Filter[/FlateDecode] /Length 2436 >> stream xڽۮ_!l*Gxe<'IH@бyՕ-Cwpd9p8URK?`QRG"U!2º~7yT2#_kYfip*R㷫$J1춧U3UOuS}EL=}\H|m%ѶRTB?8zѹ,eG Y[=-}5,`CEq<;o :hrl'GUbA_f9st\u"sNs6] ^ۛkaP'y(_+`M .MW`{%eYJ0ЛxAˆ|қA_~3Jw; 2])T'61a*3hcҋ;7FO4+ s8]] |pb<کɧ{czlN>jPJY(P*`6K;ڂL/W–A ‰E@66~_NѺb>*^풾G@y-V( gY%Z:qݴǗkTzI4HFcٲg@}G=pb (HJUݨԼ5x6SM|r`uħısù>qہ;ܶIoW=Pm2'~54T*mra k }Jm1AjZ"+:p>VS8 =KՓVe>*BQ GG{<5bN^@[tLvxw&C )]' Mߨ(|E&ZMsቱmSdRuO ߻maMl,\{ژY\Q']{`PK0Ľl;v['%Δh1Gqځ B/g{#昬c=Lb :tEY)o7e,K/A*:aPոv]a{㌎ ⎅} X5M/VY0" U':=A_3-O, cw%K:ϑQ*>C۽) wM)̞0W^d}8C$= F iI3lgMҎz k:>޽{#޽G {ˁ7˹=`-ӽtlq%9hДKՅõP{x2*y FPrmK; ħ!`hl'(y6B,Uz uG=c_c,^>pa;9N{r,YcAq̘ @yPfs(Ķ@Y!`B3|C<8n-}IХh|+Ho&]ʁNQrzq4bL`BD ԗ<={RQv SdF֦[RօhBqTpP,蔢Sr%xP!j`c/$h"ȑ\_9x1a>lW0=lI%ï9i%G7-omg4d7 FZ`BLd9@D%Qcۮ#tQBAi)5Ff۩|Crݰj#+-uv^VYX u,DQk.'i[BHB;f̄5)idw*j2 WX-Z5ZpkDZwXۅsa?m}k[r;yh)Cx͆CZ/H7 K@|?2%G8v5{|,gMjP3 .]AbބD>4}-`g".tMb0l/]\{ןLdRXtRccTЂ 5Ehs71cS10HYByfO(j~j 1a=pxe B$g~9b1>۶x6;k endstream endobj 1720 0 obj << /F7 38 0 R /F11 56 0 R /F22 218 0 R /F23 221 0 R /F20 182 0 R /F8 41 0 R /F10 53 0 R /F12 59 0 R /F13 66 0 R /F17 145 0 R >> endobj 1718 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1720 0 R >> endobj 1723 0 obj << /Length 1255 /Filter/FlateDecode /Name/Im45 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 1724 0 R >> >> >> stream xn7Ejr h, /VlII|}E"@v |\֫r;Z|]:ȤNp*U2t غds1]"h0 @ dfi5![2Nд"hi>!Xq୤L +;|@%n AKeBR`';Z}t0!@eQ@r FR>! hA( O}B4NR6s-#hz(;44>p4-zww+\M徙 Ի <l?a |}ӗוo3:?~lq=K r)ߟ<>\·.rrU ~E'2^n:<䎪\8X1 mAzp͹=<<^?n8/ǍXkWpu{K_߱# endstream endobj 1724 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1725 0 obj << /Filter[/FlateDecode] /Length 1865 >> stream xڝXK6 WZKHiEzhPdExw7w7(ɲ'KE)r8n>*x ҘIIbH&;+`}9E~G]f%* ?\}W*yܵ8Ç^ /2w3X ׃3]8E4rAӌ)TNM6\H(XkuLDSq(Ïc_f% k{Zp ?hJ`m]Uۭ"2kC$ _,΂HO 11 dK909>2xT\xg%)Sw,M\|mP+}E q;=\Z"mYAik%W Uk\~t:Jt4q"F&p K#S zU.Xޮ΁Ś_]sRWs7`pWww}={K*0ԀtX~pq㶆kӤA36fQt]F"r#QGIR7#^[8.AyhNWz⨴#HVιޏؘ*۶f62Nc_ޙ Ӊxمal8^T艸n1lh6N$;n"e\p{FqhKTչҗ?g.l0Xd"#"rGgGiW0V^<"d0nm_zβcȿصPz)Sy:L\ȼ++%T[z7U`'MQD×ap}~Ͼ\XEtDXB7: Uo&V4Z5K]9TseIM| >,.‡K&YӞ.c;0GuNںp_x*Oa^Kyޠ')|sܦr:sQ\P? 7ZvB3NRVWǛr,sKӝ;#i_@{d$; d'lQ-^EQd܀Nin P`8 o_}lǦ _xmJb9 c3#Dk0in#/ͱ%R/^0-͟o 4}q endstream endobj 1726 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F20 182 0 R >> endobj 1727 0 obj << /Im45 1723 0 R >> endobj 1722 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1726 0 R /XObject 1727 0 R >> endobj 1730 0 obj << /Filter[/FlateDecode] /Length 1598 >> stream xڭXmo6_! 1+R[ hu0K<-Bl)Y#7;]u{<ǻSxqϯ8Nw< s9L?۷#'!Iܮ9%,qn?,iƁ{M}_߽0r?3|ᯬJ]ވaޛ߷9D#8~@&$+FmD3cFJ?T(&QF 9[b\BcI<ۭEuϫPiro~d{PḦ́so4-%eNHH LE "8F+`>a/ ߑ.ŕQĐБ a$kJ8!ߔ7wLb@=FXah<;rBF괌׾j,Ky}VQ\-s}& jӮU6|1BΧHOPnb$9N7 ijbl6(;OmYsMjptYʠ '-j$4"p߿4ٱa5ZcҪJj |` Ԃ%ΜyhC*]Krյ)NSemdD}/ F><9X}WT|b@/fW O Ib8o(sp%Oݮ,6 KhI/X6{*Ou("=_Eqv ;]e?O|E|~Xqi:cp?Jy_ @?#Ʊh [Zs1[~ѫeZ+*nI7p)i,p>mjXWS9ų8ߜ$.۠#Յ0aҹƴyFI~!s5x7ox'u[=3un Zqy=81\"do^UpP J=S;\M 48Q}_^ɥnU"䋧 \g`A=oz_[5x}b!wk[Seccbjx!>E{0VnSv%^CQ> N@7u;Kx(^Qpy-̥BT{ei_qm Qg!-vz U~̖ >cq`e endstream endobj 1731 0 obj << /F7 38 0 R /F8 41 0 R /F12 59 0 R /F20 182 0 R >> endobj 1729 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1731 0 R >> endobj 1734 0 obj << /Filter[/FlateDecode] /Length 1999 >> stream xXmܶ_&-niE S8@9 |AJ I{krw1R bQ'qpe2XE"8J*/_GA2\z}D\]㱬]eݼ78 7]qgH:?XKû<Yx˯~PgqDv )2jQ:T*X Ÿj?ֱPftS֛݅^} qەvݶ>u1ݩ~dg1a0bMY$ܯ"<" -nJ"v-UvuC ն?LiLV3QO:nٮwNaohi?Q6oʜƦڮ9m:FV˰,csN#vӈy|aˋuk־R (PnCo j_ 95Y:ۛ,=eƮ G#MX*i G#X:caJlܝ{\@hqpZJ>Zĉ)7*jTX24ژA[2(XU{GB*©ND]"%Gg0r>ҝSgd,i%4с 鼚YENr#K04MԺaSe*,Ͳc,%ӂv}FH+p5zB,kSJ;DԚ鄩m=/X?ǘO|6OY*z?0F1 𜎒]C_;C> endobj 1733 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1735 0 R >> endobj 1738 0 obj << /Filter[/FlateDecode] /Length 1803 >> stream xڽXKo6W!Zˈ@6{h1C6]٫fWZHZwCJJ~4H yp8Ï whBJoBo)~~&RFm'ΙHR9O$/ٮ\Jsf(r&bn6^^g7Ŧhz@I?t/g}Ksh7B1ͷ-&og!~#Xiw3gXd8b묥޲*6ѡe[ Q:1Fmr抸auÈ)U9㔍0~EECmo="(}i)ӏ) ??i9u~ZW~Z9vnCsl%&SP(\Q|0[pHh\wN}˗_ ~(]^,  X<ڣ&\#Nb`"FqX5bmmUh[~:x4_aMK'uxF^ٙsQgbWx!lϭ6'bbE1T|E9eqN&е]{{´rbY |<|&;7 OYbG[숯K"FnJ qJ[ /xՄL4,' \WAb5UvLkkPajGi$h J a/¤R510EXмu՘ܑxќ c1kG%1=jmuNFۍ*#H`QG 6ua8V^ٙu(glឌu!CIȈE@#R 񀽣>RgT%ԣ0poa)'y HCFSSR̈]kj^⡊ SPb?u/R6'r8fTdt"pѰL-@@)(MPOON)?UOJX]]GZ G$V{$/!m=c\M[xQpghsUM[Gf\d;)26y7nz7GP?#re,&J!7i`eB%S:}~i+xrQ.7ھkUQtHp y4q- vݟMU A"O%krQO󱒩7@ֆvv@(QәGhXU.r[bvnɈ endstream endobj 1739 0 obj << /F7 38 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F16 138 0 R /F21 215 0 R /F8 41 0 R >> endobj 1737 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1739 0 R >> endobj 1742 0 obj << /Filter[/FlateDecode] /Length 1758 >> stream xnFޯ`$X!҆+Nrh%AAIŘ",)bl(yCT<#xQ{ zK)О&EЬ<@MjvAL / vkxù"h+-|}/+$'u0Pa4t.Mu{QkO$xc FG"PAIJt=M(]#ed^f&['MjC!ȶXXM,u,>=6g ½j5lq*۸FyG9a^2-Zؼ9)4þW/.JuT6 Uf<1)%QPZX*K̓zb3ꎅ*H@IndĤg%@ `4w_}x4E@NxMYpfΌHw @P7C $e>9\/ߜl!{PBtX_ ''p12䲵K}˃Gx-BvV3Xi N&&> endobj 1741 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1743 0 R >> endobj 1746 0 obj << /Filter[/FlateDecode] /Length 2338 >> stream xڵX_۸0]DR @ZCC6]-C"li"r83 ?r&iz^嫗Mʬ,ORگ2%v~XUIeWOJtz}d^Ǫeؘ,R&_џk]D01\_o|G"^cٷYXg24&+uOѣiWqw N&ݲ̀Sk|ݶ8vA`{jHD{zaHiNмދ]"+T9tlFdIF%Ш{+azRgpiӵ{kڛVFwLkT-;2?<ߺjR*v@Siq`pfB;K%,'鎙fyfpukQ=QEı~5YrnV 9gV$Ӱ҉) Ga8ft1EbsyeGLetReFKB*Rad—X邮XeK_H;T i|v G Ri,uM*/rZS˰cHV h}m=@kXsuӉ0 ĔCzdyu-w/K=zDw;1ڔ8 …mF64M޷Yi[ZX ՘Ć Hf dO-lũ/ށv=NϪMDVPصn/+2uv''%(77kTQF 8#˥ ]&vJ7O֕O0N NNPnXH=#HYiYsr`NQ΋B:bwPlbQ%/}}هbs[dS3zG/cPLr+f]99CqƬyi%a'S>u Pxn3's?rAey18KeѓRfkO Q[De7S2_ŨVxQ3_! |XDfN"8yRdN8= 37cCR;srWjӅrC"?͞7}7_?߻'|ln oʹo&n./*N9Xn0ĥ kOuj,ujSf/\ߏVc$ڄib__,MZJHr\#Q6N&%6ɈH`ncX&AB }}w ^"hb0ZγxBX8pP '"S"w<ɷbp͔צcϠŘb k 0`U7h/U` *` /ߩl8A!݂ip}݊eĦ>[D~d8Spm>'q*[r*:Vv뵻*BO'b_D*MO՝uV0s𖚴ؔp-TXY_`AlaKTD#ixa6_M覈gVG,BEΆBUh_-uS5&Hx9*p[ܭ4োC!j|F=x]AAA9[}۞.|/ endstream endobj 1747 0 obj << /F7 38 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F16 138 0 R /F8 41 0 R /F13 66 0 R >> endobj 1745 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1747 0 R >> endobj 1750 0 obj << /Filter[/FlateDecode] /Length 1472 >> stream xX͗6$6y/m륇Ae/ ?8}FÂ77h~33w֎m~&t>8RxA|l@,ݸpv~~~}'ei\zyD\㶨\/<_7"ݬ4~UN; uUjC2|~8 CǷsH) ꉀqiYpjv5=[ٶ {3."r*7-2|-hM!i7UMQ-<03Q5J3CA݂pV9 ڮw,&ޏMu.C4 `?-<shL͚?NY?L lŎlKl~hgٝVgdt5̏2Z0;Ffp&_r*xH6=5Tp⨟|Y8"BI.jlU=r:xctt4Y-Y&[ @df" > VMY<Zokh%ro:<esteUzC5r{};Dqk(s/:!,r6rnp L`>B:ahs|Rr;n ܼmPFDJ=%N/ckjʃβ=| fQt&H`r6J$SrI?.ȜQ4e'f'!oCi9DC3ro6zm!o]vq|·X1:+k= XPڃJӦ7Lt#n*vgeۼey궵V6Ar6W"T"Dn)l*H bѭAԕ*3͐7f0 ˠiZesk0$Ϣ+=~{~) \(I endstream endobj 1751 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R >> endobj 1749 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1751 0 R >> endobj 1754 0 obj << /Length 1672 /Filter/FlateDecode /Name/Im46 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R7 1755 0 R /R6 1756 0 R >> >> >> stream xXn7 SHy@ AZ)KEQ?q'[zp>h]}W$ 6!ߍ=D[s~;Бud),ӱm,N+w_9% o[o>]ҝ߽/@K; Tk=f1a4g5>Gz"Y `M1"GpW(cyx]5@0G< }uR|wjA,Ȏ<~UTidIr@ȓamxg'$^hⅫ7 YkC#<.zF")@8֋Z*koݕeb+졿xiCϩM'̨ lؘccRãwGDuy-Mab,3E@U]Q +[_[4x Y)=ؽnGgE>-ދBj80+Z,R::aآ(ghÞ$3~Fy?5Z d r5f ӎ|u.RWo_=1c{$.9'\)B[k?kϛByX<|4W#vƳ2d=~b Iuk]XpՌP&ݟ߭gy~ep,NX]/ _tpj{iNmr endstream endobj 1756 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 1755 0 obj << /Type/Font /Name/R7 /Subtype/Type1 /BaseFont/Courier >> endobj 1757 0 obj << /Filter[/FlateDecode] /Length 1645 >> stream xڭَ6_/2bxr &` hE_}W|}g8,[n ɹ8gF8{o 1} )8N`.͙@O '[vNG 0Od9|K@i yJp;"8&4)qfjYnp8>Z\(YI²۱Q,CIZ6alRcPe)1y^،=/R,TDZ*1{鉦 2MC_\Rp4)|{BYڐPS3)XLr 4X-j{d*i~Aw]!Lm"F2ZnS^:^t,H@e7\wmAc m@}zE8ZȓڙStxWfͬ(#5ks/<-)Ji.ao3hD5,-{Օq*cCȱ˟GAӔyN[Q))VҝB(E@ZI B/e۪5y?^}:EsP+zB{'$Z S5*G<|" $#'D~칞]d3Xa\yh^t!jlvQO]n$S~_ϵSng}1c=~lFq$mLr.=`'&"0\L3&z_kZdHH2ag%cN%̕beWw:=vCiznO"XM J'1 ~uY݁z37kxȭcF \ŝeݕ;*P߉]t 4w/y^,'l²sއ~e򔑔Kg8fR)'~_vvH\ݸ(NvlHйI;UrFm=v,۪8镌h ͹rQ/t_mB,llTVTiS8 |p4䣤rtXw$}oneM endstream endobj 1758 0 obj << /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R >> endobj 1759 0 obj << /Im46 1754 0 R >> endobj 1753 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1758 0 R /XObject 1759 0 R >> endobj 1762 0 obj << /Filter[/FlateDecode] /Length 2057 >> stream xXKWae%$`3e-maHT{zS"et>E"]_8'|QeqXy|+0 &?M"B6JP{amó[K4SMMO@5g}>L$fDQQ2f"K.GSuf+ Ɓ Ff &Y:qQnh{Z~v"Ht#A((L<0{2EߧMZ/xG g XJDʃL2]T\f!mWw*K!2vϩ*c[hױ ۣwQ/֥z6 J~=HҰjR\!@\/1u"^d Wq҅r1"X (Hd~oTە$,k5HEjٛAput-eT0!qQXWPmw/~šBtO?bOVͩŸV &ÆvBx,cWhQ wpV`&&!YuL6M9#~r|Z-X]m-R^^ ^1>{ ?:Ædd3։B>tuVv>7+k17EbOp\e>gʀe~ƀߊA3C8!,q B8ɼa3kh>u:OLoa[K3#B&{9-ؼF8U]Jj[O_IBDr6ZO8WO]YC׸Hlwvv/ :t"F9j ǷdtlQ"lfVp(PP@ }?K5Js[,1bG#O5 &e")VD3JW{րnkXhhSi'z``4V^j[tsMI!!Ԟ qaTE $\](/E#w>'^<5}5m:>S]yKu([Rfh~?Y89=lZ\™24Dn9 μO!y1S|W.@kcriYRZNQJ/;".\>*n `p;4q9(_m zC29{eYvvBsB3Z˗O8_w$ppW2iWT򘍜p]~ۂ4{VQ;8en endstream endobj 1763 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F8 41 0 R /F13 66 0 R >> endobj 1761 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1763 0 R >> endobj 1766 0 obj << /Filter[/FlateDecode] /Length 2746 >> stream xڭY_0Їʀ)RrmHY.ٖm]di+ɻ ;!%:)'p7Z, W>jCET,JFIa7>%e4s&DQ,NUpɟ]oH\7 ޯqk0<*|_VeWK_hߏ]Gz[š/qR" k-\iMn%D:4m sڕ?5(wB%Eu .2`‘l< VtJs":ieJ?/( #CSwq ִ2hUAh'b"^Zv0qNms!o\#Z Dp5_zeK Ʃzjs_٠ġ}]YVhf eS#17吠!M:FJߖ35Rsv TJl \JnYUs@9An7mӭӱUp-MJc{k!FjH8Xj@R1/#өiy\n#kԽut#t~Jv`+h-eyεY"aaDvP68la[j5--$(Oٯ{PvyXF1Y(Dbc$l :!~`$v2Fg!r A}[3ح}/}cS!4ht:<{C#:(rqC]A ]Cts?h͆ HB>5 -O@%&J\C5dY>5xMC3Q -rD* .-BRE& m74T:vc*!gꊤ YmHTEA]$4Mlkx =]Y8xRs3V#;r% j ZoHp?#ZR!2 F9p=  X.)㠟e2څsp}W `h|Y/2:$e mrFҩ,.o[54 wT[t(zUjVrgL^?},I [P*ilۛJ<=O ȱIsu`(eQs&;QұD ̽x$X|a.HsfX;x>ى8l~<EW #Y<;QANm;ǀ駘ݦp pY[hx%:v*xb[jFO.0h(K𐅙/˥YĖ@xg~FFgdMϑ:G *6])EVg!ϰIGB^>/Cu :ݵz"?: u (*xз{ (EDbQ"1>br@42~KŋXq n8uc?oC⿯_vZ0h< endstream endobj 1767 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R /F20 182 0 R >> endobj 1765 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1767 0 R >> endobj 1770 0 obj << /Filter[/FlateDecode] /Length 2421 >> stream xڭYKs6XpT($ʦM=xwӍHe@`0Of/8dY4[HB1d$:/0Wa)/~Y=ظn?绊T4CvFf>}I:U@)5 QPgXD2c\B2i/2(o«Ǜv?}b9/x6/nz $3E{4ɳ+Ș$h?Ha;a DD]7,JzۢʺDe҈\jRgȚDJw`=wP{!Y`{C8|MlbC]ږB%7sz̵6"AmLVk.bU7b`YUX` Bɂ)C=) "9UB5`4X'\ +]/)b)w$_:ud>'L+F#e"Ki! @u6t z;)NaeEAN?dSx6$Ԕ-z9WE :e.Po3V1,|`5ҐDLF !d3ᔙdaBB ޽F3R{:V8IUs~&WK>8j cR@%q '-7A=(d^*9 _?i_˶+0 h6v[=@™| \^kȑMvao#7p&9|>_)K(4 2Yڽ[F3ebѣm~3AgD]Ol2zb[$L3&{2̇0A}'@ }Ȁ3pZaL2`(d R'j[@ #z HA\8<툈TLa13&Bdq ݐX,X($E0`#u &k|<~x'uq N:B(I'Ρ49dJ+ո^r<ƴȈqb/$ʘȀ .rwdtd-[ʖ@9EoV;["^!{zm9]1 6$#}wH+!! sΣPCgӓX"J͢1* Ω0֮Æa&nU/l(ӇS/ވ #o݋@}*q\[DH`nd Xh1h+:0Sۮ{n*ƝӶLi{Zgaׯu*B&ӡbiN.;\ߑ"j2ǏY[{U%wMG~6 Nw!y9RT\_)xA_̱ thf6:dYZB[!Nq0 t4JiE?h4B50 X 6&x,r@Ss&fx! @=+ڡYw!CTgXaWN'Xb]4M^Rf~<^?&ְS/+%v@ RKGI*vǶ Ƃ{@<1Ío{={*Yhp5,nsHZC<2B3c&(y |j <Šw[km@ oct/s[ H 4];ԯ42~W]@Mhnp&Ok0^D&\ίǷK{1i_ O4 v endstream endobj 1771 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F13 66 0 R /F8 41 0 R >> endobj 1769 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1771 0 R >> endobj 1774 0 obj << /Filter[/FlateDecode] /Length 1609 >> stream xW_F易WUªawҦR$US_4,wVY0ƾDJ<2w71s֎Y~E9X$'K.w΋''bvV8g"r^JNf<ҡrxfd&rtNfZe9 j7.e^-RPRy!ϫ_ϙ qiܼȒxd"7nmc5|s㢨r&qlڸY2MZ@YD&O$Zӻ2 qwqmuQP9r2S+K4wp7YVZO@"J"e_ }於*Xk㜶ۉi!Kc؊C*Sڵi6)l,NbPm:U 9/ m&'kBG6,v1!"fL&1uj6B7013G9HO `mͪ- `첈yO 7&JysIjHBmܽk:/K(MaI6nإ}4KӝtgL `[ +R jk3ې5!0򬇞II= B{'m3Q^94{sޝ-?@lvNcd6ǷY@K/baDQD ⠄ W,{E1Oߋhtj;qgLC-bPpW@lg !¸O*/X{`?R,??A1 ?*PakUOQ9_h{ߐ'V,ޮp7ѫo~ endstream endobj 1775 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R >> endobj 1773 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1775 0 R >> endobj 1778 0 obj << /Filter[/FlateDecode] /Length 2569 >> stream xڥrPU*ߙf${S99@eqIt HWh4F7o6>i"7(4(D$ͼsݻOEl?2?mjڇ.SOd^mc;Tg^K/%z, V55?R4>j]8SjY_9ov H^6 A9 Ҡ2hFΠ<{oS[xcP T2$nТ"x* xHw$z ȾoSUZ ZlTj2^5gp054@_bw0΃4^;yp)fUWh0N:T]w+W 0:U{V RӔ vqǞupڗȪ4tƙ+U}W{EU06{iYv9"Gru9g" y]oWsl*#/ߊ$9v>|w"H+ޟS# >畃Q f8SH\mF !O;!wAFYDۄ\n%ig%Fk傲F@U`Hnn A73@x0&Q<'bF( E&@&>3iDfBF8䠕F e3$n #e"ȗP樰,]+/ b+k^)e.vd,%os2u#,Y6}6c# M,4RX #%G I$(Ihs,rԖOuՖkn2|w0^6Q^"k"9KJVjߐ5WRG]) yڱ@p56K') &vÿ4I2sZ!2G?U_e78~K[վ( }8G ,U!F.VR oX X<⪪FEGuf8m){:2~S6]작yx39$ArP.qe|a:Kt&hh*0oxR#"2^k1v EZzTpZ*CЫ'E衜4%`K]pK]vzҌ#\%b5(>e&BriC_AЈZ&屄"*jyV-06tw"ЗEmd͑_`5LGctcUYdžq]o8OB! PP 3eIq=2HDl{Χ^u\.#Lzdz[S]&`I3-9]D+E$W[*s3܍Ar4U~|[D iBNUruH)7d4'5.)}å}%S^K&9GWi^|j#~}o,یҗX6~4O(*|T?/Jlk^Ss]]`y04c/Kɓ*$>3#U>, Ǻ0U$Nx[`^#ӻŖKC+lfá5ZdHY$BFC * iIae1]]>/@bZ?o3IZnO}nB < (4V74#apUF̏SM;59M:TCiiN߳ +)D5`j3XaEהt{5c@ XQ ĶA ||GSؼaXh3w獘a&]C3Q n,ps&umt5!BZ0=^hlP~[5D<2JC65 %QC;u >,+pejE*!_)\]N;X.<IYeh+o4#f}ip.X{'5{#`*:#o~#CN?1[. -߷o_09!W;aWŃ9jR鋬1QPtm}ssmu`tP}=y lX!! n~QKmf:ͯX܃%ZF|%ct]smS4܃i]!qlۼ endstream endobj 1779 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F8 41 0 R /F6 35 0 R >> endobj 1777 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1779 0 R >> endobj 1782 0 obj << /Filter[/FlateDecode] /Length 1004 >> stream xڭVQ8~_: Ɛt}h[toeY İ`vw@ bu\x0 l<묑aq}`(p_&2:469qkloaOJUP`zXƲ02?3ˏL8-Jˆ"[S.v_7)+O5l?p<$ k̥e3>˛UOدR$UFx@ kXi犔xzLD-#FrO*#yEy٭D*wBe25s] a5\RBID#zg}5]?va8pNf/J*Ăb>V*7jU?}> endobj 1781 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1783 0 R >> endobj 1786 0 obj << /Filter[/FlateDecode] /Length 2202 >> stream xYK۸W(H) &AOĉ]2U9x\[DKS5O Ai٪$Qh4~|hDnf>ϾdDR>[Dl .`]>4$mO Kg78,b:ObdUGw"b{}rsx̑]+1+o,>XH"#TϖdPnn* =Tt]XUiX,ik1,DߠLє0eeZ7yؔpj<^=P<[[x4wy8:&2;))ۻ4"jyx(EoV5:I̝ IBhH pmDO䉙t8Ro6 o1OE^ Z%TƆ t^ ,!,$*[SACxSDoZ<*cO2Uz߯\z]#Ot^,iB3_ Y"NJF?aUXcT.' uUáۀ VUl6þ}eʛ@cB^ɗ]>c0jWY={ơ(HsD_(TiTN4e |,*0Te UP%e!-{ٰ0}n&ݟ$q=oն4%M:'$հ6 $%]A.A<1Hry~nO*4`ʃaw=QHzž.~h_IĤȹ&BQ;/&:9_f 'zY<çZ9th1g#@r0?i?["O<9wl,GR֣Ε&̾;¡P )`@oR(4.pm vٲC`)c5l2֑k')˸ϧ=L⋊y"iOhMD?GB@apH?rӅWFI?'3})u^A%%q.V|p܉䯔R̀e(GbI '|4-'xTF"CK H(~-\ԲQegÅ*9r,K9spyy?6(`UF}N!>{˚Uvgi,ssmRô֧>j3g-vbI?~wK,_grxq&ϔd{(Ԙ# dR"EG{jv>~A>Da2+v }Ujo_-|3jGF|>>M^VhsC+#l}KXP4e5 +sмفZ1;]wJ.\ˆB~貀eh+—/%YrXy?1WTeDWzDž)?МRK|f"W2}.oKeS HTvР`;#$mp%bl6ICMw.ܻ|W k^= \JP14}NJ5b1`SONޖS&w}[k+Gk:lPľf65W}nGMS7 RhK]Q&Gv]o0Mwusu Мz&[Vm-ڣ3^Ͽ!_d**o3^S=46~xO햠}p%c9[}g] l@|ޫKJzWZ ABa-.OK*ɝ%Ewݨ#ّ|j%ų?C})a1OY쇵ǘHk$v4lbjfl 8M endstream endobj 1787 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R >> endobj 1785 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1787 0 R >> endobj 1790 0 obj << /Filter[/FlateDecode] /Length 2230 >> stream xڵXY~ϯ"s6Ly1 3D-%]"sfĻXAnBi&,O6W~;g"\c #<{nRFv$iފ4~nMY=>N<^nf/"ƥtup4Iz:}YչV}+pLnBC;ި;;(EߴnNqk-M[(P ґ7ljPk$إ; w8C8sPfwwBjg4>-3b,8fP@hIQfo:q8DƎU}[jK4^G$lPU݃zW0 (_K+Oi)t"]hG:=B礊]іSϼADaH7 }Ga<َ&zjOUk'ƕq g40IϴhtKi#p$_ @)aI ZPw@ni]~:x.BOYjm Ys5!1) +N=Q^pCl}ReM1ǩ EyJBJ=QP[X\^!K'Ъ.6 ٵAx],:@S1x'_;+Pչ\WSu5 YxdL(ng7Y5:Oi4cQ9Ly怟d9 {ZK&sx>غ~+9BaquVAQ: M DCqGs0®/BCZuku0Y7KHi2LNWW*y aY0q \|]'4 <@U]C,lJ|н3 fG4q’J8 XPՀaQ0MCec? s>$^$=x|1,ն@65OL͛ G.MCVc) uTzY0AKMU2OG~iƝPPh4@y`jiԕ>idgi;HI!0e##%<*g ;+jc % '?Xጼ}xh}/ab'rB"B9PY1etC`lɃ7 IΊT2hQT@s/`n-wU/UmG!V? Gze5<*r:eYb_T=U&u(4ϫ)TT08x"};;~1Cb|_ϻ_Юs0f˓K39o#"+Vk ջ\ i^?A9m~gaa;_9~Er_gl1v_bb^).4ᕑIMbCxG He7Լ&X-S~l_if$us:~ GE8Wʵe,'Zx,`p5IYԡ'"umm#ےp\7 L1 endstream endobj 1791 0 obj << /F7 38 0 R /F8 41 0 R /F15 112 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R >> endobj 1789 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1791 0 R >> endobj 1794 0 obj << /Filter[/FlateDecode] /Length 1885 >> stream xڥXYD~W^{IK`A+OƙxqvSiۙ:: bQ\zIrbInuk7q,O3j+Ұhpv=$|{Pݖt}]j[DzNn⏋I(p)DZ !x5d8L1 mXoHmaL{ˉ=_ۺ"$eqUv4p%9x"0`m.l?TqŦ$@M[S DK[Go]a18y" 'FM*Gl9S_D`VP?r>[š\ 9Z }C 5Ҩ4IC}W#Eie0R%y|Fv6WN)xa0f(d~`L0r&)kF$r8v& \0eaҥg*OX,RW[hOW$ި\мM1@mzRZ[, cꇲsUcd jp,E4$CǙ++]A'$*"JUDQx"F90 L[$EүþprKh-]¥t tY7Ek ao5f! W״2څ^Q; Zת@JNB>fʝPkIW[1WilPfu1PQ@R~˭g@P'ͺ2u}r uGܹJa=gNbl8rfv[=)twuW~hSKtX$kK*' ;6:ml@[=`C&;,~)b"LXH@nI 9! NF G{K{#͍s!DLd!;R9z$%Uӆ/%0Q'ԒԦ/Y-TFt.,00d=q'.1+-Þ.m fk6XIns`,wKf|I)E)dd)Po9SqsWcRZmyd]C@ybşڃCQtV'<1Oz6NGځ1m02)ys XgrcRgDe\~gzs#o&G5#ϻ/׍RnjywP3ӫ &";rp3>O"u ˱Ss/m}|-t0 E~Ģ6V/8U3dc/ixY1ž%6.ydU ,޵]>Į}MIbauI6>,"WId<6-qPlUI_rO5 (zvI맿B)wk;zi4[oi ~x7ި5Mn$o-󫍮2txڌ=dv#Ǘ j?\K'ܼ)bI<]7;GCiTlJ|5.i '34T~AP? \1 endstream endobj 1795 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F30 286 0 R >> endobj 1793 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1795 0 R >> endobj 1798 0 obj << /Filter[/FlateDecode] /Length 2187 >> stream xXK W+ڒE$5;!$}K W~F"e eO(")#m/A={v+ ŁȔӑWyZi[?<~)2yd̼Ǐ֛]i/7fC68NF&>6fEYH}GN7~O2R6vRP+O .ڿCiwP=MvZH<~{p~O(߳rƽp]**S!5s+!o~ ^KBv8 -Xwd}c1b!?<>RT~kyU~ +9i#%"  _vP~z\|$L)4@~~496y . /'s,6&Bs1aOy'% Ql瘜 -PyDGe(X8 F輙[~%9gpH1+ʊS骤ٱle2 VY]ɠ063f] s}sA柛B@NddHt*J~]5Bj7G8XaY nye6wke]̉yOcY/ֆ`}_T|G.~TȾ@2E}*gxzÂIr I%p2DҴau C" 0^k ۛkݕZZPȔ_5VS$D;frh6s>"bӂ$&1Oh0Нvl.^GT$&pWN}~K_岶#ٷE0+/L0mQ~:+'Z @I`U0L5njk> 2*VŵZ 2BSqArCbg8|]NSگW`DU@9I;SH3Wy{Xւ$) ר ;KU,xV!j%oD@q]TgZ_XCQ;|M$C3{EYu"T2O:6@S=dG߾S5BPJ謫z4k)/_,OX3Hj3ZmPxb5W-EzZm/G6Vܔŀ7唭b.XRY̓߹*A6}./.ώxDM€ 4I!<{O _Ñ7&ZgS;Lqs/)(r*l%* \524d A0oEͨ,VIaYTxN+B |p##ػo4e MClsޱ!rLccґi.F2ɎD/ӇVئ0 us^vsxrl7 =2w?` endstream endobj 1799 0 obj << /F7 38 0 R /F20 182 0 R /F11 56 0 R /F22 218 0 R /F21 215 0 R /F8 41 0 R /F13 66 0 R >> endobj 1797 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1799 0 R >> endobj 1802 0 obj << /Filter[/FlateDecode] /Length 1850 >> stream xڽnD%/,eAA:YǮl'- s';i@<̹f&! ,_'w(d̕fl&J*D/'?Lxb9/T 튺ΥuV]O,AIPTA~˧: ] <۔_/y6vi= g4r 1e.8}- e:_W5b54Ԅ! pc4MvMfEɗSat/  -6E5:eY&oiWUI>z9[H .!shP 4gG;FXvl Q!gH;*[~Z[)Hu2RK4!wBa\@+qJ&SăUB0bZ8TS [!Wj8`ObW3Ew#9}7yAYyVE:u^!ZWKMFMfҷ"6|a Db#W쳇 !%OLJ1jr 504"h>o%\L}-*1g$!R05Y&<2WXeYc&Q2HDobU̢Bu`4bMAAӔɤZs:f\ A}v+a k+wE&UMkl7QF, :#`2%ڳS /' D9J{ Ih&RǤ.vS}iB^4vsRbMQ9',0MsNOɹနCڇB՚Wێۂrh-Jԏʦ4uR9-$ȨIJ|;jc_U ZP *2*zU涴f jj788Et43a&B$7qPOjYn*;ͳMiJ{_xK>o'kQ u+蔆~`gNP2Ӏ =ݷYmP/4+`|[W7kA\ /i -!K̒KS{>C/M۽4b/3P폼َ_0wl)+/Ɠ<;%g+3COį. ~jMq[:Kmk`ߗO2Æ2~(܉ 5뗭0g€aCRY$uÌ+)<0֥Eo?̡G a,#AgȖڤᾘB>!Ya+3(oӂlv L'k`Z$J pqB4J z$71>!E?u؁_ܝkzid4fW{?⨥!;q\E j-@u`xԁb$6e!wTXy t4чZ=GP$Z,9tNYäYGx7>X_:Ef\?Ұ SHaW?Vl{H+o3l2pܝ e3[NWa޾R5bGѫ a>Q!i Ƅya/~fiPe]u? @@Rnf|7 endstream endobj 1803 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R >> endobj 1801 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1803 0 R >> endobj 1806 0 obj << /Filter[/FlateDecode] /Length 1816 >> stream xڵX_oPS IvakР} CX]$9ݢ߽3d''79T4"3#RFIX*]Ɍe/?QJm<;g{,3=Kx nǡfY2~7KcX=<i_SͰCOMDdKsjjI W("<-Z1pYܜ4 %J?鴓\1]™n{OVh d4{BF*&Qx>ʸciV}XC.m|)W&CL/`OBiIF@%L@噛H HJYC+y'5SPc?PqM4L3*Ӕ.'>dH% 88,1RJ .ȭc*CfD^@Ki1; NVi ;Pͪ5(b1tQdH4)r𑂂HA*vO4 \E1VFY&cTm'wϐdwȮZu CW˂q4> eU+UYGTO\yP WqO5:X.<Ѫ6r40P`a! f,,Ehj@ }W1paƈan7 >4l*⊖wuW)dq)7#HM[m2"#4*W~Y~X7g #ĸ޵O_7ԻCe®_dwfif!ʨPAN %PT8-ÙB7/C*YC@OUWH=~rs+;noh@jW""H"pe-,<k-JF(Ycan|c뎩c+,$7.\"l*TT7IZttؿf-:/@;FʅM{ xa¢'z7_ktQU^Spc*&H6˪{S>Ldo%W4 ]S㽚_vy7÷5yXuڢ,2"֕~hz+0-Erprڵf3^:c|$JH; g@s :%JT&Xe*s281R1|!jFeHl߻?_Ji&(Sl'a$IB۪BI 3y&D֮W =/y[iyg.SSR`gн:g<\TM@u!ޝ1iL~> endobj 1805 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1807 0 R >> endobj 1810 0 obj << /Filter[/FlateDecode] /Length 1999 >> stream xڍ˒rUwP$Xpϥ( Š(V&n8qR,~\4f DXmȄ%xHaL_tm"4lT×uKi?d33rT[F2 wÈD¸h%$!f$.~+(v`b&X0 +'@Yx&r&&&RBJo!X_n"qܳ Ų,8gCI[fDSiN}A86~ ہL̯M$4nO,{fp" _7"- #%zr_Gv"q!ʠсtE:h{J{$%H /ŕ 0T" ̋I͖mfieYɘЍd<@jslRߒP_Jj$#)%k2،M|oDROiw&"ՏfBdG"9 xVB|ItN0 T{d4zܕˑcݽi0$`FݗWQA!tˌsW2[Xi iGjMO[Ε"-' 0pɒ. I+3փw`qN -J?6$`,T LMp~ -]hsh8eȷj2tgř;F1ݱ?ݥn @XE^;WBFF\LB킜P|tuk07Jk1 s3LpDi7jЁEʱ{,Q3%4_ju}syKz^ch?ZԶ1W,c $rn\2hsEMH Lifc$t~jSW{KY:+~D䰖]MfI"!1R[VU.X8At!"}0--=p5@ A^eH7}* lw'SCޥ @Xt]y +gt4\l5rHbfVHA}ЕSqLn~JoǡmVOxʈڗJs_aEͯ% ?egUԿ,ꌩ3k@w4 q\,uGUq+" [' ,Zd `82WoΒiEklNmLSWHÐ*Y]liGuZQP)3]B2}nW!!(:.pc>`wʢ /l8{{Jp> endobj 1809 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1811 0 R >> endobj 1814 0 obj << /Filter[/FlateDecode] /Length 2065 >> stream xڭXKoWhW aDJ4 :E )5G4 ?>cl7H<%,I!^"*YuQf,~FqCU)lLT4+w{^2O2aO,4|=D×c6#^q_Dgw?O#^0nd\X|Cm}K3%(wpm^~./ǡ9RWDaWtǦ?ӍH7<}y dVxqQ?8k)yAj(9QO+Jeeɒ"%<6ny+2)  V QJwS.x(rsC#YYu iHu&b)*x:yy?,{kw_f*}[`?[ NHYx[e`^\ŭ&Rn#QS%8 [px!2r9QȆ]{ijj*D^y *%4,VbDJSVx;obO`dCskF5cwz۶/s"(A`|\AHqq˽aN5:z,p콳 1z0!OA!f=<{Zu-}&wG>?D'MfRA5 zjDfV)g98M* wOQ0[%PAfPb?;bA?W7?.MHlu21VMV߽U k>?$ng7A̩ZO [sS=NݿUpt= ƹv{d>nXI>ʅ8- 9{oAu_HuǖOfV 4=ep cc.B0^i9!z [s $>0.D_lADTJS8Z"bu<fU''.꣈C4f״V+ o",Hf48PbE. P(Xka 6:4}YV`|9٘V ca3- *ަ2a2wvN% PM>?ʶBl8~,/eA2]w¥AUAԆ*'_zQϪqYX+ס@ 辂eY28]?Ib@ҝE+]sgs' Tu4sHv2 tl]C.SSRRh 0R*5 A>f$UU•QLCw o8Hޟkc L(Pm:^e+fLfa!te i]#; حGʦN}) XCur U ۏMעF$ǡ FctVROƶ+)AaQ]pzZF ]dU5+DuM%c` ޻`X:순0F4AUPx̖U8K}A5sm`.{qPdPڪ&4g*vdz@,LcC!J>ˆYlbQ.\yRisOHׯ7w~29`w,^%`*!]r&Jq >mw7^(;$نd.PםQV,KSG1op59~6~aL$e`U< 9@玎)6P:oe>w@S )uʡx1 IT Y=ҌVyzN> endobj 1813 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1815 0 R >> endobj 1818 0 obj << /Filter[/FlateDecode] /Length 2387 >> stream xXK W(WE1L'ԦRөf@l[;zxEi ݩsE  XGo.,e)-bi7*UBo6_6?%ey~!_F_5?3_ &<*q|cN*~(]K{[Pv “hٕ4U,'4 UH(iAS/h(2dyÈ:9@cx_{͊+J#pk CI*6-VezpNZy^]%YN(C*~tuR eBc("t"4]^j>).ΰ gW{yLAz: Qz}6w}-Jtؓ7i}Q"<%{6N=se{(uGrNh}+C%0\@p"A +&$ms#u$G +W`m9+pp^ƌNMs!Kgw,=T_KM]8"l`gs0f="N\} 3COahWiz8b*K8\1PGY ghLw̑unS"P8l Zk!KVee,S,ryKq0#X d~#(6?Zruf!Ep*Cx:&YuDQW e UZ޹nTA?wJF3Hox!ΒoAѭrmyO!Ūb48]޳ rUhT=w,opK?wP4 -j 8j+1V`?XwDIJŝX,#7gC39J[,;í~,LAyz^qb"QwcG1dQu :\W';jCr>yI m2z wNIu_ g3kie7.˰+n[}45T豛/3<=_P3k~} #D- V8m#"?:eBAIG(9"`Fs(x:@Όgh=`ŋGTo]a_`ZF:n4k[K/VaB`C I&OUv1$e,sFg=LCrpł3 ikvv8y*髀b1 OЩ+m*NUCt2r"4=8]!..=CD}|H_h!ݮs;J .$)*zv Q?pND)/KR?-֋!+ T&"W3k1CB[W;OϏDyCЧrӹ\xjp=zDwGaP+(d&W*5Dz\2Zc&\72e"Oo`$!3`Y=Lac"TNER`CqԿpL0.. BT1@mF&`M ^/_w7r~~A㵨Qd>mPej3 endstream endobj 1819 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R /F16 138 0 R >> endobj 1817 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1819 0 R >> endobj 1822 0 obj << /Filter[/FlateDecode] /Length 1351 >> stream xڥWO6 S=苞%ٲݝ׽E۲(V6d#E9KbNIJ$8JYF/’G/JY%UTD(F_׏ETJEY3QE2OTO&㒕Y2~*]"`gNH"~SCg4Zq=]dvp=wzD'w3#q`VoK[ӑԘ \Q^l|Ldd*Hh8k>4Rs*zޣW"BuxgJiQGC̾oF*z00Ζa9כ',GIeФʊedBBp@6uT(i$ ΢J`x^G@19/k_ Ps&\6s4Ty2y>Fc$H4"C۫Uu[]m8QE#}jtGDB;s70n$tϯqx~D* ]B$+?(WVW? N)4 bL(|ԕT.= k sshf= G8(Z:RkC/ z?Ụp&۱ֻ#IO](8g{g´ 4v3_aam營RB^\yHU!Uᐊ"Dsg29s~vy(Rx dZZ#y0IT_53AcCN3q[QS7};zYoHsgFt1Y$0™A- #M@؀wnU_o endstream endobj 1823 0 obj << /F7 38 0 R /F8 41 0 R /F4 29 0 R /F16 138 0 R >> endobj 1821 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1823 0 R >> endobj 1826 0 obj << /Filter[/FlateDecode] /Length 1870 >> stream xڅX;s6+ԅ೼rd]堃euz=i1?ar %"RMW/灥3oCPw %8tm`bw |' v.z|uxԟTV#_\< 0o, Hn 2_XyE8?+gn;h0*UY3L#YECɩ1=:b3okG'ra6##L/;e=!`xSnDCY;j`{ؼ&XB$cEoOIuAFvߣy$)oH$b7Mڀyz N̲XϰR%iy #Y3u4しLqW'8"莙yGN/ Vß|.9A5U0B,-T*gVxCӆa|m !&H @0^BKf yI1m_De'ޅYݺjFk}a6@6yʰk5npsK0*̤0Eo?>wpØg֦g[.1jȞ@ 3_H3ʷGu2Hwf"8n@a #@/>\<^]L]L WU,z]%kȧwIUEaSK{@2`xj 7X몇36%a-%9m4+_[jA ྑ&"/;Y T<:iN*Ssϫ9h5,a `f/ = 6Kn<Ҩ#J2ҵnH=:kv4Crb 'u?Q0iacXVVi(nYKH#I'/qʌ%U άgyFL/_> endobj 1825 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1827 0 R >> endobj 1830 0 obj << /Filter[/FlateDecode] /Length 2504 >> stream xڅYK Wr[#lmJ*=U9sP2$GjYz[I35}Ue֬lf긲Ʀn՟wWt[v3{/NLY<<C}dIríM~xx*|:umSOV*͍Iz =,wdxeO_6mjJZ ӡmŢ_HLk;V*Swlfц3Df(%ӎc%3g&O_bujfKBށV&`>fMb.xE#?lYl,5Xs8L kN8Ako9.#pwH1&^`гKٔqZ†HU%:Qg y(8e@ U!q ( D\BFf!<q&YD.f(FƱۿs>ࢇbQTn`Na\@@ uSGB-}+FZ P T1 ]`%Ők(cq"ZҌTt[SfFAH7F PD٠.OUup@KQhSsc9D&@J#!w<:* d泇&77%˪'n*_(qAsQ͹0\N"PaDV^8 1h}|9btƃ+te" Yxxr$׳ɔ4 m8}*RRoݘ6\%GȲ㹧]GIdkyʃ^"Փdr.O &T|zR5^G]IY´:w"ET׏L*yoN^"l%[o%>2=ladfRjpHq{؏xNveqxNIfO_h.]>n`B=%@WjRsT!WZ b@.~Ry ;X[=y|zvfS9r7\7)ܻ|i =U>] Py<1, ed ZU*KЬr ֙2ݔYL[L>#k2Oߘ@MU/,k#΀Qn{.mhߗPrϨ5jWOrœzPek32@ms\>s![*MԆk]0O<Ѫ@}E</AJRv}:6ݥ룛F|13Gl#.6x#?h4BbDqGP񿮙4~p` endstream endobj 1831 0 obj << /F7 38 0 R /F8 41 0 R /F10 53 0 R /F22 218 0 R /F13 66 0 R >> endobj 1829 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1831 0 R >> endobj 1834 0 obj << /Filter[/FlateDecode] /Length 1944 >> stream xYYo6~-eDR$+A} Crׂ08 -u)'Ip88$22S$h h<%)߫4rhhSh_/ͮeSm)*.H*' ait(JlԓgZ0%F2ܶSQJr=˔p6@~Q q?7} b$f^Àp*a8v8j;a*~rfkQG`spNjmvۆ¾΍ j[ t7 j{e?f/2U+ʸ׵$ n[FPevW5vFeUm pB5oEgֻO]mz.ރ!C[痱&v2fS)х cm/s6O!i" aƔү -`c2!XN>Fm+?$q1f*rIk )VCI Z0Nw ZAL8E۞R,s]*L$ ?a_1 \+@U1. U3ĸ$]B b\HT oQ &/_,09ʨɒ&J󼶒JMyaë+wR'>Axr;DOވa ʃ0lzem/% !'Mn~@e-s(KHҁu:'EfHoJ<[qYxbJ|Io}9l5D9Bb:6rМ)jtʧ@d0](.TfDʈIMOڷ m'r2~?<ʺf W "0*ai bw_u ד`ifQ}7#[-7yv665XOY"AҗlAX&s~ٮ08%`C+[N\g͎7Q~e1V9]%egIOU/bI9 \"v*ʇj1eIwz'qb` 8Kl>N4e=e£92l̠m *o@tXqd 2T&e)34AY EL9Ѥ63T+ Qs (cB/َ'Y3"Ĉ 11<^J 9,aXAK]Pݚh0ʽw#2Nu5%K5a~- Qxzy>sH)  ~*KYvy4*@TyQOlaB&*b͵OC rS,t~ޱƇL[UZ? K[7嬰xbXҫŐ'&"f@xIspWA耠e?7C$s()@$s0*.\^Gd04%jM nY4p1.V^ Q.HzPR:IsЃ/$q.xε@߹*~ڹ$Ʈul[r2]Ţo]:븗svb}*n+;+)@E tHDԉG7Z)v>>ƕvm ЖԐXR_gKY3[W+)@$ DzY5ujW|bN_{E2{*avYxͻQs(>BLneL#w m4_(,`f4ڟ yGls~J4kf87<TNXuQ6F9?m\ WQzԧ6\Zٻ]BǺ9q/+ endstream endobj 1835 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F8 41 0 R /F12 59 0 R /F17 145 0 R >> endobj 1833 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1835 0 R >> endobj 1838 0 obj << /Filter[/FlateDecode] /Length 2148 >> stream xZKo80yH|I`v^^'b˶жdHr2_U,R'dU,Ep[ǯPY*+XDžI-y/"e^m;tqPrR(Nm^/WB Jkp:uU,7pXp ,1;,W`*OB>|׀u==\~Qb=EXXpƕYnArM6_ۊGx$g;8?l쌧%Bߪ҉[&;i(3,|%Q8 !o8',vwph%(,S) 6 SeйPBrn=`@9c:tM`iF[Ln<78Q6*Ylnjù\0jl[6ǽy6ĜTG@%Jx&꟨L1m?ќ}$b~bCꘜ/Ljf_P#KIEW?" ۍ&Ž "`"@S>9䡖YWdz֮T2"/A3d(.}]bvYhHwT bWqnXPAHZ4i+i%JwNݤ࠘MW71Z Td^Z^4zEHmg;#Ȱ)7 6Yn<#mE^$VOZr<{%+:]ddM/q,~DUy-Q jE.Bw!S"8.1FLHT;Jwf)nJJ9U ){Vavq=&$1Z^b6=[,4kv z/N)/d25AD#M.T;4UJ',yْMj`Z|$X9otbׇ-T$ qza4u ZM-hiMmDQңDzZb7#H(2V AeTM!ط&aV'`PU\o0U# F & ͮ1Y>&^Hb=…%{?KK% \BS,y9|p(kݭq\ț|/"K9~*1!HmᙚY5Pƨ`Csu<,{ endstream endobj 1839 0 obj << /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F8 41 0 R /F13 66 0 R >> endobj 1837 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1839 0 R >> endobj 1842 0 obj << /Filter[/FlateDecode] /Length 2693 >> stream xڥnX5$+>dЃi99Tb".^||VTQ g"_=zRZyVw+z|GzXŞjF VUCZ}[2ū []}Sw};}Ym:J9>vyt(M Kݛvi6OHSQ򈲎"U*[gm*_/ЙC-ވ&ʋg2I$~>C73[?&,z%3Q3R}|,߯ԏ%yZy>79+үboMJWkke&(f*w_/xm*ӾJBޤzlpg]& b$dks/V16ؕ0ˎqjSlyawҜ-ǡb6{Zs_0tzb=Q$o)] =uREg0]0CNUn UqMM]SϢugI8Q?P}-ĘϢB8x¬m0FcB;B+4]cDhR^}´}N0Lc0>N`#g&OY*4%z<4 @yY$a',!xq混$<8cT5Q״q4y-_FQ ϕ?Pe_( eUJK]%6ZJ'b<*vdZÀ뷳4T0S:]"+揠nl}%=ߚV\R,~:k&[tJqFPdjmtLkGz@yry}3NXSpN8*%6E|tc]ˤ ^׈'2IR~+kX~=l&@0iO$ !wm(/orbyg̪m`rt ԌsC"tmeȠn8 Qʋx0$B5ڹ qkz^ b M_d1L0ϏcSyl=y>|.nT LD Qb;SC'0C9ya0e TdəvjJD/df@2h b7(-{[H9>1/GaҀlAXRCyoQ*{*pūRuACx{K\##‡xfj䧂InhÞ:30r蜈 iY?%o󢧾 @@N"?0 &KBs>^<V6t#4Dlr0j#} ) Eyb뽊7.@k?*0JK&_?:m%a5 u~[3ۏl`XK7v{Ԅzl9[EV~8PJL6JCdmf=IOϥ,;|V%t.u}yX%{]'ZH0NIt2@d+5>)W@Ѕ&{THxMQ$Fρ?!S\0F9c_&2"_~_ŋo.ná{ܕKEx ]K.%-#+F L"+b,G%F(\_sOtAT!x&F 2ud(dK'+A](,r?RjV"&ޗӷBӏuD@wFdaA%HrV=!ǗB5ֆ&*G #^A[}g5|XKx cHoC(DsxAiK Xm {Sqgru7y\{ &uVA5w)5cK<c jZy^_7vXuSm籕nEN]τ>GuWQ~4 uN /Lr"ix\1ʛַO ?]j endstream endobj 1843 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F8 41 0 R /F13 66 0 R /F23 221 0 R >> endobj 1841 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1843 0 R >> endobj 1846 0 obj << /Filter[/FlateDecode] /Length 2107 >> stream xY[4~W)ښ.Eb.`V1)ϩ~4@[fld?WF2M0y!?gc QH:pd>hP\pC0 N2=FA%>Gad<=4 E^Ұn&, (=eo @DWsSnݦ\tF$^ pߕz0Vo2s|%eQTRNNai Ϊ;Uzr5)V<-#`_G3RE$= /zI89W+cȖfKb c1C0J)ơXb `hۇHp@oyH\au/wa?${9U)r{^1>(GPN:ځ<{0S=2sDeE8é-/BG ,n+kc7_{i⳦70 /%>cr rwOs\28'?!ޞp}%$w*ѵw}]W1zn7e^:N;l^k8$Ỻvifʽ5"ы\e^ӒxPVw! \ɰR(iKăKHGpN,F}Ley,}q!5=LJpf\/%?9H~B%l|ŹOHahd4&V魉kӈ j/G~ɼw#r2?7'go%5ͼ&LhIDpX?anV[Dl(30,2okW.VvRCg> endobj 1845 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1847 0 R >> endobj 1850 0 obj << /Filter[/FlateDecode] /Length 2671 >> stream xˎp9sŗ3Mv\=d-RCS"%a=Id,֛UV! ae>Wϫ(d\mfXVJ*뗫W}'RF}?3ǺjH~Ca=Z +g[׿>;cZBpjcf;fz/kyI1Ik]VTyέ];*KӒAWӷɷye.Z>; 5 p'A]gM6TnY1?Q5~Y'ɐ$kɴ3³ʡ,_hvn, Fd-P%}l =wLϞ?P H(`al0ݿ5hlMhX OCcb2Ӌ1-Qc ;n`|KR3T]f/~UG({L|QL v`$PZ5[oRQqdh<.S0-@eS k]y 6j?ʹSm;oNLBTcRab>LφVuMٞ[ә/<}a}8ηeIGRf&GxLG13b,ꋱ2+F걅϶Xzu4}h kv¾.K:H}eڝm`oKp_Xut/|g}K\ 6߽< & \3giT*fniu _gTzt,)zH!R!ė!m%^=Gb1FbqĘ{w[z+SP_[HtEGU"cĨOB%u 뾤سև/I`r,Kx^ i/Bc eҒ+BM6n(3ԧd i){GzcRڸ e!bDLzztKPt.7zd7@c2 aGuK$-Ýqjå1 ~}yNNZ\2hέ\>K $4z%B#;dTOo">^sʕ)/C‹1dŎ?ݴX.|]B.q{s8]ZÐiei\C pU.歂%'ͻiuI\d$Z Y\*|!r-PKEX*:S^sc^A?RǫU ١w3R6Qx(#1`=j/5dn>/*BH PExNT*Rt]0 :qtx}۹|;ʣ5:8>1nexay^"zcLE2Nefvry:֎y̝xs?󏮃":?byyʼZTivF7Z(0=P4_Uj|v> endobj 1849 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1851 0 R >> endobj 1854 0 obj << /Filter[/FlateDecode] /Length 2490 >> stream xZ[o~P\ j¹ܬH/ 5qJE,- "}ϙ 5$GwHrfmsbdz9{d|6lv?\s52m9%,]r,*ߴr9(ǫ\ΕRMU.׿"IW|MhY滪eK=,ݭ8M}c1GYFSf^/kV%$fs% Cq$وA8׶Xf&hٵn. 7L&U՘E|}WY[C/ 0uFq[#Cٮ4sQ4ǜRI,> n[KC[U[:ʵڡ*U0 P+4DZ>cb>v3:YE3&`Fž55Fxә`K ᠔4#mIUJr֖3YXLSߚ Ϣh {cѶ?!+\r0um5-OIF}]2Q^KvؘM*ʷHM$/~]d[|7 wMF̛෋)pۢq*Y;`[ƹctk*FQzq.G Rel^3޽ DUXM߽OIwde)Z 7#8g먒sxE d-)%.q$HBhpFlj#}2ѕQz Fb!Ħ4Nݺ4@+{6rc$Y &wd󏺝,z(ʴHu/|Uw+'>|7M6;;~ QkWۢ zGhkL@ iޅ^!Kk ЋT6@irAwyR-{ ] +) ?5#s/s'+S1 9Z@jˇ\ykQ3VBM<Մ"]7vyl0]#zӥ\;`M(\$X9RX<<=ͫ7B&#q TW㔁y,f][<8szoG O0;h{nZJlQ3S#?wpѣk`*113ˇ}3KoF^;l2]29PTS|]#d\B9IZ豓xؗC}K4*A 9B@ yS]%tJA&!2L>Gpy8:qLSئl];Ŕ]7GN G.Hl: F/p SC68P|UU=gQqi_Oqyu:>t96B?#8J$OdR&7`g}X>C>Y Ž^P{6+W cuKS> endobj 1853 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1855 0 R >> endobj 1858 0 obj << /Filter[/FlateDecode] /Length 2598 >> stream x]ܶbh[FL4i:}d]ݭjjgf $E}|'Q V1<~^]VIr(b:TL ^ͷ?x%y_tr G*"ݚff,B]yBElcy IrgՎ4F>{I8?> Iy.i Pnp`rGb5Yn͈Z ,Tk gl 9Q[~2=c\P2B<ɖ\?CvbPDF퍖ϼwιx{cܺĩcY{CXf?KY[NdRe"QکC:Inh73ཾҩ$'+U{͙NTP0%"]aC|8j ,gH8henE=v9 p+7:Dl(ҡᱜxَ[}.Dؖ=F:x 們(qY<էU7` it*E”V8=B9iw)XTdR|pʉWJ`x3'Hp^`%/!kd)Tb{) 53Dzi4똥xǂe%2L$Js{7&g)c>P>؆hx.!rW w(;<0N9BQ=ݙ+HO}Ӗ#]]WYOsd4#ʔ%zH2gi9SRLg@6w)K? }SwxiJ4d]@Wg =$y1דml9,ƪ弒/ ׽TD1s'7CIG9CncdYⶨj#.V]߀~񺾦;:jj#[-?`3Ɠ11iZbNKV+b"%+]xD&0X#A0k |a[4^2&3Ҝc"-ISg/X"/.. D~V$Sm =cuCu?2X8s7Xƃyv(lvMTWW=FcD*lNJcQmzd\^~ [tT4O1b8Fֱfxׄ\B?4S0]T 3 :@RbK8rPsѲ_zyv ^}ˡg2MPs94Qd(#!s]>=dq}=ӊ|2*JP{J1ݴdm b{>ێqOG>6%cPsNkKs/3 m%jCMcͧ+yuH5Np6HR_ljNI3oGǏ4}G.fy_<򠭁Lo1#lPvUS] +KuS|8hjckY%=Ygֶ8 endstream endobj 1859 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 1857 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1859 0 R >> endobj 1862 0 obj << /Filter[/FlateDecode] /Length 2415 >> stream xYݏ _;:Kg 6K}}gg7}HQ^KDQEqo8'X"0O8GǗ>Wݼz9 KBf?LL$͏2v—p~HO}^.ݿ^0t82Kn Nzp`\*a!a.aVE8X86Nq~#pD.@hمxF.$Bӧ3D8?ʚ㩩w3nްL'X$ >PVy[K@OnTW]eW5g̏[}jLLF:qWے`YP4UO&W*BmcZ ElZ떶p"l%X׀ɍk!C*5EA `ɲ?׫[z_/Jw3"*}ϛ3/"xD0_"Ҏisr*~, u h>xdmjFmO8^#IX}5*x ,,_\3D$ (&A>(d> ns]jk|N{f07K ㇫/bp"r镫~7qCAm!LYgH^oJ OQ Sy赗C{SYnŷMvqEb߼-;֍axȠ3˴/^N/ֺ}0=R[[G GACOkI15"Njܼ?n{Dx0UM|$mR ~SǞp˚04Nw.=4؜$++rj](8=W}#KG% GG >x^v*%P5C5AWYlYzSۜ;91eoko(9C$uЛviXq((<KL}`is%5V1-k06+j箘Z+\M'kb謞% =?Y ⧋q֖g,#B$8'Z~<{_=,bzT!BZ0bV(*}mpɜ \-HO,ݟ!o0,-G},8lT:QWfx7qn!WdUII5 Gısj֚֏76QX_~ۦCu쒖LX$b  5 +19@iDy ԁWq?T됀_L -@ąȢ _ "fFE>mX3/v>(XgF ɉ/.C73h}l/V7UnBH:p-J E* |IMWh)(TÉUe\(0o 8*yc4sV4Ŝ "vYb ZhLgu$l "K`#m >88zO:N^H59nb-I·O"%JzV8T;1n zUE> endobj 1861 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1863 0 R >> endobj 1866 0 obj << /Filter[/FlateDecode] /Length 2831 >> stream xZ_۸/2IHJm$=l>(vW-;!)ꏽKM0 yH93?3Z,W+ rR1jJa߯ޭ|$^,W?a<_]WvRG>v-8U۵Lbd)X}h1)W,TBX% %.2c7\EVKI2rGoMMw}џ|1,V]\F7~߬Lg,W)?I* L@Q9/kP~NramMvl6m[[㞥?7‚SHE(_$0U"PybXn34d&j,ZfJq5%k\2Ti/5c <\+/uT)@)ItǗBR=@i$B<313OqXcȇap|6 u~h"PH tɋiY&s⟃WL"DB9"ØU@u8<Fԑ@_p짫p5Yέ&ow<%Y~=?D^080=j/p]><+wRYLCL ~9I5(fz~ɗ=d9a꜀sɳrnh*fߒsc9''wC#BjLB"L!Tg0fPȃa$PH/wRWPj>a&r~icq[PNo~-4)x_A]E-XX_y WfE]̣fc.UŘF6Fݠ箨wrߏuonƍ4/pGy()+\Uwgy?Xdy.}UWFV{8_6"K9v_ECc[5mջu@v}D5W^c7m1S?SQW2"LIiPwZFo1MKtЖ۲"Q:QQU k;X0acu6_ѣEg'+ajn gOx=ZG/Mʒ^y) QY f=fʱb+;]ȵ-S[- e"[ӬΫ4Xxre#25*FnL$IJ}@>ӃN2h;_#h!J(E2Ava&V.F9B, ꩢRŸ;fUח5쭉Rq0yϥ.;F ٹ;t چ~6zh4B\t"-GKsWTt\_f4~i~o&N& vH}>֚ Gmd0xY %+GBhqܻ&m;&~=2-LPF!7~ׂg;9!uDip#5K }ң-ӾwssܦcMϪʔE6!MQ1T)qY tWv ~?QCh#ȲB2U҇jn,[8:XjjH7,02jۓ/Y> endobj 1865 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1867 0 R >> endobj 1870 0 obj << /Filter[/FlateDecode] /Length 2215 >> stream x]ܶBE@He6&A\ч^a贺[Bΐ;}ٕ|PAD( ;]D$:$b>yLb6߂]|6 2%N ˂,悅?nC_5<j$Iᰫ[߽eQ 8&2 \ q2 RյYe*,.}{ڮFa9< ۲vbnF|G,LT9ٞM'ڡKG┰ao⹗+gJJ8R UR+:9EAI.PLm]smC f%GuM>_k,aO3pWw7'@+ "o. .cK&`-3=$^u\Z0δ.n˺GaJ#_^X*nzB<%~ҌwgѯVpr)ĒRȏnZhPU]Gck_4CSiv䔝`a_"G 䪼ǧ~VeŊ%X A5Hl2^J7VFh|Ϲܖfw>ꦟě6?lƹ3dxsۼFrU+űת>+Qk NHblB1K #`F 8Jя,ym4 I[?U}DZWPsw v0. kנDJ튳Lx`:ŊR 2Q{pn*8<)[Uޛ+!e;)E  P՛5&2ЙR#V߽0MWjC(cQn4 wk\S9lӮq%dO3$F!P(l)֤g\1!ݶAePj~6{5{B讪?(7RoriD?m \CpM:Vz,!5В]!2q_qt|XkZ{0vxlAi.R7QR@n\ʓEf[v]3MBǼMC`vU(omuҤ<"e˰Nl*5{x.p.315.8iѣpRws׎'õ9Eম=,8da_ȭ*/:yQFjz zñ/Y2A骼: *p/_j\*P @̿cwh~"QOI"{(s]Wn+B?'5/ y^3^Wi l<3CiSE/CNtÑ* <'JO2ܧOC!'~QI'FӀhn^ &bC2玏i]$=ՓGeue6yKs[.u@^_:ͫ!1H|/Yz"0D'ht.]=E߫eeqqNR1kw2=LӇf;vyG޻RŚ촷g36 +r3M`h.FMkݼHmRxᒲ1> .u規JM8:DiVU_T@qBd2XPMlGf$Wz Gx9Ćz^AN& Iĸ_Q!nrQ[4n5u-D bVM ѺB̓evޜ"E-l% ?7]d1Kak N?Kq{).3;PA}cV91R> endobj 1869 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1871 0 R >> endobj 1874 0 obj << /Filter[/FlateDecode] /Length 2377 >> stream xڵZKWh+SULd}pboy\2U9x][\"H$>h HyPl~RǙ=ϲrPb)nwYlv9?'j?y/Γ /E8iR6\!9K~l7?;Ǵ$=rV )rPKnVsSaa9 JϼbE9[pJm>ZcT7}Dq|?YNͶ_v{1As^H0Be?7xWAnOZOu{EƚXW%̸bBi='#PL GZR(ʘԌ$|l;PGzeoGnS2?ݾ=̘}wk9)i=urDڙ= J}^B & ٮ>54Kg 6I4c}&K 1=^ñP_L .cwmw8x9˴;S%ē!&@?Ա} 64Ee\LO%!="|U,w>IxF {ee6֣Zi_bJa!J<Ý׼p^4+ʌ_\ƟN}6޹Ɠ0djӂ8T2rdL^ 'x $V,渂җKSi$ĎHHLSw!MyTAy.Gؓj&~*K,xeVe?+P} _KYn.Xg,| aj!c-dl1Q,vU=J\JA±BQkn% Z9j9ÉVީQ9fK tZEQud.Uk)Os)b_ C BRTNA H[쫦z;z{0Ouw}d @ :@u,mu]L_SwvZeU'0XYFeದv+2:+2? E? ]";oO@DB;)^FqۦatCbUFjHݦ=VtsWh 6/1;di ݜ snf$CBX4gCU4*ipR,mpUԂ jaNh$31`OIr ?Q'T{&jWIG"ԓK SzOUr&s Df+ﺇ/1i:>ǁ2njްOǨ:`[PcXbN;u*-2]q41vH iJ9 (K-L8%t:A L -zL/nMǛт0 F6 b O RŧZ]N{B2Ś_L)]@VQ $FĶ0ƣ*^8~Ѻ%w:Zg c%MXz8S 'Pߚ 6_ G8d hrJZScGWin mirH!]ǷiOD䎰 Eb2նN&>PJGUMyKX:^zqˣJ MYG`%uFom#߼(ӊ] |ߝHvaV/b!>>&\F^a@};S?CB(1Еi ?W\Af}^Yʀ@tOl8X͵EAMx=O6itRDcby QxlHFrUFիؐ70 5Kjac}O875.xHj Ӎi 5e) |V9쐕:Vk5w;[KAwqyiL7η"~lm#`[~J v󙁓3IՅaxUbS(بY+vDj"_NI\r&>0>c7!82}s_MmԶV#Cjn>;>xYAiOmGH9xx?&{ endstream endobj 1875 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F11 56 0 R /F20 182 0 R /F22 218 0 R /F30 286 0 R /F13 66 0 R /F8 41 0 R >> endobj 1873 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1875 0 R >> endobj 1878 0 obj << /Filter[/FlateDecode] /Length 2095 >> stream xYݏ_a'kF")JI/h>u<4EpdH hKCn7p8^=l#4JŬLYWIտW}*XVqzx?PrR<~!DlevQJE=C6%K~o`ڦ>SӚg^<ɢVw[7 CINAa.3$4\h,߃AJpNA$H rBR}\oR.Qo)O'jBFj 8$YQe04`M㊑:2r^b$y '/y efB%,GސD4 RvepA< N׽Bnj' 7b?I,ÂTrCcv (ˑ7(p<"_B`>G yI9k$`< HI+_L| fY 3"񒸄P"Z/ /J (3j$xj \&aqby771[ n0Qs>:`x3(_ WDx5P GMWG 53i#_)`}RmJ9 hC%;}So+O$/J"hLg2!1_9MH\⺂PΞMCWb*^[>#/K(aHӏe);UG̿K+}Q |O% Yow¸*H\yQ04T"j_TҒ-]7v, aYe+?W~hS\ e~ڮh@6zY/\k{*\g:C^\V|s.{֋HS4[4v oXF6I ZNɫ9p5o2r;]SoL%okD{ H!HFfa4thO`#qC'H!st Ok; ص"0}OQ|8bC|m64a)$ƝN2Vm{v[5ڄy+W7aN:/lrDw[MA{ҡmw4cqoP8ЀЦk#x1o+"'Uw4FHZ4/ŭ WNoEO> endobj 1877 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1879 0 R >> endobj 1882 0 obj << /Filter[/FlateDecode] /Length 2402 >> stream xڭY_oX/ȉ)\6iWC ! YKi%W=;!)$Oo&eixF6T,FfIͿ6'nJVmLwq8Ǧ%ʷ>amp;ŷUdp[8hv\ 16iE:'!r] -b`tBZ3xYh8D}z%p#r;C#xx-204oI@PlΉvpum58w4!r{VcDf$%\kp5P( y0oƇ; X hbE'#Ø'4YG„s =ɧ=q~Z_38n&͝fr䞨`HI. q$sMjɬ>,!@@ic?2™KBq9u|H1D5 R[>l5 :I8 @W}Q>Hlp!|{ww/w͠cZ/GF\yƺjkye;b]8@ PEkF:i>U4wt"<y4sHvyjLYyK[>{H[7m2ɤ$TƗ&l5I 4#R,hߘ ۛ3a)a<.oC:0<"@\oiJ(Ŵvsƴ q_w,Sr<#Rd%ļ`Y&?T:Dy (m ޜG_tΔ7HeEp+7OMҡNdyh4Ъ¯Aߨm%; 9U悉,q lT_2-hLG ee4 K6KqwtսQA6MK?X`vX |9#B4R9] Anm!Oa`grbr$JEkһ>8v[4a%Ng:F0%\k&<{:W``H#Ī l XږXC+aJ<ӛ8ӥ 1:eҢN|Xe?I}K,XUp. > ^ aާiۄ:ctʰ+-g,H#˴mkI^,d72OyYɴ;ڈ'߮--&gIZÁ[H8P@'*1C,N(&|"قij4Y$jP(N6/qiM}yzvv@<͉?_QTg{j`\]!9)÷~D, L9u > m4uoNd Xp0{8ɱ݆1':ӑ-]2_mUb9 B3>˝=|"L%?^Ԙ- 4^`\/"9@uDd^ZBpr˦aLo$?bYCD,2n> endobj 1881 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1883 0 R >> endobj 1886 0 obj << /Length 52 /Filter/FlateDecode /Name/Im47 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1887 0 R >> >> >> stream x33T0A(U`Q#kg憦 Af .\@S W endstream endobj 1887 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 243 /ColorSpace/DeviceRGB /Width 105 /Height 24 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 105 /Colors 3 >> >> stream x] cfxbMfI`3t(Ve]ι<شWaW6RxaW_cvM)3$6B{eRi w0q ); ,̰)Mvni TA$;ܫ ;0I]윳[q) ͩwB˵٫ng7` Kҁn堧̮1bZ{vEp`K빩`gnNDV endstream endobj 1888 0 obj << /Filter[/FlateDecode] /Length 2572 >> stream xڽZ_s6OPi Nۙ6trgt8HH}$DҴ;b `X߮X- n"E',"1.~]|]XWtDz[ Z"l}[2qp RO\mYW,b\&CahZÉqvwX9o4,d7&h5Ҏ- #߯@1 8]Ӕdrߤ (S*Xla"Gl=vWq'#C aA|G8ë]ix8f\GKbZow(atSm;#&KKƳ!hy->k:^/^(jtn))T2*R"=#ZYAݳrO8e2>6ېY/VHO;dzl~"XEwN3wRsrjLt[.I?/|)W s]vmCHۣ roA7#Ia7?X sٹqtpYgsLvIYGpNQݾ%SSoFIӅ\$'uZ6gb7iNu߇$dgr0 g7ש\DF\Cqt@͸MhL1tcɂC4'TGM0%ȝb2,4*zȴ ~!^}#7 <~=1ߞZn )" {pGg DYCY=ѹO}Aȵ;eӞ:a/{?=L [o&H¾⠭Z ljmf4aUoCCenƍunUQ؅5-U?n1›mhšӽGQ CjK-| q7:pm.,MI|rF#Ŷ]hW0n}j>@Җ"ICAs M'˅x( B*zA8u]PEБ{L'$bJuDHēA~*Qwž9DyCXՇ] _D_ͶTp 5jte%xJ%jƢdFX@"/g7R,/ [Li13ظsԯ''!(@`c*b2Ŷ#t5H)Q3`Ňz]V67SxBXyD T$NұIn|^p/˰n󹋟է>V7zЭ1A3 ߏo5#zȲ,hVa,1>**nmנ˄LlpwKz}k3 Hr7Px]{_p)E.rv&kVvKimsH] 0t@rUap0K|q~0aqPG姑Z2urOҞuW)`^q 6?*zCT|*Xm?aⰊ9"N5!T; piី4n-:xZnB4u" "S;͑N8} m}WZ+)[ܘ6aED}3>z֑a.3 heK2lQҾ <ے,WHaJ3zi7uc_)!#Xן d !VvT6CdLSשak ]\Y:h K7C ы:qTxoKjk8?zd3\OiH/i?s "63e~ CEe t:wϒYl zyvVgmi_Խ EsZ4#$/og#G=ۀ@Q̓:Oz=#흧xSH<ǥfN)UL~H(ţ(-sʹ2{vh}ߐ|<R'TZj}柑O)sM}Ό` U ;,A3qD=ɐ%@ܡ4Oȏk&54o[ "gK,u`iS"SۗvZy| .5,'p-"!"3ptUGA[᪸[ЙcKN"7{zR S뾝s$Da%Ntp$š6AE ד́H:/ yοTu}hڗC CbJkY?=,cg辭1Ih,nؔl%x&a_&r/H_D$=6l"= MQ3!v;SX>Gk9i#%]OI{kw;r8 Y{w=!O[$6xpmO;7Ǭ`VfNX2#8i endstream endobj 1889 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F10 53 0 R /F8 41 0 R /F13 66 0 R /F5 32 0 R >> endobj 1890 0 obj << /Im47 1886 0 R >> endobj 1885 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1889 0 R /XObject 1890 0 R >> endobj 1893 0 obj << /Length 52 /Filter/FlateDecode /Name/Im48 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1894 0 R >> >> >> stream x33T0A(U`Q 0kgF Af .\@ Y endstream endobj 1894 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 654 /ColorSpace/DeviceRGB /Width 150 /Height 80 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 150 /Colors 3 >> >> stream xᑃ FI_),(% 7{\P2*D Bx* Bx*(|U2!z!T* 5u˲BG Jh|T HV *^a*}>cҔT`Iye' 4pK7^19,jM:*zڈP?Jgx]t&Qn([qSa]5)Hto['j 5#uyfIeS*u\P./) £[lxuÚ+N{WqCP! endstream endobj 1895 0 obj << /Filter[/FlateDecode] /Length 2132 >> stream xZ[s6~_tւ lmɸ/-d( 8K*IQ?~p iJ͋)|犃޽/Hz󈓀yk/ #1._oKI{7wxJXڮ]Q!~6"qg몓]rOWD:#{^9gs3b"BC=ZϥZcm yd߲*̈7x}C#,ҘnVoe^WK972b7Ry6&4So `$涍Fq;X/d?ViD83[-OR Z7cﲢ .>a_+H)0t9KZ|ƞUf6VfFս9R<5RK΃ )۩?ڪMWݪ0=y֚Y#5OPP?űXJU]͒y]nfKzB+х/ZU+NƔpJ" Cъ~P "4LCpJQ.JI":5)#ab8c Iǝ )Yk!;:Q4['!R#MD5o\xi]&$cP`Ck$9&H`}@sj;ǸԾv7oآXg'|\#f܋`T8kpl*1?c${=S0 | 2JqMe&<2Fo "'Do/kzȋ1ӳ/p`eSRc׷Sij?C>[X>X{$eEPtÝT@U|ܔY.pxU( Ø,[9NWYyjKcM2/twwz+=aO7!uE+G|\hԎ=k8N{^‰V_ʼnV/DNb..O+uBo2LȖM~1i!Ō{G|Hsϸ"6ǿI|39Gok4ZfM|\/{PR DQri@Hz]+*$f.|JiŒ"ą+<"\CGBm}g,4Eng+t4$ {2F0S|;) (b͋VPAD=WZʼ ~}<ĥL.8ENBIk؎2j=ai L.P{nRoB6P $;PÀ49\|om^fcN$*ܳ9@xi|I2s ʋNN FZG)f+^n<@M뱓3,c_n0:RL)%وب'k" x#4P4m} $$sʾ$TbK6ɆE__d&l;n2c2l /{*o c5;[p=M۵9_v/!:{`H=㡘T+/Eϲ>#-`HYWf 0-;l+^΂vf+}ӀDĆ,!ucrAM4R͌3[#VOxUF UїŢ% At}Rl7FuVt^ԨK,V/q%֟mZ3Z endstream endobj 1896 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R >> endobj 1897 0 obj << /Im48 1893 0 R >> endobj 1892 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1896 0 R /XObject 1897 0 R >> endobj 1900 0 obj << /Filter[/FlateDecode] /Length 2560 >> stream xڵZKoFPf/u<d`b xc2%KHm ?~"x7'RdUV1~/?%Y=ҘrQ ibJo~V+?Ι(V?4_o~8ǡ:7RʈKfI4xlm9]ۯuUTT}"񷈞rݛki.ۮ]4ʺN J Ͳ~ &sxWjb\Y.zwZ-?nz ǁu2306DiFO 댑,SLYDH[ KF2*^,*|8eVT=6]_U4/ǀe2#Q r,@wz;DC -X,Q/bmK<'#l.$1ș^w1IeT2!F(lzM$T+Yq ',DSFPQw/J e%!%2ay ^PRB' ҐΕ<|FIyLLq>Q@H@\8H.dki0? صI< TY TQi4ZSCSԷh+,6fy81"!f*MuWc 77]Bg e汮. "{s{Fp3^P> XKqowp|w@B ~0ow;+\ٯmjn>vs}p;!˵ͳ '{/6x@r&]!P\ lEnc0tEc E04zC؛ S,#[ WѠ}=Nh۠Xi ~vZL%pN3k\xߟVh Bu~y*Leli.ǺVjԴSP\'@o c~K1-eQSù1#M&TCvp]0N#嫥'ـRΣEkfFq  exw5IiXmpKտ sCP &JMd;2h7)%|20xP$)JGN#a5JW܆!-7L(oƟ!0O|hBs e))v e]_HG"tЗ<>OM4D.P,gs[$| 0JN":&H5T:N1;9?R"5) ۸3HYY99JY RU2cYʉR-'dcLƍ{B+i]G[3Hc ݱ ⍮8(q$L:l)J2V[Du[jCmnC v]#$fqp"TIf܂TWrUo&^k/!/ 1eY$b5$`Tg${z#5dϻ_y&W깚}Y.tn~Y2f$c{&1 M,4yK+..aJN f &'!l9 '!'BSta.C_)7DCԪ74oĊ&v:gO I0wP3yIsǔZNY0g\mЃArV(K).ll)6u žv@Y6A~sݕ5p8aӮ NF~V$jp"0i!_DB2!PړHT7^`z4>` $4:Q p\iTl(>y!ۄmzNr2%tC$<ڝ'K33a^a驴"ٛi G\]ynxVj/;7P'}ٞS Cg>Sd_|] Xf1R}݆Ǫ0=)D4i|Gp`7,n+,̓3T{p09Oʦϛhs;wQO5=͔S˸2uoy3 To~gt]BYE8qco\F;{RFdQE?he۪r\;d8ܢ1^7Ͽ\ޤ> endobj 1899 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1901 0 R >> endobj 1904 0 obj << /Length 52 /Filter/FlateDecode /Name/Im49 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1905 0 R >> >> >> stream x33T0A(U`Q30kgƆF Af .\@z Y endstream endobj 1905 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 472 /ColorSpace/DeviceRGB /Width 150 /Height 60 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 150 /Colors 3 >> >> stream xQn0 ˭9[*"MV3 Є.2 A<(PʃByP( A<(PʃByP( Yut A> stream xڭZMsܼ WeyIQ~4tN=!N3ʮlk"K[֮ $Eirvir[x1SVd)>d2mՋ7p y, c:(Dzv'ҞR,CI eE$I@P 8dIژS@ >Vb{إe$Rx5뜕xBtwdعD†C՝"5EG֊sAD*gq:g㡭b; b{"0X= Hk,* Nwin3AP/G2jXwL!+<}_F"Gjr<#}#p!Vt7ِ)/^/U#DJ[_ǂ8"8ݸ-٧\s/ 0Fˡq2&yi,H`kܴV^T#= !SfOon,m$㈁P~/7eլD#ok : x:3 x:57G, kvwvZ(=8p%4_˻.tŦ2|Og,=}A9䆐?O?ِpСBq'$, R)N51s;TNm5ODԚ3 [p\u^(cOOnJ^*( ހTkǦYEc /̼32i2zܞܒH$z<㑆uڗSvu.2 g»XvV _=5,Q^^zʳ税X1vLDqcIˠT@XR#RqʅLX?>Mqwz!Cpj؇?-o˲om:H2L>y>+}7 e_>e/[2.\/*4_+\´2X\b3=!‹̺'JgS6t3k.+sm\](12 ϔVOI@]7E8<F/ 3eqHc=+WDԻ\a8Xty!_^x lZ'Ad#1v*,[ zrLA9"PaՍ "nq[}UDb?/M= F"IÉ LL]]rhi6؂05i77aP_IÒcm5 ~0󾺆UV\دDtoiBsllkT T!K",Ue5BP>$X`)lEJL۝)E['ZF0 >>p`E(J ߣbxKl$qGVTa qx2[npR` ;S O~A.azo!Gji)fD+ g=E3~?ŚVHӧ%ӕ/ϏzHTBZig߷.@bδu aReԦ[8Zc]A9B&8PE4ݾaӬ-"~?) endstream endobj 1907 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R /F8 41 0 R >> endobj 1908 0 obj << /Im49 1904 0 R >> endobj 1903 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1907 0 R /XObject 1908 0 R >> endobj 1911 0 obj << /Filter[/FlateDecode] /Length 2684 >> stream xZYs~ϯT^L%^뭚l[<hH|t $ 뚴_!)n\@

gq|rbh ;*g|r ߍ%V]"o˝q?Ҧ߮QS5HL ͏T?G.Vz_b E| 9+z.WfeVv?)`lf T e(0Ca}e'ѥVEqMx#KXvء~ZN;QLS4R ּ\KAez1GTK~54ZZltgGt>n>Eؙ[\΅֤jQNVcԀbg{>Yoj/H =܁FpAzvKnmQ:9:#5C+ 5yS_-0Jp%؁\- b`)M;% oR"YCZc͹&Z^;#cC3ipj1<61X;d;%(5Hi`#BH4(–Ђ!mku:,Rs2H^ m)ʺZDYDSp"xF P,5\&I˵?-ѻmܼn֣1aूsZ;n!X=p6f'L`ʡu>'{'l99T)[r֠u8ZDcOq|Mxrt9n3֯c|RaZet?{I Tҽ$" / \>'j85S^HCNqKw×ٍ_RwzڎaPBXSRDztr`es>f RL n`O'~ci7YÚ 2- ':Hz0GȄSGT:X샄F)?]vQ[V -S)ҏvIe]X[kR4˷[pB3'eƪQetqRf!e% 8MAW.S LMJCo2j!KkVYLö}fRGr]b]5?OXnu=@CH!;pTQwÉ^!~_p6s ^QR86 Yʖd*`B0 MP`Lw&-Fk8ǥS y3OUzۚc ҰզcJ-h'9S=9Rxl.v:}j19ĄYqgS}8_I0Ts@k1%/(pxMԆy;wcޛ(`.Nkj!ȉ~ڰ2w~`q0~@7Sɢ&bż_.xW(F:dDbҁ0 ^lv}/8?ԃ2|!LOP8 ?^*bEz؉Ǖm 0pY%C)`P#C~{EӞ8W _O$ď}I\~;@0e)3ri|" OpF)W08DYJp@DO&.?~2Jl߭e8D &2a7+`(EQ~lnjiYWk$}@b(K#G+I{p9> endobj 1910 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1912 0 R >> endobj 1915 0 obj << /Filter[/FlateDecode] /Length 2670 >> stream xZݓ _1yaΞ6 ݐ\Ur%Tel e&xwSG#gTfPKj'UHpu?'^ݮDHRD1 jxD"6\W~|󖆫buy=?~joy_7 e$x׮02_4*E_h\,/s$W%5_%v3IdڰPUEoqMH-;{7$Ɉ&Y< 7ųX&ZCEX| ֊Z%DF SbX\*@z<% ,^W^=QJB%ң\NT],kY5{C}2_{6n~',>Kh^?۸t#zZ> ޜ w.^v鶜4K^ywv=.H6Єcxt\9 _Xעţ03rUv*_ԃN_Q"lhRD/ RlS`e068ư)Hˀ "'-~]2 >,D9I]gUW,$U,%P#IBjUE]mS90?y7uQr%땟T>)w2j41G|}㗍YbFuƒA\&#ڊ\zϕ] :CU0/$[h*ßf|{}\TbsBEhkV#m ، =LY\]Q]Yho~k;.<E,< \$K9h4A 7 uw@:/Z RmPpܸS] { (Gyn](+#.j잀SL!&"z }AjMm.)f7xWeXWpEΑFr<`\_vwWIAq%06AAeǭL M/F N0X{N$4Va*\١g쒐p\ZvyU]J2řݑBLWii+Rp%NX[;L #aQ%}$@'QkQфp,!Dht„G #>i".[e7wוRP"Ts"$uё0*Cv F_tWOVUnat1}r麩.jvQsL,at#UmUչCO ׾wzrܩoA|ݙrkoIv((NΉ0DNds'j߉N;׉Phc1iKt>;ӪzggN5ym8t oT58Og_=!?~) u1ʧYwbNߧBQ$yTV=<9 :!I BgT00VsGj-F(clC$!E9U^י$ÚualXF#auʓEetOAv)R]S54ΖϣaHGֆCMF##*uQk GёЧu$v𢲾n /~=oq4}@𔩆 .5жAujg aɄSl֌ZNe?,#b-x?HN<9b5/ 'z"c8Jd0}:NzC%u!Eي7 eֈv%?@GOp,=cs {.<u^Ƹ m[XҾC;,bydCQnhK?`F_LE5 -;WU_XHGS hIJ@CE.y]]W\'L4Բ7ahL]|]1N02[jKʊOOJ#FYbOHꫬ2=1m3Dq&敱+wP'(M=%Y jeHYo699xnHyҧ 'Z 4Uȴx:hF /EUvAŻz.CR,>3&~M_8 ZrikĜ>%#Վ>/Z$,IRZV,zHzÃgǠM…C Z g{[{ޖGUvBCX,/vEQ&VhOa, H19?Ш\C~QdLCБdiP&taոOmEag; АhS-'3om;Tt;)$uZ`6d`bRN$_' Rs_DM9a大C̉҇ GIW2^41 5ZcGECzH,%\1RϳJ!U=?o[yUӃ֣,v2hq jĺqu~C5TârVzxE"l*jp?G&h/oI endstream endobj 1916 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R >> endobj 1914 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1916 0 R >> endobj 1919 0 obj << /Length 51 /Filter/FlateDecode /Name/Im50 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1920 0 R >> >> >> stream x33T0A(U`Q(C@ ̍AJ\_  endstream endobj 1920 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 852 /ColorSpace/DeviceRGB /Width 110 /Height 110 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 110 /Colors 3 >> >> stream x0 E}QQQ&J7_Kt cgz-TJRIC*iH% !4TҐJRIC*iH% _YnR9TҐJRIC*iH% * (U"b;J*i*?}^VzċG6AS4oNJ"Ae-q\.QJ3B:_\T/Vi(smNWYT{/AFOpSGލ_DTJ$كTBWp[P}GL܃8> XLU\,#Z6ehZvU4RIC*iH% !4TҐJRI?q[MׯSZ]d+"Y8d/T"ª*߉G3 }{ÉT6Lwﱧ9|Q+)xIT%ko?P2p7C|J7Z%S"fU*r/b3,ro˞"w vʐSf)HufT)pNC*iH% !4ԯ+E*i\PebgBCeh+jo^I8a+eynۄrD*gViEU,xJf#0[ Qoffo0p[W^&OFݯi沇,*C#Z{U";naRS V+~޽ ƯN ṰWHF%8J'qy(vsjLׅJ#Yso;[>"HH\pwRIC*iH%RG*iH% !4TҐJRIC*iH% !4T# endstream endobj 1921 0 obj << /Length 51 /Filter/FlateDecode /Name/Im51 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1922 0 R >> >> >> stream x33T0A(U`Q(C@ ̍AJ\_  endstream endobj 1922 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 881 /ColorSpace/DeviceRGB /Width 110 /Height 110 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 110 /Colors 3 >> >> stream xq0 D\ r .%dFX+1}E H:(R_# _RB*iH% !4TҐJRIC*iH% !4Ư:wrlH,RIC*iH% !4vÛ (U^"b˾?MT%4/>Or{E#p{[TҠ *k4"Ae-1iJMK%x3yP.p7x/* e*S 2{Go4$*mR*=H%7۽U&>B;{7+IsJӦ3eDצ MiWF*iH% !4TǕUT"4y; 8͗[9ǩ^g$xRidCLt'TfUu~yל+b+2:~(PӄRl/ 4g_cŀ1;,NzU JdA5T4R+C9 |J`T.O"b*~F^ =(gO3fH 95_TY7_iҤJYtRIC*iH%R>)F_gАJRIC*iP Rԯ`hy*7U CPJ_t>Q;YZ";_E@OP lRwk4!tiT=7`; $l,򌨴5"\P\L~̢ά2tG|Jd-L*c*jϻAbuvnfnBFTw:?9EnTp\Yo ˬw~4']TҐJRIT)JRIC*iH% !4TҐJRIC*iH% !4w endstream endobj 1923 0 obj << /Length 51 /Filter/FlateDecode /Name/Im52 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1924 0 R >> >> >> stream x33T0A(U`Q(C@ ̍AJ\_  endstream endobj 1924 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 874 /ColorSpace/DeviceRGB /Width 110 /Height 110 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 110 /Colors 3 >> >> stream x푣0Dټr!8*|Z}H b Hhy^`#,TҐJRIC*iH% !4TҐJRIC*iS~ggrVnT"4F~O|mE*iLPx<"WY;kY^I%M凵˪\oV'#p[TҠx *kTϊ,Ae)1iJMK%xꘀ  A5[̵9]ev6AFop#_DTJ$FpcZ P{xwq08cE2NT)surFގOKSeSZF*iH% !4\ RIC*iH% *GCkzxz+:`uOK 1эTRʛիt_߉D%T[TEP7UBCh*i!=w@uQ0f٥Zn*ݧ!RiCAU"PvUJV+X7xx^ʩ#fU'[LgLۭyzd(K1_~_=՘a`eͩ2t`O!+2RhTҐJRIC*iH% !4U2ՑӨ\-sLr+_|EUJՅYZv Ih}Xo/WUsJ_$mnxRjUFf@2oG[CeV*>H%_`_*G2XWe[#=*dArBw"iW'9 Ȭ5ne:Qtmgb4kGU .B*iH% H% !4TҐJRIC*iH% !4TҐJo endstream endobj 1925 0 obj << /Filter[/FlateDecode] /Length 1927 >> stream xڽ]4_ař9=-p`R+S16<`InU3XN 6ބ AsRr$31I|&!}R?^_LŸ7 ӔoV Ae0W>sJ3ևc̋<3kDz۶5.3tE,|R0A\TG'햾c̞A ~AaÙu iSD1|q"(?q:9\ $O2ߢ69fD,H X"փz7`ܩz$L5 |qr8((X<&8J!;\/hv}<1s+AY[+RhhtwFTokewz+nbO@hJ]%?r!SIoMQV4j}ݨ ML]@h K42Hv( ʱׇ($\grUzkM`,Cx(BM6VT彫#kZL]bF_]σF8Npjyjn)v pt`5(si)`u}(;#up@ȋɳ" |\ΑFwTן~Б~D Ť0ڃ9@u" E|wAoD4bowvqcaѝx^xɓkxW-$KB&mh|b3B!GL-u*_(/.޻e-+NO Er¦_-4CzB>]S I095g"wa|f×N~V>,{&0~V^y:g$L5.s/2^9xL5j G^~|Eq 4[:B_R@N/rB( S thD }%׊y v"jU@+4p'<5FʙBqL](AЄFw G]>,Ď96vfakWn]ن<K{_@S5j<.Q}Ze0x8ؼEd96 o'hJö;4vRN56q<@ ?>N`s bBn&%2eVQMa3>S0zِuߘ\N1֜p4czs2@po8^';?/'KgsUݟ2t|ģ/LB endstream endobj 1926 0 obj << /F7 38 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F5 32 0 R /F10 53 0 R /F23 221 0 R /F30 286 0 R >> endobj 1927 0 obj << /Im50 1919 0 R /Im51 1921 0 R /Im52 1923 0 R >> endobj 1918 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1926 0 R /XObject 1927 0 R >> endobj 1930 0 obj << /Filter[/FlateDecode] /Length 2742 >> stream x[͗۸ p͊+"%;9^wnM7OgcYdҿ?$HhcڜlC"  @/bNjx>B$($fE0{u~"'\n)a-yTUE,#[?D,>=V땠b dǮ)b տo/i;GXzfrEf_8KI-"k޾hF2nv̾/0N{fJNFoƓ}|_Faqy,w .?hoxد4sZ$ J VQ̈RAVҔTUJ~3D4AoV`һ U*G|7BR7ڷoHÿ6unSXB$ @" ގVFX~a(SIyN2)%î"#0D63'!;Axڳrk'g? RgDcJb}no #*y3߁눉09:vv`eZ9EF㵑菭.Zys$8ǣ+GL}!\ݶ[8o U-~lD<%R6EЦ2ƛ8L\ōu)Őx%cnC SD /$/+s lwJi w26;o垿92?%%s50@wV(P̈@PMF1{|VO}JS45!Zn bafQ4[n~=Zg0}<}g벶~^ ,޽TXoW]1L*ԟ,uŲeyo-b,?O #-2L(}Sx3:81 V/w۪ Vef f |{lX,~ HW ãTog*CލpX\x{򎨀!jxU,1Puhs@2uk\8ve㽾g_[NQ N#xFwUFAo5^sPO'?P|90Eh 5"+\cMy(\w0ΗOsݘWw]_zPH2cP1(}byVUJlo7ÝEAW+}kHc,oX? /M@R揬L@%IF]%` J¡mOF5v[W#!+xY"uU.F}V1ղ)蚩$`S+~뫠@jkX,ʵp5^ՓMkV^А4IXnfp]ZQ5O NoR h& ^2]W)dKp@2~tb\*vh-ap .%A" cv|~ܜs0@WbqNj _. }&BI* jO 1]\v ,g|;K&s}w=i-5@^0q(B'ӗqư&[iK>HvM㽏̺QH7U'=[>>/XS@F zƫ)cE7Џ6 ^8]UatuOeV>_Bߞ4 w%&&{sۓSR^i.p( %2u@)XʗqyKpRK h))8bAM\×Oig:[ 'n"p*g8Y85b/vB{ 81{rEJ؊{#,azȪq%Ȩ> endobj 1929 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1931 0 R >> endobj 1934 0 obj << /Filter[/FlateDecode] /Length 2254 >> stream xZ6~ /|6Z~k@kk5Zk!?Ç(Je}4 0ny.Qe,y0 8ⴿ_) 2+Xwd TJ 2 16wabj{aQtsБ*TxBQ!<4m('Й1C#-|ZlԖS^)bqj[F^𭖤C]1<卺|=5 *RQ wIU^vaU]Wuk.kQtەL"5p}QlG^j T0F*'@+N ]N^5E:! ^5 zw4RC襾jR[Uw3rWZs])a?jV{uU caUGS >"góyoƻaQæ.g"o fTLv8U4^2L^jxY0\P2٤`A1z7I, Ms'U?c~tfx"I|tv:Ns f:affi,R16P+Ymk%Z\& O8\KMѶ+wX|.oкxm;rsL}FJEK*r#T0@APp 6f}7=$|(O]:kCDdr%:Vv^xP{~^8<`XrA7ljg_P#PCJ3'8dQa=E M(^pG^=:~> ˁ!Q%CE?b;V@O B}2Fd/f6H-i # 9Odv!噳&Xq̢Ψ]MPs: :\woQ<}W#yuP1"f !wE`P= NJ.1@5LOP>D8>GH̫/%J f0Ge[J@o!5R}1{͖aqdbpiq9ײ\K>OfuZSVL r;VvXрWZ*A3&t~43ZBHԘ͟&$-My I})oߜ3S 1ip>}}Д띤WM]Wo C endstream endobj 1935 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R >> endobj 1933 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1935 0 R >> endobj 1938 0 obj << /Length 52 /Filter/FlateDecode /Name/Im53 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1939 0 R >> >> >> stream x33T0A(U&`Q 0kgF Af .\@s X endstream endobj 1939 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 498 /ColorSpace/DeviceRGB /Width 140 /Height 80 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 140 /Colors 3 >> >> stream xQ0|^WvZoإ&.v>ϓͬQ%P%P%x#t:v)*iyo+k1C$]IOT@IA2,=u|XE/?IT{K\;KIj]0Z endstream endobj 1940 0 obj << /Filter[/FlateDecode] /Length 2080 >> stream xZKs6`)sR!v,HSk>'^0ݐD"8F'n !M/\ʤ@2*BlWW.F!mku|h!$1X@v1_x50Lcütɰ98&HR,/u̦ri{x4-1}o}h)eM# ^c 4fjT2d-QV`R܅B'I N4̟ C$FBY`yFH:PAg2ӂm%0n@B3c{*OL'sη9*BjrIOQKz3_A!<|C< B튪hFY(<F$C Jr6eW+p(R)(< uILW ΃Ru"V΀$<cuW`.3sS_g»ȏ][V/4ip|@[z^c'g ZJ3N] ƿn¼+;h:@,a1Zٱ/=|Ϡ!L2q "3w]>\_ΠKwP],@9NV00S57{ƻ@&"$I)Z~^ BþN/љ=tp`!`Jh(Cp.ޱ./ :/ s6Ie^=e[,*Ka5_e52 \dؙN]9/e] Nu<5Hv΅Pٝ $ U~l=Bh6HXw'@Ƅ Ri7m=r &7/V 1Du@|Px\ZzT7RJjFT0 TfO0R3HCĞ[/BL2,UlBNx4ѵ sy f*oB81H!)rv% 6=ywUMW`WϾmO;~ь@B8Jv<%e渟~[B''l RuL^RIˉ 09($[[F"23lm?ҼWEۥy9|>? lp@(:8oGcfBv 8G"?ѿ:nGHi8k_?OcuǾ#k?ʐ endstream endobj 1941 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 1942 0 obj << /Im53 1938 0 R >> endobj 1937 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1941 0 R /XObject 1942 0 R >> endobj 1945 0 obj << /Filter[/FlateDecode] /Length 2677 >> stream x[[~p3")Qn'@w ڗ}Ȥ Y#j$ז3"?"S,_f"OÏNyFˑ|`4hcǑ}38zw͇pXMwJX< t|Ut9POf:BL^yyUn?O\ @AߗNmMN z.͍zkԣ\|^Yn.cI滨6\r=9y\Kf~p84< D[2۪]+ՐhjzKC҄4)]Ftu=(HwSP?'&KjLVRtݳ@dR(I-yASk4McgZO:Iʬ ݩH6Ki^%Q½Q}cXST;@X|BHM^ r&#ÃꢭO6ٿw&.:@W3 :lC(:xU3M3eGZX~8 062 D( aaCPqEok5w/Qj[bpA5][v Dl"H.o126yt0:A4E^H yU>7cO"f %s`D?l*O$ [E@eX&$4jZeo3zf 5cʿ(RIJGŮLӔ ]ȷ BQG4gn"J"EjG| /GᅓP4&j@8Fas Pn*XL c;%axV6V=]vgleǙBO,a v)X ?&ddܴ n%6ayje_9a.yd8r=d@"JA ]R;[4eIMRQ# ^c wzSSo.T`U0vӘfr+ @nzyOKv]$CBքNEZQZ|L2~?~Pխ[MqvL9¢CD꓎Zi74\] YU]c$Ȧ} a6:[uޡx"ߣ: \ i@32պ,m'D؋(/_-Ac4ۀ_  P~;TpzhAWbqxnYkB/7kױXt\ྪXd8Chd&x%#9%ALys`|=}`E W/]`|LSoSYxaW+eK+`xar,E:~`+E<Ǣ/Ǣ_ccݳPK=Wb?xQ&Dz?{PL>Qw^Asd;$+0*+Ű-<"eE/82_}PMbe^0X FL6 `,6A[$bEj\$cKPO"~LⰋ"6k 'TXW$!l#~=AeрEAӞ}ّ'>U3>?+_b!W}@S$CRFqiͱ]Rg"x8ayf <백 'Z xA x&rɂ#N;2 b9;b P`["t/l/tA x!yʽ&kj][ey۹آY0 ?}'wqV^N@`^LBLrS`Q,ݲqaAH>YD0E>bj.Ve"z`{C=?oXf$fkwLmM(BWÂ5Ahddd=.Dքu;;k}EsMc,)na݌f !Ϭ_`j.#F2ںflXܡM\̋؈vPnL_{$+?SHa}o/wVa_bK$1VluQѽrh.#J$Y]ܶϪCu&7Ѝ endstream endobj 1946 0 obj << /F7 38 0 R /F13 66 0 R /F10 53 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R >> endobj 1944 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1946 0 R >> endobj 1949 0 obj << /Length 52 /Filter/FlateDecode /Name/Im54 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1950 0 R >> >> >> stream x33T0A(U&`Q 0kgF Af .\@s X endstream endobj 1950 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 643 /ColorSpace/DeviceRGB /Width 140 /Height 80 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 140 /Colors 3 >> >> stream xk Fq}vA 9 $ ~D.P;od3@| IHR$) O8u:LӔI x<:>B:?le.;ID*+I>YD$7{IBQ\IZww2DIHZ"~O=!$)sI*>EJU_vC$P$)\IHR$)I $@ IHRGvc e6fr@ $sIBlI<Ѐa!I*Os mt8u I}HI$HZ$%MhxzX^~ƒjAJJ#Ҏ{kxOCo7λLcIv2d$yR.kC)yɒD\7=}MS) ݭKJ,?08Tt`:ڂĒBm:޻/}j ^BR!3@HR@uI—unC-U9@W9aIB$"U 1$ioYII $@~M endstream endobj 1951 0 obj << /Filter[/FlateDecode] /Length 2107 >> stream xڽYn8}߯/NIQd. 2,;~K@ȭ^Im'["lIxf}T"U*2X%{J"cLVbH\,I~K~zgI"s&7zw+tZäK~[/OEz7ZO{/1iZd奫XHsj=q}:mH0TcjCu{&mwvw ={9Q@oў}Cз[9gd9+ G4:0"x0̝m Ŵ`gkUh6$`Cxm`&6Dȱ L;0#An?_r`@J9.$( ҝHE]iS"OPip{ тB1,nf?hc'?Fsdc@hvPQ/cNik+ u6LkEPJ3vt8ȉI#rR WU0GApvJƉ X` l~Y@h= LLJ%_M?fj~iFP/~dzH&x19W"4΂@T/TL'+!a[dW/&q0d!FfS}l"1wO"(˙5tj|Q8gos $8kmpds빟J=AX6G I*`0g08 j a I0e!H*LC0ޛ:Ѱ, $.y`+̣␿}3ρ'\%nzV_{sc'To1sL{P ,֥q-O=H!\ <bY B#x[sG|+͆5F3]O=ƢE#8D"?=ts=ީVf7-k͝鷇*2uSb)Szgi20h2~r{AQZ;b,=3;MB1/y2LZd? êC.ڟ#!bt>d5_2| rI+)'XA')5[OO BGА` HK>fdKdV\4mj{slf6+|ߖj8QjY[,lk \Pn`P\3xxչuvs.6Udx{̪.%}?HnfLRQ^}*J֕*TOUBCT6M,Y9$OovcGF}1Ge[pFB-Gqr8i`"+vOLE -X(Fa\UIDŽwF?>a(SFߓ)a+ E^GWaLv`0~ xcF,<Ó=? TwdD9s*KwwP&rQ^t1Q _ LeB:ݺ_3x(pm_W"=9B3`⡞p%FYoZ D)m~9E`cUW4,iêrsz fZuk|&4"UY-RʯW51:\AЙn8.w`ԉ =!EߔH7mG/ö-z7#@f"7t?_Z endstream endobj 1952 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F13 66 0 R /F21 215 0 R /F12 59 0 R /F17 145 0 R >> endobj 1953 0 obj << /Im54 1949 0 R >> endobj 1948 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1952 0 R /XObject 1953 0 R >> endobj 1956 0 obj << /Filter[/FlateDecode] /Length 2198 >> stream xڭXKWE h/I3Y͎\3^#P3RG*)Y,~.*YO~A@,A$yp Lr] C,q"Oσǿ4E\ Ïy0.BQwHk~8,mݯ NDX&JZ`6²ks@ C4phoƗ(s͈VWo4{̰ix4DZ{9|@/d*n;wjǾ~ocgUr13ҩ $΅"@R$ĕ02gB8l?u*MA<ӟqnh^tCYж~}v퉨mժ!Ӗ%˖")%T|omQ=[+ۿh(;Z5xpMJ񫚁&яo,|@ i= 5`EuJ3X8гasBG2v˖,y WoJYν NY r'F\U0ЬҎ+J b,RM } g!6EO_d;*phx*슦>Xq#--HC7,O,[2]2l(-cUhZv9viWZ,s.nJx z>E!z{o 7{V&{ pi2q :F;??Wc>/8Ztn ed謤+WؘL>"ZGˣRTaU _F 0/23`l个KԑX嵽i(23|ifSKK9fc[# *M(Z? {21k:Ԝ-5~&h#xI- k |0_+tNmw664㾣NLdf2EA-\CAxw7hvIO^ LO1әk `iD`g"R,f[BJqRK[\xV/LhL':qza\tT0?gK/mcv;w;)O/^`4 ߸gEj7p>; I`k?=.3ơcW=9}/bR6/7.O~$!ևeB>$4}2kjvϻwP; ܲ7B`M Eʍ EHIzsAhlنֶRq-1 :_;B#bR m76Aۿ0Lx 7&J$モ 73"DL4 g^sy.oGF&?S5\-WS!߼=y7WwH54ž#R\Bhqdgp9 6^`KO-l_1lw`jx_>ܷm |hQ,[3߂5oYS^5d ~%inqE, C'DMr*|\ڎcpqdXLmmPߞWj3lttBcAXfJ*S_9P0 endstream endobj 1957 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F6 35 0 R /F10 53 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F30 286 0 R >> endobj 1955 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1957 0 R >> endobj 1960 0 obj << /Filter[/FlateDecode] /Length 2390 >> stream xZm۸_(ΌHQom6@\?~K@e[V2$9{;|Z&K~4PyCBJ^%^ݯjc݊GpfտW?޾*'y=)awOme7TjiPK0(ΧjwӠ8 ~i;;X^}竛ԢC?ǫP"GiY '$Ym'4ob-i:k֜pjz9LE=aU;j̓N Kuփ$} 9ZBNt@GFg.oԡxʇ͵MGČ"JHGs8[BrRs K]6%Y*_=B vms˂+xy. 2Ă.]ӫ.gIz%sWzPںnnAȄWA]Fh#6< %7Wye$<=_zwTlfQ'A/ޤT4O/n3# zk,]%0ןE$~Rכ2y1άo4 )F/1lirymu<d" +x z޼QKS Z]^C0|3 o h35!tVwQ,eL'"Uu' 2w/^veEs4c*4㘪|C`W[) * xf0D^5VNe s֛7,Bj )Y;$BnY,Q|b( -rZ*tc!CVKaѧv$N#B!gOR&OIawOs3PHsbSͱTkEHQ,|!EI}$$eF;$&?i.SsP/2q86PG\r8^<'`GH<#FU=6YTk?Yk/ C9%$Q65'?IMR},`fjmfAWwZo/` `^3W= ' ,Džxݕ{uDNJs]+1jԵmtbԽ=a4r3t_{ezʀ:3x%fA3+Uf}_mk=wī|0>&ɨ({hTTO+t̕x_tĊMDNJZ0aEQ5IvLa {/AG9$4zxch7RX^S:c F| 3JN!cF(Osq3ґ4W/r`fB]}WX0M&Hܳ[,-u[Lr&GǢ2Lz pKxR DDj$"۳ `ig2AJ8RMUqR類 qPryPVl&胦 &4c2Z=,~WKuM䒀y3|~iWϖwt%}JMv>IO6BQ=a[ҟI! ;+ߠdW ?]v4$ *MH*D(7]$vvQLC!BgF`L@yH11$cLŋ$"HyI(78~oϱLBH3I I5ҋUX,2[8( ($Sj^"if}e-d )wl$[f yaQp&H1#la! /!tɭ3!ŵ@Y gk:Y,pt>UtF4Wxr~gޝۦl*a?Te+-6xRóu2Ֆ~AyCW8$xl/UW&!*UِfDdJ59:m,Z"Ds*Tj?~Neu<ɑ:scpy!SVT} uakA)*?4`+]RY,n ;Vp8'i 8Z0)ik6N4&p}zR琪ͤ> endobj 1959 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1961 0 R >> endobj 1964 0 obj << /Filter[/FlateDecode] /Length 2488 >> stream xZKFn>ql`퍍%@2@3AIDCDjfgV,%&'ꪮ]c7N#\$X,' ?`O|3_|ͫ$fq849柷3B2Nr%^E<<*nꃐer`\iR RL¾0ٽ~ӛ3ۗ})t&pgL.p?d*tf(vH,Ӽ'P t3M:j:_"N?E='4╇}x$Px"̗ъ,1=xHrBÈ-#9׽XpncjxCXJ)}PgcG O=(-D@>U 9j| b d>8Qa2gÖ(yV2[=Q`oW.N{F#\? I<b*'ECɑr(O^|ZeUOC!$5H7|4.TBGHhp3/BzDblez~W֖ڇx5Ԇ+/ϱAl1)0(fi=)՗~v#7WXy@Bs pԘyʉxSɬ艳$n-ѸIA݃^ g0b&xCpR5Tgvs''! z95ԆrU8_tRY :%Q*eBU5VP\Q߻>8*Lc& C#wD4i܏\gGr2@KEbw9) O4 ) ZjlY؀b)Ɖv qa&Tj/|0BWyx@yU)\}6 `;,S؁@?#jTHfǺc0~bBNz=h1ZYBfHe;pK/h/(Cѳ޻. ,g\/VNqvuo kc6/oA8nеPe8be]nbCadHYH^FPK̘5PE8jFY#> 1!tT1 omgP d3_h>i5#>tVNHcӤ |^ţAusVNyr,bA;Ϊ<кSp,cfw"}~b!У>]_+80|\紓=s_n)ڃ7 5T~c= yBg .Oj5xhOMxs3CBtm N]_i磯Z'\4O`}<71e/"X*V?#:TqZ/Mz@U5}۴2' ׿N٧N٥$..hy+3 Ur&yn4:|ZeĄx>aQ%õ }d]u][Y溵;71zL[o4UɁ}akߒ@xJ띉k;)C V'Eap+f\#gI,}e[4+*[%>nƙxeVH<$%Z[jq#QT^3[\j!ӝcVAgɣsXjTcIN1O}7}?iOƊ0\gu*|g qYvt$u\ endstream endobj 1965 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F8 41 0 R /F13 66 0 R >> endobj 1963 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1965 0 R >> endobj 1968 0 obj << /Length 53 /Filter/FlateDecode /Name/Im55 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1969 0 R >> >> >> stream x33T0A(U&F`QCS0C@͌@J\ endstream endobj 1969 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 1547 /ColorSpace/DeviceRGB /Width 420 /Height 150 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 420 /Colors 3 >> >> stream xQ@auiZ%ؗ 3`Ys3@wy{{"v@b v@ b v@ b v@ b v@ b v@ b v@ b vD=p6;"%vD;vK$vI-#ر[bG$cĎHbnĎ;"%vDrS#v;"]o.ybG #rĎ@bGm'v;"klz#Pp>??k)Ymgv5jU vDrή&29sSWc[[AV"#Z͞3~xC15s'/?jFn_XVMb-`Ӎ}\%'OEZY\ݤ89sy|  29ya틏=IEZ[Lb歹XqNM6a~m1T M_bm|(v{R9y=]ꋇ {صؕ]/;:]ܓUGQĮƆnǯnn&w/k*ݳb7pu-]=KJj߉X}Ʈ0sWdر[Wn_uz)kmm5?מPj➴×oRWcnr5ÛcWޟ&$vlVK0aa^>;"c'&bG8#\o;B4 o%v;" c-s=#k&3W5vכĎHĎ;"9gnn\gn;"%vD;vK$vI-#_nĎ; R; R; R; R; R; R; R; R; R; R; R; R; R; R; R; R; R; R;nٻٻ7ׯ/cHqqGqĎ0v///Oܓm.KNm>>>ZJBޅ{bהuB:gpb؅Nkt~~~H'j]Cã#n.gc ֑ص2W[NnşA!p]+icWٽmwi(qK׉#bǍ؝t1"v;:cHHAHAHAHAHAHAHAHAHAHAa endstream endobj 1970 0 obj << /Filter[/FlateDecode] /Length 2136 >> stream x}XK WuՌVoɮ׵)'ޔʇllM^ý_L}"R7Az'OhɼAxiyiiwЫ*QWӇMߘtxL񐅾>d><w^AyJʈϽX$I.J ' xM+w:aMzQQeNy$NH?J;U[Z d8Ĺ D(IPmWV}i]웞ȯqAG'\r\ΚhC7*w-b^+^-C:2Ɯ9Hx'ae!WؚZ1KF~ ,#`^^H3[zEjg~Ky%gn{b3 r!-sTdo[ g*Tae}i<9d4AY~ %bUGU8ͰZ@~]>Zmc;z .ge~AQyp#?$L|u~3K c<}NIADz99^~Zf s2`>,I;('Ϫc!Lq.t;)KVzM7󃵏DI#PpOWxtM FrNZ5"zIq༤$Za墤+ǻLF{8JN2YoBGı=F*V848t39||U`רE1fco Ȉ?j{ź~m F ROdH] ª u^z1/6T"8 s< /jMp *hu?Ÿ[eС7<5>㚚oyeʄ=[e>Q|-"c6c yAr6`|F3Ly Sc]g7$dT-geZj3q#N&5OQ;\;$J6Hlq$1bNL7Nþ` *ndg奤PH\7*7MdRF/shAZA5NQe,@Gy+E3y65[ % H'fc-*3LS!LEwA hz\T,D)mMfao^`ku:MӤp^ᅧ*(r`u{4y$ܛ8qլU/o)! +[ wi[bHl]*JNewϣ J؅*W"E2m-cjz|P%c?bUDCjE˹:_\,!(˕e{$[=1҆B&ۍWMs[lSx>-0umUY&VQH=|Oii/Y&x-ɮ0_ۢʧ>a}7ݾ F&RimE! FJ$uAt2 e9JjӵbI1W-q=ʩ a. 0-;+fvsLsb䯟 P*ѺVpKU"sPd~{MڵHoHd+f=ܷ3Ҵ2 bd0~>k<{ΫMGgzQ* o1妹ӲA Zr nhtڗW3nRs +,s;S?I~_-ט"l7)X=hCki,][6d?58J1 G/`>8 endstream endobj 1971 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R >> endobj 1972 0 obj << /Im55 1968 0 R >> endobj 1967 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1971 0 R /XObject 1972 0 R >> endobj 1975 0 obj << /Filter[/FlateDecode] /Length 2323 >> stream xڥYKW!20护$J=; >r4Eնv,ِ ?>U,Rvf/mb+v( ~9H#`',A2fpSW? gy? 9yp/f+b>eJ)C.764MÏ*t_n?(SKCtϗJUJ'X⑯~6) 4ϥL#B$UXepNƟ,,tpcdn9Ɍŋ3>\WuIt};P`$rĐ{=/m D͎xnj3[>>W s]>VnU(Ea&K۫hv,|KA/tQ2"u ԜLYn(D# 813>VӣmOlelFvtz3>DH:pI+fOQcHYژ?USvqHK\=հ%V0 ¹ 0aNi[* j|Rv[KW 0T{4-.qto7z<@ `>.K=rD!j(16$ $)zcW-nC.Ru Z 99̓H"=1wA^h}[ewU<Ks2|XĘ9٘WJQl9,qxSTeNgIsA g~6nD6\Qt6:/dIH o3~c8Ԫ&Wkq%VQiJ0\L?~YK#&vo)˕aqńe`X H%!IG:X.\YV$ X1p,&a鑙,7+ &T* n(%%.Ӌ50-ZO_FSeւe]8^؛sp%YzA̰ LhL{.MA(@ 4LSvĚfGMʦCwd$做ALe<fٳlWǎ5 PG93 n,F qGFOUSi@ZOؿ*R|̤'%`;Ď LG[6e몡P_V,.T2re?B܄AC H’\&wD0J#s%.f gRLxSo4a\"0}Zy`Ztz_2'K`ԝm PF|9]Sš2 䘪_m2C9A-~b 2&\,NM"lpExʼna̭wCߍWg~J?O_('GqƠuHCI݁?g(j)*o~jgɬ}ͪuV5aMv~abs\WOdgio|.],t2Zs9CwCn:-'8OQ,bݭAX4dsB[,R!}^7X[ m-X*ܷ a2>5|,+1#7v&%<q4vLޒ wl EGTʼ\*p qE0h<Ǔ.-1SGV dCXm;j{h6.`).> fBz5Z6F _(PG. C%WF1=´ygHݞ03xTSQj̔=c] Pˋ/iBVh@$ /8ɯVϱēu˳|~IQ:Ǖ7a21Edf~DU_pY endstream endobj 1976 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F10 53 0 R /F6 35 0 R /F15 112 0 R /F12 59 0 R /F29 279 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 1974 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1976 0 R >> endobj 1979 0 obj << /Filter[/FlateDecode] /Length 2147 >> stream xZmo_@+!f_hrwA (POd2y_WKJ a!wvvvfY'a5H$F%KR.CGMo?̒ە Z&~}heӮ)z.ȬcgwC1Q !xIj)0.VoV3*#Q"^>M׮Z[)'i&[%z> yPd'zŜZ i {+rSx2b8e<4MY_5m4p!BT ~Re(F!ꣿ`3Mݛ@<*9"<Z g{8"ᤂrP b0f@4/֤t KEhf^/kh$4NoYB۾|^آ5w`<7j> U+.QQΑ.*CYidiO ͔6nFJqВջmF. OgU3`L7n.BdhM=b'!8&6LM{}UzL?xSm=y;X;D89@ 6-Qaa[v9X ZnGn4cFQNrQh%,dPq&p6'Te;o]$=F=VUQZRd*"fspH1-a ]ʊi֊4PN,Z²¸gf[W'r]Lm9E8M\Wߤ'N}é̙_9 D$>}9u-o{:56^ S.Q2Po#>?({bK PLf"CM"qhbF`*,"OIIkP@Mr?ULW]1S&x٢а򴊍Z Cv>~05qO!~dx:x""+Q=ygcJs3T![#ZWv?ayl-.*:!M3"u*QTAٔMHO̫w /"yF5CB!,#ؾGeoR݇s6/0췏~wtEƭj|5E}y9h}2z Zל!\JĄ#X8VN y[ {T1rMOmh}1?V4^S\ rBkt/!<WڅEr١)E9U` ?Z<,->о¶yM01b`ͼP)mB%9*$gOwK^= >섡>ť9"xAvtC/V~LΚc8;Ұ2JiZD/k[#um5]Oh.8 ECtz@#p{A%%xm{M-mæ|fO'?=1٠*= ః賈]Jcxf1^^Y1+;z/(l0K.ƹ׾dCkU FNE_ȕa;e[-9(znpxQXzU^r> endobj 1978 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1980 0 R >> endobj 1983 0 obj << /Length 58 /Filter/FlateDecode /Name/Im56 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -131 -206] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 1984 0 R >> >> >> stream x33T0A(U&zF`!c X~K>W   endstream endobj 1984 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 5414 /ColorSpace/DeviceRGB /Width 350 /Height 380 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 350 /Colors 3 >> >> stream xَ4Ax.*2tʾC @ -kO'Q:q93Nj{gyG}`;)38رcS9!Ç{ ܿ^5 {T:駟z5ށ0Oza'L|>cPf}L44n` 󪫮2sϹ 677[opn-lG-wuw}^5>|R购sV_nFcZsI0]Wxc2evިyWGv|mo[&Y14뷎AS8{hqSfhxﯾ5Tջm{pw|Ug[5~^u>EۮFEn*vnR{ݾrnv`fР0Mhxć_)v-s~mMEȏ?^5E./^;bۍSV:d_yAޮvq`"Ԗ z2k` 7{az_㷶([R?p0L'l70yyhx75yV˯hX0=\ oVѣG:dy1=`;@)ѐ4!s*m 9XcDCꈆl-N:DG4)zi-{ѐ(7n I#-rhȇB"RG4D E̖zUwW9ٟٻ}b,kB4,{HRcۺ@K4DA4D0W4h!/(/4if)B4h`z/ych`b4$Q$GC>x=2tlw(ڏ%#@4,vmz!z?V@DvC':)"Tɝ٫]=U Hr*qawG#qHѠ׺In5)"JH}dRA4hm d "G@E4t_@.-2[$ #To/vD DC|YNn!uDCd%+1h)9e< qdvӱhHA xc,aTD4,L4hXT97ʑ3 SčuE rPDF4h a;M yK)fD8tHa*fḒt^D4 ŬEpND LԎڎiC4c2y\C6E4̹7_?CA;̍^GnmmUoF/AV]P Fa{b|@U*CFfz27J`zP$hXw27a~DC!w,W`7HPo>7ʍV Ai;jDh/(z DCڢ1^%F'*ڝs J! 9(C4 9h2Cr޲?čgn9lO'V4 \07z 4x3ex>)~A,<p Ē;8 bF'}8A^w9Õ %csN(DCTQ%N(DCTOXDCԼc;/s= ~PsP3E):*&M0 h`1q`gY,ғb ̛ 2z#:AxWpr =AyCp (?ih`1W'Tau"9ĐQ 7Qݣnh`&nTBSc8&!]`9s!c>ͮ1 ,78!]lx93"cJb878f 93"N`b͈\psku(,'j8}6+anq_6c}ogѬruz="c4yQ2WٟX kZ3]osP;2>qe vO٣DC,:==]ooglE4XsE\Eè^e"rG,OVtFg[G0SLAߡX#U6*sMh?fD5 9s/YmQ 1b R!aJTa,.GE8{4 r}ݧ p KyJM4%@4A| c]͢.Ƽ;Gpb8ѐ:i݈`UҼXIQhh*d~D4hd?8"ӡ x..vhH u..y\s=WB?At\MY0AӌI !uD,I(?u ]D,ycDCYRC'A!h%:5/ܞ,E4ȒzB"RG4UU.)<2 k%1G-jDCꈆQ=#* ѰivP a-3U(XDCꈆ.&tՄDC=5BhHE|UҍQF9%MnP>t"Kܟ\ hH#͠ZBG+s7BhH#ߧcxC4XffX# /,!uDCp8><DC@-qP#RG4>{8?DCyF[bnȅT k+U` #W%r.a)*iP#RG4 o:QR&)VT)EɈA<~1I5~>Km7F?AFq  bX$hH0?/S4sXDPx51ڤsXDP}o]QU`b!!D~Ύ{b"dSt,\"B#dSlmmUsz!npD+F4Ȓ磖Xa,A1ݷN> stream xڝVo6_Ad/1")#Y qd-XgMe{xHKw>%8fKf-͞XB(wӳHt.,tr< #(-U6RPH<4 n6jVUtg72f33?02 zO 5UO\hofIr[nV^ikސC! ɕtx^.vmz}s(')/GOj@-,2(P̟,!aBs:GCF.I CQ. )žPH8llh##UR_Ӹvn瓷'Nrn]ɞ>ydQg/`z'^EfTstvU&9pJBe4) tq>nnk&gr2(Sji[=!S4W0gv7mT[|fGO8zeT̫v-4W[3_I:~$'d%754e`-b4> endobj 1987 0 obj << /Im56 1983 0 R >> endobj 1982 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1986 0 R /XObject 1987 0 R >> endobj 1990 0 obj << /Filter[/FlateDecode] /Length 2021 >> stream xYo6q?L&V|6|y/VoKX$l(#4C4р|YT ,n9x>hVS07B 5]Ufn a1|Z\ RFej(0j=v`i-ej-0Nicg)1Mm5ݔOPZttzAK c΁k )b̼ !RТ*>fsKko^b`ZҵuD;R}r+^_UO-Y9N9 B%Dg]̋uQMXNCQB+ꉚ&Tۿ(6zi'Q@ɷG*F/Tp߼t( :>s%ɱH$E }oBi,{OOHu[İpNHC5@3O<OGjP|*b^ŧ Йˊ(iDQX2`ѢfV4*kQ2P& ;e~$bV/%^6i`'!P蘬6pdb 9KnDcK= Y_=ka|i"č;܋[*ʏ[p͟ypPޖ꺨E_<&\}GEµ<dUP\Rykkt]B6DVI>f7[9H&JE!跨*G !ggv5Pljљ{b?6v9~9[f-m.uuXxƟG>#; .`P$rOiuST2@DtD9FisKMæZ:6;?z-o@Zݸ W>qƙ\+p dV?iƯWIpd |oo2fDcS^B۬U}N S^LbK1r6F=ƙgŲ6DD`aQy锱6.`g7Ӿ#s[J*M)Ń>T: pa,}hdMuMhXv'۴1[[;TW)n> endobj 1989 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1991 0 R >> endobj 1994 0 obj << /Filter[/FlateDecode] /Length 2647 >> stream xڭk۶{zPjŗGf:MIdx"Z". ARR.NXg/|",UB+ʹtŷEh &@%r% efYRP*+7YWU// 햫P`YW|d-BBąkƫWsw>;VځxX ɔ4~WvRA 6~U9Zڟ^;zO!(xW IO @mҩ"-eIdMdbQ {zeogIv̎z0ܱx`^B,1')X~!4Ě)Y  0MWJj&cJ>S+K_hrkK:_d~in،oN?e߄wa'aPy|xz}e!W7m?zwp,YeySܞv $8Kx>DF0_&sA*ٟћMmS?(!"CJ4!4=$@_ⰨheP)H0aBq}].Hpgl +pqTF!K D&jF( cׄ61<$F홵"/ZՕØu4BqSRLk_j c R2Y*p팘3TcKXir0-Q5a-Y%*::VVv޳rn!to)cUզJνBD# Pvv8vF}Y{b8 #Tjt l#Lc7 ΙcDPtuvUQ<~u\D,kfA$bݖLf8, Oi`<a㫣cq9*@z*?<=}S=L: OG$bILbELeC.EZi6gn(p)g(Nva%y=5\C˧Fk^1.TLc]j!U6 L|A$]e?@%h lQ&=WT'YPc 'WHGU2PaԘy2CG51N(WT A.[F`iMS>#=:ͦmXh&lvug>u sccV -ƍvӭV91l:=Mo.YN h i['}{'#,$KFN mB upЯg@YRЬ{=wA<)ʹ:xm^iUoۆf&η]aj0K}k:khr*J葖6=cpAK,ja *h<- wQ؊=նfS҃LiPa_8k8̰[匳d6Nsʐ%&9U䠇F(>(„y5ؙLi@ۂſ+jcY@bxlbzʍyڢvO$~Z,RԲè&&[Iz21&dw3#^\/\,"gy>y79C@6o-ܘ F )x9dA85AN)Mbv$-0`zH]>DLu/jYezȣyNKzF'Nc`$Un%M%QRUl/!\:nzA?d> endobj 1993 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1995 0 R >> endobj 1998 0 obj << /Filter[/FlateDecode] /Length 2120 >> stream xZݏܶ_!/Zb)JIME}yW@?$qE-w}gˉRÙ̏3aq>ҏ?CDQQ΢ itq{#wo r~ZOͣ?+~[5yI6FCG/~﫿~VNȐּX&vm$H#ЫmɢOʢ<(h' 9?l" g8J;> 9AR8%|)@˾_&: =T2S3R3h!:9<4A@!@IRLuSH?ACr֯تCw_^77ٛT\u_z1^sdR+y*2#I]ع-7 a#D\ԣ @'#*w aYGgJ)(][î j em{wjK[YjmU} ⟛vkJ\yclNXnSo4ZA՛۲#~u[@ʝevk1|kmu9t>oӸ/O[o;5Qȸ);Àn;;FYccxԥ6jt(jksW=T;m'E4fk AS,aY ku`W]0JDvJ`D\N6ÂC,,1)J' W]n(lC>LSBjzQ&m+yX׾_ڶ@k#ԑ2K%\%`\Gf|eW|ڗ݃p*20jАuK/6K|: |!3-S5HPyfLO8ϐ]VhZrU{ɪ)+8Gύ}5֮5]ﴋpC|?(4##f}-^M<~ 07׌y 1s`΁h5D 'Ӷ59Cdmټec,]J ::U=*zNU_a:A_qE΄2s:ݵ_~JY-jv_PT"I>L6&4>Yy8~&8_8[T%~nz,D@8蔠w*Tnun|X&Q, $3+{g,X9T&9 .`ȏZTW؃r?Ib~sBҙ> '`] ٖꏎ2><fL'  Kdg"eN` AQӯ)wP)I/0&={~'B@0?=aK|2;Ts޺-~ yJ"KX9~ruCg.3Ʃ>GDb$z_8 gPׯBx:G$S$v endstream endobj 1999 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F8 41 0 R /F13 66 0 R >> endobj 1997 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 1999 0 R >> endobj 2002 0 obj << /Filter[/FlateDecode] /Length 2088 >> stream xYK6WPCT fSwU{ܒYgw}Z/,@f'9 #M_aHb(a"8Խoߣ^DdtD?,M1Lŏ+e[FxmU]_w(JqaZv 2ozwMwJRCpv0q('~V ߮wƦ>$Fڤ1[BmN}T ~&R}~9 LXg  & Ľ}ZO?֫Y #"Bm-UEnhd*W+;h6A_ SP4E1!*`&]NSqիUΩ '_ yC ?uF\ o?^rB/ '&ԂT~*ex~2%%g!4O*c41@lSқeL8T"&LnR ؂(lbR( !{ ^op^l&{.vbz2zs8LV'hjny%RR-!{'a5A#q@'4z5h'(cڦ"T!Ai 篺ҏYueD 2S[P0A$MSlUA lʓZUP2^P$j=EȚ}?!h*)Ě.W%ܽ$LlvGahYN@T`BWʐpQU[*6BQq dd/Y^a֔4PMUsPlv.5Lj[Ӹ}J#;9ɓ 9ʱ59ųv>'^c9=RU6u Q18ԹHNI5_õxzy5^׺ؘL>LǙ]W(t )lr2A8S7:$3ˈ䗅ϞzCFFx95.Lݤ!Jiʩ`_:yQ^nft>7.+\ѯn0#%e P n$duD\F3sgy.d=x\ ]E)W˓~[mۤIM+uJeX68xZHZ7<Kg!Y ^{}gO]P{}›)^&̇|纻0gTLXY>JkWRsUQ +l"'2;6GBtҼjpa4yФk9)'NƘHoz"w1ϲ^N87mGpS{>p۹0/fB'$~Ǡ%>4JS ǫUXvlP*Ҕ,n8 Ї̰? xiVE^?˪\!^ c]GZ[ fҳ' 3avcÙKy^,'%__^疛 LzXiPj̶ 5Nz9v6'3l얮vmp Gf߄8FL%m+f0r6#^[ oFk_uf3u|~Ƿ8y:,_FgtDvы9Tgq?otR/ә|?d:? O/n*P1'b3P׸c|q*) endstream endobj 2003 0 obj << /F7 38 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R >> endobj 2001 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2003 0 R >> endobj 2006 0 obj << /Filter[/FlateDecode] /Length 1994 >> stream xڵYm4ί*M8/-EJ!oK/lCx;lz*Swf<^H{|%!əĜ;x1IL~z& wG.z O7PaGa |B=)>Qz -}<꘦9?d{MU&&H3@<34Sd* ꛛ\u'/iN*b 6h=kg¨A'G40pg endstream endobj 2007 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R /F8 41 0 R /F13 66 0 R >> endobj 2005 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2007 0 R >> endobj 2010 0 obj << /Filter[/FlateDecode] /Length 2560 >> stream xڭYm_"\Q޶KsPMڒ)Q/k\b93*`A:Vϫ8`X2bA:LZUƲx9 x" ~(EzUBx\Wzk?cr*y[Te ,5CɸBڏB툗7,@u&ɧasE_uQ{v +G,M[Z ~"PfٔxsSȱ`0$'`0L'˺DhI~"&WL&b4oٮE2i4g}Ip3+6/!` Of۬ovg=HLJ?'7Es9/@V]A/g$1)U]i{R4ۜߕE hP#5Bmu_J-xw [PcSVۮn0@o2kTCʝrGPr&Ʉ8b9RogD|]8UShtNH# h%$Gv@sj~d_8v[K36S#k[WԸrػu~Vi eݟ2iИ ,j{F]EAʒ̵̖x\83!6 *) t QRA9BVyե-xuL`[P-jW ۷؀Wxm}N}%|pv3\-mN <I!*x$~5k?π \:!y}P Ų'q:La-LYhVsmSZ7!D0bz3!?> endobj 2009 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2011 0 R >> endobj 2014 0 obj << /Filter[/FlateDecode] /Length 2239 >> stream xڭY۸޿B!2Ztcshq(b˻ٲOoE!S,;AEԐ 3P<3H{=$yJT$Dk/:bX,?i/:<{Խnk]V5hds?4WףP죫.u#I3שW< ﮽jp:a$s}Օi#"ƭo`rg7˜ٻ^NG Mz:TehqL"0BE듟AN)F*»?f!WOT#^Vg 81JEzY uoYڥ7nW%D~rbNёW+^ㅫn &u nTwg ~JmQE"_b^3X0$[,AV724L*KeJ?Dzf<NF*jrO\}Xv.~?RڍSS`xQ u`dE5,ͳ)(a1r"ѳŗ|V J(t.f+.*\&٩klvS_"lwYn.Bg>w@`ŗ!1f)F+`b K ؑȱ֏h|@? 'N2ijpuesCC+I~JZ/#Gu>Sc~uk2֋bQ3ur5éRƇ/qϕ~@0L8aOʯ bnܲqr`$hKl85K4{S^s:u *1҉fb}3 ԫʿBڤ0vi99`Hvî]91xX~Ң͓+  4lNu5d?Rjkvs) ~8)O+xO9ԅL z"Up[n1'Q*9*'asC/_o2 _Mh:{u*v>O{NU}% eDDbr';D#XPNp'kP c^Of^dL/_a˪xo{H]cCHL F=y2ohX!^-qnhą9W#/(#H!&}cw+t %1zCqϙpS3js!sKGwnb0dvW3o:ZuwP($)F߀Aw5]h}6LƿT$_x67TSvR1h\t҅Fpz?: Nn@We8izik#$ɩV>ׯ_k=Wѫnt׍t##_r؝eч< E'y-4Q,%,Q"\`(^đ0wP)fiПI""OYmk aj&sA?$ endstream endobj 2015 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F13 66 0 R /F8 41 0 R >> endobj 2013 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2015 0 R >> endobj 2018 0 obj << /Filter[/FlateDecode] /Length 2147 >> stream xڵYKW8mH4:ٙA,vAe[r$y:S"ejrT|ջV! aeVO+T6bXVX$qy绷URw9ǿ2덈_s더2QnZfmQw{Qn`ƥC[o*,k\9g6f?&E!bjцfYh[N~g &,Ʊ&/uQ{v63fx)WwſK7|3bQid7ӈ #7UfR;s'1SgHw JoOq8H#xPKj$q:݄91bydKM/Y\Ngd.נ:߶vv7 уNNvmZāh<ŀŦup|tϚ.YIo&;"#_Zk$T{Ddֽ$K[z3$#}\K'lOҸfok'r\̢[8:S1+qs,~[UDUŘ>3z<hܱmxrwEs>f8-m} ;l6:eI워v)p2ChF.cOW0hZaDx3v9Y6 ۵>tw[@m }F pЌ;, @ 6f[8jl(-&\Xȏ; Wp5e{ʦ$!k1 SfJv+r_O?}6{*TH HL#ЩB%L{I>>;{_O]܏XHt'q̄V ۱!;9ߏ_[{ʼlGV@L .X_"+fY +勅Mfj^!#P1\.r@ͧQ[uEQzIw5wȟ[zh;eZ /Mҋ HFfBF,t;L2,Wa̤ f(J=sqxI4!/u9/unͣC }1gݐ%PLc4Fyn'S~37a}@Y$רmlJ߇?1c endstream endobj 2019 0 obj << /F7 38 0 R /F20 182 0 R /F11 56 0 R /F22 218 0 R /F13 66 0 R /F8 41 0 R /F23 221 0 R /F30 286 0 R >> endobj 2017 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2019 0 R >> endobj 2022 0 obj << /Filter[/FlateDecode] /Length 2584 >> stream xnPvOG]`Y$C4z1e[jHؽ_*)Qޙ^T,ֻE! .0??(d 6JP)?߷?0HYn=g" ?Xn:^*_kʚ@Y{߬Ǹ'u´BK`#`* 6B1.Ͳ8uaNHK?,8'\|؂ G%Q4( (q$ RBlXCKGr=S,TY͢RF,M3Aie@26FUEL%i  b:y\È4$)FawN=P/ʭeEwSm^WSY\oP]gm?/F\Gf7] 6ΎzYZR2*^05{! -:+las}K?}<|g'+ !EEEu<0* I(<@tyXfkNiʮvlj7}o~^d ,@8upE~88E&Sb]g0F-iL&D,Ndc7i=Ɏdj%<ȃ~I_@PcE:1$,L} n2z Auk}5RƁf)2:aeրzx ]'w)b+&O"*MǡE˴#Ȉ9!Ip_H+)\S\Ǩ+&P4I+dqC澪ۼiD( 'BRl( R.z4'a1@4hb_\ 5r°ʪf6Eݱ&IKbeN\6I۬_JqR>B8pm?J&>kAfQfMCkwEfU^ݣvly!-<ӱĠ@㨂angdHh|`EGBQ!gߘ3ȑqjlEHk ԏ:,:XvBXq O&v! N70E`J +WuՒ! O+!S42&*ivEdu|{GLylPѦvn= V戊~8-OmO5٠ޱF&[%FV.Y u5 ;34& ̋ V{C:TXn1M(ݥ~b_7TEb`p!DeqE]lo) ;j NF O,YД3(T-z/!JhPގ1v>mVf&Nnjgs[b*,)-ArxG=hԻ$+&W;S@>'@W(&OZ7-ps=Tto*Җ6wGz0GҹCw#|fIfBYg#fqK'l+:.햑&hZ'$bw9 bt+Wsin;r=wv;8 "Ez߱e{9fY'M/i|zldt^4Jb~S"6Wt>a$n*kv; 5%ʢi oCٸL3gP-yنI\`_ %5^-<-4n i(/ _8Pz7o91#J^zޯ^C}b;=, T48TC_twW]@3J+f~ɮfGH ddlf{z/Ӕ*/VE+/1~ːN/)\\א;Gw!Z~TprE_c_sx;)͒ԯ+ ŚE/,E eowbh[FTƧ ";{9!J`(bƈe /p&H;aSN-/#oɏZv[IbmyE\b 8+2<"FUyZeĢ`Vcr|%/fPoP)im..|QQ:z</wL*Z'䦰\ER[o5ҵqb9޹4r:6?sgwW!=g?b<0j=nXmS#]-*dr>凳_4ogyc3ħ%FiKF`e.yPmGW[].y,FOpΊUskb۞Br,Ud{/W_eP'WkP 3w]Lu@bq endstream endobj 2023 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R /F13 66 0 R /F8 41 0 R /F16 138 0 R /F15 112 0 R /F23 221 0 R >> endobj 2021 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2023 0 R >> endobj 2026 0 obj << /Filter[/FlateDecode] /Length 2379 >> stream xn_!ljwk^DJmv/@ч4oMQ؊[r-e}QHӊvQ!9 #m?ƒ'/`*d,E)wߒ?!K TN'w_"]h8]l=n9oV?Agr?E$uOmIZT4dlB18h g0$JES5V(V?j9ch:͇DaP}U׏;}9gX(/;*=C@$XBt:-2YQEt%,}XbZ=/D5n*"ҰZ93c+$0CEOt%sz@8o;Y,oYl<V ՍW|suon\×&/߬3̂hT nD~n. f~SAF}4By$ug]HA:4lK|Ym`$Q;"+R̔uS)׫տR1BNlsS\?)U Bt{:!0ejL09P齱]71ʉCwʒ91.[4)gR!}L`j/HY|~kmVOf =m[J+s;i}d 35pKpD_).N|q&O~[#:9[.ݡR?D,)ڵPȀr=U(*T.: 'Irn|*/KNKQ_e]_|+W_OM1zKL/\Mp. ~[E@V03|?A(s`;gc83G8@81sJivd:).t>[.jigՇ"xMyvfS^S~̖O?Rrh,,_~QTO+BWnC^~ b}>|yW4c+a_ˮpvhծz }̹͕:9 ?x.JP׽I|KgN> endobj 2025 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2027 0 R >> endobj 2030 0 obj << /Filter[/FlateDecode] /Length 2309 >> stream xYݏ_5#l\\d<@e[w,|-iu !5g~CBÊoWO$^mXju^E:_>'U.dwPfW7Fiu:ALحߥ3ǫx T `NIbyʞ/UO+GF>1/VsQ7SA7`ف~ U빇2wBIYéw@߷ST 6on:Ϋ\1u*7r MN ȥ#o@@5\v7&03f"֋ 2#AO"=Y(Ibrc{*ʏ>I,2\Vs,Dj!f~ڵIOf& g. =bLnӏ ?L#Y5G78Dt~bȸah1JeRʕF &\i!ρ_EM9gu@sK5 +V/u}/~Md ?ً乸S+@'g7fRX{|*Wze"j(t)C3ïth>Ĩ`O٤wq=TpЋ1 j.T<59X7ꏦs YS;j8s,b'6n hZDwwMI~{M3QA42#,y1B Z7NΪ*I:ޤ^kMF*SӽTaĎP8n Jܺ6P7GJH =rE^ݠcaz ^ePj2G:*~E_,$BtTBܚMLٶ%cSEО@G, h,w^$ty&xu`Ÿ ^[Ov,l#cl7-@[ZkGt`gG1H%axJDyK9yIs8H~5bŶʪ3Dh*SUڍ>0caiģX+a-X γNnoﬦm1<ȄP[zAV*T`_KWM6*NFz@ UKoc)(Ym(SH@~jC9M^ p?d}hvX%΄92CX3GXţڊr x2 {E zuMVY.0v3' |@dg >ÖrCtU WH[hPD= xd! &zB1-hl,~/'cDG2^eF/cH_|(c+砯!Ej8F<ᴟ/U 2EQU ^4ve("_]`zzH p+q&+qS!MEq¡GTP>LܻR1Đ2% qHo@-F TdC5pYU΀Jpmcq)Uc˜G*f:T#f'.3f7Nk#FQlzN""o2Uq)ϝ( ~Ӏ5\~_ܫ#-􋞛'VƙA qʙ2E9 5濗-DnEУp7 wX{ : e{13w<&`q][ NMn_-&A.2 nğSY6YJ;L~&-:/~ۿ]fT =~i6jn_ endstream endobj 2031 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F16 138 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F13 66 0 R >> endobj 2029 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2031 0 R >> endobj 2034 0 obj << /Filter[/FlateDecode] /Length 2565 >> stream xZKsW!K<0l'v'WE,{ $!YL@+)>`HJڜrИGO7D$&q"#\$bDpAߛ?&yC2H&'7wMsJX6ۯSٜ Pٜs>bl.~w8le^]5w?0>I͠L((r5hygڰ MI&mG|~}{,I=j1Є<Ș0M@rp #mbW$I3g.v'17 nTd#JIVu$gu\f>Lqۯ8sc[ z: _J==hr8V3;99+bSE*V4AZTBHIJ#\=2QMܪ3`9 p9N#_)XJhDx|r^&kx(.*Ŕ$9my?#G@ݫ:ld6姸@H"]`CHjaX9%$`ڌw`9\߲~ӂt 4_#qNB7q5@ץf닢`9앖1ݔ`0NTuJjIfU޲XBO H)CѫUW-egЭXDT J*~C~̷,Q`nUbG,(emZbWTM9>Uaq0 _Q3^~cMvYPȈV*Ӑq& ΪП #;dn,2b\k b"1L`> u\)ĩ%1al],hayP,ELq0t 3u8Y!8ΡbFq&>Z?b0ww|hoN覭cJT@MpdLdpj.c,#4sc ͈sf~WO ė6E.wЃډPUgń!GX6qCMՉ};qghP@u8/9^Kn(^,>Ұ΄NćN *ɡɠG&|!q&:c_ǗD=+3A!IRBvLϴ46 z9Xs^nY/͆E@`PɅ.dǻ(\ "u?ca.Kr\*$,Oänw٭앑F*a;>Ku2뺗ًB DxڊjBc.,U>V_T@Z %;j3"$3 =]/F*$ۙ'tA& l˗W\!OD:`94i4THmO{VGg֝/ Ki{d88aL /ATWp }jl֜3Rj;03R3 Qߏ1G)'; C/)%e-ʦXsEv+zn%kBXCl 6*7y{FY(tBįexO_{1s/XNm/vj*xDny\?òΉIs|ꧮkx7OHőBSon+\C#y6g1ֿ^y5Ծb&;Ͽ<0c;{l֖4Ptz]9u\:~-u=|%l?`p endstream endobj 2035 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F16 138 0 R /F8 41 0 R /F30 286 0 R >> endobj 2033 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2035 0 R >> endobj 2038 0 obj << /Length 58 /Filter/FlateDecode /Name/Im57 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -131 -206] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 2039 0 R >> >> >> stream x33T0A(U&zF`!c X~K>W   endstream endobj 2039 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 5442 /ColorSpace/DeviceRGB /Width 350 /Height 380 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 350 /Colors 3 >> >> stream xIDÍ/ar@Aa#¾Cqۛ@ Brc}ͼTT쿷rU#x쪧=dNժ38?v+f߁WN;rȔ->裃~>-+0Ͻ'={LUmb'g[`0,a=1wޭO;oa駟NIC=sհfVWs׮]: <V=j|/y +ZAO̝;w4lnn:oa_Lj+\a`Vkix眷Mlx;w}|MUc=V-_tE;$zGK.m/rAR~`Vꫯixoq߾}-\㭷joa_W۬m&{s=~mm _|5߿_KVB "+)<>X{=1J^x^a޽mV{yo'裏v섙ypu쥾ffG.2ՖUK^͂ -`766.Z\ Wix뭷5C V}Xa zb{: oVCvH?޽Gq+D^a`+y94;[XS^w{5Ӑ+iw\[Y2}ٹ@rHi 4@@Hi 4@@Hi 4@@Hi 4@@Hi 4m6[[[vS4% #iHiO& ys" 444llXb!)>ut!)>.B[HCRH|" 444$ # bDAi(i7?! @Kt1H<0]0 P:# ꂶ6qk!̷Z۶mv; G]PAQ !w 4 |A0L"Cu\! # dTTKui@?LrGm2y44HÔ"؛ 5!w]P}iP!yu^&-ii)|4x\4$suH~FEp 5!wAQT 43ICHât74,W 9* wHPCrG'B4(Ұ,ѦiiXs! :4,EYEAB ii(,]PҠCHCfc!wXN)iPs<i(SkbT_‘us44%.(iPaVIM$Ґ;0戟>REGR4ּ7Hpwko{$^$Uw\ia.-ii4$5 !>0/ BrGf0}?UGFf01 YLiA8aqͅo^y\iʐ{>LZޡvu0^ DRdӐMUٝ&i)_!ipϔq`\CHCھ9Bo(B^G#G!Qρ@iP\p-\ ) *p2THCZҠ :\ " ("44$.XiP!0"7iiY#;ZTp^aNiPEɹyws44̠.iP7BF0iij97~Ԓo!E YҠv" 1,B iin]PAQOHCX!w!%Ӡ}}! ,|tΛ?io7jHCHgt= :LC|b,VHCH7 D[ iP H DҠ85HT\ܑIBtҠׁ448buK* 5ip8Zgm$07" # #ЅLcUܑARcũ4c09H6 : ig!w]"4(0i1t&" [J%M_;S]5'iPV"O{?V5BO? De][[SQj]=ضr4~[8êбA՞ӠIP4?^-olK ]54ƃ=T*!{W AP-ԐJCiP'Ɔ16Ґ=i! # ADkhSP=5T:P-54dh ,U4īdi ,= \CA?P4 Adq$- C쑆O¦//IC͂`"O8)o R`^(fczTcc)i~"H- ( 6KLb@TҀ IC[ AҀ RHC>01o/`Ҁ JCo @A18% t#Ҁ "a` NALèy4 8iu8c: c`p҇[n%i ~AJ[ N(a_~P!0eHrałD;! 򕆉Q08Š KŒH&iS40n)8HC'bAqr! Ʀo NKOb1* t!A14i40p΂s:i`=],(N'D[FzAb0y'.d4x򮖆Q08Oi{"9RޙDXPDHQ*bF$Ec`yD @clyQE!Q4l;6F+՗60i{:M{.p+9 //ii1(2NAH,q9c Y# e9*>d}@d/=qލL~aH,љN Ґ;*ZԄ4dtFZe1FArGZ?FSm?AZdX O"eQo Ґ;c{Ԙ)&#- iisx͐.43|X.$4=ˋArGĥvA.4 1 ArGy%nl5҅,~r 4Ѕ\~^Fr`ܑA&uA.d4 <44vlz;ډeϫn[j^4*̮%|_w(ᐆ~a5s ! 8V qAnQ wU1HC2Lα*i 4@@Hi 4@@Hi 4@@Hi 4@@Hi 4@@Hi 4@@Hi 4@@Hi 4@@Hi 4@@Hi 4@@Hi 4@@Hi 4@:묳Ymmmͽ:x>SO>Co-\# @Hi  \?KiE endstream endobj 2040 0 obj << /Filter[/FlateDecode] /Length 1361 >> stream xڭWYoF~ ԇE&Mpm- v`%HȏED-j?1;3$(IU0. 4G 6 1up$H`1"2 ϫfQ5(< xaf]fú>9;OGyg ѲL  1F0luŘ )ON,c$>%nYԂ/IðM!7+~jtHL'D-:NI~4wj @S '5y=r׼9vF E5Knڦ7EWCP 满CUu}e4D dF1¶m1ݩeHwcmD{%hJQf4GRe?h1iEQe̋Xw _mew?[fj*á5! ]IA>E -e׻S˷kݺU $*K.ެaRDD @`0 j]FV }}2 h ^sihp&[wpYv@6: eD=6m]/y#PB}(,AGNh]N eҫy_'P [ $C.t&&Aa߶u&ëo&_$yaf4I&GY6>"tʌM ax"Oq] Nb˙)pR- f@!*h -fn4}d ?~ H Ibkf{(lɂ~B,oשOVIjT )},D`v-|NB }*tr.:M$i@ۉmժT"'c[WHTX#NƵI d5mE =R3qoziZ,n2?A5Gm H&nۭ d$K3[uoRYF .MC UCuO6iXZݩxP ܰ@DY*_0]̈́A.,ܣZd6Pø˂b]l ~5P7=wڹ_^uY_Bw؝ƾ̻BYmm~c endstream endobj 2041 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F20 182 0 R /F16 138 0 R /F8 41 0 R /F23 221 0 R /F22 218 0 R >> endobj 2042 0 obj << /Im57 2038 0 R >> endobj 2037 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2041 0 R /XObject 2042 0 R >> endobj 2045 0 obj << /Filter[/FlateDecode] /Length 2310 >> stream xZoܸ_s-<] ŎùhCV+-$m -;}h>Q~ǏP#k2)W[fۜzT7DɨFu|-4:-kTYi~ E[FM!߈qC&nѪ2+B/&:r&|ay t)_YU(<CeJrGkk yfۢ xR3g|kR"|$Sfym̋Auo@{L4D*ʛ*@ !#R[4m*4D:H=! ) Bnz֑ `_B)&l3T滛%V?%AYrGeT49(fE4QEVW[\L3 i[(蹌--@e¾sfr IOD=z Y'Un{Gz'Pp(λ}'QnW̊ϙHݮFw.qy , :Cag]%5e>nFL,Rmj&g MGYh?a口fSF4EqdE0mvS,FK;]MPM7UOC/9c Y"xk^GG;$6d <„!+տ_8aQT1#ST" To}Qgu=R^s?:zo5wIlZw(KGc'\1`#&xW|aPj!1b9 U7C8 5YkJ30\,OnJe( f_a `kՍp](GBCGX|vy&epC S ߿ !`7IcjƦj2+0ˣ|MN}_$sZfgǞ:g.kD8FH%?.>Ќ\d@e {E['/mUxU@T-"N`RW+soSq§8x{> endobj 2044 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2046 0 R >> endobj 2049 0 obj << /Filter[/FlateDecode] /Length 2521 >> stream xڭZKW0̅Lyi[hS9h%D$",@*>rN =\PBⰐGxXDb BB~|/onat4ZyFxۻMQވ0^YtCN~;&D0\PKbc' XwXB MmxjW) O8${թn' rvNQjɾmg]ބO+݊wku1&n.[y-{?a27_' ѝ6p}^(w.t\&:|I'9^ Ō#]1ҴlY>Er8þdon#i)^w}ʫX ZcbP "D֜C_GkD螌r4sϮuSSTH`R`M-HṔc NMTƿ[Uk*Kaj$ qkX9'0M$]u=2WWW!t(`l!=X7ƂlW/yeO6".Y$J,hRcE`v.Pq2)kZn I hHY_æ2ܦzrk߁R"vu?6ja2uGspTӍ;N2vkb F BX q!~H"\F4lȡjF{ 82y3'g5.Bm+E$! (\ĢR툟 XsbkЋH29T-EYyFDCW$"a2šjQfPE;n^eχ>W6|q6w_|{D6%7O):â_d2A/B\x!˛/*ɝa-IMW0vc"qؑyY{ؐNkbDk]֧έFp嫺G>R5ҨM߾DQ8Fܺ}sLA1δ{zxfOSTYY~]':9S3.?XAőO}1sXN1 l~[ gΐo_\D@[b  -Uڒ9|2.Xu@qOQV4F5[3=ֽ]Ir!^ +CGfǼ*ww3K=>MJs:&P[?фќa˦C3.EV#f3ӄ;J3o΂>Ӛ{4l6DKpzDIU q<"\*<5y۪\͸;fĩ!ݫ/6#W<"JґVS ^?s}2U7觑ss_wŗu21\Y"E]V0,hU|W' Mv (0Q#Seo%"@@'Ryh"V=-%G|cnsf=cs1 Lňyワ A%[L.K@O`JK~'8AL7= OMRWpy=j>~nQrCT [FF#밁^%o ~:?v?2Z endstream endobj 2050 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F8 41 0 R /F13 66 0 R >> endobj 2048 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2050 0 R >> endobj 2053 0 obj << /Filter[/FlateDecode] /Length 1275 >> stream x}VKo6WVQ/˽mfhCZme)Q 3JV'3y|R$?>QDL&IuQSN׫Hs'7 MwEñ;Iynal;MW4b!6.ѓ^~{<9WڻoE]t+tC%sMfr PJvrAdQ'6Os@u"yUJ{!m(W[ $]3Ր'mOzDJWJHv.kN.@ZEγ<$:+xEdU~7"^x ai2}QZ'2 abkZU=*4T,:)zBU!;"{SDS9HKvXl~[e*r"&5-JeC$02P ",xeV V#E;Bwy=YQ)8eXb"G"TkD$=@FN -w>f t$/sxS kUc' D`JNBFpNTcHVO69j⫌34)ॅ$PMuX ; 4+`ͥ쫄Ҧt(H{y0R.gJ=RGDݓ.*BZ;ׁzz#`8VK0d 6䳅1Bn`{l;;A&=q::6:z8pt'1kqkED#GI7T';+1Ẫ󺖺>i*#3\¢Suz _XiHPҨNyVbZ/8D/Nu՛ ~B(bX @]|NH;Hd8 ݨ 1_×0ܩ"ٷa= t%؞> Nh#k\Ԝ 4og"Ӕ>a/RIA|@(Xo~X[ݼxPgj3˒%E~NkD_hڄL`,4 X.vm\_M= dp!?a#fKMyEk;hEO ?0j&]KdQhe?C2V5 mCJ#JN_>꽩8/73 endstream endobj 2054 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F22 218 0 R >> endobj 2052 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2054 0 R >> endobj 2057 0 obj << /Filter[/FlateDecode] /Length 149 >> stream x%=! _ё@ʨ#:f.\bB/P)xAh( ށ4Ρ&vEr'8-MG4n7@H 1oz;iNrZZ@g4at݂C\+B endstream endobj 2058 0 obj << /F2 14 0 R /F7 38 0 R >> endobj 2056 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2058 0 R >> endobj 2061 0 obj << /Filter[/FlateDecode] /Length 20 >> stream xS030PHWS\  endstream endobj 2060 0 obj << /ProcSet[/PDF/Text/ImageC] >> endobj 2064 0 obj << /Filter[/FlateDecode] /Length 2797 >> stream x}YK WrYwUWwI6V%]C:Zmfdl)s" Ui%ßhV_Wy'4DJt^c돟wf^tߤᷛxzÿi?iǬ=Hקxn/he+W*\5]w^oz Oehm*PsX[/V/fy{^D(ִG8adkNЅjW">3d?EV;鲾hwe C|"Oc|/'Fה MeYIgd/6)u)]z<?0aE?tmTHq.v6~LiCMb(3x_][#cQ:"6)$ 918TgK7#L,T|*5K&ԝ5?j^*_JI36~[зb7 x{wgE͉솆{zruPCDsyM]/d.4/?\D ;Y;&gUD*NF<6tQxwA²ba1#+6mm:9럁ĽlGP} 6%8O>E/WS@S ?bS !#0K~FO<éW?~FcExGyqhʾ>`$4^T ~lIQ|П1)Pw=cCdoÇd1`9X;IK| H?O(؊%FѸ@$A1-2ߔ,g(@m$x}WԐ pڼxhߕN/óxP̗b0gA +/-gFD7XuׅGYb6RrnƱ(*|sɘ Dcb٨Kia%޵,I웚Ha>MH;Xs 2݅=#?%_GBzn!##mT~*$)4YC[-X<ߞuNRtoOV߆2 &d7g5~ t~Ї&eQ.9USa;[.|$ Z [ZBaCʗ/*{dtDX^1]9cƩhjyx9 -n[ I䲻e٘ΰ6}]b{fKoPdmD†b5^k#Mkz0 j$g[#ukgIk'{9EhlHrQ/*gcbҺw]?=y:;g8VdoN:ޥS@]%CQ6yϻ\sT :&x r Taw]v\}nGң @*zBeB ^^9$fxlߋG%PJQG rW"<6\Ѹ1H"GN㨸jEx"hīF&/z- |=Q W#B mfW*iW~AbBBU'bu8 ,P]cAr:QiVk!C'%GJ%CF73444*foF?v8xi[((MCqR %٘ZIU\$iPM*ACmu@l˅Kꑇ@KCsLYa4v(Nx50!*ոs*--\[-(ܴ<@z!*[=(~b`RG1cqDHPK!H^9{cgX8meCIZ+칔e%cEqav,x"m̐[hȗ%F$$&1]wΓ#(f^p^;{rGjY,|Nf~ž&oiu~;qަIhMZn9<: cFJ fN9'[͸5BU״u{B7kL-I1 "n֡Nۡu€RA+`]x}yߠ* 4{}3VK. nU&30F,k@J 'GqwsH>_LݓZRf<{ƾ7r`fD>׶RE2SYxcn0p*4$#w!-Lb%/@;;QN#=!I MM"i̤߈7ߌ_RvZP'}"4\ ɽv =ʖ7hDZ uV8",xZbI1}ҚYqmG-õe} ޕCci>_?^"W endstream endobj 2065 0 obj << /F7 38 0 R /F10 53 0 R >> endobj 2063 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2065 0 R >> endobj 2068 0 obj << /Filter[/FlateDecode] /Length 1011 >> stream x}U˲6 ++=c+eҦMgwi2m1#JVƙlCH4˒KBUrK,mdWVi&!)2-'_wI6ur@t<s` Iq?JWf[Ϫ ͧ9!jy(%/(~`zd<-/:E{;;yA;5~ϛ7JWg0T⧋pjJX\z->i<믛 猝<f,sק>r7ܹNZb,b'@jGgZD /HMbO5 Lp̆-{cP߂i!w=o&tˊ4p~+[B`}3tj-"A 8Y qz~e}1[G++jax"JuٴJ7G^ ; F b4(-ujXݽ&t*Nv=&ƅI, D$)Ǡ#] >Һcըxwd/Sf@El2yd͖ jIU]ـ0QN~vN\=_;l]<˷ 9gUHMP3A1ݧZfӡI%Z|)ayOQ$Y#T4($E4k[gGãsgUZ3/)~ASM+Bx paVق> endobj 2067 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2069 0 R >> endobj 2072 0 obj << /Filter[/FlateDecode] /Length 1768 >> stream x}Xɒ6+tT&8vR*-CBb. AΒO/EF~PϻvvǝL> _KD$2TRbNjӞθs>EAwXkAH}"wq696֜Zj+&6V. ׎ 1u,Y{e׸EkӵLHXv78rXSݫY/nJw8r7 yGW?L[=jHҹ>`a&@{Srf+SD3e`353/Wk{} {֝zc57<@<еV3M2I̯;b P`W-3m8ɣ%|WkW|vVV, TYjky/ۍ}IcPLwx$xy!\1v{v C ƀ9!/Hc0C=!\޳XvuC1 v`o0mZGSd&=1Ad:8Pgq1^+J)IX`$t+8w TtZ V9vȍ*Q݀%jSoYkt p[u{ˀLH+audF2?<$,Cęnd:X7oz1h~Sꀞgc]й~|/eAoyi92]]d!F(ib1XmsHBLiԀLH0> 9uXydZxPNR-b2`_l:v1|?CAk^Fz|PMřpK ;+F-+H2 рu`ƛC.d2{dEStDś7) 4@8> endobj 2071 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2073 0 R >> endobj 2076 0 obj << /Filter[/FlateDecode] /Length 2470 >> stream xڵZ[ݶ~8uQDCKN܇qExn IJ׭&$`/Ӧy?B?BQ\x&w `Yڂha{tŴ+ϗJqF)d}r}Ǯ9/:*2CWt֔^dۗPVw'].I>\@Xɦlq-tK2&#HjJUӶ*HP8ZYf2Ƕ'm᪟˪.Bl:[/ReZ1"Z3yS0}ʽ+FBuHeEQc_G6UX۳L-[6.s=ޭQf gꨎSK{_Z0:R]QV끫͞""4fʊ F2 "}ct9,RZRVR U_,ŐU u| ҷ{6eWQ)_[ P" 87eO)CеAV ^7f/{yRk8xz8 8ªNx~Ղ1(a3rL 6yӟ-XsC0&[8×]ۗ6}5yq{N [P;k[xL]KDőe KV,1MI&KL! Y'Zupў#,,2! ƀ;eEk82&L[ao ʙU+sm?Md%Dd~Hz^a@[\ǼL{e$!|s|aiO3;N2ּ;Qnfy'j5|b')BgNrAX#d|n- H~B]c+ N3T]3Bqb ?ê3&.НcP;l,dL꫃ c<1p\Aʘk R :ȏXh5tf=Ts\+,h_؃>:Z[qFc$#R ָ$$gqI;.IFi6Hyi.Oq^X(e6!9Y޾:ڹo {:nE$S+7[>}|?[l"jH3R1KgVpߛ$gEF/n0^?}vl;v_{gd axjٟfxlY, egtHs,YAbA"%X^ "1͐G*+:N3= :'4w|T x]2)]*ʬ7\;Jk{h˾/F};/K w屘4YǒiCzŁσ9}O]a GIu &)iijkjmzEJ7}U~ڀQtNMuztVZ-R/{wE;H9q[t:6jGq *Zip~vmHvy)zݕ9i _/4Jafr%԰ endstream endobj 2077 0 obj << /F7 38 0 R /F10 53 0 R /F13 66 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F31 329 0 R >> endobj 2075 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2077 0 R >> endobj 2080 0 obj << /Filter[/FlateDecode] /Length 3048 >> stream xZKܸ WVޚ%QOrz׉SqmUvrQs܊c HMʇ$ ~6aq;\M%4MIX$zn7m~}& 7ePf-qnnإpn۳4VZl_ݩizjoi/z>t0^$ns3f_LvqG~#a z8ry*'k>yUG9mF~{{nBpT(^NsUЧ?HA zۧQŻh[fiL}]@M!QfB?Swtw6?^` P ,H_eD3?ox䲯V"{/}pqgGAaz#6Qʰe Hycau% Ȝ$̀x|ħ\<p+ymuA@ xM&(@ U(̮Idi& m9u a\iE qsp2W/Hl- A}ϲri VD V:W-:$> ěͅTUO+vKs 䡪e8kdqD=K&^j@*(Bt"C45Ko(F\7`Xc3exWng-!𗨀($>4$(ֵ{Lt.*-Aj/<5Ѡ`XpO rȃ,"vo$7N+zjiqC3pQ5wO`&U21Tir8dAaCXLhl(҇̆Vx4xy~o~nŠ!2\<4%+im} AF(h9(i9 FRC%証"i tξZY;(;Y'XSD!b,b`ɢJxPRvN[$S#9^TRGXj0e)0Id&yho-gjLjTY_ 1yxjc\ W}1XAAe e IGαo-gY!(=f+*Fʌj ?i^:ڤyEI VŴ B00\㲖[P =~!?KǝHJURd@ȏj&L*p[y_F:%-gt!%i-hlwo߿iV,HZ-Q$s,[BYd?7S]ҕ];?Cg%q:hVY$-|h<(L@-gAuK#iMIiV,HZ,D!ce $M&O#t=Dm;kx>Vscۖ}ӬM OHfc';F}S}RKwsȳ•E"Wr-gq_A:0'^w/<|ΡO/uE0)o8p"`q/I~Xݵ\)ϹZwCrAt;Hcg|[@0-_aj2A+ϞXjXWk xSZHAGIY^􊎋Yi *NneNF*JPNgT+Gx}C/p©& ) TMnTW#NJS+V9T^W[?(aY],~cΥ8bA7(Q(u󃔒Rڭb?j{Q4H) =2'̪WKIV<0a6l!kτ _ M[*Ļ T4`"ЋFQwu[|*4TXp_uB*fd9qMI @П`9ObؑOR8E5&\!t;UgFOU1j_<72t}+iY.ܬa@gKjB!Qh.*Cj>'R % ^+QIJ*~ã{vL jE2B*iљdbY(kz/r9YaKTe gx\G P,udIW]"."]euʠ DaC͏P|Z*NW+RF0UHD7R4,c,"ATU8bR=__'.,ce҉0՚ #+[*Xz NMU; TfR|i>,dx+ڳE[OW]yJ"\N H2-Ƌu,c}/fL֑ bs+R4 x}@-]b!Ψ]>4#[wN%DtO_ endstream endobj 2081 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F31 329 0 R /F30 286 0 R >> endobj 2079 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2081 0 R >> endobj 2084 0 obj << /Filter[/FlateDecode] /Length 2575 >> stream xZKWfj%>7YWmloʩJU*37X B I̸ӍH!͌MN£htUp2?xJB&Y(VU$# /W޽r'}OΙWwDa4dA:ݬ7RʀG~I4gS?4x,,z#`w.up@`]QW-uU" umW>;vz[F2"4eG*pU`Kaשp̽j'.,EKS[$;ZdFR_ EgSݾ.I{la9o'ǭ-Vxv#ɔe#ay16YB`yhNr$@(x hM9XA* *}H>ҼE<':̊~6cql'瓞 fv`y$LrO vg{.`el,GM$51aDH=4*Q`, V74h2_HU{X['heA"bqE$IApӚ&N4;'z9Cۘs|)vF_81NU.F a(a }P;ԭ&^;}/nfUg7l45ӉTt[^,yp0@:J-Qx&0d69h ƑsQJ hDʣl!l3ܽ3$.NDpʶ!KlqLG4 3; & <]gԖte̵?AZOW3*Iu>!!2{v #RҒhl }PCˆ1x(N4@CAn331vCUŢO!,WuGLQwMU%+ Xڜ!<' \Uagɷ053v]٨8ap&H(>$r7s  Z2#2y, 9Qo(&SQycTqm̀JuK= f UQ"O x1ܞɁ牅 |I1 m&ڻ j2ea0 ƛ1q p?1rX8'ZЂ 6@l7Z"AH acX!;*?n&:h¬gb$ihH$U.RMS*,via:HE&w!UBW󔺛D"E@_xq7qkMueBPC<ԫrRvͫR7}Z6QA7_ ~2)]pKCTȂ?cDc6!tZ}ڒd5p .mSC<숀y p3m[|FA"#>p[&B ﱥ>2 vSԢK1X(iol*ت)k[3hW~RtcW<94Pi-/7Ce8t_| ^ZYTFN5u! ўʢ H,f`î_}$f@HɆg~;M0rqr?/)tk4R,loy&\:]D4Oh/(<+anwI᫆sLէR&SFWA,a{&a?D)%^/j4 ,ڙPyͷ |i4TCl!T gT^Ѯ 6idoz[g/8`2~^PӀ[Mt1giUjg{aAݥ$1+GK#1l|2jLxAˮ^U}ޯptWnV>|ÃιC/߱޷sLligp}񱲰|TBDb8/<2 endstream endobj 2085 0 obj << /F7 38 0 R /F16 138 0 R /F21 215 0 R /F10 53 0 R /F22 218 0 R /F13 66 0 R /F8 41 0 R /F15 112 0 R /F23 221 0 R /F31 329 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R >> endobj 2083 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2085 0 R >> endobj 2088 0 obj << /Filter[/FlateDecode] /Length 2774 >> stream xڭr힯裺J$WR$n$Lm+G )ў&7E 7ABǝ#Ļ]\2f!5;)$~׻w#(8^j5"NBf9Wu1V]Kkq}L=8ޅ sGbI&$b ʁK:}1*F ^T< pQM70J#< G>08c_4M>F]Q 7ׇ!r-MXhj.糦@IJ`Cu@(<FbIt'5 U{c_a zε"x9ggʼn0^TRQ`|'sehI'; m7n8aR /u1a1[YjaQcu9Ǣ$?`sۡ!0 JCKQW({'z_e $eWͻB 'dMlC0˺ZIgqkK9}9ZB2&# &0%vC=H`j7q/9{2P2aZ~a黝nc20ط|EkcjByoKN x +%7'3.-#DVb {i@SځyLѤ+9TS4M3͂ 2-_ I!S%SՉ~ ɲ7R~өZ,ڎz_ݢvJߧ1L Mb坤LXN V?!rvU[A*qE1αN^hlcYw5{@3~~(Y3 " +*y &zR&o+uwV2G!46¶D{@x#~D/ [b,{21 ah׏2O}ZY0t+PlC1 L{Y :² +G0F?yE4F%D(ܚ/6\z:A0Ɓ1nL~h R aSדTaZ8 ~In.I$Q({pBS ̿،Ew5^M([+;{ >uM<Q2Eޠ`X ŦƏʺ߄9l_~9| endstream endobj 2089 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F12 59 0 R /F21 215 0 R /F20 182 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F11 56 0 R /F17 145 0 R /F28 268 0 R >> endobj 2087 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2089 0 R >> endobj 2092 0 obj << /Filter[/FlateDecode] /Length 2040 >> stream xڭXK60$6# $; .0 ۦÐtzVHݳADdU79޼lrWbmTLo77_?|mY]lN3;g|!;MI%^%/8U-X# 354#gB=WpE̽ i1bW ԅh #Hoî%2ak`E&h֑߈i||o*:FYę0xuj`dzj"UԻɦ6@Z"D\T9B_q!'> endobj 2091 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2093 0 R >> endobj 2096 0 obj << /Filter[/FlateDecode] /Length 2265 >> stream xX͓%઱I MKzrs`1S<ߧ[-!y;9hR(  |1H#`',9ebaM)G9`w9yݩlet$ Ӣ?&.2XsY݊q*P߷x+];}Q#EuS8騥ÅPeYبpϱfi:7;~sG סnQkqaw/k~čd${܈,fx"rK9vyYPYDې]uemŲ[3 >1G*f_dN$ L(Or, 9pJN)" ".͍t,Ά$r5Ov/l`ĒY#>g-`fE$jJŅdQ\`4yGb<ʹdC,X ̧5)G0-o1H4yr1&0& _7[D+``y7XX湯]p-iۋmB,Ա:zOSw!8eɫeRLΜSn9ע1/*}g14( r05! dЋFM+W*¶&&VS(\d*0bEs=DW@W`wWpu{U 6F. JLgE.2RhKH  ia]D$ ˽""x"!9Vs&!b`^(³.e҉43C]lT\\ٖ&REх[y0>I,w ,æ'Q$԰i?{^}qft6KeR"#2 ȶ,V쉔ϸ|8vJN9ZYջ`Zc] .whb˼>9| XGm2DA2a]vX@4F N>AZh K 4LY"APs5 Ep248)Q{ I񶃀`_Mw` #{s=vPc2r)mtq#-a6I?B]!UQԮQ@cww3WN.{-/K$(5w!HMHOR@%@`ꄳMo o.5, bXD 1Iif(Bd_U SW 4:9AҩXXb%>RΨ(3J F$&E(cbnL_<9y`,ݴ+{qRS`?:V "+艨>Sϗ-X{*&->7Nϛ61bWf|z É%g?K5J|˦*o v{ vODe-6&quS~,y!L׺ 4e4!pM{Xfl  q>7]OjP? ]f5Xz 땚bjoцңnaM*& endstream endobj 2097 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F21 215 0 R /F12 59 0 R /F13 66 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F31 329 0 R /F30 286 0 R >> endobj 2095 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2097 0 R >> endobj 2100 0 obj << /Filter[/FlateDecode] /Length 1360 >> stream xX]F}@C nM*UT)+!l^ZI{烱Xi463ܹ{Ι;YK \Y;`R{כo(n!@uG@丐!:)clC"Dž*Mvi"qmR9n|yVC2w#.սËXx5,zyaZk6hj+]_:vIJ1;Ȋf,ɭ3/nԃdk^\ |^^; <оE>-ZIw6%`Pugy~'ǪW|sլo|Yi?d/eD}H£[(.{fsq8BCP]ZF F$qxߦ2. G˔ExDpxC @}T&M¨Yyݳ# * O1Åq6#C:6O@qd|ݛ]Q\&q#0@aC $XtP8I;NbLZ+XHrY3n㙀 _d5nfWZ.՛e .fm~GNl[_m6> X |ؗ`lȾ-u9M߰qxxiU/AX큚Řh6nN(j,.^ĵ'wŕ˽ئD0 :"N݋P=mۚ\Y&ugv(]կ:nmt/a&P_~I x,"NbWȊf(F_\!0j\Fs$ӓJ(sfU2!?k;`8ьs|iW\c@\Og;rk^m@ 1P?(:XLrD ic'ȑC^,jHK.W׎we(Y[].ѩw  ^+Oh 8wN.Gv=i;bF=G:=.xlr`Z aPNsA#cΑ^GüG3c_^AeEKœt,Tqqj>+5˺nspgNMe5,ԧ%PRpNx^3*2P®3TEkµ$E uyuF<ǟ>\OG^5A :T:q֥T5:W+dxsOZtRT+EfX;WUO)[!?{zjж ';sLؖ~۔~sӿ endstream endobj 2101 0 obj << /F7 38 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F31 329 0 R /F30 286 0 R >> endobj 2099 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2101 0 R >> endobj 2104 0 obj << /Filter[/FlateDecode] /Length 1754 >> stream xY[o6~߯@̊$J2`ڭۊ=oM16k%Cy(Hv/{I$<~G{q@RÈy!IȚֻ~XxC/%i-[zJX-~|/6j6(ׇ295rQB r|/sޜrC<%!ZP&jxzƄ?c_ 3`WB?jrMY#Qq[bߪr P+ǴV\iΗZ%+Ū鱖P"(S4DA/|mZ"ONײ\suKVqMa7\W<lN踨5#!qЗ^>˪.)hGx,Af 4M@GXՈw\/}?3ͳ o\#W:<!Ttpc+&^oTKYz;I /uގr7&1$g(~f]D}&\k6@;H.ז&.߿u#И*0;!¦ROF׮0m4( q +&|Tđg팓7)0'~D A=$H)._$cP?^ L^z(yMBB^, ED[*&Rd <#'SĖkIu~%wf$|+u֐I!l0'%֫l BaH8-й!(iʨ.K۲AAoଲdbPJu]6anl,*[n"B?/Ʈ׹ˋǢЗۃ1w{$4;pΰd[U;!I(EBtq= ^dߧY-VWk\?T1%! Nz^O"NTHETYOh4=BY %iXʺth̻DZ`fѡRkgf(=a?U_J44O2&5w=8pvͽa(kNlN5Z˅mm 0Ȓzp@6BF͆,d;NBVǸQTvHgiוʸ\s/a4:~{^ڝ ZSfն+C̙bھīs`s05ڬDu{W˥v 5 Zf R'@dt&@_ kv۝3A5^\qoȼ̊C^3q}ByjBa{x Ӏ=!{};?? >BUj@E??dʽR Y>T6T ECxl57PS +t9vx"Ҭqe#3bcуap.0~lY4 NU.No/W:)´qn ԪâK8|| ܨeF>+*8i@ |0rUj{i &[整{wvfo<#+QktdI{htf@"xh|7d< ~罟o@ĉf.Wmu^C endstream endobj 2105 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F22 218 0 R /F23 221 0 R >> endobj 2103 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2105 0 R >> endobj 2108 0 obj << /Filter[/FlateDecode] /Length 1686 >> stream xڵkF{Uذ \tH.RVU8{}FR|gvX s.wȻ!KyJ*D{?,^so9O_q5 x%q6RJς<ʛ*yߕ@j/^sqŤBUrDYeM^x!O䄡H%R~boΤV9f[:W"keYMRj, ID*Zk$*ǹ\+Ņa_ ?X~i݂,4'm #, 74` xk=QP,$Yw  QȄ! 2E#Uj{ɚv-bHH5CD),6w34vOD5VĈAak{1 7z_,[~ eŨb$Ȯ{F8U(eJ 6ktO S4 ֺ3L[&ҸƦpmbiڬxxd9#<+W+@GgoteRE|36qG.Zg6͡Yj3Q+amq/ӣ7t,dw 0(Cʢ-˯Pn.B< x0ts!Y<TP@}UWHZ^S0\O\#RbsqBvˮ ]u (0l $!)cvAq͈OX",?A옺~ƪ~ꠂv$)mQ RHr_Ք VCG"X͙-ǜXL !NK@]?~<0~Kl<,`ОӉ`@wqZǥk s.~RzH@ӧB8 :qX9q7aTA=ggX4U1KG|ljS~!5[BWYSVg)[4:^4#mNnMJ{u_SlX CWHe:FӛԌdyAu*MQZH Iy BRӀ8뤳;s&d|?f(AN4v*<'!fj@qY])z#p56ѓ]MS11{O)`^U7aBha~UOfOO>>x8d$% /oN%HXEW"̇mASSa29vP;!ЌR ~@gI?_xbc~SGrE=+iĠNfQuJ'{39<3䔾}"B;9oC7d:bB8b2L6H}Yy9"3Sv(uo2f0jwi<9&PܔJRzjLxfn ,[ W endstream endobj 2109 0 obj << /F7 38 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R >> endobj 2107 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2109 0 R >> endobj 2112 0 obj << /Filter[/FlateDecode] /Length 1646 >> stream xXYoF~` 6܋mhm(Z&J;YRKQRR=9;| "Ql;AAK!IĂ] 2x|zFAF8Xݎ)aYpuK.mQK,UV/Zu2R2%R sƃ]Xrx!͂%r{MWFx)ud],iBa@ZG+"I,).]lJ&7jvf.+޼oyH-=)}]Nt(J2Xi.7EEja۩M[ݙ ҍI*RsGuxA-ҭ'*u7pߵF_Z`ɉ, , kY?/ftzcxxWyWUKf6|Z8뱽D&Vw3:2!u^;ç"|X78諢K,cSs^݌{N>)#Yr*Q5<.V =Laîq}ħ˔hp(*N5*t"2Y Eܺn^0fZش0dik&2d=D~'kDFdg]d,𝲶6W%^j7"#Kw}3PtoK|-LѱwWl6RDQxc|r#Il̀EIq_(,ya[)/5j?eդkTͻw~0H* Pڹm ZadW٪ODEV&6MvSTD{ƨ1(髲ҏSfpA7JsKLjbcĒV 3sq¹Cm_vŽ>)+cSk!Sa0.:y`D> YF)(,DlWFkvRtP;-m`Ncs bPs)h hYnb5Lf4|0h%=(Wn)!)O? LLI$|Ogq%OogډQX2xy|o_cT.8 J &{ CaXi!} I~B ,|jνó B{gss3xLq۔\`F{> tӧq~F~L$M9- *[y'!&,sa~xgc ~/:m; endstream endobj 2113 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F11 56 0 R /F20 182 0 R /F8 41 0 R /F22 218 0 R /F13 66 0 R /F30 286 0 R >> endobj 2111 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2113 0 R >> endobj 2116 0 obj << /Filter[/FlateDecode] /Length 1503 >> stream xXY6~PԹA HP`̵Ȓ!{p$2GADqo<"΋IoF, eB1WޥX{뛑3y_C/W-kxso0lx Y S[rC)DcI.fuL/שUҎʿZB@G)sqǺF# #I/>;DՈ1 +Bwg>\&=/”{)p>hT @wȫTa 5+~y!Q_CU 9E MoO!Z0\m+Ū@$9M^^Y?ֻr,{>lq_L'mDdݎvuCϱ4~8gmh^4 5yբE ]GP2 Y's*`?UFl \%*Ucpg,}.W:iA?X]y7_`+UQ> endobj 2115 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2117 0 R >> endobj 2120 0 obj << /Filter[/FlateDecode] /Length 2026 >> stream xYKo6W(E d!QLi(=4=Ȼv-+mCΈ^lji)r8n$."ѻ(KZ,UbJ߻u< 3Ytz9&:\ef&gQ_rO_jE9K(q׸.dZ tb\:ŎȄ'9%(;ZOF֟v8-Vs%3֪k@xJQYoEYN gn \0Y}*("9sۀe{ ֩H7\ӀdC1o4/,߬f ZfF&;**Tz? lL{2O ,}4L-$w?Zq:~Q.ȸs9(@e `)yqMeu٧AeDz\]]u+J)lh7v_bq>Id~tE#d?;˙J[S9'ݾ;4p@Ubpt˘N6j!c4t[͒~W\퐢y#Ҭܻۢ #ZnSA*_K)Cmu[8F TU2|i4EMlp,< I.' =[p1@+8h0q*)]E'9Lk["elew y~CPr񲧿z~\%%}xWY/rvpMy.`!J(D eehLjI1xN֯YP`=e>ug蘍@;/GI$T 1uc.MS[߬0o1kT;^)( Ps!Zxg/ 2䏩x̍УxY.ekI쎾5&~63Ld6:T /MfL)ӨykL^:sdqX6~U5 "} !<Ʋ!{ lyD-jo ԓf7|æd"d*kʒy7`Ć>/  8VZUH.0an y.V!ՃV|&c Z2h!SD-`S?AB/ ~:8M FV9M;~i=cl?ُ I+Q` +%GG.d>7` m_p/1x8/33hያu Um/e}$d&,e0p_} &0SzC;@j udf_)6'3> ʗX endstream endobj 2121 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F16 138 0 R /F21 215 0 R /F12 59 0 R /F8 41 0 R /F13 66 0 R >> endobj 2119 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2121 0 R >> endobj 2124 0 obj << /Filter[/FlateDecode] /Length 2076 >> stream xڽXY~ϯ5MKuMbNub`al4{Z#>uP[Ҏ0&Eu|Ul/A=z4CxI ;Xһx*RB~tEx<2w_q~*(Pxw8%Iܵ]q_3l'X #/T ;I%ˆ]Z$trȕnvݾɅ fo@0P\)J(eAo$mh\3~#Kt|Rt½a&wQU[|OhGmgs_KgH|!=/îL HDm[UӁpO?`0Y\᎓N!fs0;$L{U9,hl.cr˞rN{4 Q$&{bE0mK_QIf.li"gw=JJRUQ]6Uś@7͜EB^, _EKmZdGp(E#yy2d*80b>YIR4*nC1R,~{zEkdN&8 PYJqurNZ~|ܜT"6D:V͹;̎im+oxcݷv EU~׭@D2K*dK1¯(kn,dƒyB{ E}[6/T*Oy6(4L)lўpC3$z &sm1R>V,Ha ^Gؠ7LPX\q2jxԯC/S[y~a[ݰ% Y.ddՌyYj[{()V;ygl(nEl]]I$݄nx~5SҦ; Af(8`lC"$U =+|$%I%I2S G%qtJ4 ~anvo܁ H2]grJַcPpi V$kb;FspX"@ Og9/6nB Ϩ킇@'3C\ uˌ"܈ŅFfhyeD\t^~m( K)-C>R.XUq;U!.2䀑r{(3~e*4Bnب7l&* `dЙG`BGqEXXt6WӏwJ,ۮӷ6{+J܊aR'(ۥ}b q!;waco]4UD(4H:P='YZqPmdtu6V*mFYmeo0I='H$! 9c3~" L?3:Y =(Vl=\Z0A,p 5dt"]74<ږavyQX0=_#٘-Y ͫ0XtW?䚶^_>NC@ځ x#*sSrضz805Lo==^>3ux|w codbҋm9p]nAlNnl U0''=AfW~d~\ endstream endobj 2125 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F8 41 0 R /F13 66 0 R /F10 53 0 R >> endobj 2123 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2125 0 R >> endobj 2128 0 obj << /Filter[/FlateDecode] /Length 2060 >> stream xYKW0Ʌ2m` 8`#C&ę!"H&U]dS䱵H U]]$gy<%34:2ɔfHv)}/yR$w=gLqyZ>6qYeRvtsw;#uNrw%s3d"ud?WR}2;ͲBb;x`&hxN͙).Ѓ \H25~ݷGx}4N;DG=c=X<??FI&1sYEߡGA2)W38gy돧u(q2$xT3@Wfh<'Wcr #dYܵhyD $R2a '$AFfwLq4>;qy 36-v7*B 8藎 j `83 gGgtsh*Wxf5,O_},'幬"*CsA bb`NB_)66{-Omz9;H.~G u8$#6A9TuGPݴ ;paavS"NOnvJC7D4fD~C j'eZњYlJHo*mf,b,9<2+HYkӄ$4aa4_M}~:շ4+O۾̪iW!W`[F.Q`v3r~rx[ rQk"BlRQY r_ 1PcP |JŲ!HMr!τȾPg01Z 4x$ga۬+,si}\*C}Rn%/ xlz!Hw巣d+,qeB  -q)V*->'0fr<̴} V/={c{;OcBEM\]*'#mT'2p mwqY ε#ztܜ\N\Bu?$XKM'-!s>}U@;PMu3 V/#\]a P!0B̊s0~(*5€>" m[j+ E(n}[_niP ~ZC-M[N PO'*ڮk0p1én[Qb'|@f>`XZP6TSLCYK=!9vu-hn;3 -1i85 M޶s a7ja-I7䅵'vjcF_{\<Ae]> endobj 2127 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2129 0 R >> endobj 2132 0 obj << /Filter[/FlateDecode] /Length 1689 >> stream xڵXYs6~`2B$HMfڴ1$$*IV}XɊYĶAu$ Z@Fތg#~BcS{^(YzAS"oM }?&4P c¶[ Q4CG}]|)v Hv=lCTǜ\h&hiAF$eIKͦ|߱ $YI4{,a.n%n6hI'iD>e4HHdǁ]%5866U* N7gx|YU-J|m2`Waz;ؔ ĞZAj ɲ#=LBd=S62Z],cW܁Jp̾,0ưP3`6/B) =uzefd9bgk } 멎KvXd!aK74@N.OCQ_=4* I@S6ͧҁI a͖ah}&%HӬ$ۆ50 R9'$o;KgNX/՗d-#.7~Kwe \sL1e$4 ^YDTMlKrmzכ,+GhAWW5 |Q@[>R{B51;I<||ı}Hc}*lg3 !W^=>Bq2yLmoOk0 ͩӲnBac槹V"/Sk.m{]S?C6tIzFz>rBI82~RcR_/= endstream endobj 2133 0 obj << /F7 38 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R >> endobj 2131 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2133 0 R >> endobj 2136 0 obj << /Filter[/FlateDecode] /Length 1602 >> stream xڽXI6Wr0fDJ6]2h%W Y%$G#9:G|gAHBߎ 7s2Ϝ۟r߱zQx8' Jܷ$ܼ-x3oM$O}}I 43BCgꇄ4#!Z]qMv4>%^f$eH&˶\ȪeEQM68]Yald.!kXSw!Ug%٪\b~$֬Xwz#Q `sz}J,py&e J!vb6`HZH5Rczs}瀁D^LbH"u>+uʜ92;־¤jCèr V]V(Mt/rt70-%ΕH`anI6u$(;}\: %hPcX%!YRߵk^bb`5Ie[JԳ}y\H\Q7\ RoO"LJs +#D{z}78薩Oxp] ެKzt!D$6FL=0Ȝf Mc2 i2sm7|8eBԦl2reφ)M۠kΫ8b;_Ǝ;w%>,9VFvm)|=/QJ^-r\~#sx=70 ?OkWq_M;{  Qpk$g^VE~Il8 s\X|$.R=U\E=̭`_:|,Πq = :@862qBB hZ?8[f=.l@`sBt Jiu WiFUFZJ&TPOd)PhKSXV5>fF]ѷhƱE#@e$+@ǩ*mA鑹4p+EBtYibga0ּ BNnI4b^c,qPeVi @R*C{@8$::m| ? >L_ŀ=OUPg { 2aYO%ڳk#f=Q3GltG=,Rwϴ\[3< C[/G6hS (ErEeP{g{,hZݱ(| JЦ|M1%w)0zM._ ]Od'zֺw#- IloYy1z-RAXSH}vbr(Zz_#^Np,_h> endobj 2135 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2137 0 R >> endobj 2140 0 obj << /Filter[/FlateDecode] /Length 1604 >> stream xڭXY4~W*VW@ o Eigf=bk1gfɓuZ}|-w(l>%ɹDy "X7/ߥ^NĻ)aw_`,ig{#jsS7A$gSo }#f_6W/B&喿lC =㾮*NHc@loUI>[t#ch[[ۛ%.peSX]W —M͌,QngFnzr\׍:ؖRVFU] ͈cBv:N-ץcϩj3CXݏgA("B1Qe1>_099Đ9_Z8޵`L Ϗ8#p$+P>0|㱝$1[zɧ".NAs_(HR< ad_ S+ǜ `L$NN0gϧ(2zyU"Jĥ'aFf I;C~{䷹򗎟{_xJN.Fi`QFPƢ$,#߀[tNT?% `vnu\7z`euV9\;LJ612aS9\z34jW߫tuQX\eY) wj"]*yD#y.#r  .ϰ7eF;ϸ2tZْX3/82f[+qVj[73.{r(`y\ E]N2L6()32پ@J`=mǻFxl]!( OU|<O^D(#4ƙvKb²~5%xsC(D1_D2,1'igKi4t]x;]flCÁl0G?1!ݰ8H}KRk z%6(2V:.EZu}wѐpCh $~ں8h@' Z> endobj 2139 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2141 0 R >> endobj 2144 0 obj << /Filter[/FlateDecode] /Length 1799 >> stream xڭَ6_!#R7٢E>$ʕ^5dHpHYMsȹÙ,w![F^',֋+f삇ޒ-Sous&O;Z$_/(Dnw:]ܔvod^//4Zw/?]d2`IFQ,"‹-AEK/1!^HmWʲ%,ˬ%Ek3^!t/4Awc"bId(CȲ ن8s;EVabbv?)IY=4arvy5V wLsŜ#N}a>bb~H4Iy5e?_s9p|qBpa+6`;'3ܾx}Tzلbs`#e^~G|$,wGG "[֓Q bT/m,?N[|C|"<,O !Ok l͵D^ӮNP) uM$E3˩@,3 FVp]ZiHA ^=zIˋ}jdZo5e63)JFJuxyƄ{7|&8]t:)NN֦?Ϙn#N'þ;(;KO9®]c&vM5r:!AB%%Bd>([Z7@h[7˪j/fdbtC #9{chMIRGv꺲I4c|m5i4s[l|X&"ؖ=_kp>oQk$`]VGBk#-TQ9x6uң)̆O/f}NM"Д'ŠOF[ݘ;9C4eGC>$NP`£dBVGY miYZЪ(^<$7Dψpu]ZN ;eGI '2tiYʫ=m.s} \AkrKm 6yColų1~vocؙocLKn_X4>FBB6t/@ |E_]jPZSeMDLMYU{xR+DQ[YZvPhiLɄʶ1v6Vn I}7GCGk3<HtIVMn;f RûoQ0iK٩5 Kƶj)֟ XAܦ4 y\)!ō;4V:tC-v4t0p^5-CӸ\ҟ,x@4#Y IYeI]fHX/~A;vcHM$2RF&̉g~lUhr|rqnM_b{ ۺ#MUVVnR5变TQ~3%Ke&‘JQnhq%swf(F/6E1L)VʆpPci$!g>+>&rE~WnjD=YmcOC)xY$Yml Us"%Qj̢M)l'v#"Or Qq*8;~rx흌ˈe7T_ [YO% $RÝ*kJ_> endobj 2143 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2145 0 R >> endobj 2148 0 obj << /Length 807 /Filter/FlateDecode /Name/Im58 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2149 0 R >> >> >> stream xVKo1 WHyPjY iժڙ 4j8| A?;w.߯N&k8?]0}&y!11,Pk-> E$3=*3([v<C]!}-k AlH2MhWPG9D]yaEk+xxG#5ّ)3VyIc9vOly,_[3ǻ-SEA29];9L0n4d5B5"( &I̲9ʍb!r\*Y ,6".rl*/@4[["Yܖ0iQRZ5hxLU,+fZPfN8A~'@VI(,[["ܸ)coT}f #,|Vv"HYZ>'n)W[ۖ+1ڮ90`b7A ,NS4[[2Ǻj?Vt-52c@͑zmT`҃@t *T0MVLn䀢[ Q"iS8xc^R% BeQwBcw 䒤Xj7v3EQ9(z)9 HGE;a޽"BQ%i]Tˇ/CAC˛˫}7ɻor>v%vHo7:(*_mŃi> e'MQ]ntޟ Ue endstream endobj 2149 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Courier >> endobj 2150 0 obj << /Filter[/FlateDecode] /Length 1339 >> stream xڵWM6Wʅ2Z$M`G*vuj'T Lק[-1,k$j_Z/da=3C=x*dY2f𶞌$}ޭzyvx˔\꜉[^K!O4/6z7"ETu۪Y68"__|]-$zAxa'`"w6N\x1`]IƅZy9Mͱ=HrgDeSM~ !x0:a !I)7)SjQ,vR|Lv]7$e뢨mtߩ}lW`@sUq/ ڕ=bSվɇ~TyvS{@$%e X䰹my X2EZRrL4 %iU~M'[-lb3[M(<0It™w*smnGAUфJ"aF-"k(.B?%ĺeK9f ܞmj s6XU54U=)ܱ?eAcC0V[pND7O닦 oZ*J{]_5=Yʿ{%.Vo`sx1Wˍ x{(3W~J}%L[[tqCo@S 29q}$eG p !-Cq\]mKq"6!FX1XW7:%XѤ丌In!X&Y? cgő: hyj!/w-ɄΦ(iN<77S:2iUO L0YխEP__< ,WYfSd?4V'Q"zN?uT ,JS4N?"f)|E pk//a4Pqpa21g{$9݀L_+z$JhGCj@e?T"2珆64j7ޯcKg4#_&[]kIX~o^XC=tObݭKd>;T‡72.?Pp(m.jgu;Ͳ` zW.`/۸Oז0l##Q3tiN j0Qc|cNG D4}B@n&8#( H#C9 ~W-,jhv|\=l]uG3/n45Oss熾1Mw'$O';`55)R9zAo-Y).NYP : 1`8XFnjME*(2GZzR7ٺy{I2'P endstream endobj 2151 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F16 138 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 2152 0 obj << /Im58 2148 0 R >> endobj 2147 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2151 0 R /XObject 2152 0 R >> endobj 2155 0 obj << /Filter[/FlateDecode] /Length 1761 >> stream xYKs6W Չru$!`TH*}w e;Lj_8< ( b2uĸ 6Vi}wW`q &`֔+3DUJٮ~ۼ̯^z1xcO+f篒2}̕JȂL(KWH<čz{5K"cmĖS)K;m3'QeڑXIisWN 've Yb ե5DC|x "@o Q䅶F?y5}@MRwդ-מ15pU'18(ͻ ~`$L8__Y/8c S`G(iI@(XD%Ĥ?"0ԣxBt.ۧ=ȘJ&W grW.ۢ*gs)EXvW ~0AyF P> ?dYBFgQձl% w*ybbRJe,M܆U@ba;{{VRIKzhadildSҎ3vC_ L$X|ٚH&umܴ6(- n^;οhYmD܏m^ FRj\dǽp MJR4mUβ8dK:P$ BF4W eUo4ݸ\C}ЪSvjMVt\:9ֻ{> Z6[iCks{:gFDHRgMڭAHF2R_s~8fj/a?~P: oO)NF퇨VAq;qϧXr,#ڿXzC2(1hdhhFm NAH3Ŷ9"(F&1.u 7o v참&~o&Qe*.޶Nm7:ri-'*wt4MCƀʶ hvKcd{ gUMEX*J"H/1WFId3N4^ ^ x90x`\wNH1yM}~.YKmî}n9AĜ谈a Aҋ^P{/DA&hF]EβpC^3haĹ)8ĻMe}K}==EC+j~r1Fxދ10uKxchnGmǦ; R һŋm\gqtfЧhoNIwôB_т}ո|5~ǩ!xjScIIL&@xzФpm᱉ #;v^_RW-QE#f ./Ê[7, Fc8 +tCiqNBJ콝{{F2ӔjIR~1 R endstream endobj 2156 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F8 41 0 R /F16 138 0 R >> endobj 2154 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2156 0 R >> endobj 2159 0 obj << /Filter[/FlateDecode] /Length 1624 >> stream xڭXY6~P9X wiP-eG-Ŷ/Cew$p,{֣(%O(0e_&%o]pu"&$ɓΖ.)'c|7=)N9R&a]>4DA-latPY_vsf:v.&r1O9}Xo5o=ZEZ>}CX& O<|ȭQZ>iD:0ZEj$_`zSU7Qd-H~qI F~v~W fwDWI\q95F28(C4CҔYH~I@6;Ƅ58Qx,"Ia+1֘M/yu;7=l=:hbcQrd?%6! lnӲJ&e*|UOe0:l V1+ /|hiR+F((LG.R2\W8f╾a+ѠaN8q|p2>BcPZLvY=QPjE$DvuH/ ~c 0= ,$DD!bd<ڎj6+!&25l܌s']3&(Gyr,Ywi^ոtUWr\<|{#J;5ve_1(,d!Jrg=Z8EBc H '`*k~QI}ʷFeصTr)P)+<;Tjup"1gN+jI٨~|Yr.\<# M+k/bLD~5InLW<;q Oa.,?gl/Pe,3T5G_봊TR@? u=0TS {&ijWS8Tok'j(=1B& ]a؈aͬN,/Z^B mؤ"`(:5Y=5.X `949=DXPvQ'fcFwnQϡlg)iR3 YF3Co`VJPrJ./Ӥ(8k5K&+wRVM ]U9Lz56&m>S`#z0ئex3?E endstream endobj 2160 0 obj << /F7 38 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 2158 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2160 0 R >> endobj 2163 0 obj << /Filter[/FlateDecode] /Length 1939 >> stream xڵYYF~ϯ  1;샗/`;qO;1aHcj&/fuuu_!oH E$"V!0{ ^m|#El3'Twzpt<9?ʮ|h~{Uv& џMۿ&/"I.2*}etD2BjbX(Q&ru7$|Ώ_9N)'" adXd)  nE&q THvŻ% ӱzh>npYwCGxa=l)rk}Tj<,;оl:ܚO=0erP ta~0"¿/Dڽ~2!7rwYR)xLJQ$ĺ?uԃa b6"@bܪŇJv?N9n8xcwuO.5\Hgߖg͎}7 g*̷m2IJp(ɔ<:%;و YTp{yψ0It>hP R=.$q8njK%.%;\K\ (껥 |RLqxN<*g39AmRG+z_8(4\SITfg-5z0 }9bi/o5]9{!Z2RA)Q ɦ2uMNY|%&mWIP&3QX†+*_=-#sP'RKuZy[\6;VN97lnH@/8Twx ;PϘm?8\LwC)Gʗ<,,3)d9p+cNYӔ1]0MB9 l)Wb$3P3"8X]u߃W I+T͹\\j -ڰwZm΀}\mq@Fn 'sW`/0/Ӹ$IzUCcԴ;f%4gLzpYmFeb ӗr!Gџ"47ʛt3?iNPv[_rЩ몑pQcE+'(!pu# #9g4/h;PXWbVr1O}oFIn`oO# ON}t VPk'ubr f`4l(PBP597wq{mg@P!d5 70շ"8\=_I0u엯_?H+%K/( Jl>6(-DN_M+ i*ﯹ3k3}P_7L(w0T&.}3~r##_n+;*woGڟΑoi%N"w^DD"w` 0( ~q# {1b.,Y{F`ųѪ>@T{J/-o-~7 E@hF9^ҾFQlɉ ]+J=rGbU|0+(=k7FOXcCeL B-7~S,*~anVơlƚasAIInRϭ_(Sx6asϿ˫8iL=wB"L{Λn m endstream endobj 2164 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F16 138 0 R /F8 41 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R >> endobj 2162 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2164 0 R >> endobj 2167 0 obj << /Filter[/FlateDecode] /Length 1966 >> stream xY[o6~߯аY5mdf[NR&ma}<$Ebwbɼ;s$+O>#>{q@2È[{!I{=?:2,e~ٜ&QM_9ܧ!>g8N벾e-Bf6gTE7p46oBBr7I2jLA4$ ,`e(꽿{!Mt@#Sմ8ܷy勾lj;ꮛMKP% Ğk)Pys%qě'Imo7^UPc&;3̞dڗĘ+\Ƌm᪢wY1( N/fC<)2D#C,,e(7FWLCHR !,0ISkP9U]U& 9F~˜Q#VKR̥dF25,7wX:b$mK#?aamj.[]HcF4aMJèt `.p_(m3X'dk=jOnF%$VK'n:=cG$Lm3؇xHA1( x@%Nr!5rx}\M^ty&`7> ѭ<>=G0>g4힒$ N< >v ~ $XCi/Gpg;;c r*oRVZ./e{Zc𡁈p #VUo-S^)x/ۅҦ.{c-%#!,{l1+9B5âIrv`2ap1eTV#Jۍ\s`zsFT OOMat]!s=`5$kȐBv2ÙΉ,ཬ&,/ݖ\M}t@B7rQB N 730D`*_,:+>bV2Vs_wmęw>mpU:KƢNucyŲahZ( kw؉[iWPYm".Ud;*@ufZjіK֟q% N1c59#\"Rv8>@O 22/㓝R\9!afbL_?yUR]lشp\nZ̈́dk@-e{DG6Hj6- ' X. Ii@ }9ghLuQ}mFϾskA aa`{I66ĉjme} endstream endobj 2168 0 obj << /F7 38 0 R /F16 138 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 2166 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2168 0 R >> endobj 2171 0 obj << /Filter[/FlateDecode] /Length 1816 >> stream xڭXKs6Wʘ /gҙ$=9D=t( 9H  I)q'>}|],_+t')s<:;3N8y?}NJșoZԙٽ*E0p31d}!._ee-z7 }w:}:n C׊8Mq2'1 IxiFևT'83y/JhLf(Bx;O -cQ@QD߉"/77Px! 'YUFe=s&0JRd1;"o&<%0cKXbhNLoe N ֺMyfs('Z8)$70 X{NrZ7sZ_&B*/ / )v&N)_#D=[TnHjCxBbv(,ڽQHЖFUzG%Lv A >IM7ڢE!9ĘZv)L-t)z^̿BۉqXBeFZa.GD}l]Ű\j{$6#8=&ÊaUH 68dR!^ݒhj}}mFт& j}(tIfRDi]jj(85|%z %]qN-[TSLXoҚ-n'g+${cVxek~G[<{*cgT<+ m +UU`Jff{YCa?0ۿw6WwM~a;ľ Y1˶1k@ C|ZWBԝ<<=CTVJ\a'Y^GF>!r@dd M- A%` 6*+ < +DHڨ.<6v=NCujl׎F#qלD݁FÅBºl0qoy-0ߪ+V Q*{1SڠVFcj=9As~<),龮ZkMK3lż9,]C9:?ա! @ jQ{ 1 hU_\կ@ $#|0C^&aljA@J9#N߹ Aygf_Ǯ0F}84ډz(ķt6U 0"_K 5mtC")m?P`QT_mAUtaecɃMUXlv]Su b:O㮰4t3|AT"[t5!l+_oTHE2&uTa`p؉PhDӫ1NDUiAa,PH) [J]]0[Mɗ_ 1zL]5!1 v1yZUa&-uOHُ­N7e4%IQ::"0D3<{/ߎ`p}LcGS&o& ,qWQ:OzD#jDk endstream endobj 2172 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F8 41 0 R /F16 138 0 R >> endobj 2170 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2172 0 R >> endobj 2175 0 obj << /Filter[/FlateDecode] /Length 1070 >> stream xX[o0~W ŋI!meE6+OuM6cBۙYOnNR(SP./ L) @ 98G L8]VQ!,&,CT \.2],fTofgN Ģwq={Cc 1{FW: u>m@1Q<-&ۃ@) q"YI`gdE;EUxA0A7VIioPM}4H.M;!+KEzCe&%ҩ1f<R.j6mE)/]MB/^qq;묐)UUzRձ `]ߤqVo6:Ӿƺ{S)tˉ:[+*WF/&WjVԬ֣ey^:uyP`qLo |;yV1Ѩ窺ކ6ʺr+}y>[Ud*%Zu@au%5ulm0lIvт;O p[t]nkD3!F2TEĉrb9qz2֤Dtxj@fL^@\̳ V®3eٸ֋u156(L`Po(Jnސ85!Q>n'0{?hHlSj*YWnp:ƻZkԀ HMwՠf(V"?U#M#1qFx@N$bA:WCN*ic-j-i)De&hьE4r ۖn'5/C>ܰ,f>RTDD̼Fԁ6rn9N(" ivr Qn> ʥۈ\{wń*I%8n948 ng7'ڹX|> endobj 2174 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2176 0 R >> endobj 2179 0 obj << /Filter[/FlateDecode] /Length 1948 >> stream xڵXY~ϯ8/j`(Z#vkFv6yH#qf1ɯOZji?GXWE|}O;b_d"]SJy{{7Dn&@lw__L//{}$u"v>Q0}RKpL$P*D!WSۗO^j]9Wӱ# _$N'dVW͂<~ЃM3|~U3C&T0gj&B*XzmWazwۥ֑d׷YϫXIx3c]N$h'`dΠ I^8tM'kEKT6!!<2J .֙bN_{%!\hL~hkpۆ0gWp K()6Ydå: ̘ <2e[ L,KKPgj < N8R衄@ !H (S¤F&+*$@$txQz+Uޏ>+ PK?Ki{xҗBƪg粪xk3E&lj9QSv2U}[q˸%NX#d54)8s4FT8ũƏ*x SLz~X %<`6łua*ê ޼y0k]S`NyC;V/&^Yิ7Abc!_8V$"H Ny5{&=P*0"0< mEQҢH#72)$sfOcʥ R]k(҄SsFsRꥄ=ȈJ{uPw^oU1 % MDN .Vy8pe1ǣ.)ѫC_ '\SaBfnڒ-MdWc`^`;y {;f+w][[Q RvZT)r)~w)6.'?}|+f tX =عsl1~n ^r#ЍJen]Fxk$v+9w֔R Z2*e! 6Q"HD]Ud /_E z{ CJ<En̲kdLT/g?~<*mbZv3빔y~I),9iLʺĺ^ #rۍ??ЙRS{,DYqyХe,4 (/6,Y 6s+3co G|OWÒCaяᖶڞO+0UxA4Vږ.?G(GrhpneˢO0 _ciHD 2qof13z#l5@yS&v?hxn^6p\5e^T4 zYM][UTkζr8~k'j8mR9ꖺt2{GO>GJ$]IgiX#МL\M2+mz͛g?k Ė^1KעC|>~ kc#s;ߢu?]D/^!#pbM 7 v, ZѸ endstream endobj 2180 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F22 218 0 R /F8 41 0 R /F13 66 0 R /F15 112 0 R /F11 56 0 R /F23 221 0 R /F30 286 0 R >> endobj 2178 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2180 0 R >> endobj 2183 0 obj << /Filter[/FlateDecode] /Length 2215 >> stream xڍXIWVc`]9DzRʩ8JI}r#瑈-m=~3+_긢DU̬a|V Uݸ\}Y1Ye*Wa{t= ^o$J{ۮ/qgۼ/E^Ҭ:vGmV bǫU`)MJʧP4 5 Sh&3PUk~QR5:u2ReB@(p H-;6L-h=nLsFUDmW뼿v!M0^w>/ Ii`Mp, ެ IZċ=BEԱ*ruZ⽬u5CQٺ+:/yIO48\1ڨ,_ i=lm&޾yYdzs23,o/ZáS4m 0޾'2vrL{}]߂B;"3g AbvitDDQ}xoQ5i^I* d[aOq@3+92$/sgA;|Q(?I[+]8~٣t"Lv,i8TTlL9CG}SK0U-@%yVi4]GT١Sj:.;3/5t `,dkG̈]ʃmԳ쀂 cVI\C(qd %ŬP5IdMdn_A\PPv,FtPtdw*:},)-e+Đ U=Y{\=s~1vvέ b'!Tqss5T=Wv9Kxz;$U)G2t<&cbEvnKM{\x. ڗ`E*pdZ]CK8 sfE dLTyy>78&l #=eg8&F3@%h$N'[ ;ۙRȦ2$C PPzsv_rE ]#u(z&.lul*6%fb_Ba9n7/\&(T#IM1amNc2V1hKޝg_:(2Cb'L=3 \_fn)r鈽^^G(ζt/ ;[7wEU@VQb{j  ZmO<іTsL@FcI2SwM/w7<b'Cp<9:y[C11CtWixfBiF.r`V x.trWrE`v={3N+p;U_.UouXkCE Pnw03晲d„.Qo=@.Js g˼.2p p_/\QKW%8+q:ּ"j-(:9(#q,U$_~)qJ2RW%"Qʏ&o[+_f;#c ˩|U'i}KΒXy!|XF~SޞpxFnXN4_chtyF >Ϭ*y:NC uEܳ`[l]Q)5]b R KcE!ɂ!Αi{b}Ty]!wȩm.ǓȶsK.|8tl08qIs@{f0N־R TVR^]tKapW'n_p^]q8p$z0 6<-s&u  }q$ ?}(9[ ? "Cx) endstream endobj 2184 0 obj << /F7 38 0 R /F23 221 0 R /F11 56 0 R /F8 41 0 R /F22 218 0 R /F13 66 0 R >> endobj 2182 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2184 0 R >> endobj 2187 0 obj << /Filter[/FlateDecode] /Length 1110 >> stream xmVϯ6 Q^X[׮ƍz_D1SMnNZBq2A)g༕"Hp$I+.Rd5#+"lul|`"pktQ7̜_Y(XXBV>,L3\G{,Kg o%ž7WS?Ⱦwf ܇=+0GeW*܄N(@Ekܦ4-HX.0Q]I\XPHY%2=02 eSLf^t82h&Vk&"kaNCH<-"ww2>򰼣S+7!x^.qønzfsHjq/ay p *1O) niv%?Ϳ߻] E!Yb9>tm줈QC9o(JA.v,hQfG ':nˌ9K^VSOE:AF8$$ޘ_w`mj<ɳ(6(A#> endobj 2186 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2188 0 R >> endobj 2191 0 obj << /Filter[/FlateDecode] /Length 144 >> stream x= 0wdH_/`6q(J;.*|ak0qaFz3Vas[. I-4Y/eZk"%e:rJ]{5O2Q)^mp6h(? endstream endobj 2192 0 obj << /F7 38 0 R >> endobj 2190 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2192 0 R >> endobj 2195 0 obj << /Filter[/FlateDecode] /Length 2105 >> stream xڕXM WVi.ӃVw;"e;@/DQ2IO^~Ǹ Rd)PQ@?8=@6؏E&c #ۗ1SL5 *GS@IQ(߆nw%MHGZ~:_Ђ#z miy6k e~u7I'Eц O6U74*ᬻM('YKWqX)[ላ '6{@\硟n<px{]w56^#hA O|=߻W%ڛ0(aBE  !nD%ș?SWqJU4%U ǩ4 oIzk֑R 8M?REGQDKj"4aXߐ~XkfMW3yiPH: KMH#+!TɔTʊ6SM*'lDOtirꇫ%Fzyΰ\g0 zHE*KoQ2Unb!is)$riX_?xEG!Ilpo)L83`ыPWZbdd 3-^yUsQ7_ĪYr2K2_nGSO;'q2(iĻFkRu. hM{z 9_M/ؤVҒsKK;yC&)Z{*0n搂;xJ$"o :8|,NZ}Z0~`0"pPOy4C46J lLPT+5f-z']A z0D8^?bxs }FąK9;3r-0uMYjDZ!,nCoby"gty9yA\(>ﴽ^@T}y/Zf]E|#f*!2" @6وj3ˠz”$\ϵTB#_")FҮC1/ms [WSmx'I)w͸ ˠkv1w?fԮ 䏕8$ L2ab V UH g3n=l)Sӥb6 FT{VuqKyLם,   /hۡ96RôA^Ӽ X(s.h)@a:ۭ@pv$pK OU(bi r@~YܮE_Jh ’Gb_*'G} x<ۜ`h_v 'o|޾Zt#hG }>I$ۼ0ɺ<.9`ߓp7D.CnlzƺH_:~:, ??}UGZ="&-@aE⍆mO5/&FqǗS&0P 0&p4k;qd0kEmmMpƑqtrI!XjsAab7֑JKaG<+]GXLMqÞ*8PpIō]ʕʥ6 cZ}6? M\Zdn$+tkWbrJ<}-KAud*8fQ5q$6;#t;q.P^#UA2˞0uK=MO=BmP et8s U&`@VkGoǨRᏽ[(?\jQ<"Q|A{ endstream endobj 2196 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F10 53 0 R /F12 59 0 R /F22 218 0 R /F17 145 0 R /F32 356 0 R >> endobj 2194 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2196 0 R >> endobj 2199 0 obj << /Filter[/FlateDecode] /Length 2313 >> stream xڅˎ[wK9Nc x9p$v^IlΌңkHV*֛(yGaw" tw0Jv.K0Kueo~8*vϧ<jYZqU}.vCAPo>9n( ~vTߛhwH0N^!;PfI@ &O 8S G59ȉo~HEI 5W . [O/#y3aqyKj#PQH@oq(2Kgj3v2%>"0Ea j޺Kk|^}˵7/fhydZ(;Z۳z[|mӲ$Ɩ& <JOTB=UȖwNz~5dޞn)jHBu48+ yurRԓNh,{sn+oj;p(b yz%C}2n$4$lpu=mV[JkܷY,ti7hA- l\8D{0;@l&TD>W0)!ޮ F=pLHRb <D%3 48X: e@uL̚)6S<1 sNh ݱ'(4u(6cVNq3+Թ50Ts3a#-XH?wY2d[f63"}% -*B!;^62Jẅp5t<'V1G@u$P9wFqj PAEqqCvJ+| ?Q5'$ if#h.2`o 4VorYUt< (ל '$lF©+>t6bbFq@0p\(de upy2 m"BAl0#jpN8a$Z3! P.xVEspW iSkD@TN2LQa4,Y>9if̜qΡ"Z|?2|2oz9jZ#Dd+󣰐-`=f{?$UV)l2h PTf3IvӎfJ5|2<wd =h.pa( XWT&$Y <2ĹUUi~';97\tp#Rsgs%l2K3DjXkVAMST=hB&c 4U&4Zur3'qu4KȤom nlnp+gMnsQ\$v5qeqC}'$/~R1Qm/*f>♏}80+HylMKVQE=LQY*fy97h`dI0%jYm+b(j- W}U ~xx;{䪑H9%a>㺂_lYbksR*A,%L~;K"˪5Q^Qnk4UhH9RIEGD٩PqfT7.̉L/*jJ_p!._PeK pfv14[M:GThFҭFx>cȥ)i0OWIQ-؄|nSOxFpeϖثRC H!Nͻ&P%X\9!h[: 1+WO6LA0. /8qPgB6_b m8wx$%YmLCrFE¹?OV7P=1R nd,$ju wM%)yISKoj爪F&0'bYیSS&Tfa͡2 %^apL_i, 󾂚A=?`8Wo:?B2CRts_DzﺢM!o'/ endstream endobj 2200 0 obj << /F7 38 0 R /F22 218 0 R /F13 66 0 R /F10 53 0 R >> endobj 2198 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2200 0 R >> endobj 2203 0 obj << /Filter[/FlateDecode] /Length 1865 >> stream xڭX͎6)t5#(mC4 =d2VbK${o)QwEz4$g3)JXD>,a֩bQ*S >DݽyǓ`E=9Et}YJ%q5xJl%ߛa׮7߻޼#SXfJEeJM<7ݎHv[ro8KRǨtl&sE{4p~UW߯BǦ+y< q6+@vŶ,laP8fK-pKz ¤I4);إ`9+]z̶@4MJiM9I(mr7x*X"-ʦB!,eW{0Lgا ia1`0(1N5G]Wut7 rj+AI=zT-aVpg Bfzpc$yq${Z 8 >mP+_Dx#*"g"r%06D!Cn;y ѺWARx嬃>ScU{8 iX$صۮP(e_iO .;Girvqg_k,)ZlzfW$x yayy:v]{ѣ_- Qf틕^iyt϶<(h ^o1DPb`xiHsD#p NǝLwF e rΑ( +z2h؛e(CNr]o} #ZpzZtS5=mwz!Y\m9,Hԫi]jQ3_]!:Dx7!H/,~`s)B~K5  hbOL<n\V\F3dž!fǺ79_93ۣ!m#N@Ĺ(I!x{DKg\B. }%/|f̍9uY v?{kZޑ>d坉IfkJsWr΃ģj4^+-YO)Wtzibc=W&,y =J|n)R[lP8S?}>9!hP5nJ06 I֔Y@-PpC!JIWqe3 hCЪ\Ӥ׹8;U>)A(an1l䲟: endstream endobj 2204 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F13 66 0 R /F21 215 0 R /F12 59 0 R /F29 279 0 R >> endobj 2202 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2204 0 R >> endobj 2207 0 obj << /Filter[/FlateDecode] /Length 2522 >> stream xڵ]sM_pݙ~N3LIŽm\I{# ۹Ottt,"Q4{_!go3B!If׳xT锰t'*AO/ٮɫy9-e[Gعvi>}U.X9fa hEa)mܙ)Q y$1~gIu%FBK=֙)QKZ5 Rшm1&:2Nwcdѣ"66Q z ֪2*&rM0m:23!t,moIEDr9x$a'f: D1#s:LMX_0 '<9Ç:` 7l4N.c eՠMrL|:yt <9Օcl+peG`x jW#{}%z}΁-]_s U*Z筪v8R5=?<gYuVx>Qinqp/<_hU{]:^̳uΧ1Iz`kˈ":v& ڦ!of8T_[9cX:yWu n1;2l&óS fKs5 n /3e1))(܅jj2Fu#cɲ(s']Oaط;>avXr{E }`Qjz)etw)Acq>cjUc(,; iݶrpz:0[qTR_L':q/-ƍ3B>BuAL^%7.}cG.ѭ HelGY54|>bA7WtVq4Pnn0'Ht`B6xo X(Rec"6 V5~A=!5%`z{)M(~U [ܶzh+"'7Sȕ$wjaf'9ņKJwl 8Ĝ̎s/-,l`Ja:%_@"jToby$~4DGHh.Q> endobj 2206 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2208 0 R >> endobj 2211 0 obj << /Length 215 /Filter/FlateDecode /Name/Im59 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2212 0 R >> >> >> stream x]Pn! +|L6 \#"mhd4}x){f@jqp}0%F9 ϠEshT%Ki9SaD$jZ5Ip- w^sB8* f_3;m_ańdXje:ǧn[U|KU}1F;qT endstream endobj 2212 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2213 0 obj << /Filter[/FlateDecode] /Length 1668 >> stream xڕXY6~P M MEA--Zzؒ#i,;דə97e$ˢud~.GF(ψQ*$XD0DF?_=~IHGW7A7zQrYoVDfq5yj\fIzL" l"eW_~R&Fj׷ݴ<)"rwobbmvp]Šxj0Zxv;6^Z]2G uB앗njꇥ<+HY:hAk9)Ra܍Mmڷӭ]U',}:p()kK $gAI3Wx쒱]mSoɢ3TW'//FS$ 3 pvpa8';6F$ //)H&D`Der9+ Z7B#L#IO4QF'D 9@-yB| !rɌri,xf ,` ?c Ǻ3J,?Gg/?g?z⤙$)-hp¦<$k><''xSys7UtI塌A׻OwmJJrfD R8Pm؃\)C<ϼ nsռʅr/I~ }J*zSxhlm#ƊPooaa2B/Fp ]fjRP«vȖ̡(WtW5+㏉Z =azp<)]dKn4 s&osx+T0xI֞6mY}Lq$3lǜY0G:9dNwffhke_ v΂Ȉ> endobj 2215 0 obj << /Im59 2211 0 R >> endobj 2210 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2214 0 R /XObject 2215 0 R >> endobj 2218 0 obj << /Filter[/FlateDecode] /Length 2191 >> stream xY[sF~ϯyA)i܀9*ܪRIUmZ@v~}rE@ϥ/=u+X}| qČ 6JHebJ߇OJLlo lsTXq~yYm!|qp85u"V" |DcS6YYw|e YFF(dmvYa]*/<늺ED_T0a=J@gfx:MFpwvM]v8Xqd@ɻ<̧RJät]vwc J7N̤s=y_twvŗۼʛbGmN;kAK96EYt{<'n:$,:\ظ5w`/+7s;JYACpL)RQ[>áHYK^5 , V#}Nc]0;iΊ-QxR #dg(qr$Er1GC맸u}?3,qdeNMALS!D-gO5MaaD'C6,H\=?2mg0dbe}zچ Z'2oKxSKmN1dZ!v\R_ R`H9EM r %mJNXpqF Ma>@RIk2%ȏ`xG TF\#biNW:H)c.a4};|Ql- ؐ͞Z+i:' c!mB9G@\]tNo[#0.\vj îgW\@(\, {6|8\y d*f &X+T MbWma^*lp$ę*r%!g$ݹpsmo<X轝vP(YMy1\YAi&e<֕OY5Yd&Bx5''35O]Z_K*:-Dj4CWWs*G!ta0H :b(*WWtF^#fto;4Ve;8sg}sML":Z/W|(&RcKǀqBҵr"M{X]3x1h5IGC=|JСg9 /o/&WAVbo!₞IʱS9jYsm|ҷ_kߏYa;β=pκJH)lС5fDgh}Au9듳ט3I̶[B6-e`2ۧ-y? ,Q7s =NR= )2˘ R}c)zf~NI ,<;aj't;?G @+EEFlfkQ+{eH{uZ#Zy5ū[3IYSkUȗYs0a `RnEbVpkɩ1nYcZIJ-k2u[}Ȫ@ 豯DZT,x~#@n\k2хRZԑ> endobj 2217 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2219 0 R >> endobj 2222 0 obj << /Filter[/FlateDecode] /Length 1919 >> stream xڵXKWW|J}If IsAٶ&Աv~},R-;X|WǪ2e&_#c2R(d,E "7??}fQIJݏ)aetO?IcTCݵ}rDfN8^绿}! RF=K, u+R&vY|d`G[7Lxrg~q2 X}?Ӌ:M8f 9usJ"7uU3yAQ"_|h9h.8ʾZWeڞCJ4Rp=iW [g̊w2izܞiXnOqu.g=m~9V[bѾdRJHstWZTD$J}Q3;4kl.Ե8PyS}Ҝg,++BP UR- ݗ ph&xs0!)xvT4 N!ayCYl:Eխp}wk}e$^ /LO7HsW =CV+k?ldNi WUjL`'Qs= W`p.m,XO$@wDʸju_?ܣI)d 2k{{{BXAA,=d3uB,߱)u9ȃ!2׋O~e0ʁǪA6,tl>--w8l6u[$zqVo'L.# cRڷ.! @&vW̺0mZEAɂCȱREJj6"Uọ̀}zhv\ &;֮땹`È S=MjI3$`!}]Q3a,d4GjsTj`Վ#s3K)mnAE4mEB[Q%))ر4)  $cbV]{Td>X.c0c:mbt%2OXƙJL0DT^o<(7(v WT)U0Gj6)i XVN2, r3[DÎ Mw2Wq%դe C X[<xDR##&;"I 4pn8vvy C^@cDxڶ*S N4}N€ʶǺۏvϢ+S&KaI8%<#5VmSz,tjքЀ_Qw3,|ݾXw MamnՄrlsZAy[W"^gp}ʤ&ZֿAɌ߂q[7{}%(̬!N77ukkF/`#_ЪzFg^`%@G\8 \pH7P\8m70I ;'PYj/`TW,NU \DPњc~ Eդ.ob_2gW&g>|glce_r[_鲯e5Ǵ 2d+ 7ٌ֖Z\d`^>=4>}G_mgcj/{B { endstream endobj 2223 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F16 138 0 R /F13 66 0 R /F15 112 0 R /F12 59 0 R >> endobj 2221 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2223 0 R >> endobj 2226 0 obj << /Filter[/FlateDecode] /Length 1939 >> stream xڵr6_:/DW{m4Nә-N:KpI*AADŝ ]E4EQGHʢU$ e{"HGӻa9%?~`V$4IEfdym#VĽ"nmZ5o((%U$(a]aG׫?;=^筜}Yk4; %i"{@YpL^XKWAmkdQ0OVƛF#:]Vm,L.k;]yfl`9L7=rsp0^%>0ˣ,JhvRv=9,hn',+ 8/ 3; flhi/! mc(IY̒QKEF=[/'(VӓW`䶕`fp ÂZ>Y86gd|ד/HQJ̣.0>4iAdq()rW}zPp =M1q_O^%pa \=_h'6,P3_JQSH4+1lBpaW[: p˿nە %Ĺ\ Fy3k(y.?&A~ZVn g>`<,]/LUd'?i &!skx>@4K= Cz~Jx5Ř`5a'* u 9 F s̳u1ګôw/' -"%rYENCwJ/3½E\ԝ 'f+O+35ߊ6 ĚgDsd6;;vHEQalw%;5k8%LN}3Im"dY:41&^lk.cw5뮯׷m&0Ix7!'9ݕH^Z5%@ v8 3[:-Yػ[׶i̯SוKcW66H=#Ή* P,UI@PQ`QܨpWARreA?]LΣu18 bʀQK,4H4F/]jޥs/N CYw*0Su;P6Oɺnwa՘Ѧv qlv4᠕/aw{o~ܢoﯛ^N!NuOU9v-Xi|3DJpTMgX(]tyX*i4;1Uu [9%[i-51'3 uDׯTd4XTfMڨ!ףiG,)`/W[[ԡp_XryWDipiSXƕ~a4N7Ns k[Xa&zAAi~pq^Ԩ8LLt_ endstream endobj 2227 0 obj << /F7 38 0 R /F13 66 0 R /F22 218 0 R /F10 53 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F30 286 0 R >> endobj 2225 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2227 0 R >> endobj 2230 0 obj << /Length 425 /Filter/FlateDecode /Name/Im60 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2231 0 R >> >> >> stream xSMo@#@w]>zICF@[E@1]޼yfF)O{&ٓ|?Pn+v )g |.C#, ;1$D:WJf4T}/|pBap&5.z w<]:pn A81D(0Z4s@j}n< endstream endobj 2231 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2232 0 obj << /Filter[/FlateDecode] /Length 1950 >> stream xڕXK6hsć(r`H2Ammʒ#qO~}XgD'**X%O\1+Y&SbJy|Hz|geV'/#?g&yrڦle^ϛoymUWQq=tT5F?]8a'xpP;q5+MXԠKמ|lNܓQ_ ʘaaU"O!|GJ贬k

:{I ޡV6ؾɍ#]hvx 9scpzrV^"KV3MvUYW`̉Cs,PiDm=ϱ ًuΧ&)Ty+`1'Hvnҥ E -ͽ<$4g%(A~R2"ۼWM&gd}!Hߒ"tēnXfT62/k`NfJ;Y05PXl$nL&}β);m # J`*8Wbb .lPF=cYn#c@٦팽{6Cϧ%Ǝoؽ}z#22mʵ`ؒK$Ԅi&0|q \vƧjZ|>6yC3mV=H?BUsLK>-goυAŭL' 3`2X.FB5WL65qʆSzZ;1,_QogHw܈ Qv 8Lcbg4t#0: ~_f$̩AY B`loҕ> +->۹6EIm-~CB;g|?I endstream endobj 2233 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F16 138 0 R /F21 215 0 R /F12 59 0 R /F29 279 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R >> endobj 2234 0 obj << /Im60 2230 0 R >> endobj 2229 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2233 0 R /XObject 2234 0 R >> endobj 2237 0 obj << /Filter[/FlateDecode] /Length 1936 >> stream xYKWu0ŇHɏ$6m ]h92ZSŇDėQX,>$9!1Q$OIœL$g1\!W߫"LGuJXM*dT ZקAs>dRz;d?n7LޡK黇>a?t+(dL͊M}38E6)*"$7:iF>j04ަAM1o&絙~M\VX >RHCᠣ ID,n*"Vx+ D.Vm@Nq$ (+:&T0B n)r{WΔ%Μ/9S$/g2pƜasV,J07ܮ BYAT%$HF* n(F%t~ݔN _LNSNr5ePkCڍV$`Oa΄ a1. Ν~aI̒R u0QIqR)ywf]0 WaZ4n%P0OP"qDDڷQDhuYPp6&+94b<2a(yE/>ֽs/d"v<-I \6|;0)F2g6l@ɜaW5g~*`0k,ߋuz2/[5bmfv}\W?gOt(P[=6Ny%Yb>r槗mcQC>_1?Vcl50_hkއ/qpT"xNw{"+-z}i?<t8[")Gng"bD"bi9 |v %[2U'v8Ս?w93xwM[lc.o1`쐈Mq>Z5]k%fx('i |l e|P)'[c[ 䀐+6 13gwיݭ}oBoYZ\ Gp'(ۮmuB^=?qqwpe8+Pw=<,=JGH= k).HV} KdӪmvP/2(Q 7rG Fԁr@X-;]fHRBfb; Ggc&84+X&ʟ" cN s.$8-JB+. Q `DiZ zΌB ,m?t8*O۷7& =Gu'+xҽDg}r䘡_7%O*=Rl_=> endobj 2236 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2238 0 R >> endobj 2241 0 obj << /Length 394 /Filter/FlateDecode /Name/Im61 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2242 0 R >> >> >> stream xSn0+D8*")I4wPU*wggg!j.f%9/K|2I £^nO08d\Eo+  "IUɰ,Mw3/?${ecf(]\ʸRJbQzإƤ`҅c%]QG_Rr<`ߥ/Dq%ܷ5!0sאK{;xQ),g  endstream endobj 2242 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2243 0 obj << /Filter[/FlateDecode] /Length 1460 >> stream xWK6WE*VHirh-ˢ"ZZ,9ᐶlo(Z1Ù)XE~AEQJFiX&M˜"]ϢUEX9UxG|mfLRt|}IT/m:-x*E6jq4S͓+.Uɔ2'8ң"g\:m;I*t+,n{u4s hwZO`Lj p5wW ȗE)Hא0,YÚf Pvh{{nQ1ͽ2tf:KJȯ 3!_yE=`< ,\\YF(;ʜI+hB.]ËS1j"H[LY Е 9@g P%2̷f{z ,|oL:%+12;x%{mx\)"j;k|={"l/w/Ҍ+GLxpkMo{6 G/@c9uEsOF{j^a=@J cqGv&Aa!pQo:g5= m*4 }/:鯩o8N"OD}Ku/<상}1 D@ A/k]ńpi=P#a;uEP,ÃÀU)N0?@ ~ qW; 1G@i xtZZ70U endstream endobj 2244 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F11 56 0 R /F20 182 0 R /F13 66 0 R /F21 215 0 R >> endobj 2245 0 obj << /Im61 2241 0 R >> endobj 2240 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2244 0 R /XObject 2245 0 R >> endobj 2248 0 obj << /Filter[/FlateDecode] /Length 2898 >> stream xێ۸_a>TFZ*ifEoIN;Bcsfؖ!əR$[[eH84I? VO+&XR%)_WRDrޭ٪H ?g /V7?I1Ӓѫؙf !"hnkEoֱ̦69yufMД}uxX|UL7^"TGrܙk\eQun^v5[sUv0&{=AcqQ$^>Gֹl(~@_3C3x;0k /%(*3虅1S=!yz@V*P!%p^jkQ`BpBC% %%Rʝ?S:@?G)/YJCʖ" BOn|?zgETWhYiXc ОWH~&X|nd0L Y0 DZLVINkTt()dY6ppC ñ{ f?8 +zX:a[SVZ:ăM~sMNd 'mS218|#_<T \'YԘ{d%A?ng7p Z JXP)ZP:~F2ǠsN~peƳ7M2='Hr7!v "~?]El :lTFC"kRG-ɬ8VΟA*zVQ &lg"zz^`% Zk!bvppAwd؜49W/TB2r0+p`QCa =v[œa8 LJ˭G4qceR XB?ZgC*ɪU߹d1VJ uN܈ByAS\ôhzƌ޸Xe&XɝmXpٜ\Kڙf_.'o+lc5&سXVoN#+bsc?_Rd]08y#IjG"@3NRCcզ(̙h}B367G!1ZB:lLB%B-2[ ,{iu^[1k~KzאA#W5! ~QU9 ԳRCz ,rLy"mY2l1xBzT9Y`fU%a5'36Mf+Q  tjKߍzz:s5SPP/f'exm^͇eg_|?}?4$˿dv̾~YbYƾ3B9¾VukvٓB՘a:w10Dʵjo&,gvYl2__޼}jYRK3-Ϳ]δ.(.>?H[rjp8Kf#{V-{?L ([2XoH;/GCS 91&ɝx ydv˒([7hp3lS38:Z{ mw]?+U=`JOEUEf֒Ub~;cbc]F4an$Cz9>((SB6χ@;0ٛ@ X>r endstream endobj 2249 0 obj << /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F16 138 0 R /F13 66 0 R /F10 53 0 R >> endobj 2247 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2249 0 R >> endobj 2252 0 obj << /Filter[/FlateDecode] /Length 3238 >> stream xڕْ=_©I9qRVe'ŕ0#%R!|}H:yM,[=hz^,-jMaN _~+Uv8,W~N ;ݭɓ7?;%avkXdmBj8u8m>"WHYeX뜿lU kc-u*mXF(q4WUvj 8{wm9d4ڳRA rɞkRB Iw!y;,$OT8SֻYɡNϱ,VCǀ荂w;:V& 5B.Y`]$z¦ %,Iĩ܊F8s r;4`ҧ>R(jJjyO_sw،$|~(~w!@=;Tu[󜓝6 sm״at/?{S?ޣ,|,UņԨ3zARĿm` ^~-[z(EAbxH݀2.Vr\IVI.+>TkiM:bٺa{$Rek8JwnxB{Se6-y "ze@6fVT&К~zf_#k<9YGG%G'w), SS<0 ۋ5wՂ`c@SVAح`O{^;%#i "-ҜpM ;spɣHF1p ,ЏQ>݈~dXˠ >a?bX W6Q!R`WʙmR¯NNAx9K3}s\M6TcR!uAO9Z4}gH"J9No>TH -'z\Hڢ++DzK5HUp曺ev8)o>A0Acs, 2AA7@LTbUot='["9™> endobj 2251 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2253 0 R >> endobj 2256 0 obj << /Filter[/FlateDecode] /Length 1587 >> stream xڽX[oF~@"R07.NM^T)ⷺ0;k.6{\v8}8;bEU$A$$YP "O_MIp)ayp*y [EJs$IwH&_w]KA+j.V- T(b[[ UuiK<,Eåõ:c2Ev¶[)ȋS"+iԱFGPК67ָVSjB̍V~pΆ)hepRmn1RB+PC/ebH51}3MՆOXn? hBѬ WmMQ #e1hcc# ٪bk.Ɯ"pjkB#MM( <7<%9Fx ~g Y@G8prX*>vfs"q؇huSKѰyGA' ֢RY\ mPo$XdH4E]f¤uiV0O Ku ϝJ?*k60Ԁ~V揈Oa$aMfn Enm!%ƮiM,( ԲS$=dc맶1egQ/V,#̥#8>Lʡ'KP&'@v6L^'Tx$ÀW Y^ds\b|…w&ګf6H۫^Ruհ{3-G=slVQ[Tv3^Mb{x-5CSF1c19;#ۣr3LR&qy2RG^/']y0eC, :wȧыi F$: )I)^hq=8lNX%b8~mԾga~~aϳYog¢؇;ٸW/zU˅(שwKT,m_WPnn6Wz 0tE8}YWzzˣy@ƞ8 ڴyo{Er1qpAГFTQCQWGK9$ L'8tD:\)ma6d1H'r1{уőbJNSנXcvKjSg+s| C%Nq?CI aQ㦴z=~Hd :0c1O1PksJe̼' Va KϪs{]w Uw[uu~tw.U'cY!" `%Bx%6BNtWdhmզR0޶bƬMꧤ4ToMrɾn.ZG4=`W,P!AT}PA! &K[n1B>كV1^;Kz[5:J88/C(qxcqdJj> ABij kSO`SNF-g_.ǻSWV rG3p endstream endobj 2257 0 obj << /F7 38 0 R /F13 66 0 R /F21 215 0 R /F16 138 0 R /F10 53 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 2255 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2257 0 R >> endobj 2262 0 obj << /Type/Font /Subtype/Type1 /Name/F40 /FontDescriptor 2261 0 R /BaseFont/HBTWKG+TeX-cmbtt8 /FirstChar 0 /LastChar 127 /Widths[531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3] >> endobj 2263 0 obj << /Filter[/FlateDecode] /Length 2774 >> stream x]"8^o;;=t7LKL}IQ;dvK,QER$E,Mg3+|ifRBivbJf~tnz|D>˿wݺl ݧs&nQ7UǓ g&q7-om2g#TN\θbR!£,bG6( [JV&(u M&+npU P<4@1mKO7F·ݲ)"?E밻r. ';On9x8ɓ$:qo[=]SoeeoTutN[_"|Rw+@/?V#'䣳f*GzG<#D$`Ԩv YՕ?[Q%.^0rwU~:U]UZ(HuU6ED(gd`nf&!c߇q)a!e8@a7G6牏d*$pBPS8, lgB p@SpD9Dy90krk}B؝)xۉ bӘ9e;H0 H%Ƨ=qq志 ?w3<6(zPixb^]nucY^ cd*ɳ߭Jn]xtMbIK^*mkDsqdQUS3Mw몽4-:c)ևh<8y*fE-6V{>~*!wpy@_8 Jׇ7$ M+a*^{3#1̡ެFޫٞ!GT Xe0i#. (~(5bE\\h&s%(/j9t]5[q c3By_$=&k̙9t8XwOZE6(~Yc#`˄ ͌`+. 0m8k7p {!XF(\z8|B RcڲP5vZ ?ؖ,r`f…31>s=mqI*{,_Dcl lYc=v6優6Lٳĝ!b6 c{@0l| 佾#(<$OU,bTh G {!Ba;"GȅԸP'N(^]nɿvNS.Q6d0^#e\.Yj'63JM3{¼b^,V (Ė17s˼aJY+hAR.ףc,t{},9x>63I]a~D׼0pi򥕟?w+ %h~W Zt߻M]\s/_6imϿ)λA1 cD@i%gd}?`kjF." ?ϸF%B(ZdX{\=WI nמ{¥GZ? 'hX3tYFhS 0hca?Q9hd~M#>3։PblNT|b ٣`Jo 1s^xa923a+̎V.L@ 9*$7,=7etJ&vzPmܾc-@@N]C4'2a[T8un\A\9L+% Y޷g_;Pp3<5K*d6Jtx>3qFE4$ alI-{ifE#Pk]dqvqꈈ(J\I%t`6[AȩZ˺GG!z@#bl<4pF//W* g 2sp}/A˓kwj endstream endobj 2264 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F21 215 0 R /F12 59 0 R /F16 138 0 R /F22 218 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F17 145 0 R /F40 2262 0 R >> endobj 2259 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2264 0 R >> endobj 2267 0 obj << /Filter[/FlateDecode] /Length 2418 >> stream xYY~ϯ0&/2护lIli`B-mJrw;>ueu"ybEM(ps[ěM\ovQ,Bi6D\||t<=NR|sQnw2d|:۝:1owI?1Y~7/c+>߂v,>ő82#E fXN͆ĕuwy9fedZ&3k7?v'3v**,o}ID8oWR/mK;t;$ӐMBST&GOA"fJh֎cъC} Ve|1w렂얾eP7MF~ A=jZ$o3hh M&THEk͓[c 7FJc%_'`i53g *o>2296>D."1HJkElMA|ֱC;?US PV%`<{SE(2t~#d1zΣ3ւӸ\ꫧԉH_ZQIY)kdL!Vuȳ{-Q=/Mן$ sR˒"|_S2Xm)<$遣A 9M1{s pY4"@ހo^=6ؠ(\=WSLe9`ᵢ,mG(~Q1j)Pscqԝjve ,D>r*}%cgqD,.HTP4`>bh@.rC@Y3b (pT vU\B[|3@fIP$BZWAo OU?!-@6L:v *u@S(RCȆ^<'VWϗ/!cv:g nv[Cv(zijtO- e8Tp:Qnsw+{TDzET Ͼ,\>+SK-R"a>>3S>r".衭yWʁLܑ{IWMgNy3{$9RA5s.q0hGn䇼EJxx.j?2Em,Nםe6Z w+ڃD9>:+q)| 8qu}hv_ 4B,w|LCx9|/NF!&|+dbv9F3U&GUlryOd} ӭYGڽ@?/2Xf|+> endobj 2266 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2268 0 R >> endobj 2271 0 obj << /Length 632 /Filter/FlateDecode /Name/Im62 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2272 0 R >> >> >> stream xVn0 }WO$Kzf`Cm\ ~()v`=MJ[h4V{qK`F ы)hʶs ت$4`k2LxIS>XG1 DêD,$c!1zT#(cXWZRL"$b%-Z+DjUaK{MOmк^\h: v&2NzSKUĮϸ7c)lԖĺ^^ F_[zόfw1q՟wtɤ:vu·4R3N9Rs .agLi4!B]KsEs{uvzɤ3m|֦{C.H}D1f[ci-77YD.7d /1Ӽ_}K}eq9`& gN$FOr|է|;-?) Dz6?Qmc*%Z(q()IHzX?7+V-@Z7$9y)8|X-g6U0!=m_~ endstream endobj 2272 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2273 0 obj << /Filter[/FlateDecode] /Length 1804 >> stream xXIFWZ rD-d(b7 m=jjnr_(%i5|d:essTgn8HF{7+ۮ.Lq)r<."S-D3C&J/<+>LC,}1RyF/Us,#/VeǐJxۄ1ݐ2džDo\ٸAsMp-0AwXZpWޚ9Mm$t BkjX0!<(J>MeȡJzNT4*w(hJwoG;2eLBON ˓Ffww;iQn\~B}cU ʬA V?|ozC +&PzQ|(J^)Ŝz p%s[sU͸M 3u  Su=':l N!aIM2v S2Jɀa7 ~r]l.z9`}90tGv5w#L6a';)~G?~vIWR͗/i?5i<ְ|WN endstream endobj 2274 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 2275 0 obj << /Im62 2271 0 R >> endobj 2270 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2274 0 R /XObject 2275 0 R >> endobj 2278 0 obj << /Filter[/FlateDecode] /Length 1831 >> stream xYo6 _a/!VOúu耡i '$|?Re#NӮtDI?򢔤it_GF"G$e>\]6ɫ,*H ON +BMBEtI81x(o6T_K47,;E".rߞb,G B9ԓ(5gaѭl+IiZl>T\26Ϗtc(TB83. f={?W=ߪm*np/{)AD $IhƼ@i DMށ3_ uxt zΎ[z/3j 7xa绲m2zRWU//wzz4DxX_'xJ7U/~'/<I{>GSɬ֎^wW#ܟR(^p5BNjP H4%,6ʍf0']C 1B g)u݂^5H.v#e\6*ʞv);Z:fk2=e`䦙 W +^uWNH41"i|~hj3 MsdڒmQ!H*Oqm<ל )q1!"o cX~ a <ŐCg{ ̲ӯdK;i`KWL AHÇ3̽F@C%Qr+FΙ,$ e-w?I5@ ndźfHeH>~ts\Կo(/j]D܃,Ge HSdT"L߄.*iT!|R8If{dV!齙&{3IM=woLb_|G_tE{sHJYL=~{4f$FR\.BVt m1p$܍1fxGKgd0{pR`wK~>PWhU,|RP+=̬>:k4ɭh 1k@bQfYa10pP%|-q0 L\81@r]v3+9a2'VVؠ@3E7a\ƯV\Wek':@c`c`pa:~z |!a-Kw4&}тG7V.jǝHgQQVpU$0- y$Śt@mxNRZnPriϦgƅeqsB2P>;ݭf]"u%_jf#~9q {QFKŞW(݈eIUa(KNCX)eImzs}٨fej`þ&ܜ|Hr\8@AK%R9O6$?~Ҷ` n%L]hA3o/M=~ޝKp[vH[JiMTk&J{h=Uk;gA1I#U@jI뜗2;U Z`gApS=_H 4x;X4pI H)P.r(A*Ek6a{ޏ˳FVN lm]\l0 'y1-O<Ϭobݡ>=Vڒí<'a85_?pK* )1?Ƞ endstream endobj 2279 0 obj << /F7 38 0 R /F22 218 0 R /F20 182 0 R /F23 221 0 R /F11 56 0 R /F8 41 0 R /F13 66 0 R >> endobj 2277 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2279 0 R >> endobj 2282 0 obj << /Filter[/FlateDecode] /Length 2235 >> stream xYKWևPx mRɺC|) 3bJ"Djv߻ C' -bNj~!Y|Y1babJo?x<]w9n:^ fIj?Ǣ*l_Oc,Ie󋕣b\] L*24luSlwEXP|h;ү!f'ʡhw5΢gZ9VihfSv ~.U;>mJŋ,O:mYWHiH@/KJߴ!f mG9bi3UH~FN4m[ǢOm㩵a_ |ejD_vwFQG$gg(!A})Jxqf|SpksiZGMQm?2 iCAm*rb Rx4-w[wT'xC/hDBCr9qO]y':wlP CCs%w.vұn W*//($74-d7uX&<2*zv"}șnrU1'I7@-q uSA R+%tZ2& 5a,*O֡ß=Y;~y0W=`Ə4>y?8>jg2G &W1$d%@h[I[ENV؁Q+Wqʺx,|?%c;dž$p G465v }ϳ 32L{Z1>-[0%qjda ǜ%>jjX$7^F~BAK#tey  -7}z[qQLs vSX54%,OŖ9v_ƙt)4yhIJkHU+eR,]BLt4J}J ڱhN$p.p>0jDH3ˊ| Q{7g:!!H `MwW璚 k=GpEXz~irw"=-!ͱ4n$j*^1RäTqR@JstG~8۩5dL'dߝ~JukX6a&/+"bZ 6#l5OwhŬK*f -iTBuAδy;&, $dp&d/3""g$$jgW\1! `hDZ^!|m< mNvf~|ttMcߪ,|=/ne< 1Z<Ѱg!]BsC%f2gRʜ\P%YGoOٝ8`wؾqtߘ,a< {lN!p+&A."gx}RgYx׶lSo'=܀AL:|͓ߦ/E3hܙ~b 74hY>J6R6*|vxYr{i9YgCOM/(\ z;,MB 44[cgxwu[~;%E}8T3c[v ]ܜSBm00wcq2|I\oZ9l<,׺&q_[|{yT۸C֨9qq]k|(<O3xzi4][s^=pO ܀2#xdPt]8B%yo;Z8S: ڴR_1V]LD*snYd>bGz RbtEmǗY oF΁)_|wU8^,}^#,Nwdo5U&?Ҷ%og4-k4Woxڟ8=r)L>lZ>\[u<+( endstream endobj 2283 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R >> endobj 2281 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2283 0 R >> endobj 2286 0 obj << /Filter[/FlateDecode] /Length 2628 >> stream xڥYK۸WvR#, 9^'8U8T<} )M9FntnAoċ E(ZDaeMgBe+h&YiYց[I*6bӭ>7eQ-UtT5XVW.Rb&"XHHM ՖyVJhE,b *A+hi#J۳F"!IʧKeBK7MmP0 ip׀Hs :5M,Gc*IDNTa(!6: 2UQUw#=V:LP$5m;IGKӐ ,U[n#fܲ7mbLe#iip̛ `O .MHz:wPFH4?URI<$qtkmibJi)ZkMW>vTwp7e>9g@hj꩖CzgEN?"ޅ|sޮˊ3kWTEwgtjI)3ha9N _$(BRGYp}ޜl*g8&~wە'Ǧ>r/fs[ j4:6b|!ZWAikYHDeb0) :K1ya2td_HQhRxkg+&XT+Y͓e %AGGt87V^iȒX Hs(cgIb 8TȴV# ӧge~1tOpEȣfo&eAiզI@f3s I| xa98"PA&O]Ku* !CPM@@ R q\A^'+!hyOΊSK ZWn͑hU.040E?Ʃ >GOˁ5fX7oN(Q1Aʖ[/&G[B? JRfC'w`n9k?,&Qy@)*gvF`+*s^ :K!m dš6C?%g=f!˺)g;Aqu2\$S ͶP,`[UbēԚ n2ğu7/h5<{ ,>"agb L#_x=P>E< Eb2J0:"L***e`P.Vѥz vfH2s6d ޞvBNxvT#.(/(r.0Z\󟛲+eCţ0KvE۫OE}<ͅ|4sopÈzݼN j_74YtS Z\ځS Ծ2wX/W2LXX3@ ]z’ޱ|=10s<۲813+5 V}wv|:S7ĝ?O] r%fw5_Kw7d' Ǟ*p%}`_#}= (~\9XGזҽ rxƹ񎌗=p j.֩4@MwC0uD{ƈYf^H_#c򶀛2:?2#WKI3*\[ " 6}w=I%.|ORk*}x\3~&!n-+k &Ddeڝ #ddb2B >1S~`e;4yh9+__ }cFl K޻$DFRF=܂n_}eC ;@‚ePl3l(1XN(ȖGUۣn@in$?5XCG#1/1ȥ0UWF+2sٱS *$ڗfr~^+w\9z3Lũ$z=A IE7 C)+OݕO|wđHڈKP}&mQG?|d=τz{*5pdadT)Јނ~5zޞfpI2eL ʯ aA޴'{j&*\`/Ko|u)p.s$#B? ~ endstream endobj 2287 0 obj << /F7 38 0 R /F20 182 0 R /F8 41 0 R /F22 218 0 R /F16 138 0 R /F13 66 0 R /F10 53 0 R /F21 215 0 R /F11 56 0 R /F23 221 0 R >> endobj 2285 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2287 0 R >> endobj 2290 0 obj << /Filter[/FlateDecode] /Length 1953 >> stream xYK6 WC`Z[oNPEbK,zBQIII2ecb?'OJ&KL$DIŔwm?,XϙHN9Ob)sv2ncnw?~"YIfEŦ9}GR J Q1tpB^ } g5w5,a 4#xە 9&KHH`Š,`eZJ.PL."&J`"WxB䊱{Rpӊ)&%CG*5+2aY^5XJt<[b9z<A08}r!B#ђL` fo-@N˸}N{J39k"Sslj7י9J똋9?*qt9u U1+J~ x7FkZ ִD[U]@ #cmQ"KJe0H,o`ycc'?,-5Aϖ~h\էXvV<ғ*FzyxUZC[J]F:M1!g)pKhsh(=񝳥siɐK2m۹w sp?v+0mu[[N5~@E^&Iu!'(Y$t^#mK\ s:X 1KڇX /nu);;G;QC5U̻l\TSYM1~\U\R&RR!Y籞`pA?l!= feQ2n_xk߳)U]7ڷUwn]]ۖO;C3V =e13!?KRgsrPؚȗ: Ǒsp eW0Qf y94;:{x;j3/8"=Gzbv0Z K,1|yCf'I'bzZ"Jx 8@/}oz#W2),M;KOPM?Ԥq4nҊg3L-,ß^in1OݻIdseƇ!ƞ{_8/jhtyŧmsߵB)a~7WX endstream endobj 2291 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F16 138 0 R /F21 215 0 R /F12 59 0 R /F31 329 0 R >> endobj 2289 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2291 0 R >> endobj 2294 0 obj << /Filter[/FlateDecode] /Length 1759 >> stream xYo6_!@'5+~J=kaV JJII$+Β(};*00k"y0D, "X{x14*X\)a:Xg(ͩT}9<ҎgRqi9K2v3uE,,twWHy@)9(x/t`kt,fw{ACCaqi2ڱ":ȶ6nIܭ3J"e-pd0ͫu^,.h$}Y\˼[r-D0-_fRiˍ;8\.ewf S۬!X3I# IwRjcc,Jpq(2.{i|o ymmVIkw4r1 `n|F,ŮYi OKvA~okSMkg ѥa~f;#`Q#uE\H q 8'Hs+&0W ֮` a­AQrZ@NS&pՄJj DlZP&Ί:i hcp q3{;M6 z 4AX shq ]y'gvw΄`mvVPdJl/r{ d1QC)W?NiBDY|\c~O6t]'s! I;Ք WY/cs*}:n .}*pwƷݞn  G?PzD@zzT`3xGtxbp~Ӊ ASNw/#&-}x[8vjmgW2En{FsrO+—tcyloj[;FwotY+W{nvy/7Ezrfò(6>5`1vq0q_2,{{LjW;{Lrݗ6sZ|⢪K,XtLUۏ6@4|BH@=!pt?j z ˣ\ d_(^zwBI[m8}'t A=;8wl;IX/'@!HLij GhӤ6!9O~🹾7 ,'i~ODꀻͫӚL^!ZKӓR/'5`:dR/?q'- endstream endobj 2295 0 obj << /F7 38 0 R /F13 66 0 R /F21 215 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F31 329 0 R >> endobj 2293 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2295 0 R >> endobj 2298 0 obj << /Filter[/FlateDecode] /Length 2620 >> stream xZKsWLS&7R]NŹDU>xm5$pBrvn4bHXz_D4lr盇{| y19+f4ibJv_o>盒fsss&߾n =4j+ɪz_ͮ_ 27ctA"X?a+e I(ƥTpBgc0>_oFz~plKfsVǯ1gb9R0ITQ6 E6.҆«RgΒKf'bkaJ8WY2!]LorWJTdC+pYT8_Jei[B<(W@lQ/\zfZ hqݕ0;X%W-$:mHt1 4UقtQhw=H(d5u4#ɨ8 ^LK-9\*eDwiJ>VOłYBV,7Jw 8M fRo]f'ƴtx@H.Ν7)YQ^2)mP=\8HVHeSfBX0KR@ǧjq 5 |!|׌޾@ :Vӄ dB~$|@MOSn]նNn 6&zFÀ"?E9 ݓg6OX!p P>)2o H-zͭEEZr)  s_۱9+r߹KU4#<{B1#4kC`=Q<Ɗ0-5N_NQJe6oQZz`KKXj5[YxeM(pi u_&+PS о}7Y|F1{S 2?:5:̪ݮC T# ] >w!Q[3z=Nno,l`Z+|<8P@1w+ ޺A@&ֻBw 8Խ)xb )ed'LWsߍ2ǚ891W~==pЊdshzX* R x +0o88nk"Ј(szBCc!5}awPpm@$I 1B'uD$K~BmVCgag!)LJ$ tGNp<LX01f Zj') C ݼN,/̔N7XB'Ƭ7`xq&CzM½{MdA!PW%wϸۦ.I^i[V\qO5,dFcU;E*B18nPVE}Q}Un6,V3a$T/0& F1o+bq+ m߀>|jت8QcLIЯ1o쬂$t(YlyH$tt *|GMDc<&{sJ7NlKoSLmb @3]^aOLs_ƣZdz i4.7ܸ\_j]oj\'uTHHD *NOHk"Qe(n&x1`2+;ekJYЉƼA!I I<wl_E)T>wm۹>^+Vfv~'% Q x Q^BIBU$_ $FEL󥊅s ud| OV#p?{?ĉ0ܢ8/ x6.ӓ.O8:9/Y`Jz`6%`)2$;B2&K6<!#32[3 HN'cyQ| ,"] Xnu/:_3?OIpG |[P`UB||@zusMLJ5CJvxv BxVi|qd6'IpǪ[xr10E4n:R/a¿ ~ /]2j9B:>?Sl endstream endobj 2299 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F8 41 0 R /F22 218 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F30 286 0 R /F31 329 0 R >> endobj 2297 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2299 0 R >> endobj 2302 0 obj << /Length 251 /Filter/FlateDecode /Name/Im63 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2303 0 R >> >> >> stream x]N0 ~ a%vH< i+at %$!4y%FmҕȰzF 6_ (bгG&)+~ɊͶ,Ht iqUGjWoԶ2 ,|F*9GwsYE.Dō%| p3>`yZBrmCZ9yy|N&W4>~w!fCeUkR|7l>}a endstream endobj 2303 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2304 0 obj << /Length 280 /Filter/FlateDecode /Name/Im64 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2305 0 R >> >> >> stream xmRMo0 WBz5ҎUXGE芐gKɱc B#_ޛW rL92؃-}vP?#`-y[؁3V2,h ׎*6ͨʊT]]kF9dȩw7_rgZI./"? I!=ԛ Y^**Z7hܐyyk>D&`|qJ#YcZ0}{ZVy endstream endobj 2305 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2306 0 obj << /Filter[/FlateDecode] /Length 1072 >> stream xڕVM6WRqor$[@[ZUX+]w!֛Di4y8#TD;?` .pIQ8>W7w l$ZmgwAe\ Wyˈm^Hy!d]mNU6ڍsهU.{-'5Ol`"[w4Uo` ˩TF %Vjю}ݎ-F;b\`NxZu>bʽj_bus<&V EnP νyAuqocvDUa'B0Sv>:9Y\j:S/Z`&AкA7o}"82#]= 0֐l@P°(eP[DŽ)x؅ʩˁvyr̐H z6C .P)=WB"#zrE h^6 sb!g*m`#|m5 HQ**dcV^тL}E7"fQpCГYhW"{J\n bFfPIv[]:C7ZmҊZ[lW$$f4bg;sL XHL\FWA/&4! ) iX'6q9"5k+‚@"D&CL$P5ztb os R L;/7) nWFҽ/brLcs7xrA0)|gFڊ2S8qc*\s\~4EU, ax${BĽ XH2Np +7^,,;P(w^O>f*@_-+<(B0Oq0KZBO\CLP~3 endstream endobj 2307 0 obj << /F7 38 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F31 329 0 R >> endobj 2308 0 obj << /Im63 2302 0 R /Im64 2304 0 R >> endobj 2301 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2307 0 R /XObject 2308 0 R >> endobj 2311 0 obj << /Length 317 /Filter/FlateDecode /Name/Im65 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2312 0 R >> >> >> stream xmRn0 +rd8u:i#c i?;Najl??َmʗ.VODBkpe/›meMp8fa dz#ޛFe5+TczTSV(GU-z"-D 3AUOiҤ&AiIK *Cں+WA>ԫHpD2S#: gvrf#=xh^Zx~Rai?Al{:n#_sڏž?2iw컋DIJLb5s3eBS][VuJN܎{N%fK 2d endstream endobj 2312 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2313 0 obj << /Filter[/FlateDecode] /Length 2466 >> stream xYKH+Ev^`e@lh[m d1דYYU*˲c]Үze~VG KGGoQBF[Y"SbJy}GD+L7Y_jlN ڪwxO>fXeZGBօra+`*B1.Ͷצ+G1E΄7(nO+թ߭vr];TπyK5; 槄z@ n{5b=hT`3^pB#O㞧AƎs#gd'g'XN0:.zK*(}60'i` R%<.wD,*CS=Ѳ1ƊƘ ̀|i$C_@K,o!숌fĨK ezzl|P)pGY̲ZUOOY[DI$wPY'h7D:{3 TS0Wr3TA<=:)>j7Y'SfTn; (޿\ }P2UGpy4ja%|W/pKĂrxؔ 4Q͘B2Kz'G}wBU8)FY}PIWuo@|T0%~ fND苍D~}0̳up?%0ƖY NgIXО࿴B FhgLN:9tms2$WR>I 24ZKZPIEPCdDcgԀErьd:<uE49̢s7Y*"9FDp<ugZxWz!P7,Q`aMӝ]Gö^O`DxpjzQ5siHomx-wcSɭKp0Y~ԩ6n7ԛE v vB#v,6E$m$xK;(i1=(հ XAL,>qM9^kH90WqkI?îl(e&lfekϙ\ sߝW_y/`W҇2e ]r(;lk:J}cDރ+%}e4bb Zr&"XJבC[6MJ9SK{9*1 %G1Rb+oS endstream endobj 2314 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F20 182 0 R /F23 221 0 R /F11 56 0 R /F8 41 0 R >> endobj 2315 0 obj << /Im65 2311 0 R >> endobj 2310 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2314 0 R /XObject 2315 0 R >> endobj 2318 0 obj << /Filter[/FlateDecode] /Length 2058 >> stream xYK6Wadbć(,; d1L{ln%v~VH4e=Q&XOV} K~a_`H B.bD, %S>,/vÛlQB/w9gXCtV^?j2)wX,z^Ʃ~6}RdQ_mHDfߖc}/?.\1Pdb0- $ r>]Wu,DN܄{6^6/t11I5Pl vAqy%`9 "U<ќÕvms C1=2Fg&n iӸygJƺ-7)E'@~\腠Ȍ Ψ=R'9lc1I2=L`5)%R c|ˡ9PbdkN2꩘. P4 f93id3 :5/4b8irOETn ~" YL|Q%uV6 aĨ 01'8Pꛀ'ۧ sFu=si'6#.ݥJs9X1fh4Phsֽr I]LL4&!R)@(085!t}*} sDV[|IAԩٗ_YkCxlm@>T|i:\gp~r=g J/|sC_5M٤5}74h(rT7PU E=WOOrZ7Ple@Cfb!Jq+I!/ڵ> endobj 2317 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2319 0 R >> endobj 2322 0 obj << /Filter[/FlateDecode] /Length 1693 >> stream x]o6}B"+(ukņa0l˱Wr-w%ʲKDw`w޽$^$dޭDz{?Ϟ,fy_H~S&?-"YlgECNI{"%)R hEr=1QFxlZ<%){}6>|ᐶw ef(盃Cq$Y_si8 (IF ʏ|+F ?fWOqX j??^:܄*»fva4¡>[Us/Z yGDvxOΛ8ʹwbyp Edq=G[mܸڼ,_>>*C_դ-'M9#,R,k὚AKEBR/@ِWR2".MDy so;H!ۼ):e?y)w 2)a E]4V~պĿW?/\!_OQ]pW#N)A# s^G\!E"rCJa R7IM WƲf7cMgMr =Y0 u J&;۪2tZ]5r\!J}ӈ|z55ۤ$@u\VA'T2Y:c@XYV/* }# Rv >J[=GPUMQSG= )*I~Fhљ\-M) SٶX3mMO9(W*ښ:Ü`Foe5(#Lj{o=3fkzoGJCڕ#%ĸ@W*B~x|:ꝫ I<`zbcv.'J-MULyd&팠(?JvxuKzlq0UlboB^YM؜:X/ʴ*Lrm rxnA{vZ&_ѳbK #l<09iK"2!-FߺPh2j^)3Qb(;hO=.,qϻP$&)Aɳ Y󦡒tA]й{ u.NvM$Bk6]g83쀄ACa`Ύ'য়= _T<("Mr&(ldx/2s,=W'vUҍo׎,ʧT Ozu Ɯ k9_rFгS\?DojqtK&i"i\= ($SwB. Lm\ 6Nq ; l૆ O/j7&a:Nq, $ &&(ʞ5!Lsgc#B&T_\]tL=g/DUOiWiN0g{Y!y.iB^;Ok endstream endobj 2323 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F16 138 0 R /F13 66 0 R /F8 41 0 R /F31 329 0 R >> endobj 2321 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2323 0 R >> endobj 2326 0 obj << /Filter[/FlateDecode] /Length 1814 >> stream xXK6WCd`ňiSA!ڕZ[2,9Cn6ȡ@OCr83x! CSy'/Iʽ@D$d\|^z)IcoyשSRo'_lXWd6?,ιO#Yq,E? >ʼZ0硾?d]Y/>/x^bv I &j/b7˪^4$2j?^֖J{E*wMSjsfJAi95:&λm4!Sc1ITm OJa#PJ%RuE zag1ȂRM-qk>͵4eMRb7}MEͬ!8pHj3҂uoN Z ٙoHL)/ 9Ku,|M?|MN6efQ jVK=ZCmix IlL "*Zot}r!?6ި-٪id4P*bm}:0ju9R3o޼3=~;-tqE/Zyh, sF "gFģ$J#y"qb;_h˪eۘT0]'HĥD[zAJqܮ-}х3&f Y `A Y(KK6"G*PPAN!XKo''zLUc\VO"dW%'Kg/h [>_Z"[!` { !`Q~8B>1'±hi4FNNB Ҕpƣ`\`a=bN 4aݜ#Rg%} /q ,uqyKv6XȚ%D1~/>׬i_lO_pn endstream endobj 2327 0 obj << /F7 38 0 R /F20 182 0 R /F22 218 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F31 329 0 R >> endobj 2325 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2327 0 R >> endobj 2330 0 obj << /Filter[/FlateDecode] /Length 1655 >> stream xڽXKo6WC$ =k@}K@vX#w!eJdIp!qpg$cd΃HĤ@pA3O4dO _/E.JhXvь'iXԷr˺lU?E׋5@%IJH9r0,#Y̘ +ހ^+HjT]"Ӂ*HF5pd30M==*fkMdq.b\i!Y&}NI$슪f K¯1~bm9(S~:r&][ڭt{6'<WU8r:YM{Z^?` if6g"k' hH RWַ mt谏J>ն:"<)N1N8P&my*Lғe&!م"#0YUJ媩-Ъy7juD/Rluj>Y6kjY6]ө?= zcXkkx~CwW4{<JbSP Y#3}O+9nn-$SP"`LJzSw8w pJVͶi[q53`[.$7P۹H 0@'H}HZ^F!EɢÇ Y틎HIBUXCFia@,=eWz9')suabN&Y1_ SƋǻ,MͰb J}?_ b[@egZ3U /i@ ʦ0Wxe|J($Hv\O ǪXӐ,OnvRύׇJ?" Fm( ? r"k S%MT2JbLO=XLZJf-i4Q#8)e~vc%nB$,eNǺ ]uhK$:w+)@0>R!`oK(E"Sd6tcyERNDv"P&đ gv2'7VA~n@7ǘ[z}s/>v+=1evaE㹾FPVd`D į̂!\6vu7+;cns;v^C=ʀu0B} )R_5'h 058T m? %)Q覵\RmL4T;513Ӗk'nPcܵCL^:re{V2n^2[T%\ݦhw8}A>y_5pd68-LL@]"4f´lqmyyw{r3kؓޫ R&=14b_zׇ^޼5PhP5kti6<Ňismptޘ>Gpp3~>(Or\6B{$jx[l9T<25_d;1*MV KX7M$o538:*-hMlnX endstream endobj 2331 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F20 182 0 R /F31 329 0 R /F22 218 0 R /F11 56 0 R /F13 66 0 R >> endobj 2329 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2331 0 R >> endobj 2334 0 obj << /Filter[/FlateDecode] /Length 1864 >> stream xXY6~;iCSS6h[h%ÒrH:|lvQz E 9>rZ[K6AXwVij? .n-aex.bӒSkGCC&/5~xgW3' #r%]5·}3MDs_m6/O_ԊՊ0PONSFl6ju1=5ۧM9eU*a7s4oh>"Պ屉]PMhI(Fv%v*s$+1VD&L/` o6yQࢫ s$;m8'0Z ا,^ap)whD1l; c{]\GT9 ±hubUf[5s%N`)RD\xѯe%Hq_# Bb3k#H;o( Mm*T/+1`#7!Y5Қ ɍIhR,8bP4~,xTQ^Ei[6BoWkBtjwߡ|B/z%_ΔoT]D;đڌztm=>rQij'[06l7m27v|<eeOFY1-Pg!3-I &*cF;?Dcps 4јgމ`Q @R T}'Ыs.Hy._`"^*ˊjیmg5|JQ,o";F{lxKj [wSۜ '%vڟJjTR5/(M= I^5,xx”7g.B1jҌp:>ɏNBpcs\ .~h endstream endobj 2335 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F31 329 0 R /F8 41 0 R >> endobj 2333 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2335 0 R >> endobj 2338 0 obj << /Filter[/FlateDecode] /Length 2311 >> stream xY[ܶ~P ÈֵEb fvD# $_sxHi4hDrse%II(KX)JY"CbJywݎOǺ}leU{"5ZCp|7r&4sRZIT [JUqi}vlS+ U }Jn^k-mKx 3OmD{m/{cݰT9qYI07"Xcx@9Pb#E쐢)`"Lo$/I^LAѱ)N"zyR0A|H.1q}CG|pbS< etvHpJ 6T^~Ȇ#3e$ F 掑ˠ Y{V3B)\)ċ@0qφ"L*"5Uo,#/V`]V`.%IGpH5$M g̺&I$v2AݗplZ)Mvk"'mmq !نbPvv1uM(s&ipdTx3(9vXŞ dl縜bLl[[{NV)mP[yG$OP=xX~*pGo~2Dz[DB,/IbK?-.)CҸUPp;*sQº*~Gj!>~Ԕgseiā窡sA\aOľݬvq=Q)(ΰ1> endobj 2337 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2339 0 R >> endobj 2342 0 obj << /Filter[/FlateDecode] /Length 2261 >> stream xڵXKW8aml+%u0I6L2BmȒWS*J%wf9"z~U*Qz\ᓬW&^mDDju\:/w}r@.Ww:6f&8lkv5&]lۏmiJ׽#Zmx,Ϳ~{2>1ZmU,v}^&eѮ'eo!2Hc%Dt˞Fy Lڰ_ʴf2'r4(v}ԝ-z&.jϚOI׶}h#cKm@C;舂&YJk-whyrTh:KâAI4аgON$vl%e1dO;T R ߹%"w"9H V\bYFay22jທ$ϥT |h!lQɭ 1uTw ՕAz4:9=qv }ܺ1@ip+ (Gt}YI <\۪(~"t r]{"=2z=7?{LA}ݘ4O䨫^M, O`-0TraY3Y8|i 'MȰtI⾬HM+X8%*K6EuvjS K~#Y=1<ހ2TS?68d.Td BZ(?WLGt7P`сgHzou0RC謏#)s(-=f4I~jh}Cbv֥8 錇jkx-SyA+{@!ZD2sIMsMFvޤ~217@F?KpQpsاW_i 8A&#稤3XԎ`gc'׊I`^m o6xw# endstream endobj 2343 0 obj << /F7 38 0 R /F20 182 0 R /F11 56 0 R /F22 218 0 R /F10 53 0 R /F13 66 0 R /F8 41 0 R /F23 221 0 R /F31 329 0 R /F30 286 0 R >> endobj 2341 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2343 0 R >> endobj 2346 0 obj << /Filter[/FlateDecode] /Length 2481 >> stream xZݏ_FcF$EJC\@ d>t@kkguK(sw-͐EBdP_aLHT-$eaןh(H!;V,nX75ٲl#_~lnp9}Wן2G։DKe)]fXPӛw(#<7D6v`f9qf)s[soź|W`IwM)'<B@O&vQCZI Iȑf$Z itWHVsyC2:نj/@}?[|_ /1W^/qZcm#͕J@#Р`PPY4bSTe_EYGv+';IԦjZ2")$)@-FuPۨ|KDʩlDzO_|q#>Y9t}24:.p!BTz7~ YDm9jM3 ÔdFEqPUʦaijZO^h )_ZEъC IR,<_zx߶8W\Om:mfD* =Ζi]{ڨiNJqڝ^*=MחfXUsǟ~nr3o5{Y~,Ŭq#KdTP?͆?2R۲Q_ }6O=T}W(cA))Jl$:IRbg($s`BSXh}y*7}u2BaJ E羽OU0 ik+mNejl%z[lR s#>f)hRr$D!zynu /Q)2BC ~73{|0QU/Ô#bȗ6jHeit]8t F*r2NsE%l.4x,:}A+VľM? Rw+gs'q>]A.]_>@mɮ:8EB$~rPw'Wi<DŽ'T˭۝wLd~Nf1:}UqЪ7q:pp 4BCuW.%K#gm=Zxuv RBbUeUӶ9ugoFMвVϽz,Oé|s>6PzC_m_ ưTϠ+~ -& S8pJ KЌY#aWU%mcs]m?}[ !x1#]S+| +2dY~Hw,5)] 2wik*2xʬ:`=ˏqd tՑ x4#Oӡs=bhZ?-o% ORs6!.`_ 2 qy 5qObjo1f׮s (ן3)G J'ݟV=nc:!x0B&>/ۗX.YҞaߗ!C#HJsBN$e ) ^fmGnNz ("^l1@#"c/{ȻG[櫌><uJY~fe=_՞[~Qh#@zy&/`%Dֹ |drt&yf3Ӎ)}bhsZ! endstream endobj 2347 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F13 66 0 R /F16 138 0 R /F15 112 0 R /F12 59 0 R /F29 279 0 R /F38 688 0 R /F6 35 0 R >> endobj 2345 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2347 0 R >> endobj 2350 0 obj << /Filter[/FlateDecode] /Length 3166 >> stream xڵZKsW( UeaHrrdRJjj|SZ-JCRq~} %{(70><Au̲Z=! ea:: }&:U]u3`u ҭkΌnu vK<ȩ~Hb3vGw( ɣx-M-qHtic%&%x̂?2̦$TD~36 +I<ͯgURyN&l=uNPo2۔i lHs[!^#=X+M̞QMgk.3(S̸V[GeYʗ򌛃MY !#[W^`cMa_x HyAܽ^:2uuXX;el# ^3ԁHg6/Ь6ߏ-J é^Ө Q6V)>Qq_ؕӚr_̆4( R hPNgagvv-Z4Uq4@$e"]# 8睉\1 @VNzm HfZfU&,`K^gdp:Ѓ. v$#>* J37\2rH2 Zz+}HQϐHeP[001\YGvIȭl'N"GLDH96nW@ݿ$z^g@ǨcԗnrFP|F& a ŐDր,@8mp6]c)j_u钇#{svO[i٫{M͈&؉'0y4L8+O @ڍLS *M3%sFƜ3Jc[旷.g39j*SxO1C31Ǭzw1dĖ}l`=y8.;A%ѿzWdՐ{)e/06?GPbu%$<+T#IvֿR5/p E avlkͦ_:L @U=2`s!]ީl!o*-}j[T!(s7  nξwaq&#"6.5F*o2`}*[U_i`#P{*I?2ɜ97TqKU+~ y}2+?Iy '׵>jY.gS"C\\28&Os6d;I\ =u@G]r{eKg3zK(@ȋ-؊ 6QBk8 5=[z' ^Mg (x1n$L ޚjpWc1oljmcbd9$+$Iw|I/0)S`Xh˸)=wfSvR V ւO%Ą.n*5%V&#)rQ{Hi&}οgC@KN%ԴtArR 󒓋Na/%_52βקN9u\#z.ۦg^AD iGi&)gC[u~Pu6֖Laˠ)} +鵪KF??C&ݱW;刢=T+p'W 20B=CAtqYH{0pMXrRZQ_6禘 {}Ӆ?F#A"_UgOAh)ĴL{,qI|L.zz簂R*~p9w"M2I%VCR"K^_u ,ˢ;:p$;:̥ O B&̇W"QPIt` 28qP^Cu?79m~ۺ(b)Wbʓ8UT6LH"=( H屸ߛ#s8KԱ6HSjK 4/HPsEs_An>>b:(N*ir6 ƪh/qcMKA?1P"&R'7hT2W~+R i SM̤m1){,º%L;A6b:vآ> endobj 2349 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2351 0 R >> endobj 2354 0 obj << /Filter[/FlateDecode] /Length 2081 >> stream xڵYKW U5I|mJk}[&PЈIhIjɯO A㙵n/t10$%H#V`+ LrGA41E?, ]5ֺ+V$ii|n_M;E$ "K[nd,}!E{W݆gVGa?L y}=2!o<*TM9 ;,xiثJO7 nˮR c=ƪlG6YXjCmΖ Q+yE[]>aPȨguwHOKJg3ӥ/pNwx]L2pc,Yc<#ʳNy%aڜЍHCN*"/y#gS>D&s{4H:#^GR> p{n3 oVr~el(9} 5GM$ҭfOmy1>Glڐ/:Nh[Kܝъsgm],!9_N/ Exe!QP .=lIk5ϬS1PNGg*!M""XZ|.(`0JV |( Őfj/NßgUuP.%O/oAPdЭ5ù?5e=pңSm2?WwSef% Q9* .5=,qhVxn22[Ӱԫ&l&5lgi9)O*{1<#Ś/ǥ xRl"<ʡl#>qvşs&\5nZO|bbG3 ILˉ۩˯ u7CW4qkY õm,"=g<'\`Lw8&vꇟYl̄Xxzvs+_qqK:2%FzкYxP6z[7zˤ7=o0/ YLwx2[A"@ o-ƍuHjJSnl-Uv'kVN7ƒ`iCB.zzXHvPܟfOo˶ wn ep+@*5!%1. 9XD?rk),Sq{L7'vAgyI єf__ݸ.Y RsθZ+2_7z{yzk{O#&'W7Rfm*KoC^q5/[c{A8"+輠oQ<˾ߍ W;a=0pdR̀i ]rĮI~I\w88, eaPlE 15A!.!ob zO}A Kz/ś8Ob]^~׏}ٶ4<g/0AuK}<26} l&aYiaq7NJ2̮p=׶݄,zZB}X3bǾKbH0Ԩi)_bt~hH8i^p}?&Or, hJU^@!ea0>z@[y-Pt6'0j endstream endobj 2355 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F22 218 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R >> endobj 2353 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2355 0 R >> endobj 2358 0 obj << /Filter[/FlateDecode] /Length 1933 >> stream xYo6_!`I{2-'lɕc;%ђ&i{1#y$/`A]yfȻTamP,p}iws&w~9W!tdl.yD\p9G3[4U3ME*tɋg|ϥ9xs2qGI&$ww nW]-" wfJˬF S-~ӵH-(nP&|\q*P+D D62+;YОtJ\U@:[Je87䂺d/4g!ϹlizS yɬ/TN99#ؤCYd̒ ' B9$F!Dc< ~ĕpp"aeG1]"r+4f,e$x(d:Jd¢N)X慧Έ;*~3>Ո?thv;T4p: wPI#v¡>?aqKj~aXq'x>v(D8pssuY !2\`&Y4) |ޚD,-묲7Ys]`Z֖bPeԠ3MZPƬF>"(bb 'PXtB7 tIAhG) EvGs =?MsduZvVnVCQ,VGTȽU݇ 0hD8śsY*U^f plތTr$ ;Ihs嘣`D``GG^ʎȡ&TT6kHa9"0sΚ|T[{gd7Ͽ^QVoF[ɝN~lpvۿǎ d 08q\8A:@uBו7ٹ4aR%pG!T*e‟!|nҷsUؑ,8f &pKJ*؊ Y2-=xb|H5pC$j$HuB5<6"N8X  O_1pO:X S}Mhn)|W8:5 Qr"OY׹$Ԕ4X;`uzeW-s(3zկUV߬Э 'V,x! 0Bu6dN_8+k@rΥZvG?O # #ѫG'|G'0Lx ?_T/kSӢM}->V0hɔM+S9TA&㼶!PpL@))  d`o+u endstream endobj 2359 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R /F16 138 0 R /F6 35 0 R /F21 215 0 R /F10 53 0 R /F12 59 0 R /F29 279 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 2357 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2359 0 R >> endobj 2362 0 obj << /Filter[/FlateDecode] /Length 1864 >> stream xXK6W!rbħ6CѦ@/9dob rDJ'٠EOpI,5# aEFLki EA2m6Y}5+.1(/}W`&eH2Q6 9Ae!죸UyM!/O> U zi#ʐ<*SH#eG;u L7(C}*w-4/ >CA2 cΉ͍-B7&M/ ܬg<\lRifKó Lő>XJ?>-vy O>:g×7H [ ]zM]q͍1ob{?-g} ]nVg`kbtW݂QO2urb•_rx b+C}8)tWTqXC7 Bo^Nugvnl̀0t0 [HF@&hQjgf5OhAY`& rS01.5<ɐT\PğNՓ/3x pMfi\K@ " TŮ0 r _z:)Y34哓n\H|K'>CV88f(P9DGQ?0窸QVST]U1b ]T@%1f亜V̌ /4X!bxSg%.ӕ\rwl_s./W5{ڥlpm˃1D C9yZ~n?,[@*ʮGܻ'bH5^>3z1,4;x]9‹ punA^O@1 9t(\/BkS{D/6V_݃ᴾ8 %-HT!өJ ȣ> endobj 2361 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2363 0 R >> endobj 2366 0 obj << /Filter[/FlateDecode] /Length 2162 >> stream xڵY돤b`6ᢳd;>K%GJp{vx&}%W=U]lbǛ}IbM4QR1%w7_5e~Ι67x(#ˆ'gMI):܅N.)*&=N?Oz[7;1DB1.ihXa &S禬:S ^f(Lx<7Atܨ횁gI2κ f(fl- Wk   gSF.E6*6 g\L&x8[.i`t1!*aTStH ->uoÈ^R؝s çt>Lec޶z_*l}Jح #fue]GTt:D!GL/mTtkpm=e-g EI8cʜjVOKN¹Rk4[XO*JSt* ؖ:YpPJ]R ;|B іh1z?E7P$!whG8GVZ֡ pr6cw*pEӲapZrevchf7rU8NP1;2)`, lf`?legg!SiIű89ш).b%W"qqGql 4l:MG/WlaZ3h clI8DЖTnVC3hS,Y6.Ycۛ".%3[edJb~Ikt>8)}:y9doyZ4zW!'ٗH9B {'EP,.O@^WWnVRARlR^p`,TLJoe0}yRV(30,'wx,CɟBJA *d{gz7 =#[!rU[_ hY!A&KV_"OH2o*2!w@Bb!N(>9Hhۿ&8r7Ym^$0ךUy8b5`wA72ec?`du[i_p/ endstream endobj 2367 0 obj << /F7 38 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F8 41 0 R /F23 221 0 R /F31 329 0 R /F30 286 0 R >> endobj 2365 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2367 0 R >> endobj 2370 0 obj << /Filter[/FlateDecode] /Length 2031 >> stream xYKoWê7W|I$@3AsX_bnӶ6j!OؒN6IYb=zJ2e}N5>`{K.3+`fr U(!= p8GvY؍Kz܈@̘zl_Ui#{)ݩnnzOvoϿ1nD+Wa["O9[ -1{lsFj 0 L&Ry[c2͚ʖ"-sVſT _M=='d{g[3)(5ULA!xykeWKSVf_m΃;C/Ut3tvR}bKu݅?YZٚ*c[oI^GoT]TaSvgHt[g|?jFƹJU[y.# S$_1Ԙ%7xyB4-~JZwjn_h͏JM3piȡXg;{Қ(Clފ\S<= hC;1Gw@ lJvioq| LY"'N _pl!rŤXBz0:` > U{ۯHG)X0W0AeK^-*H 9Z?GcKSK?Õ4x7ujcz^]vi]tg`ȅ}nF0z<Y."ufGQՅCa,.\_N0c%^,߿B\ѥMgxLhAZB&J 9$-^~)Y RbAډnIG_l Z=[[e&vxW X.\Χ=xI5#=r(t?,> endobj 2369 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2371 0 R >> endobj 2374 0 obj << /Filter[/FlateDecode] /Length 2159 >> stream xYKoWtdĕHY 62[&0ݴ[YԑߧEJò={ȉRTd=X Xl6z# a,_F,F $o?\1d,7׷={xLcfއc~T^x8>~{Tצkө&\P{i_= wPyTa_mMd%YDQȒ|K}B Ps{*ګ ZfSF1D뤍i9 ֍5]+GTH$ͩ$j$=-EAZ̠D S\Qa'O֏+%,q]J\Rܿr(Ѽ:/pb7l0 [&zl%9T#V>@G*T+lߡ>If{Lv'jKnvY=XoWJEzcOJ\{hsN"Rg_\f,n!&@S/:id|t(NJp1UӒwUn:0r/gV,OC Z~C#}T ?C ͗ifssK0ݪ /=mJ?uWߘ&ʺ靝>!tMs"h`l!ڑ3t YupCev`؃;G vqE9tRy@LBX:?&KF).#p4f u lͿԬ endstream endobj 2375 0 obj << /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R >> endobj 2373 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2375 0 R >> endobj 2378 0 obj << /Filter[/FlateDecode] /Length 1846 >> stream xڭْ40bG;$ !(,Eyg;&{_OeSIRwK}Շ@w;bK:E]W7ExW#*οtZq8*\d8i$x{+zޥ#+ qMƈ ]HР6E ':MYU[ J%#-$lUE[6-\TON 0a7-Vo-c#|+jF04hZ.zcR f4)Ț`"u|C}PxYH_lUR#4rf|#le: &WMHlLeGt_Z#Z*fd'P#ʄLDcu"E"]xZP\Dҕq]]*$ *?r_BPTHg4h"L&Tod" Wk0%YG{hai`9[.I9 R~q/rr Yۓ'H%h&^v}oj4㿚809e |6fb+Ċqa q/h'2y!ʑ [@KTpPV„؍Q(/c`2%6N%&IEp @ 0j &DS<\#Sw-UQ*;f(axu";4'ptjFZ VwA!<ɈK.$<0{Nk,،rّ<->#-=n~D)EC(?l/`kaÓq%`S'~-̫}JUR4ؑkM~M{{UyUFrsX[h>z.+)}.j{ť{,'ozV WυRY''U'ʦI rIarl[0xvxt~h')yoܲ'+^R=RCgPd iLi.M)ͥ49T'~5v< X&Un!a"XmIrL, Q93h)ۦϱb(,/zUk9걣*;FMvwDK鬻`mG&ߕ K]B%sR.K IUXDVW6HZka7h BwH&|BOA+@P;C'Ǣ|,nt@*~K\vvac_ݏԜ^?¼v;5cfrH_*h07 =W1?]H:7i2 f 3JẌ:^21f~F[[b=>8`{ Phr3;VN'-/T hkAIo 5z~IU+9z,J,2#DGƪZ ԰YbSV5ҒYg3HuA!lh$# Z}]vS9?GNmZȖt-# |)DpeQc!PNXYkD ^P@zmBdlZ۸cieAUDGGi7g endstream endobj 2379 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F30 286 0 R >> endobj 2377 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2379 0 R >> endobj 2382 0 obj << /Filter[/FlateDecode] /Length 2066 >> stream xY[6~ﯘHml&TZ%R-E a&J}m 1xM>c$Y_O+T$e{m7/*'\]唰|uÛ(UrSLKz,! {YiF2>e 9+bjpVs{U;TPXey3t,b̅1{,3+ Lt}ߖ+sR6;0e?$N5wFc ZztǦhmQխ#cEЉb +V` mJSa IF{gL?)fLthS] \F~(mޔ־1c`0G̕S&#ZDGjct`" ɛ96/B.|@3vxy-6]75%s u9 -YQ S(?':?? 9H (~A_kʝ'TFI߼!/#Rbz$|N"^QNVU!OzId&@ƒ36> bk}`wD zV$U]T]xf91}.ґ?x/Xv_zj;bCPW6;C;g;8QMvs9fHIR&8ID )@:f\F_UoN-sL >|tv~(H:G3T5|,焃P:M^xP`^* J[(RM.a`(TLW\:B1ت()H[ӓR54DI> ot$ ~CEhm`4Ŧ$Iz#F~"B^7WۋZ]OtlP&K*𗡍s/r]+0LMGv G]<8X1s! {-YF5p3#*WbCy [&")gU-3#u j?p.B ń`LgA}"(VѩCr:-hpl ?aƎ}`O̒c9d'dU]uq&XV4~ C]f0yf!> endobj 2381 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2383 0 R >> endobj 2386 0 obj << /Filter[/FlateDecode] /Length 1678 >> stream xXKo6WZbD!OhC  hb8ί E<^L.oyЎ|߹uȹwbew d+;bf8ϙȜWןe-T>z (eQd$Ϲ9ቐ@`8 R#~m ZޯʶU*n|-{)V`E a.xq~ʫ1rp4k4ƳjC3PVhx\|Ṗq|%"`Q0_@gt 6nve=漾X!HK2v(1jFAW 1]SS2\(B[4 [L' j)i8 O"SFqgRkK8*FR*&~q \l퐼iE Ų3 <4} Sl=gG,%ǼU9hx a(9/Mv][Ux#@()ZA{:[tEZio>K]jԃé)97u{s7|F[9XxށR| WW6M: ]s \"l ᧿Շ endstream endobj 2387 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F21 215 0 R /F6 35 0 R /F13 66 0 R >> endobj 2385 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2387 0 R >> endobj 2390 0 obj << /Filter[/FlateDecode] /Length 1801 >> stream xY[oD~W!SC !y# 9Nְ6ď_nRA6>ۜKitٟGFwJQ"$IYDy~xZG]\)a&x[,tvP%\MQ'Jy\'R&yS)xſV*l?Gi0A((eWwfU8 UΛrQ eqѸ'_,y]5DPvBIJdRƽuL ˏw뽵**? =U(wWy/7;k̲=yW6+ }ޭ?qQGg|}* ~6:%FZo9:wkƣed=Z_( MR.S?V\hF2xTE%4픳7Hc*/>ѫ pN)'La5A4 .ЖYUVZ:HDv.tiv| //!_V댨je0mfaGX_WjHEx!>M}3+s֋kCW.Gi"j]i{w m,Og.@8)An2®DcDX*uLazBu5Qef< $xq (*'tzԌ~Q7r&)Ѫl7v8'm]^nfD0e{v)CPL%T Xi!DbE<%\95yR'mYS5"{y׫ h AU]X]E(1q=m*dUWiOE"65c.oBL`p@ib miF uyT)FY2e@c-|(b.uw wSۿ@ OK'~4,U%|9ˇQXB<=ftO|Q0E %G#6'|MXc&h< mF鐎w]Y&G>sA1q)Ý?%h%ݶhiA@u`T2@KІg$dJc1]yTv2muxԶA^ 6PI6ԩd6;*^Om77#^切f'S;?ԖCj}c?EcYNӪ|lј&4Rv!0XƱm` Dd3Ob`SI0#/m Jam)(yu SYEsbFS}S} /+\ # C *x̖#Fac0-kSs|TX"D3u:B?yv:)Eh8A!'r.񜧳{' nbg󍶽kh9 Ul@?0{{4^U6=deJ&@ B-CA]v~k75NGuo9 c/|FK#jBp$HDfp )"#@s6avS4߼ cx&`Pvn*%fy;2e:K!TZKQW:NVvUTes+#Œոśl*0#}ԖUjMem -+R^q9Y]H>tʘdЮ.klOm|~\*~Uuxj]Hc_j /=*ӡ/w3%ݯFg_=pëWs endstream endobj 2391 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F11 56 0 R /F20 182 0 R /F30 286 0 R >> endobj 2389 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2391 0 R >> endobj 2394 0 obj << /Filter[/FlateDecode] /Length 2192 >> stream xڭX[~08fě($AA̓eגww#Ri$Rp73\%,IV+z^ j4KRR1%Zk~*gy9_>Fll槯~4az]U- 3b\eme",d'D$k>~j_CcȘPڲZ(fGkmv"EC+Ht߾i0cdFl"[' "& EhVpEؽKfvKd>/=$~\Skr5 :K2MJƕoƓ"sζOv0~;WL)S&F"i Ӝ S4S`"XZԩD!J e4 V]S``\@3ghy4IAcOvKu]4g& {p?C猱2nvkCl>22Ɲä@sTӂE]/`=hv<-m*lcѫasPD eZZ%i3CʌM8@FcQ>NP1 xCmvEJg{+*E88?+!y ac0ⱛfځ~IȺ8X'ȕRv{ހGsp0Gg}ɂ6 >}*3"ӵ .|E; H{y ߑbBck4"ue? p s%43 mZ<BW,Avf^V5^*H 6\0/Y%z@ dgLx:Ł7%xAHYʭ+}qӈܫOi[hUݠYAV^vqgo2Hjrp^ X؇ {ذ:'7ش[dO]7}<̭¸0]nX73t*q [Ǫ:Bǻoī]ZгފTZ+mU-@A~7*y)'T t^*5U23 ϨL#n mQ wJoYidy7"̚xze {y7ߞoYj~Ŋ',؋u&᳅O*VNGX;0kFpK endstream endobj 2395 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F13 66 0 R /F8 41 0 R /F6 35 0 R >> endobj 2393 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2395 0 R >> endobj 2398 0 obj << /Filter[/FlateDecode] /Length 2382 >> stream xڅXIw6ϯPo$uK^bbOrI4=jS(j @Z/ x|&^e-4xIr{~x{ePf>>IQDջE~r~ȲXiaV⡯L;S?52)}uTf'/* NP+>+b3[8u+^ 8UMn>®y7K@%) kP10d4KقZȈIJDo[< 'aFK"RɌ.>DrhJtF`bF=12OL,\ c+0yB*}@ /g~C=!bW4U8TvMCTRsaC <.3OP]3ʂ}s̨EÚ ljf٣B 9)')u R҄4Ad=fg.^mUX@ǩI"7ImNnO~po)6@?_"cPBIM$!bL0K& O@7u@8/,C9qr+eN Q>", bv 8Húk* "i"3H;7,_gAFB1vR EpX0AX5g/qAq{Z5wTa endstream endobj 2399 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F12 59 0 R /F16 138 0 R /F10 53 0 R >> endobj 2397 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2399 0 R >> endobj 2402 0 obj << /Filter[/FlateDecode] /Length 822 >> stream xڵn8E .%`[Tv{dM& Mѐ1oOaZD^UuyJ"t<$z@0 i^OMqN pfUhs%ʺ|ť.rI2KR>_7KHXChߣURWL`eV۴ʚq%eK0mUvLQ9+ǝkoܥCcbG/wX֤jc:Ԥ}WMW2xa}o<3>[]mj宭]k BQU0,[>d0r+?fu&&o#&2^~1,t"_&ݤ+,*(h+h'~|?MhiqJ}*Wa"vw8Ÿ2iM_ ]ĩw I-q>W[YUp('l3 PAD]׏q6]zٳICWvGdmr0lkJcopsJ{haj vgwcOp "ȵm:Q#F5LTı%c|) Z`N__dL¿6n ǡ{^XI,h]g$\\p'HBZdDaR mmE\KE϶f򁥜J1EJ ?> 7ŷg.2=&IkYƵ7’Ns` E^2|xx׍ö<kq nC7%A(p25|"m endstream endobj 2403 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R >> endobj 2401 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2403 0 R >> endobj 2406 0 obj << /Filter[/FlateDecode] /Length 146 >> stream x=ʻ 0ݫ?Cb5ġj[%}O )Dx)NHXa`AxJ8ےiƕ*\7Ɛr_nj{_PgyCܤt|OVmf<) endstream endobj 2407 0 obj << /F7 38 0 R >> endobj 2405 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2407 0 R >> endobj 2410 0 obj << /Filter[/FlateDecode] /Length 1750 >> stream xڥWK6 W(Z-&mg;=u{efCJEv2E@ څ괢OwDժ|p\%i6Q7JVq%m hM0Έ}^5ۤLpָ01l,i8)nAmÞTg H'cUVd60IV ,gguqQc(jwYz0O8tKkCҠLyAr۞6(OL.l,/Ǥ^uXǮFyc,\N?'x4mqn Z0q8I̘(8܆qǙMwmpc'I*@e .0 N߆Å~/-: wh c1a[.7 7uZ3;0̹dUF<֍rǷfmI6*ӜRȮ!L)δyfߢW` ^'YtrzyQ9EwmF;xgd^afΝG>Jo`ڳμ^i`M+|yi{{9^?ښAus^S~d1/JXy41qVi WF6i7AlG q2zmS6"m<Ǽ@>Q_}i^,!YġOsy|2l$e>R<<̅ ]G5g~D4vr Rfõ (*QC%RJr"} d:9o\DpVPkAɊgZsu,˥eH&y~5q@! D#V#wTV/L3Icy6[9ZD?+U׃t2'\"]Sm2d>2@300˥  :;ppN zu^=NmVpbVHٲQBkaĪ M^H9jhCJcC?[IAHOXdI\y\I}SMMv \qk\hK^:= tQZOI襩] o10F5),DQc1g9Ac<^(xx2YaL0ؖҟEo)bTet[?mPPJ;Q&15A7<òÆ(CRzE1?+®:+,%P]Saeq{#&.tvSm3BF^|G%/ gƺŗC*r_RI%GӫA\0%v>`6'jFݲ9E:kZ`# v<mG PTu&XG&e4>6BD|>H 2JOk@7lddR# ztaF8>HXO&h.}^Si15d\ʆfcx|a6Pw S&&H+ގ =2ܨ}XN>DePQ \!e4Qⅎ>4ƀ23 =fw!˾&t~w! endstream endobj 2411 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F6 35 0 R >> endobj 2409 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2411 0 R >> endobj 2414 0 obj << /Filter[/FlateDecode] /Length 2455 >> stream xڅYɒ+t"$68mẹ|@Q.;7p՞L,\^fB(uG?lˣLwGQkw*UJwO2,eI{uRce|~շ1MU?y^mWcr ΦqUwC k];&*S:2vrt<6eH<0aÄ3it}0_#^F̭t'k70oFy?(P1*JX!HX2K'L?<&^K2#w;ntmf67)h*:p)+VvTS;/n._u55',.D}Z<w#@';w%#!Y]V_")!)p}~g  *d ՗dxgd("(BkpFMxt+7rLCϿlaj^٬t`C2/HX+]3|_ RY-T o " ԐrcDvBc:Y5E$+Zݶ\ o ?ZpKM9>0GpLOgb(&.o(+׶Y E ݝ8iyrH~p$ )_;ɐĒ9&If'8`84ὁ<3@-7X`ڏꛏ7HrL壁?΃_T#eb؁"TBӍqZEZ.]f+r?0@"'IԎdbse|P5e;\ۀ͊fzi4&+dSȨDI Djԑ2)AeMe 6 ȺVkȤ/anzbJoL$>P<5ޜ^1*L?Jhj7JE.flJD%6Uᮢr|HP޻⣩\*/a=~\:=P(5J!MW h+A~(Dl h+(mZ[i";gZ +ld_` srM: >om>R+ɜUtJEV씮򈊼VyDE ;{_ /+>6#mH491b82톐 keٲZ U>g>hՊ+0LrRW"n8֜'v\&U`2OH ^uƾMx ^W5Թna#1Nrc~8 ~7݇ B- R:䫶)(,:zNXPnޏ¿!8\\>e?v2f :yd VdOl#Y7&hq~,7" -)T۫B[~G[#t]LWN.bq8qE$X]XWD cqhS⻯##- YEE:RŜS޲T&q1"/sX% endstream endobj 2415 0 obj << /F7 38 0 R /F13 66 0 R /F10 53 0 R >> endobj 2413 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2415 0 R >> endobj 2418 0 obj << /Filter[/FlateDecode] /Length 2044 >> stream xYK6W(E ߒ&e}o{e@= ǧ( 좁L_},D#>yy.#NO{ _J?,V&ܫg\ yZNk%U}ISۏnFBxC AP#AL%IHai5MMF`NM Yˊ~YmhŮ֭MqjD&$SrJ# 3=67}~ח`8WHv^!6pbqu(Zm[YQ7qוj4d_'W:A[Ik?h;R;ӱ*χN?NCY2ꗸIV0b1|R]<6T2lOZ_R {]"]!xnە3IY7*>ꠅʙ#˺SZTu=MuN=P{ܗy~Φi1KD)?M@w_-?nʺnB 2kV587oRo`<+28 Y@ִe^p ~3tXє!`jdugE350hZItbϷ N6nB [5?6$ Pk !^!HĚͯ4x"(=qƙٟ@kY БǴqߙ\xů3nxb\?yu:6`&RH (3e @R\߆x+@ R$F^C)oAyկX3&"܅x8oA6GV@"(58{pGF)y痴5HĚñ}1ܼշǚbYj<k= 3JEnwh6}Q.E)r/|8,+Vݍ!V< $둵XWĨJjF]e۳S֯,豭ۦ@Ԅz6+bHk-]섮(/FVҞ$aV4N5~p7dQRSDk;(5SiTUՒuSAfXISnJSn0"鿋8*dlh}L?ONׇUOɺۉN0II5F74[s)YqjyC3̡XB[Jϱ:''3L ,y.10d/w';[58\#O+_2+gt,l~}3aC{< {#0FR]V!ɻ}[9h 5r?E4Q6MuS z\{:U'+7ڣ%)*tGU<# LQ "MR496S05bwU0a꟭?׀#X_ڝ<.$U i'nxSe;||zG*yS$dy6B%fޔw ^R*I =VjϲhvNuC2V˨s d )NOJp̠1>"͓ةtYUX^h4<*Pit*%ل:T endstream endobj 2419 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R >> endobj 2417 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2419 0 R >> endobj 2422 0 obj << /Length 535 /Filter/FlateDecode /Name/Im66 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2423 0 R >> >> >> stream xU˒0+t$*rfYKG36[-!,^'a~Aj'xdrс xT_IALzﭡQ~PF41412!U P*:d@91vRQ1q0C44 F7O:cLg K> KqQzhw#<+ ͑YͭS9^# | !FQ4kξ|gB{__LP7-O;K> endobj 2424 0 obj << /Filter[/FlateDecode] /Length 1681 >> stream xڅWK6W=Z+QO6[(ȡW-v%!_ȶnsqQ`ȃcP& Y",͢L.=i{PhS݉=&~#̪bNdS[y]4 4|ZE caUب-9~{in`-(IVP-ChJ^}R24F;գjp! 5o A͑rexęEHgmm+PW(lAD[K7iE g"@U9@8*2IznGo{Q|q30Dԯ@t=+䞹rQxˆdFvQɩ o*XCުf4p&&~xFQ0䍐fb!fUm>aO^ZU%u@@*Ewq"h&wz}F+O](e萏Ar.(P@cazA gTz)~+QW E 3AYE?w{ZH"_ BQUy !* ?Yyh5f&dzB!@Z?~~|{<"ZaX,| M| ӊ#z Lx6\H̥#{ 처.Ssa=~">4" H5i%+{Q6zҏ1 8_`dbMm3zFZk)x1Z  ~ aԑE/Y {}s -5nt68TIm5wD!lԼ o ;FnJ8E iV;*j$dje-<9ÿ#ZyW`t\>8|zjwh\D,WJ+N:%2PO:)q|6d(6>".ӠǾ(CARTQ~Gi>| 5FvbW9"m-kQ`LYĿ4=i\w0R'z*;ځټymZYy;ϕxQGh--=|nP>;W*0@7Hz눴 9n{f^AcE[&7j;;l~uuS,OGUEU~IG_jiT Ȣǣ9!!RJ#CF v};[.Tp6M]QNjS97gfs$@<1ŒϧqZ>Y?+%DB?-{"E17>>&&PZ+\}U5 ׎@mQGT/1ݿ:pN endstream endobj 2425 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R >> endobj 2426 0 obj << /Im66 2422 0 R >> endobj 2421 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2425 0 R /XObject 2426 0 R >> endobj 2429 0 obj << /Filter[/FlateDecode] /Length 2466 >> stream xڍX͏ `%R`@.ag؁`ח)fHMQ#](pu u"WE(wVZh_<}9 wȓi䏄wO?]8 L_ۮT Kà5EBSZ1Os:"w#P9rZyjF&IL%G=R{CG|?4jH6N rm*_k9Dx=^&{ۼHeU4ǽk@+N8}p$n eOEۘ'oȔDnˊ0jq'"wJ8qtۥxInm4-LK#4xUbM{tj@ bx,/+M\Ѡ5í1-ΔAR͢.9+܇ C>h -yOBܥ'-K?P#П3} 뼲9]Y?8D}ۃ ).;*Y1H΢:kKO  I8VIOo{=ȓ`0y╲Fb;)oi=uHE߰h2$ഝ~EԿ7X=r.\}շy`Rt2 +c,ftc͹DF~}:ͺvB,f6Slf{0)cd7>?YG:[w[Z7G |\e(}}I+XHb壀;w0\a+L&WB#@ʙtašq`(;P?{%o L&mz@8GGyr݅E X̏K5:-QNNĢ Y"U4n;bjk_bK0ej$;Ws1R D6LW"HV `Q)ʼn`[ `n U?#$>U_ӕe@MDGPcvI`)BFhƚAF 8׵@{{3C04Y&@kIiAka}ɆHD"~95AGNZG_}kˌi8*Ap<2=|OJI" $×Ǧ"6hE]Ȇ'l 9HD(bqx;ΚerpԌ Ez!y)'1RnOve)Yʃ˞(P*R>^@9/;pqO.1ì#S>/_@S)mT J\uJV1^ m~tv e/4R>ӒPlWjYPc&Kzd.qX x큳aHø)"t'$P*O6@.\ZԠ # eʩH4XU"]񸮣FVnBKV")!E/m<瀐MaV/W"n@R,`VR9J4 %`|$Ԗq0k`.Kԭ.E0t꜍6"CR5SN$$6_+"gJr_-.`~;p /Շi,,q+2&v]9ٲ՘d#9+4i(#5V9D#G>"QW\.'il[ؑ Tk(0 4{ע0ڷh&;j2s7VZg>Gh@/k`0c=PVxWS!QE}z#󳯧oMmo)4tIq}d/˹t(Ro\hAV")ܗ}Оn8 GJ&k![:\&^_P=wNսB:jD; eC{+wxxkr\G'N.2ϔJ!V 6cr7X(kqi f`8v^@zI(ˆ(DoÌOazeL+QѰ; 2Z@ {$L"%:5ʙr(ĮMb' lho/ȢሢܬOʱ-[nI:34^dJi,}tU*w8 w ] uW[> endobj 2428 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2430 0 R >> endobj 2433 0 obj << /Filter[/FlateDecode] /Length 1962 >> stream xZKFW!(ؔscroU^ĮH!Vǧ!ay7,aYC8\sԙ3a%`iT(`O ̌.[#a.z-(ղ/3  =2M\J͂4HY<y`{@,F]Ф,54:1nQ_ؙ9yo{ 2ؾH3 }٦18)|}RV ܻZk;pʋ鹏Jny*eQVQkqjڈ6^6z.N,,vD>S!&R(VQٜ xYնNq|FB%B1 %6֘0֠%b~/5^ׂPt0^zjFx^pFǾ( ֣;mqGax))Ylf[=(hˉ1GA<.<Њq$s-VđuT@-{S!/ \c@!sYh|683) > -:$0@Hj M VNF\+w qM;Szcx:kd,#0!ATGݸ@m m\orG`=Da*GKi62*v!ն\sӽ2c&;E"[>Q7r+~URr> f }su~DJUE(ϡgcY1f>(Ga2Β촎8m{>Dܒ- FϓГ!qSy Bq cOH?u0t:У nC$#,&z:ZNrc6W~{tb{U_~0laAQ%{d9 A_*P `I:vUt_=ń_v l(7-s~YE6Ř?6"JKCd/O<6Q&/|;]bCUDživ[ƁP)?wQ0 P=:ǒٕ_[Bdʚ8Jּ|6sFwZ) m=lm#m)%x{|BE~G,; endstream endobj 2434 0 obj << /F7 38 0 R /F16 138 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F31 329 0 R /F30 286 0 R >> endobj 2432 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2434 0 R >> endobj 2437 0 obj << /Filter[/FlateDecode] /Length 2440 >> stream xZY~ϯH&&CbkSq*?x\ ѡHIxӍ +:B=X,|ⰈyLbfW?F~7̧\9W.z"]6;|fS%;$$ˢ8ջ~:Na""RY_dfšbbBV{ٖ SyJՏza4&)5-)̌3lQKz*;s0=o %|XyBPN";)fNGZnDg8 ,^2ij.P^|s&ќ(_ӄl6y:ѫ$'z؀5|mi6]VY^yh)`Q5Ewԥv9f ŮY\rñGNxG4rd-}{YL2z;T~{)c|e7kςUѮ(ҊZ>#&Iݿb>}\6}gjǣM%S&Y+sf?4l;KݻO(Rg#/G~v)3nSdFZpW^&D$S^[xM?hkbׁuWf^Fx%J^LjmfQy.*nʕGu_ f_M!cvTo0 EӼ{kl `)%q6:TfPS@E^<,[J9N@Mi^n^t7WSDx ~̄TEg;2Wxj*'eha Ko ',X+3V{HRE[@ -sXeL;]q򁉴63F>AOmBrap,JH:yas.]+J8(=JZo03Ds3-єר=Ox2z򩓚X5ѓwYw}Qo̤XYoQ=D V5dlSgvʶ٪(fc`e@'̠.cUh,jCcļn }Dب0)7}⋯)B¶P"[[j/>'U|M]L1^GrmP1bflCުG%\QAڬ[D Yu8(S*[z3wjڵspYV]}ѳ5)#EBwsd oeuP/vx>޲j@DJ@&]wI{Wq B^&Nͫ5(|g] 8;:gNͩ=٥A-k1#&_P3<V5lx `m qwM" |JhcTG@iN8W'x`~f >ۤyNL1.L(twI7Mmr74P,)3&PmpsğZ+4[/1i7q=j! '!J1B(z;.[VXWj1yf|otf0N ֆa$w v2ECVM':$K+ Og鞊XRdwT1EbsFڞѷ|vJ3RGwr2 *1J85&r+aVIf\tJs$qnd"}h4uٲ8 1- W=$vta[K:˃M]Hx I/rF`i}[܃&`?@<k{g+KYi6ذ +fN͸pdmT2FY֛Vs@%Iϣ4`Kܙwp&2A\ve?Ym(8(!ﺵ٘K1F9+}J4s;MnӖCI4@[GqTGf~|u( {%A1.Ҋ8΍K#tl0>N۠w-3bl40~LL:lըH]TLEa߿wXBŚ.'ŀYX|".kR%> nuwT)5sx-OD) x d@ʡ&RX#Ghxnjnu ՟9 :q?iP:̊tM* ]֔# h^f5I endstream endobj 2438 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F20 182 0 R /F31 329 0 R /F22 218 0 R /F11 56 0 R /F8 41 0 R /F30 286 0 R /F13 66 0 R >> endobj 2436 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2438 0 R >> endobj 2441 0 obj << /Filter[/FlateDecode] /Length 2380 >> stream xZK8+{cJcI~`` ]fFCVU\v6LR5ӽF'۩#|I)B<xuJB:Y(VU$# ]oUdu9w `X) ďa~C;B Gϓ":"\&t&b.l34l^؏M>O%K%< !S/kDOS?c|U' BbH ȠB$:$UKxD7%q(אFqQxZC2ymrFE?Im˳$h.1bȇdY\̟9V}]I jManC+f݊^CƬ6 > 2щޓ`zft \Sv?˾}m0vY? OxӸ"ƝDd3ݯ4y&q0lJYGXAڧH?l]B(N;K}^D{G⇀癑 wE㜢0>WK؟ybOE XyFȑ_8!G\ƞO(ޜ]vX{se,Ο_8$FpdV x`{v2ɱN땂vJwiH7ITNwՠWD)uQ\%D)TC/飮+n2 `]5F+h^SJNvӠW(+"3 ~aSM jjF'\^;[C&SRl9 V[.&{W ;+.zɅ4,7uڞcjgxfzUU#EWl*m-2CIkbK)QMċN;]Metl*[JoZ˼nj\]2M۪)T6i"\뷆'a&8IX{M h8_c$#eDBS.@[Ulxgxmgqu^uvvn0+r8mW{eq<%C׶t0F6hv(`my{=!h3x~ӻ yBFh!F1Wڃ1 bh͢n1~XQ:ZdL E0M PmN~jn! <7]n %P3>@DtE@{{{!~IXtonv5?U:_A݂PoQnA,Z M"NM5- _mMXɃU ? K)r9ğuiVSj@:eo{x ]L€m8o뷥 ?!7Z> endobj 2440 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2442 0 R >> endobj 2445 0 obj << /Filter[/FlateDecode] /Length 2605 >> stream xڥYKS9Z7rjRu{\,w^(Hb ;{hǮ8u;dy|T.K8S~/qWu;OIE>O"=XwCQmct~!tw晟~68׌n߾RN;t# 'p$%18>U":;t`؝MXFIęsgIşJ>e+vkFn=K”xF~omG #!I:-| l`-;f4gn>rF"Y9*⤒+}Wy*7W*Nctcnp`x)ta&HJ9=N\U |]/Ϊ8 =U}we)ع!c|DJ ^0aZ%?nPw)TE={a%ELǐ{w;,xcxiZ=i@ PwuÑd6iWؾ"iHJAd|~ vUcx+o2?jUŖĠj88# tH)۽ u~-,:O&3qN8Lv⤍}dg<,ŹGװNz h3טUy)W({.J9 Zkz2#R1L#s!7 Mmoc&ili($aArtK#+f$#B R̋Us(rLvcٜ @^wۂbl5}omM;qw"3jjO vqm8GG)pm̥ߴW z,rh +Dc A8[+g +s?rH;tn>TafSuhE«BC&1S~'v慉>%eN֜z}uR6k }{ctF WH%]:ߪ#SN8,_ҵƪ In (UHC0HW2f,E[B9oR%(U~noe{2|N.`qt'kcL^(Fϫ%dD6ElTYD?3uzI&t~c.C_llj"1`fգ,HrI 8`}M/%|a TjsBuds,'ggG&@!!:Dd⨏bRK~i) :$~y04 ƿBZCZ27ho BAU%ku}w%ـ^k ۾`%EbC\Ony؂rNQ憙•n!O 7"wBչVY7vr`Y[Wq]-J,Cp-Ll& ;LhnD|S ED)iV62,q䘐τV8ќM:=ԁEq>FX/r]]s=CUY> G l])iIpZ\žs+J`ZjwFty$c 2mAK7@+2,>q+1PXu[1V  *JL٨zU#KTRdgSgT<CE2T:㿟Ex' (M~"@7|!J/ls:)& RPG￶D4mX*%|bx<̅^F='H ^|+>D$쨡{a" D8maC(6YCZ-@U58_2,Q@;!lqܷ2mDWHa yua%!ӝ,)20A4> endobj 2444 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2446 0 R >> endobj 2449 0 obj << /Filter[/FlateDecode] /Length 2547 >> stream xڍYK۸Wj/TETgr*)OwLF/(NRwEa]fafe]Ѳj]fUE;^sՃQ>yv 5jKмS=Aס'-ʅR 9T+_<<%Ae} M I3INWR3)JWG&P&r[<9Yn'k rsMھEh f44$ߚN\sCԵ׋]0`Y|!EpHٍj̡)1U/5iJw: j]LjNıeHc'Wol=L;QlkWJkbw׉uk(L=V }iҵEH%r.Ee`6K*]W"Y'm%tu;w0))u .sego?o&kL˯eG4 Q\y4pށr0-bSk-NE;d0Ma!LJMQhߑu(} zR4QɣhcLfĘ~R㐫4mxOxofDWy]sbTb6I [t+Z^o,o[`>i~Oɽy^F;onż 6s`8 rx[E)1սbUĸsUe%U < /x˄:gH lY!R߽R-uG%s񋟵2-m/-pgz^i|}'8KG/{-PTD"<+D#Hbkwѣ~h+S[4(ƒ|! r$U3. endstream endobj 2450 0 obj << /F7 38 0 R /F16 138 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R >> endobj 2448 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2450 0 R >> endobj 2453 0 obj << /Filter[/FlateDecode] /Length 2308 >> stream xڭYKW(\^;Cvvg ,ǧU(9U*zbcdI(ENE,KZ99n$JQf!h6?|д۝J͇MM8l7sJ Qlԥ=ȢA$DTd)Ȣd!t(#ER@# *B"'<Nda1Ѵz.LHČsfi)WՊQ/'F<\ _D~%; 7|~L=#7\q: VZ$z<(ɕp"F8ley] p8%eu}x&<1ńLxux s:27^1@t6Wx8Pz x9C_1wLg0L&L2a=I0s(;@ ^dYNYy@+$It2_j(I)7|³&J7dd'J7dl'J7tzJ?t@NB i\h wlsiOHf8a&:lmSM&Q 5<֫l ^anfi۹ $7;B%tQ"y8٫6#NL}T ۂN3Q0\}B'v_4t# >>2~vyv<$#dVD2-t3Y @焩M?U(|d0VfM'[t͓y88)⚉|d)tYCY$CPJ( NG{홨(IvX{ Edžmn[0s?!<ف qHFeL i΋@ 5/ToLߘ4p6,멩P}VuiZ7sKbR߻83IR`5 !Yh~ڒ('LuOj* & =(w&=U4~dRcL& kcpXd2c'J x9=M;TŗD©s-d%@ȟAj3 D2,|d&% aI7T !E_Q0ۙvQ_?''k@5iX\ _ѕDT8y'mGk]2#ahmd4)hYˡD*0c%Ymt[LJ8TH`I^;Z1Mey',Ukːɒ7!2"O"ٛP;l0 ًz*r';*ѝ3MJ~ʯTvi≵bFSM-2thZҽ{3 h2mۍpC5ڃzس`-=Yf&[MAGSht/=lNCEC`.ҡ3*Z{hzԟ10AnSlvDE,JO ߒiuNʮb`W&1`rsN95 MoM:5Ay!UvV(Yĝ'c3I3#ZYzB7ݢlDv>0z.ޕO" LA~=Y-..ugz۷кK -rP$tOOtX4Ml_qN0ܻwׂco_1ؗJpZ pZ.Bh!(!U {GP=Y X"旦= 0Ӵ`~ ʫO8VݥԎ?=s]DC|c]m W92|sP{dhe.K᳿;^i2E(V/`fT7""3<N.;ia(+3t7 '{2CPZb (xւd=i_!_cRN MY(-)?f endstream endobj 2454 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F30 286 0 R >> endobj 2452 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2454 0 R >> endobj 2457 0 obj << /Filter[/FlateDecode] /Length 1575 >> stream xYIs6WdJ!B,8:Ng:f@KEjHʒ}pZnu=<|x,5xm7 6t`-L1]1ML|%XJ43Ő*;oХ@.{{KCi>K^J;kHp U9DYZٷ&YOds$$g`:[Յ1-$Rt1v]@#e†7\ wo'tU{z"ű{K!B@5AncQc-d ,>8*䴍?#mmI$5b&;"k WFi9҇R5/`?. "j"gKR?yS4Ɗ]G'ݥw.4_;2 #24wܺrG RUg3xIe*ͧ ً\7FE^rT7X4YZbB[vjŌJ!UcPƌ(OuyrF-nwh[ܴDݵAhN@zCM!tu㱶n,6?j >o]{(m KtCyr|=33?qKͣEI}h EMŮ8Q=b~2NjZFuZmi )ןIS&U_(ZMupcܓen̵R2԰.{eet)"X LJ!cMY F-zɰVT|CDĚӎ@o R i}Xj F5-VksLgR5QdS0޵]|pMls GKprGP7v 1XAlQg!?!q:ɫ?ƄS m?u, ͂vRfBkCQ9+.YI\Ft&lq2# n68a'>ѿR1Kd?S%46H>6y^C.1rFEkBHq",ˊ*yKcl'-$3oB? endstream endobj 2458 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F31 329 0 R /F30 286 0 R /F13 66 0 R >> endobj 2456 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2458 0 R >> endobj 2461 0 obj << /Filter[/FlateDecode] /Length 2003 >> stream xYK۸W0!R%^{Ⱦ*%<>gDHZק Hqll%Tci'XtrN2(R%S"'Ot͏g"O:nz#Y}:]5wwG3|-eZ'Rnb\\UpbM_Zՙ6]㓅' XB p2cu͢ș蔲9TVx2T:O)EMY_6Ɇ2țwjUwU[iws-,3jQ?F'nxg^z/K\0WİoAA~7vs|;9:vD)" ځOsflleHr?mvL3םw$dvP&3H\̵@9kDVFGn%Ԇ):iU:&@D^iX6ug3b$3,1deErSPSЋ{.ܾNI"þ)gi 9}E_"PH1*9(\}(&u@5r9Vj. S \4D.QI$I쪜`"G?%v#fpn)'P)smspi,W\JTF%gW\T-@"kTmܘ] Azj#˂۪%jy i,H3v |\Qmƽ7D*ܝTwT:zQ6P"=-muA"iOwmS uĘ曀:Xr n1IZX2KQ_]P:=5L٪lځk:khGuޅ'GNJy%^PiHќẌ́_1dV`5g7Œ7B(`; %%>}[4iYԢ5]Wأ;p隆:yBOĎ) <ѺV%:CN%7| ]*(bpOߐx~hڡP-&h _U<;"&`ۃ 2f>.s1?xKA>N|j2Ru53,nL5{6tKb2DКN*CrLUWrQ_%/!Hx M.*Xp@̗6$4{4HeL?3ph>VP#~emOTՆ 鯻B2pX*m֥˹y絶>OE|7<Y-D>{B[#jƿz,/fXh*Kԯ%S È`S,s55qy6uI!mu. \29giNs7ϬKµ]r*RٲԢH8JΘ0JB"ʍF>%.Hzpa endstream endobj 2462 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F13 66 0 R /F30 286 0 R /F31 329 0 R >> endobj 2460 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2462 0 R >> endobj 2465 0 obj << /Filter[/FlateDecode] /Length 1952 >> stream xYI6W(!v3"J494m-z= M*lɐx_MLYO& z?{ a |4$2Ĉmo?._<\1"ipՄ|:|fQMg K'MeU^.۬{9d?jy59[BĐCC0"g+u0H74 B5Wl.9 S3zr=5LQ #Rİ Aң;!Kٳ[ћ1yZ ܝᄢX~nL>g"ZDN|`ƌ 0' ):TGs. -I;VOR0tc#+ae&d% EeٔSOy]\UV7|.zyYM7'ӳ6ym!*݈cBA(5;ѤF>t hY3AS,R=l-7J}c;ЄcFuA'AB&a'!w@ ۲n" "oV8:ݠ# PM2j_g8v}1X/ 5w4W#8!lLd7 !AYٙ+;ٕ07@~{Xp/0)ܓ:1q Hi+.ꉋD6vQrb`#m z, HvO@4})2zʹ{`cuSR<09t+O6;r1nhj<6 o#:x@J;H? `<jaW(znF=hwx P@_I/ݮh0b5c:t;pC{lB3cr3D>Kp9#"cK|G$'F>IBIKeK,̝0uOfF"JF|r\uY{ff3.`E%7ò}:vGGt}6bA7~>hc ׾GOD>ĒLӃ[nmJvR;%BQ|=(X-rn,_z>"#{xB^} endstream endobj 2466 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F11 56 0 R /F20 182 0 R /F31 329 0 R /F8 41 0 R /F30 286 0 R /F13 66 0 R >> endobj 2464 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2466 0 R >> endobj 2469 0 obj << /Filter[/FlateDecode] /Length 2239 >> stream xZKܶWХ9L"RSI%C*i+ŝ0S$Gw7 +@4C vJ0 ByOpA/@/!InĻ;SX ]ZMU٬nQ1 TcDq/6֜G&ޚ B}5 tf*A{ID)TWƛ"m,Y`1axK0 )6uFDrSzg Y_AM.kՄv  ǑBB#pGzy)Z2o1N>UpF|Yu@|Q?۴z;&jb봦1;+^}w^*`X}V`P..-q(ILbC|X58L;u* XmeI= UCˢW +w|z)^ON2n ׹\CҔK ( [YH~qTgN[].F)VʮN )W,O:i"?SY+XZc%)#׎k JgM7K-7KtL: Q爑HuɈ{A-i[β)=ks1g9BNyN*&&?&LɯfOQy6*t>d{gSi0kډ"ϠjzQ 3W1|Ŕe{j3|׵?4UP$jMMOY!@ȁs\ &gߋBaaJDvI\~F"s ;즽[(]\ KdJ5p`Z̙:=-ϡ|{*ga `0_2'A-}::ΘgGJ15 ?Bp5Ղ"FY=,BmS2NIaOYWtt¸U߯[Ti?JVɟ!ޙJPŋP+PnO9&?>x-|7Cza|.ëϱF0pشf(ŽN +jO%viEX'%E+cclxžTk W =OHxg49f r}vrC|(27(P6vh?-ʲi?K"P¢ٝuvK'/js`0eD"}o7I2=a|z )HV;jǢD=ڱNm-İ6hyz ;R[.+ywA_ s8cs 4h :X33q`K8.n ̪ǫĶnܑc[+AD9sم I`m@(0^d2L6{ƽm&'N=2\R"oES 1%X䐺A`֖8]gwoܦ{ m=(˂ZŅ 8 v~7pF= H;HTGwG$IZVT_\Gu }L1 1L> endobj 2468 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2470 0 R >> endobj 2473 0 obj << /Filter[/FlateDecode] /Length 1679 >> stream xX_4S Q-ɖcx(`>]bvh˧gl9N\$yZ "E&0O0$@D$2NHĂ]}Wui\o8KJi.[UXrT葇ie[vM~Y,YUM9~k7.;ٗ ,&SES구(Wpܔ-XW5*Hvf0&'֠XӰy۲X0ng:lV벐Z[ ]:U(%ybTiHlzi`di¾m6uu7 "7  2Hu;}Fժ]b9f,֨zbT], 2ܵת<8l؛T+)[k[nZ 5o!q zV I }C:vڃ"4hfTabR'f nѪ?i3þU]!8J1d `.,S!evmԘSxQʪ[mfQic4s6YRʕ8j,H([&ZД0H9b#jS;>o{il i"MʄN>Tם,_`ou{3 A(;p1#L6!;#~bh鶄,l1,JB 7#IQO~_. 6]\Ȱ,|f͠#@˩j\Jݰ$m|{TUux?i]C̳x= f|M0鿻F;gLt+D8̗\^]> endobj 2472 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2474 0 R >> endobj 2477 0 obj << /Filter[/FlateDecode] /Length 2259 >> stream xڭrP&PUTgRLF[9`Y5ޯO?>D۹Dw7(d#ʃeQ()0*P}K21XBQ(f&DSB2l9̄llߡaZQ*ua)H*aĘC:6;#z|8ghd7AFO\!+C! P(v)t%R*o#c("c!!=J:q`qSwӕ57{A.^*{t{[VsmNb"DCNTlFCg֠ۻW>y9V%Jppvvyff_:C9lƱt|&]IS$tbugq(vĤ-x9o2SLu"Ku!MWn.Ll#o?1Z-dr_)Sj/f Yr_z3Fl!8/޿+ГůOVYX4CRTifϩe3 evM+#qUMY44z7Ddy?m:j SxI6J&<#R -xW2q*!VmJY*}{uLC~s=6S[=; շ'3MBfQa^`w&Jr-_)C0P{^Q2߲C~@O(4愸]*3X3kPx]tg~ WGE:&BW1?^ Q>O}ؕmKh >ݔ@W̓>ƺa%Qda.@6~aVG Ye T$ IvS?bULNE,i䂣:||ݹc71o jMe{:r=^J-%KswO]Eb-N"79R,g-~Ŵa E;Wx>\! r~\ͱ$.K8B]$-.1;L#=}A%}bU))Q2Lv3_t~ .IkB47:S!C2GTV#QG@6-[&[(u,>rZT5ώ)DBc~*B EH8Ǩpo!,&`B)nQj:Se8ё_unx>tYm~$Ye'K6acnlv+]w.qL`9eI:(\( #o\iWq|(mzS#š-M3ei0f@"UEŘmƊ|W k< )THrE0h|ݍv%!^PǓ>N=Uߩ#rwvdtjۺ#Mw,GHc.ee{BpZ9yUyor|6kYLN,KQvT\+6; Ĩ-9F*JwskݩUSv#H68Otlݞ:ztȜJ W_H _]e^P!#խvqud!R0 H߭)?/߶<-ꞃMI $g$jPqЏ΃z@㫬ם^BP;[//d;-۪t )2~8::s}'7tr}I(_aaXmF&$^%!bC%iqm~%/_C&{t(}3;?^݃;2GG]B}~nFeEɗ.\P@@ʳŗ!e*+'d-L VLEX yU~$UUKF2cbUb#=__`4nBi-~jv;8,Hwҟ>9 IN˜qr{"aaU236?9o endstream endobj 2478 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F31 329 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 2476 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2478 0 R >> endobj 2481 0 obj << /Filter[/FlateDecode] /Length 803 >> stream xmT˒(+HBtyTUQU,mjУn~.\ɬù׹e9KI.X[(YHD!׳!_Ȼ.X=g%he.󶢏'8&4ZЇdWU5}EWwR4';A{夭tlw`y_NOxMO@cN{9flН"ި8:Ppjzv+k^<)u ڢl}@Loi9kːƠԆmPn.ӽQx@%)+ rܻEzfcfEOGð*k|Ǽښ+C}ӿ Ιh9Ёih!aE +Z2^aDmx=lIYµspnyHzV+s%}~JA7KJ T+h(ڂ5& }@m"cMk(*ؚZ[ 4F^-(5z 8P֫UNdxcFA ./ v6/~b$w)Ct֭A*ry(A:TxY_W<00Ns>O-V߅h9Db-bOW!;_9l4 ;{7oU׷. ; vITm(\Sڳ~ z* xdWồ?@V9s~ {o A_֡W^KjK|X7`cR endstream endobj 2482 0 obj << /F7 38 0 R /F16 138 0 R >> endobj 2480 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2482 0 R >> endobj 2485 0 obj << /Filter[/FlateDecode] /Length 1551 >> stream xuWI6Wl39MTrToImeynz.IMF没槧զbtޤYFf@/qwJ7IY' aا͚L:V(7UX}a9X?ո>ͣMn`ЫADҫ?3lo4s&c3E57G_E$/{7!bmQVM`UN&ni*V,pԵ;nyOum8$`+[ 4Ԍ+s |곩HQ,ݙ 77'oc<8w=bB/ެBIZ.`ͅ< 40}[`1i^+%0=Y0 OpJ䔽t ?+qY9iGAEV=Z" r^v9gy'YrQ ws~2t9Լ"c:!Wj3- /$?cGLrzWa h0pV[ӵ;Y9R/(bֲlg՝h[7Nb&s#.S/fEbcoaLll?$Lg97z[;]3270T!++C_difi`RsybzNV9Gً_tnjYWDe|*3]/6S\&%?\w[lӴT{z'B}s:Ϧ!tf@9 ,AwZ:5,~=৒1> ~7~ߺNc 9))*@Oe@~'J?PE.Pdxu;晖YS!MW9zth77=d=iJrpjB܄uPyb칾qWB4Q9-3ϖ4>d[cC4Z&ָ5@wKa\7P, DbK(/5|U-() ycč-+hpaKbl`7CxoKcy$m/!%DZWe7O`&T BY2½{7N^8H,p!Wџ"L F<ؚaNox\3- #<hzjg[ρ L;_\J.X 36Ś>܉F[NTLl2|Q5\$~Ves) Σp} 9`SkeG>a Zгܾ#:+`4?Jva>$~ٮ/եKCrbIv9iIk:;˗)+(aRBvq%T2ۑ1=tZRgjG}v0MlĻ/{CW?LF4)ēμu3yNe`oD]: x=Į19iR4"Ӊ9Q.Ǖ*f(E4/Ң_3h52 endstream endobj 2486 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F15 112 0 R >> endobj 2484 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2486 0 R >> endobj 2489 0 obj << /Filter[/FlateDecode] /Length 2607 >> stream xڍɎ0$F %K2I ![mNdɣy>Zl9t*ZMF漡ϯ6< tWY%F*Tכ?rؔao^O<rYlJ$o4M_|<n`ۦh8%&i`ORoIv&i:ӛfÅJﵹ2Y{bY_MC c k6gY>_ˀ[qvGB;^ 4<:;Lx$8zI$L8j{?By8,3):6OʎxS綳N 8V( }#!yK^Q}%3GHm[:װCxI@ځ3..SI񣂛ކND+^4_v8Ɏ)Hd lffc$g[_Ps^0lF$r-DL)(D" ('cfOEnЌ٫dDOd2tvzMR22[IW3 o hBsOC4X!|TT8J+\ҟ?f 8x侀\ xOJ'Txjc4G)+ZvT((QO#op j5x-Yhc.R= |]w(`ҙNQ혔;[C(6Lj&Ac \(ɤVb;NZvgqQĸ*$P)CeҡĈm@z (ގ۪(a6/>:!8i[82kG/mGun/ &ϊbY@s l{Fƫ\ifhz*Gz$|a p0{8 @|@~t~xH|D۞! ]fmr}"8 +O Fᤖ 9Yw$MstGS)[+R0_v.-$V,?dWK dt,dSn[hn'ޣwo5w+^w[,x)R3x'y'T峆]q٠l [V\8s {h/zցSTܻ..FT TڤJ 4wq [e{]ڈ| RA܃%izS zzGv[ +[|޸!w_@ܛD鬡]D!&A֑̞^ΟU[gmU4bm{O|9).eՇwԿit[E Ό&i8M s0:M@wzC|*ToD=;I8- M uyI'-;jPI@'/DZ;h0t^(@rgJyLP,^`sKa/nyEa*$W<_YpEf,YaC^W70 !S endstream endobj 2490 0 obj << /F7 38 0 R /F10 53 0 R /F15 112 0 R /F13 66 0 R /F6 35 0 R /F12 59 0 R >> endobj 2488 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2490 0 R >> endobj 2495 0 obj << /Type/Font /Subtype/Type1 /Name/F41 /FontDescriptor 2494 0 R /BaseFont/CHJGWF+CMMI12 /FirstChar 33 /LastChar 196 /Widths[609.7 458.2 577.1 808.9 505 354.2 641.4 979.2 979.2 979.2 979.2 272 272 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 272 272 761.6 489.6 761.6 489.6 516.9 734 743.9 700.5 813 724.8 633.9 772.4 811.3 431.9 541.2 833 666.2 947.3 784.1 748.3 631.1 775.5 745.3 602.2 573.9 665 570.8 924.4 812.6 568.1 670.2 380.8 380.8 380.8 979.2 979.2 410.9 514 416.3 421.4 508.8 453.8 482.6 468.9 563.7 334 405.1 509.3 291.7 856.5 584.5 470.7 491.4 434.1 441.3 461.2 353.6 557.3 473.4 699.9 556.4 477.4 454.9 312.5 377.9 623.4 489.6 272 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 606.7 816 748.3 679.6 728.7 811.3 765.8 571.2 652.8 598 0 0 757.6 622.8 552.8 507.9 433.7 395.4 427.7 483.1 456.3 346.1 563.7 571.2 589.1 483.8 427.7 555.4 505 556.5 425.2 527.8 579.5 613.4 636.6 272] >> endobj 2496 0 obj << /Length 276 /Filter/FlateDecode /Name/Im67 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2497 0 R >> >> >> stream xmMO0 >v8߹2qA$.1L @zdJ7Uq>ȞPo3`lJT@8-~GPhcwwHf^B@ uukZ3=) Ryz5Ӷx}m٣drwJ ( cw Ў#i25U/q% XdujJqx0jr$Ԁiu~ջ2S3w?^%S.6 endstream endobj 2497 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2498 0 obj << /Filter[/FlateDecode] /Length 2034 >> stream xڥˎ-2H{n/&b79s`l^Qק^ WX,Mq9 3<8:>ˣXmY)_O/IC'$R?Gۧyf@ ֭q饜BAL8$ ,J3 ^eБVY2 {Uy{fDЀE^LSe8"ňZ+RIR0GZ 3zQ݉e۫/C <ԃՠJŪamkeNss;nt(VfH!)".b|_|"O\6n*`*͹xi_`p8ؚ۞lp5 ޔ/̨ɋ(&`JuTU%_'g6"r( 3$v bamP﷮Ư?!yO֍༛u/10rcD\ṁh$}38&1N-g(E, }`s4  .fRQar|1*dT2ypTʅ4=F&R,M$ "H$J~s**e*|\|xpD—`~Y҆ JP[A:x,a5 Ϧ3 Иnx TF@e;<1 d'96Ws̢,ܕ۪wDx_G=0דCBdؕpqg`尀eHqmx(gq0r0=q[) w_d<(2n$'kZtB;:דj'fF%X5oq\ Ժ/oQHi,-3µ7UDg '|ro;Nsr3$)Z>'b@C e1zc :p$)WrI9X\*7o.tmRHZCyUkD ZgdU%SDڪ -Ɩ@k/8$yF}CBAwӆe iI\ƣx:Ubϸwpϯ;^vlL"i~=: &JRVwq15Bm]P p讣'&d85 ^Cso=qŶQsg:sG7 9Cŋ$Vd1.b|fꩰL)3 .g4Bb֜,\ژO!;U7X5eTH}S|+&t惑UtsϭʊǢ}<%E>b|qmK Ǐ}7x{IcgLez#)FaeK7S tqJIyQE-BUiST/D*'xa[ǐav4)/I 'gbѮ4΍íw/*K_BF1 >,'[px3OSr@B)XD D$I1_2oMHgo ~V}es3,ē2\Y8|c6}~}b(=n:#7^ex*9Ξ\ߊmv7n ,%k (+ň6.tUϲǨ#Qԛt*f& ,.oL xoc2`Nj+OXi;=spJ0'U^ ?21, TG7y;ӿfCg0j/0&t1X[ï`NYeyQ92=38-~+qsZMgѭ)CrRqհ?Qdhǩ"5Ə 7θ endstream endobj 2499 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F41 2495 0 R /F15 112 0 R /F8 41 0 R /F6 35 0 R >> endobj 2500 0 obj << /Im67 2496 0 R >> endobj 2492 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2499 0 R /XObject 2500 0 R >> endobj 2503 0 obj << /Length 503 /Filter/FlateDecode /Name/Im68 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2504 0 R >> >> >> stream xn0 z 'v]3uk\SAhѧ/I( ͏H1ϛQ<oOal{œy ^˝*b2-r&ߌ5(RWy[VcoC0cgkg K JRV\z֔NA[(V0*ᛜ=sR`WP?}c[7ć/8-%9܋rAE a<|~Ihp{l`fn,Wu6 wRA__pPH @9O,eru tƭMa%bgzX/ǥžiö>nV&Ѭ o³eR0wzz~uJ Pt ~GŐi\%X&2S)ЮǤpXc4W3(%PtbVm_s"+B/ۻU9+W|,x %Ʌxݏ {3@5L endstream endobj 2504 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2505 0 obj << /Filter[/FlateDecode] /Length 2034 >> stream xur6`J;8oVb#BjP/P\FQ-%^"4ey 426_pt_uT8'ymv*KszɌ]aR⚅?ovEQϭ=op*JRro/q #PcݹUo)wi3F7$Wu,&q8y:&L1'o(8`YtIktDuNJ`0 95lhP{GF J6"۟p;vb+#w? 1nb v T* n2/V}O,lo;1?NYt:xׄdr%@7B$U3VHil;ċ/)k;Σ9q4>nF37$"nTylT"|֝ } ˥ J$yyyR::,Un~S xN$B8japgY@\' ʠt亦A'p&r->}9< R:uDgs'ݶd+\,cp(Hi*^^k 3y{&8qu+>aWN0Cda0J2 "{h-> TIQd"A+VAC)~n;QlDG$ϣ&)a=iѩ> =VkPe9\QYZ1XUtt :,$;3jU0lk5 O,HiA~y}REVM4$lfpDԇX Dz!0yU2n}Z3 XUa->=w}TG,vb>k35]j*@qTpnMCCiݼxGrnE(yp:^<=qKϕ+_qtS{f/1yUp(Hz,#A|*x&u4@6l:> L AXq8ˠȥ<}0ORsU8I2 C8MqZ$ؼ̄~`V(CKzL6b?; B^69N MҚY<ܼ3T5oS] HM^3H^l}[44=ȿE,uI<{(M endstream endobj 2506 0 obj << /F7 38 0 R /F10 53 0 R /F6 35 0 R /F12 59 0 R /F15 112 0 R >> endobj 2507 0 obj << /Im68 2503 0 R >> endobj 2502 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2506 0 R /XObject 2507 0 R >> endobj 2510 0 obj << /Length 458 /Filter/FlateDecode /Name/Im69 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2511 0 R >> >> >> stream x}Mo0 :"afի^z)n06EDck%%`_0g3}k80dg-#~n ؝ }/:;@`'*oi*2[񅸯]8)(>V!fb2: &4B"|%v:Ů|5mnp6l<߿wDrV4bW8ّ< endstream endobj 2511 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2512 0 obj << /Filter[/FlateDecode] /Length 1512 >> stream xڭWm6 _a_Z: ۇmDKf;~HQr|qz}$ȇc%Y Y!(Q,A$2a*x||v`E,F}D,_.E$UVt8tmodQ`J=bE\BFT"0)kWD ?**@~*]D))FB8?/r4VZWp*βdzlj%^xsDMJȫ$EXu9T1Kn{G65@fKy1EufpH9 &>OY~P<8y:%ITBYM=t1JZ m|=Fk*cBf)P~KzWHUSLB4Z ECg fpD7)L@_ӰI6j 8Rl֝t=|-M&zR +B{6'98" D .t9te"|ps )A3KyA^ &3huƕ<˘gRX$}:6$ncúcTt{~d> endobj 2514 0 obj << /Im69 2510 0 R >> endobj 2509 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2513 0 R /XObject 2514 0 R >> endobj 2517 0 obj << /Filter[/FlateDecode] /Length 1766 >> stream xY_6 ߧhT[eC\Pe _K9v`;w=~(w˵ئ(H9[<^#pX8K0;{G $o3w/X9glTJWC:-j귧pωP?b%zde7$/nuEg. ˱deU:=",N WYLRhVlfy™@gcf29PLWo' saOZF(Jƚ֘`]/a(8zrtFcR!"dJZg1/ m4b ;.ެ_SD}þ(fFs _`/2]Uoz#1<.@"5y%!sϼ$%`o8$7'y[oV i-=fP$MƔ:]?(( < rugQx=|n!3=z͗3KE<`E$yB-5FX1Ѱ;N$D}+#Y$9c ڮ٦0uy{>:㤉a|@C{pi]j ߁ěj͠=ϩzbغv*K1IAX$CBoD/971&.@P-c*Q 4Lq]L? 40)L",69\8Mc?lRMܐH a GԸzE#i+wp,cZ jG=W:dE4AcjVgBt9-L(8bbkJ:IM^B +R[74чzc,Mda5'RyM\}ՋW> uQ|S_'[)hº2TLww &KA`JiCRB%TT2POIMo1ܥl H;V&+˸K7z`;t xSIyO{0Q ݙA~pm`SDeߕ_c}<$eZl*"Qsi o[G.dXL? endstream endobj 2518 0 obj << /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F8 41 0 R >> endobj 2516 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2518 0 R >> endobj 2521 0 obj << /Filter[/FlateDecode] /Length 2323 >> stream xڽێ۸_/207]RlvMY}h֌%W3sH7QyǷ?=RI!E%P{E;o$i s O9|lW&$1Ҏ Α&ÜMA>W#W}`i%b#!)VRswrMYKu߶}AfѦSǶ+Np^_}5׺,j4m?u͈T9ϛdn2y _ۯх}ne_Y%@qe: &3/J@E܊zLM-/_mAĄ)3CPQ1%Z.؆PTl8Tf`,ǸMxȅTWj"B"uk>cd*tX*/})+  K'M ۻ$݅k-]sI>&_7LƩ5 WSg&s@FlUOTåCӠ\SDu12rSJ^d  %Z$UBqK]q}lJKpɱxտ핃f$稭 2LZ9Pf.~Il6B}Y֡j,"H%#_yyY<Nn2 2h%S+?!sSp nWԻs 1uPLMq%ed"Ы@ VnG] }ї{ڎ=l*h; ]f%L_BTbJԝN݇G. O&c@f6sj|SOg/?d2y:T-_HD6ΝAL{5!ύ;yz1K/1Dӆ6x0ӪW;k[}1lZ8'N5kAD,Yl}y-z>]F_x1|A*L+Fyi2O).0MOof\ls" ׌Z%TfЕv> je{a3{vea}Cqo8{1Oc3Q~ZkaWaR ^`0Q.#[ Ƀ60;t3ì8AyS.k&ĵg'.+㻫ؠBSrT,RDD9 jg`®/>巏7U,{)μ`,,&F Jpɾ#ћ'諁XT<:bbS2YU ho^91uO)ldT"O1Ba7Ͱ+s=XB!pwѽ7s}gWhzLN!=0Gy*K ZQb6ׂT`]\z蹱{Ax"T3)Dܜ gbL> 5ˌTi^ umo9J2`<4=AՆ^AAQzcmh>f@ (|=}IDt3ǧ܄摋8] 4˧=*̭rXy%3oOL gZuj<13$vW/P%J``\6P5H]GΕ5", M]έ)F@uPG @HB@ =d6v𷍫Mj&䤟'`sR?9㌔>ɞ wp u" endstream endobj 2522 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F16 138 0 R /F15 112 0 R >> endobj 2520 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2522 0 R >> endobj 2525 0 obj << /Filter[/FlateDecode] /Length 2363 >> stream xZYoF~_8 adxv=AlH}OLYc(ʓ Y$[-ڞ33URS;h23~Efz'k/ry,:?4Cs3& m8lA$B7%ԋ?-4wBo D,X*EAR|_fjOs3.͘CUK%^ke@rhgwL|_/_Kx =gQ?kasmB(% j>|_oh0CX;3 ;uHeP `]0Lڹ~9@ՄF7*57כno<$ItK#Dp"ƉS#?zL"2!^M^4Av%xL"94ʫ RBSH:.kz!cf soi)#"'i=P4[c%dv'S?0&֣YU#.`wY}A,cL5i 0)pQ=kgR>Z-Pʤ<%HD)&P k!Bӣd2s E+0P!hB XjWX+@A!ДOa6K[s5Y@+"I Z{uҤe]mVt8܄фQ4$LU@\qn6I%%. x~4X"qՈs=$Sqanzb,!{Mr]y1';Y  iM o_yѦ+?O/O[v88FIA;?:"هG>XIk[Sy=@ 9+v*U6֩4Ci{K  Q (@I#~$P@BԮR ^Dr40W}d.# 2y>Hû-lHAi:Qӟcb T jkIJm֐T / )doW3|&h#픉@)$.R(\.@*i0q'Ip= TUk!1u3,ԫ[M5F jJHJp iRI@ĨЙP j\-dCًv$H;D$wtRH}u7&P|Sg8 *Lu}y2VPrf}9h1cu4hfWys۹oCHU A J$D[O5LgJ>Y;Oꤽg9'{**10O'TzKHqB*cLXAčJѫ;_XXN|/ltKFAKCMVDڤ35z4;7vX*I~,v4RO*vFCu:w3/B??/guݢϑC"3~1v>Ӱ 8 k0k2Nbh9PoH놼#V+ts ^4nA*<a @BIz':Ac_EOE[ueꨭʎ[U8/VivIz_F0 cuXtj31J o`-I?\9z8 O}pŭIBz,էY}0>Cgp +Qޑ0'KMKKP%:{̨^_pz : oF_z̡5:r͋\_#u!{|MG >֏RDڍ 4"AOT!z3LHjg*]͉JnEӹ[\ cedߍBH`yKoXpa=S5p|ek̗5 Zʾ}f$諯NyxGo ;A)1/?C8m歆|e[2?i9Txj#6]GdPsf+X!:΋ ѱ aH*6)^Komx,Gۼ1h3bv ^rה'מj' endstream endobj 2526 0 obj << /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 2524 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2526 0 R >> endobj 2529 0 obj << /Filter[/FlateDecode] /Length 1229 >> stream xXKo8PNkRl Nb7 Rߚ"c& Y$ |HPh%mdy|3 )#kG<ك8NQ8nH ~p>;o<(U'!?u}﫩xrZdt'㬤7Gq- B,u?pF !!Vк;1&z84XQ:"!B<3?A~/g BPʯtLkY}&ʃE">6 ^/վiMƮx ⱎ~q?zJ zG kF4”B-{6je*ovM3P%]3l z``,l]g҂֙Xd:yx2/ Q?m{㪇v>gbԁ卑 !:X;B3~!A1 L:3Sw{x,X-*&˷ꉽ3hIWNr⧶#ϧCh!# /=vYAG${ݿ#Ǐ2?Dp3?A]PTbw&F,LH y/6;vǡawS /W Ã\2䌀6DJoYwAXMnA?t ڎ /v}ޱsEPJM2p5;jE CcX9dX+#h^&6NS( / mi@?Ȑ+2ds\-x`z16Qx{ZTz$J'ʾzʌC˺ڜCܸ/fgϳO'=-1b\;8ͯh#AaMuV6%߈av۬EU=\y6VՖՆ~Pt >E>^4zBf v[g~nZMw8V*NZ/"~&0bȴUDqE)eŞGo슐W+Sv2iA1To{T V&l9yͿ#.-a *V_k5rSxW|b{l-'w1ݫ>l{QS{fY8kgݰNn )g\} endstream endobj 2530 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R >> endobj 2528 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2530 0 R >> endobj 2533 0 obj << /Length 935 /Filter/FlateDecode /Name/Im70 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2534 0 R >> >> >> stream xWK6Wc $9$6cuIZ9E΃Zc7'I:ߧl^ }n)1zg> &͔ԕREi?}.Tm c$cA{2 LD"ۨ`N&p0q{#I_0@6:(4Uq)(ރlA־N&Z.t"x{nQ z,h!b { 0{#*zᭁH1a,h}D5X܎bÊIs(vf.`z7Px~?zqᆙv~eR^$IR 4pjuD ;ڲܻ`15xqZʠ+E۽8>ϸU (f@M`tt ]NW. }n+t-K7fe]"ʮ<-qb+Ope/Ja;2"ӗpGEꅻ!sCǕ RqO'C՗W(I5] ,6{vC͆nܔ5r|1+w›4#X}L[ ̡[*qkn<69>:&lUWPJp%x5or7=~-#Аf  ~F6tvTc:5u{᭳#Q!8;UQΎ#U]]d}OׇG9˿dǿco endstream endobj 2534 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2535 0 obj << /Filter[/FlateDecode] /Length 1450 >> stream x}WKo6WEb#E&vQ`=4=02c + vp(b&JXD-`K&ZiTLi_GD7`EmfqDmn5OVko{smZK)c:MpҌU6݋$=șPԶ[:]BR0ЗU.c1+hS`Ix{-3`LD{hGiy/K/t.+ݑ/ոqo詫^jךk%ks5]lojt\رJ hx$a ͽ%e<|8}\:oVXS*8k@/}5Y0EU֔荽Gˈv0HgLR{'42.5}XCM ] hrT XXem"^)YBiR>SD.z@Wz,t`Np -AGb\NCڎ8ju1J<8-'= x! y7$T.T> /$6'(%,EvD`ŹjθXSiH߼!dUb~`JOWbOw>u@iZ9[",--*7:p CˮzvWGx\}NUɡڲ:`3R| x$1G'8T hzM}2a R _炥 ^j.S3gܜf $1Hm5FRw v2t/DfMt1EdZ EQ>-3 _3Ds *M `hhqj="K8e' KB7Hͤ%@ݱZo\p.ZBYG?kQ#oD@t#47`7MR7ra@5,؟+D'Cjgrb>U4Ѡ endstream endobj 2536 0 obj << /F7 38 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F13 66 0 R >> endobj 2537 0 obj << /Im70 2533 0 R >> endobj 2532 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2536 0 R /XObject 2537 0 R >> endobj 2540 0 obj << /Filter[/FlateDecode] /Length 1679 >> stream xY[D~Wŋse+@t+-+5,n2X8qd;M_ϙ=8n-Ř;g9(@=^ QNq`0#w ~\ 2aSMq# (,\*ie=-I x+bN1fNmVS-F# *BϕhM)P^4$awG_IFi z GV+qR@9WNPBJr7ֆ$[mNם^SRn% u{Y[yY<8_]R']Y~^"Ip%ܴHWc4F4(`+e`y,_f !RZK4$Hϙ=GPΔ3ﭝ XR~q!)Hgi Ug.:є%yxh (b+f9bCDo=<Ǩ؆ϹPM4Feۅ~DOUґjj9@[X^FhwR6Af"Ek3oc"AuB'<?Ic?Ā<~9b$y4A0+*7(;aSoW>wTz_%gNǟ;jECkE:SxR;eLO*w%Qo4&q}4ri@ϣϪdZo뺚%y (/ʏ q}7`HxN١樐({Uv+ث;zL˭~Rch:M1.'^_Ю`v*IϮ"bx}AXCutuRw__`_E?^R\]zwV$Bp.]t~Nm>CB`DA&O2;$!0@^2A7{ө.C&HA gEІ:<\v!!C9Ɯ=U5eJl$}Q6zA6Jj 26ߨK~%l65Hݻ2B"zҟP}_v U-QW"j1 s6; 8nŃR"мJk8 TefAONVsիAB9Ad^pСz[;an&N]~-C;!g~%>5tF&>|a2mr(ٶJ_ˮ¦[4P4ݳZ<pPzW{2FƤƜ8ُ='}5Kę$ h-H/䬲|tk| y0w6 9jS39ː-М }H/Eߢ~w>\VW endstream endobj 2541 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R /F21 215 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 2539 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2541 0 R >> endobj 2544 0 obj << /Filter[/FlateDecode] /Length 1411 >> stream xXo6B>L*V|"5]y(ْ+ߑmN\ywG:>}g's> I|,;W×8p7-9%,vop%e .,YVxsz }\qRyVNT3>\?5 kIb"~k֚yY:0Q|M9[d2%&AO;/,ǣOGY5-XL^V z-^"A&0D7i35c5Ӿ%:^fD"ڱKL49"^$"7`ܲDvLLD݈%QJKɴ-uT6鱱a{5hH[> C[E>YdF>%؊aq*Wy17NꗳAw}uwN7Qd4YF3U5mnѵ Vj/,;M:pR)J+w9Z9\Uy]VcLfB,tpt >VCKN2$JQӌaXUE>/y5SCj),po,rUJz PHy1I)TZ({XS.k@ekǥ&)efX7\NJdB]ZjcUXU#7YU"ZEr9utlr)$G6 S:%R owז!&,ԲPBI>/l|M6bURʶ1Wz)g՗^ya3[uw!'z"r{9nh| wMMf3gkB/tjlf,,25sjy>?Ž.~.۝oDx~mp  t>:6t+sF!Bg1kL3QkH~Oȣyc 0'd]xȐ0fxvZM ߁L&>Ax Nl*VDg" .?x~1ޛ1icxǎ:;x 1M#Bs0-QtCx%ӏbfǢж6k\?f3X\J8Bn_}l endstream endobj 2545 0 obj << /F7 38 0 R /F20 182 0 R /F22 218 0 R /F30 286 0 R /F11 56 0 R /F23 221 0 R /F13 66 0 R /F21 215 0 R /F8 41 0 R /F15 112 0 R >> endobj 2543 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2545 0 R >> endobj 2548 0 obj << /Filter[/FlateDecode] /Length 1955 >> stream xY[o6~߯YKH2Y+u(Gɖ+MhQv$Ͷ!y+?2#xj~^]|D|4xl .`{6:=>yIQD"4lS¢u~02fMRs8~//O^R R"YI,qQWiD痝Wd-u"G5ORj= |Y36y\$)ABK[Y>TE:w9ab%zA\(Uc874U;+ $ZtcTRYrbrJ0KG-lF}&WaeL[#b/,  1J&:&ٍ.wgJE=ƑX\t/(OZbn4kf3Si"yΒ2 vڇ0V&;EP';T!=|WɅo'(4y 2/@cSnH8HXKݦz}C 59/j]gw5‚ )J2m16:J(V!lMe^8V f{4-}"ՏLs7-i@<$mR8=j^P05y3V".`f H)/JioT~ׄQ%"},$Sgjf黣ͯ'_0lZ rOsۮ śwuNq Yn&6[Hn&w Iך-'+N>kOqxA]DC|S>0o9D]vzY5[HIa!!y# Eݚg%BDVbE>Yr_D(rzzN];97BxZȩ®v,1aPPuT_Z6V=;ˀOHcx9 %&=uYh`tt B jp#"ў^R8vN/j6w < *C7`-2tޭޭXIʬ$[aSy˽`"R}2TroiwaCLcy_r/;^="-36A89ECM|ҾީwhM|襱x5cGbl06u+Fb{TqˮSB;LoIr7#hJ}'l?AҠTBZDuYRQ?G$L,iAG 3ki-u̼F$BntJEU_&zHln '?IP(#ѿQM6!R?Bǡw{0C endstream endobj 2549 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R >> endobj 2547 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2549 0 R >> endobj 2552 0 obj << /Filter[/FlateDecode] /Length 1054 >> stream xW]8}_}#Rq&L5fLԇՈd ZL V_ۘ`0!IJUs5z<`(zeB20]l!;Q^%8(O꜍\ƘsI<8Kѿ?_bx0@(Єo zmp܎4  E AXcDRq_ELE bpQ "W0'I$"4!腶%ʩ1|);H`Dh$ $!Cn8J׊9KGBg7I_k$+h-0~gDyk 1%!E8nm2Ƥ&SѺܯ¢"JU\On&WӋWYJg#]A=A6e__r^ޭ5{HȧLV'{|d~463k#:}@'RH4TO eeI r=Ӥ=,=m=xú(O}8=Ģ22N7P{ETWA:0tuS\mxi9] c /A~\%> endobj 2551 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2553 0 R >> endobj 2556 0 obj << /Length 55 /Filter/FlateDecode /Name/Im71 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -126 -228] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 2557 0 R >> >> >> stream x33T0A(Uf`QccSX~K>W  q endstream endobj 2557 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 5566 /ColorSpace/DeviceRGB /Width 360 /Height 335 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 360 /Colors 3 >> >> stream x]qFsV$?qb6`mǿ pW,ݙݧpyF 1ęP""~Q"REqH!"eQ:>\r C|xY47%~G.:`I*QG?H>5q~/Nw=<ӯ W~7Xw{||S=ťzr zq<ϼK϶p5WZz998̋T!˄/϶+O5"#Sʹ"qlV[ݞS#ah,_ebL?g<ӯ͈#~ ˚|{_O(gs?? |K|'Yy?K3 #mxȳDb_anyewkԑl0Ͻ#]:fKecz=OtF;l0*{zht !̪&7vwLwsdnw`Ȭ 85qcGTq|SÙUV$n:f3wn_`F65+ő*#`fYqYFcf@eh$aWJqdIf窯8biʼnv[̿pVf+w.cb-+vX̿"sVpk8AGT\u5Ȝ1)k9+QcS sVpk8AqcXLȵ,̿.vVQc xbR5[;p 2g8j vdJqn5Ȝ15Ȝ1)k9+[`V؉AJq411hYyXϏAڬGOT1xYyy78#Sf6Hr( fMnۼ=`V gV<ݱ% Bfmk]V_ aVeq To:npwwF>=∮{T}cu/.h`{gSV;L猅sP15Ch~^Tcbu2}96YuG#j'n=s˩I}\96Yu|Q"RFqH!"eQ"RFqH׋?, sVTLΛU7q0?wLՠ}9+fGn8t` 7b !q0[LV2gL68c890S4欘#QT+Q`3Օ;E欘lp8nIXLȵcΊ*lgVf8jvyl0à *+߹2gLe(nvyl0Ɉ *]L2bk8nLˡvY1S >Ok:+f*|@qtõcIFlPqp`T5\<6dG ?w3 ڠ 6c <6dGOscmx~TL2n#'VB<6xҫf8Rx+Gr!"!"eQ"RFqH!"eQ"RDq|O'=n>zˇF 3fM"fvq~0AfGn8q\Ptl*l;ı#jy 2S )qt0d |b8:&nt3l*leqtO81ƗOd6L6@M'%nr33 2S .Sǩ;:4VfTadqAFcVs1d BqxG 2ScR,q#1 1d LJqdI㚝Af*.8qc3hd\;fTa?X()+w.lp8nIX}5AfY⨡8( 68j˼.Af*|@qtCqQIFlPqPyGT5GődqcX}5b4q0d |Ÿ818 3 >88($#68j(<#Ɉ *#H2b38FqtCqQmo۱yvlAT<#ϠQq'{~Tã;♣yG`/q O9? 3 n>Fq^3gVw 2+o_x[eFUYqB 8nF_G5wv;zYmcu/a6L6Nq4F[A}UƜsa6L688;W}9mbu2AftG#j'FL-B0dj|CD>CD()8D2CD()8D̉-Z sV~햏{y&g42g'G QBMJ*3U [ɜߎ3طc'_0S4<#xq0b QY~oUY=yb%sVs4_l083Sl 'w?l2870SV8w<l38w1S0P6Hw2gz3L0D*%T\;h7OJqdIf窯Ŝ`6Hն8.޹1Sr혳ȸ pVf+w.Y Am8 qܲsc 1gu;~6Ȝ⨡8( 3É˼.sV7AG őGq$Q=QIG őGqtCqQIM7\,3U ׎9 18'c#bS sVȣ8(($ȣ88($'É#;dΊfdY)#w+kYR<#ϠdYyXO `I3U OdΊf6H_lY'lgPSEYu?F#yޟAM7_w/:zYlYG}sN0SŮ9c0gu-Ws 5ChXޜTcbu2sV;)cgA]`jL-B0g|K\h ^9n2CD()8D2CD()s8>-Y/_oh:T̽:oV3UC'G{u:f@~Ӂ]f*^]?C`~*߭d~;Wwj8&0S4yT̽wV{qC/a Q{u w 3U Od9L+¬j“3U frL+Ȭ 0*r0UabgVYqn` =`̛ܘ{H7nWYA)LHq0ogbmV#K25֨mT̽j[ϱ9Mf@kln3sR6S]i+.{Ն8ncceT-;X3S3'{H*yGGPb orKL+ά;vݱy>ݱ Tmi{EUYq4?*ogWj84{?*uoIl0S1YG@9'b e9|ߜs{u׬c9' bUtdLܫgA@ysN0S5&;9Y1S1dh$_ps5_P欘 2S]`7qAqH!"eQ"RFqH!"eQ"RFqH; endstream endobj 2558 0 obj << /Filter[/FlateDecode] /Length 1264 >> stream xڭVKo6W=ɅHd{(66 . Fmb%ҥ(_% p8o1JYF/a)>l!$/Xʣ&Er>]F Eͨ1NQ/|Vy^6psW `Eެ 4D(9˄W{$)x׶*wIrvsr+yŃg\Fm8gnnugGSϸPO?bg/⿹x\s_xoڼZտx]MU5 ,X[e$y3^P&ĜCk$|vLxw(^%nhJĄS mmpkՠYܩ5#&(7 ^ (X-(MtR3B,brOsVLeD/w:HCtAmџd-fFV֝`$?GgyE`beVgɈ 'xj_x +SMдEЖe}3`aǛV+A yɺ.z,cS@zJ1B,g"G o5FUKwEۃfG.ϼ,<5$zRO]Fz皞Iu52$|tAu V 8< E Smh,Ff 8`D4dWИ6JV# pCl 'bP82̔X80(<]"= ݸi.Ґђ${*EYuSb;3\QjA]IsvJ] Ám7$`ْNk( ::Dy< 5YbNayx>v3P0[z@Y~ xCdpuG֗6 A,̮ Gӟ,u<;k)i9LzF֫IЕ iyZ\ǟW\˜xtl ,p6"8ǒ+"0BrZ)jQl6="^;> NoCdQ펊^/i^qEe7>ڋ2/J4M ^l,)F >ʊ1Ke=A0wD~,8i5q:"g%d9v_6gW^K0iL3P[7 mNb4dbB[rz_MP/z]jY endstream endobj 2559 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F13 66 0 R >> endobj 2560 0 obj << /Im71 2556 0 R >> endobj 2555 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2559 0 R /XObject 2560 0 R >> endobj 2563 0 obj << /Filter[/FlateDecode] /Length 1873 >> stream xڽYo6_b*+>ҵ@ҥ =4E8LB YNHJLIbYGx$ [O=Cx^{A$HȼD}/OH{n:%,?g`ۻ|zp}3OfAz])few4p +󥄥IZnjƩߨ'8 eO_oKy1"&,mXR;ah-D}Dљ gF ѣ;/QNhdGX/fsã(bT2]Ѥ[̛37Z,#dR Y)w0<_ @R.$Mnxhaz5B,`6?e?j[TF~hX^\r!0v "hԽZ)ODl{)k &/sMR楦Ĉymq1*+|+Z+Nv`"Y(ƅT닙~1Sa]Ykaz3wÔޢ`YYHaHR@F &f$̀"Q:5Rr#㵣dF"j7M]4:c90<]'廐e'v[O^~Zy6x# LWyo& Y5Jn0ݖ?ЭX5Jhq!x I֩6G~ǚ'BOn8Ȟv-2<D$Iau,hBV%py8YeqCr $LY2oTfxtf\\٦ۃ { F* f˧O=7C@ԍG?$f 6Z+X:NƣY2Hac¤LV7&(z󗡓&ܡVg$$qjkINkv%Z rcؾk 6QBC"K:`aJ"F u>zF6F#5Etp+M@/mƶ$I1c :Tx|5_ E~}$b'¯DH (,, 04k1'{0] X\^9K-tPUű~:._\j${O>Y[ AXVg< H Etxk Ar#VG^nvJr:Z'T<D+pUKZ4T.٣PBeq;HW]mN|0o[wDW/j\M-K6Ӌ ˽!kAѠK}SmW!o7հ̼7Xb3e;G˰ 4/;p9TnqPUUOz r󅙃} )T̗({˺1ō~۰TQ|G|Zm:/긙YbVyM*N;9pxC7c30?ۇ}pJxc@$M#™Ճ76> endobj 2562 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2564 0 R >> endobj 2567 0 obj << /Filter[/FlateDecode] /Length 2354 >> stream xZm6~~)C$M-mkdɵs菿hQͦdE3o5 Hf{L$E(HfYC:yrvsߎų7c MEQiYYI :YY$ {:'k' D6_riR# ]R+g5 }Ԣ}"1-(%ݗhy&:5eQo{=Z6_mRgw}룙y=IyzdJso޽sBIdk.=;+Y֓(kÖ3 m>Ov!RuV+3ݾ-"U)puj>65tCAE+-`VEJg2a }i#)pS:A!8<STJbFUOz0Lu^*͓2F77A{5Iapܦ';/ fj("Y $jK 2]ym:*)`.)6"Q@@:![ӢX:đ:f$qڔ#J $ qʖ#ωi<=檑xˊ'T$R(S$-q|!0?&\ E.)xL{QKʃ;IsԄ<u`(i u(ho޼'  lv^bsSZ8'7k?δf~Knc1CO/ozmOrƒ#|1O2T hsfɊQ+먰(c)Y1+L4Y%}Fb2O2~֏m5@-̖8l03[WPy=vedaUUQiJ"=ـP]ۆPb |} jj/ j/Dju \ )+R"/x`:nF+]+ӄbг&ks*J?f"3WC գV=ܡU\mα,LPyAAɓ*ZH؊iyUKaE(=jgQQ} LdK(&[090­ %]<( Fqb-Pf`r|6nq {8`)//2AԻ ^-q!+wmOKm mgWʽg ղ~3ʹ>UJ;?t*:+oľR$9T-ŜՒZC 6RLLzPHI*PtII_1ddzr(aw&2 &y`Usݮ ('ugDDLq XG+zT:P+Axv RK^VR25HWbYWc!?36[!. u>!?TpOPE cP{0$O7I'<-41R#4eEcdP uIBΊ-`rZFZ⮲@F4 Qڏ[*"1eʽ;˘MA -5i *JD5@(QxQ`!E4  ;q=V.hrr'7,8gϹ]񴃪vOnwsgt}'ZA:՗/Z> endobj 2566 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2568 0 R >> endobj 2571 0 obj << /Filter[/FlateDecode] /Length 2078 >> stream xڵY돤6$RH{(Q"Eږhw5bhO7:ّ*ۀt (e$ ?#8$yGOpA+wo/#Ym锰ޏh _S/M9iOqN,lnqwoRTfp!+em azlOa]ߖTM1$"A ?l6 Dbƃ  x^GX 6E.^\hɄGއA%j >"5?ZT5#0E~M)$&4h*HDoE-d6,!a3 ␷|RMeap݀U|P2YPp]0e6 \2iuDu/BS*;v_ˮ'Gy@Q9bJhj txZ:yUinSyչd-"pD4Ul:n3Ho\ ;Sjj}^;q4Dk>|W Y5t0=*F)7U-% ? ߗn$exp8RhJRYR2>$JldsMQЄ[$ٝ+kqO6 HUqzKDWtͦ<8Gf =pm(:. m~yӳ@/StgDRfrm?.+B͂C< ?_ga4b}ik|nn徬PI&ESe}OsG>O~U=;uY=/@rj Njw1MLim4+?43mĮ.MY%+9ϱv,XÐ; %M}PH7,Gy<]RԿ߰ėmyg&#~wM{ T44z͂Z^5w:*awOgI,#;v6-ʰ =fkhmb U 0t@5֪l΋DQE5OcSǰDxR&IH2f]_:jXΜx Z.4zrqTy"fټϫsnMe@ m |+ȏ,!CEX{HH5'kEf8v,zt@y/氡Cc>jO4X?tOGzRJi`w `!Ս9Ǽ-0!|H?  $MY;7S~kԲ n0N CN Y#uz1M5wZvK}P)KFcP藃 +to % e05f? Ot*>sXB$edY(.3K1 ! U;KzrW KHf1P"]T@.ljnp?[)|gC*):Mޕ,u;=AmWWnF 727u^učx*s:.ɜH"Ӣf92Bg8dPk~M5j)\v :[q07j@Eu#ܭ.6ƅR4_ endstream endobj 2572 0 obj << /F7 38 0 R /F23 221 0 R /F20 182 0 R /F22 218 0 R /F11 56 0 R /F30 286 0 R /F8 41 0 R /F16 138 0 R /F21 215 0 R >> endobj 2570 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2572 0 R >> endobj 2575 0 obj << /Filter[/FlateDecode] /Length 1944 >> stream xZKoFW7;6&l4)CQF/qD9().5.Å"kH7o*.1<&%#."#N2+y$GLnfKr$dDbwaRH RD>øOrIsgYl=H2f%*7{ˋ\Tz}'.VbЉSPjYjOW,r:eVZ mƒ !gg@ŷ1c,8j(&WbTE iYib*23e!xΐ#Nj_@}\n9.f޳r:[, 2@|X|>LC$]8E2QsC\G38)Ѵ&8ϭQ{n[%E ?K+By^`C5ˈTgr|Y]+V+*W:v;9;}jUG5ԚbN5'O~Aº%C9=Ǡ{ĢCz u5t1&2o_e461!v4ɴu&pQn]I9;XU:# ̿o'_}2]> endobj 2574 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2576 0 R >> endobj 2579 0 obj << /Filter[/FlateDecode] /Length 1835 >> stream xڽYo6_!`?%u4k [FЉ6Y2$9i;AK%]H>I{ ڳ) һT@bͅ$֞~g{I$Vb-●dl?I6.gsιOC3 l6,]&uZW'{QÔQ•7pfdfnnHĽZ޻(!@ J) bo*BCЂRNb~.MU'%WsnН+) Us1.ڊ59hS~G0NdR,"L NfS':?~=|j'{γ'y<qNiHt!{:'`0 @&p@/:@O"X7Yro6 3]UӠ{z,PO'@>L_' HNzId*eaGhsAGС }p:rƻtJrH[qYPOY=w7kO9 ) C<Xm]\mjz,.LB_[ Y)$K:Y778RoLEy,{w+,ƴ&6o˃aЮ4`~hQy} fzu@ #z̽_osT6RRgp:y3 Eu /bm$AԉTa6OGL{HvzecI21%iHAIZVK[k܇aJcI8ݓ %4R'TGhKvt]Cڽbޤ§-z0aonB3=Z0qŲ҉1ƅr# H=[#Ր$ܗm'oR1S[[B C($QqP '[2đCj#CS{+{)8 RF~ Uۙ%Y#YamVun^Nm39TX%xͯ"5AzQI:Xq!}A&$0REϡ ׏Cvs `vC)3PŁ>)6=LJSl>PHuNO$+/k< endstream endobj 2580 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F8 41 0 R /F16 138 0 R >> endobj 2578 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2580 0 R >> endobj 2583 0 obj << /Filter[/FlateDecode] /Length 1617 >> stream xXo6B>TjV|`6u(ʒ!qGJl7Ǘ<wD,u`?/ࣃCG,\i`(~_O.yd,uϙȂC=Kip/ig.~~rp"+s!1̥LςPKv)MWW9U¢){R,LV`2WE`C@Y=<&?PMcvG 8l6BlgѤd&MXM D3c8)K3pX8BJ 17yy;eL"m>IGVlevkhјk+`LXDo}CU$x~^_Gy}U8oF#1| (?OA΋돹a5 Xyp0y>ϛzᓝxG5Xŧ*}O+-{VE:^6~>Kj5m׎G &$2 hU_u6d"J]STbv!F[8PkF)#،`Q4l)b_f" o ZەE.o X#N}zyʹE|}U"tEc}ѺƯwY-B-cS0dZ8wE&Xэ0YO~QSF]eγCYZ/]WCLzv2m\9؜ A %NF?y1SǏOu<i ӣѕgz`1{ u d$}AqwfEk$<^$l\g@E)0UF'B>[ة!OR'A||26 0`Yl)jMceUDj Lcn`վ Rpttek :S=]l58mNixT`T)$ fԼBZbXfMQAJ2"-,& ݴD଴8"0/qCR5yLCCO*+khnSvx{!2,Vo66;'R^` tiZl cW]oLđD_-Qޡ2#nZ]^F7,2u[/`U$Ìa]mPiXvLBk4OQf1VFdX-m-j4Z'qTƍ ":Qmt~x"j]3}U˲[nB2[ѭ|n´dӒм3l6oCߨ..궕c{⧖1\\D_n:- Iuw?_S^ ׉_-O2 9>9 ?_| A. endstream endobj 2584 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F8 41 0 R /F23 221 0 R /F16 138 0 R >> endobj 2582 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2584 0 R >> endobj 2587 0 obj << /Filter[/FlateDecode] /Length 1554 >> stream xWM6WEޮC`QIhAニDʒ!q;PZCD3y3|3F>rd0H$棉F}ϣއr4]ꔰx4}060]%V ܣzU<3g ׺ynw}E0e/M3:FOMF}b{1eGǺ2A֫pFb )M#(ҘA`IJzLzXDXn6&Qش6m1=*TU`wl @KED$(V첵 PM F=-s( ;D#" dTpՀuMzŘ՗oDzȍ cU19XNDŽTv μ/zolڇ:ƩnLz*U8.r tʾ(`y>XlY޴IayPNcs$@fPfkw3VpM{]69ܿKV%Yߜ4îqvĬT\II)Q_FjQ+>vI> xyit%џ > endobj 2586 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2588 0 R >> endobj 2591 0 obj << /Filter[/FlateDecode] /Length 1863 >> stream xڵY[o6~߯^VXKX5ÀeYʖ!qGR$EQ,KlP߹_ '>n^{> @#z^[[bu>2}k=^ܤgϑ~P1pX1L<8V<1K6ۼl0 b72B  b"~2D&/gU[l;aa:6Q0%@CFP@(YWæΪf7SO䳷n"T.3O!v{ۊPX4E%|h+&3_c;|)TdXEf M2u\c˧!NVhf44$Ah'RJi\Oy9_R ѥf(^\m_SFf U&qT Hi%xHKIڏb/?X50m~y-*5urU}m;ö.KCp)+YΜ6-Zđ,nC$*چT$I9ݕvJɧ3Mk#+O3 Y4N*`Cd?:_iʜxK ye=ͶJ^EϐF n_6  3N6]O}ae֎NثÕC#%bU>(&c>Hi}Q5IT-2qΩ ַFl 5Ħm2Ok,0% 4ũ]3O@!ӻFyybAY;OCMP *!2k_Ta`m a(t(K[&fݗX5P,R5󬙜%|H7f Q °ÆIla39.0DHqмO$ZZl`T78徻!g=mU1)^@Eȷk OLA  JC'!'*9MؑM:I#_N"Ģ{sP4掊V,͉9T]'YP: k ǐ8Ȅ?wImU[#Vz#r8 Ȼh9Um)9jxo)M@ESY;YTnnѓ=uaBKܦvخ/)su[omfk29TPZvqze߮&j־@{#Z0E=]-*a2US6i@C㛹x1oF:,cKU95ƃ 9jbrD]$Xmzp!gɜ̜Af& r9iLqGP6|M':}|MaޠJ}uTmbGຮ)#]7ۻ%woj+v…Vf2+o7&V#m-S5CG90cC]u> endobj 2590 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2592 0 R >> endobj 2595 0 obj << /Filter[/FlateDecode] /Length 1945 >> stream xY[o6~߯:M͈WI:`mnnHv*30[2$9?R$J;0hQ|-zy/{y`jUE bmy7>("sD0Ex[t1@dvq:g~niWio~;BjA .駙ރ=eeqiקxJl]NFhM($UJM(I2|p2?SDgөq}b!d ""R xi%W=_e5I=*v"rȣ|q0#"y}y~jQgkn9yeY ZA}# K">F@֊&4f0ԬdaPiJY bzkKVÖFmfSB#6Z? y@bbˠj^gcF 9˷7ʳm0cgm0֏16>%!}g5 '$e˷ۼ>*Buwv8S?}B&xm{.Kdt"B6/w<\2 d%TsGSVk Fxm 1(ni\Hӱ ~; ?#2U A*]sXD#90YG=-+A76N'`r͗µ!"c[HA ZB;jRG5FQx:4"n1,ߙʩ ;" I1VO!0bE]e$+LS# `yƭsLh2W ̹Xk*odQQ.ߔIyv¦Wij~-ڟwh]Vxw%Ms<$5%"j`+*I;"V\\R}`Ɯ] N3Tvޏvf3g>!|vrmcj}vs ;>t4#fIљTyrCֈ]8:+~j7qL3g;'~WG3(!F ^C3pjgSO<^?WbeoOϷHߔ֦ҷ& ҷ=D$#*3J#i:{&4u', ,-8:jYz-W}B# ylAG6/⒏"{q]6*.V>WI/ endstream endobj 2596 0 obj << /F7 38 0 R /F20 182 0 R /F22 218 0 R /F23 221 0 R /F11 56 0 R >> endobj 2594 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2596 0 R >> endobj 2599 0 obj << /Filter[/FlateDecode] /Length 1798 >> stream xY[6~`MjI:ɴOgtLX\}a^O,;7{~^#A9'ޭ(C4;{[x?1[9CǷIyWBW8opP/攆 oN´%F0i]\&r,b`&xH0Owe-sLpof5 ŮgWZՆ"?8Σl~mUJe8Pȼy J9FEs/_8S{UZ[(]o[\ a|ٝTϻVdh]RyߖQXVq'7qPUB$2sz^P L|e6Ǥ1zX~se^#qV!/QK m υ,$6+]ŊF87J]9 C@ WUW,"'b ]b Cc& aT.qH&@dL:lfꣃAHZ0 joIvAds# IxhַI#i# P@ɒ ]CkEUdҀ4Oا2!l%1'8e8@5gI(lٺ ҒQ~Jh 9q֩؇A-P~ Qpx+EY ,cIeBU5f[:ד spޝpkm732om:V84eg //lH㰮{CwK=SkH,"KZDWt>Ӄݶ,F yEzŠ窬Seu[U(IŇ^Ńq] JT#hJKi$-OwPaaD5(ʈUSj eE2J3k~sju Ѕ[Z?O6q_2pZ=ϕ҈m N߅VwK[7qO} JEXީ=Ґ=w(I$O&j=Ii'3'PD_JE4+;'7j32LNN@6-)GJ)NANr?1\iJS2+ssiLj.$1͍$0ѫir( y{[y2 TwH).a-m 3Ngp84Q'DTr , m t/^-`K5|i|3Q$Be \yK8g2տx endstream endobj 2600 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F11 56 0 R /F20 182 0 R >> endobj 2598 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2600 0 R >> endobj 2603 0 obj << /Filter[/FlateDecode] /Length 1233 >> stream xWn6}Wy FH\&) (})j],$"up9s搲8ުbp9 M\ +&=Z7w6;(6?r.Z}8c1^A*GZ۞筮8 Yei{O[ %1/j|l9{Vh(xT;ܮn:FmB"C,ݬ`AX6-(B~WhuV^lύ a <7촶]Y#MI?YP}#"&#g!m;F(JY`^d690v}tƊهhA #6h"w"PEn{F;Vn #skbtv=@&}O:#I2n%;F3(16 ,0uY]ӥ3BJ"G+R15V"tIss T!cSL<VErmXzسD?g1+"y>6Jrtr?$J_Ԏ-(ۘTЋDZ&m@Umcn*t+y).FdХxPc` rl:+OѽNxiGQT@9Wxw]ƍFc uOzZilӨJHx=5v@わC r*o҆Z׍֍u&1u_zbATD7Jgq.ޓ$}_i1f\]xȲk)MGkk?wEEMp!/gZ0L_\ڤ 1X=zW1f>41ӟ̣ڤ&^r_8K!݅LXxRJ)d%aHIa%yԔlӝ/#o6vo[$۝b@9;jjKSEG2^ "l.}zSe3~/QE{N;Uz*8>q l8T_Q endstream endobj 2604 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F11 56 0 R /F20 182 0 R >> endobj 2602 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2604 0 R >> endobj 2607 0 obj << /Filter[/FlateDecode] /Length 1555 >> stream xڽXm4ίå_8@]:J|`z[Ҥe{˯gq4}/=gD$ekh`$x01XDnfhd$K}O ˂ُm98 գl_j߳_^]0A8g1KqE S3u&NAipLs=_c;l+YEMF5s҈ɾŪ4 g7FVzC:ߨRNptUqIN[>]ibϐ uSbyޝ;pTjOSlSZ5vqiuݘQ4/۹^#Ό$c#hL 879Vee}%0\0sѦq;E/O?\}yfDaጰ9;uyLb6pqI4z$I"eI>߬\7ZL-}s* ~([Sic8$)~(}z1n ]f2i͘xIbفV@BPF!Xxgj(\?NsX&_²;tLʰZcWUN$~)Qw}K?XIq]JL-XQnp+4^2vm I}I6 p5\K4BPmmܽ$ }vgM5?mRC endstream endobj 2608 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F13 66 0 R >> endobj 2606 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2608 0 R >> endobj 2611 0 obj << /Length 52 /Filter/FlateDecode /Name/Im72 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -1 -126] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 2612 0 R >> >> >> stream x33T0A(Ufz&`!SC 22)s!tg  endstream endobj 2612 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 13154 /ColorSpace/DeviceRGB /Width 610 /Height 540 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 610 /Colors 3 >> >> stream xK$G<`dmf 06xai#@7XYW dc*2#9'Yzz&+]ʳs=Wʕ+S<;;;#r`݁9EN?O^ gn+z.Mwc=lZ89)r󁋜tv%uw^z駟;yM??Ro;G/~18PENʁ|"'@;Pwخn&򕯔{yww%Wv۾7exw]kԧ>^!C_sIG}-oyȁށwuW=S^MJ 骮Mw}w;dlԺt>uXݟ<|?8.'>㷽mݟ }3\ȁ.rR&՝&Kkg?&OWWiϲ޿쭬t1~򃃟,?wq'ߧx@>x;yQR'@t>pr]Iz]M~+b w]''i)@rR+oou'=`W`[ŮʺЇ>s&=lYWi^{V:g"$^u_G?OX|OyX9)r`݁3Oڕ&yʟk|Ӡ5K.G)k_cw?_o^qe$ ,\ǁ9EN:f7׿|;l/JdY[8SrCQWIŽޛ^jDI9\Kؕ^&֝&sUy7)(yK1ʻ;M=clENʁXw"']ف]Ij{^krLM[})gI9\8^t55)%W;ۖ ՗MI9\]Iկj?AkG?;~wu׏KqU\ȁu.rR\偯zիX}A uѥ3(jA$IQ &DM0`5 jA$IQ &DM0`5 jA$IQ &DM0`5 jA$IQ &DM0`5 jA$5w(,K*JMp 5 jA/?sw剫׽l dWc ѕ_59 K%Ȃ,y_MVd(ȒOYẑhJX94yMjudFY@hZmMw) (vMiXF)hJǨ#QSZդiG&j1DM) L6MiR)hJ#zSRs5n)hJh[G ݦl&U~U$4&oz'o\ޛɺ۲LïMiבɡ&^S2,m9Ք)rn2&5)Fd:'end[ͭ)u;2Qsd\k k9ofeshJ&Lze>.GYAcZR#Sܚd4֏\K].3,Ry86Ⱥ&-:2d2nJ&I7b,3WM)N" 3mx5̚ E0s:hME 9؀4d]ɠ) R_3zdv[Й XSoJL5,BVC[o,S̱ZMiZ£&̲d&4tꫡi։9؀eБɳ&EEY2Z&&i?7sETOA :ؽOk5{yoVhl`Y#ҳ 25u.d*Ol21f56IT.mYDhlCITr.m99Pq)[93j5YEjH߈։9&Q#Rr"f56#IԈƭc j5".q+'bXc8Dŗ«ߵ#K VN$@MKaʉZnj1XpL5`ߤښl!Y`%Y+O\k}+j,c3$B:,FI8 !D\ ẖ&/,ȌߎKaʉ9؀94yMjudhAĥ0nDklVGfei[)ƭc xRHaה5iԑW{c 0HaԔV5iڑЀKaʉ9؀ӎL>ò"^>gvk|mf46&MSԤCG IJ7 *m9؀)MIMAk9-30EM^֑,M:97s#n)trg<;˜sX ՟Rsh.Kq30&ozG;tMVtdnI!3] AQ7s]۱xŦ&(^}:s]9/LRyLfb)oYıEs^&M&YS2-D|տ7EaԔFk24%Zבou_߻_iođxпy(2+87H):29dkJƱXO/DLf#i5iA 3˒᫫v\PJ)3\K!d>"z(ivY:tdIQQZuP=ʙ+R>h)kRRa9lwȜ|32,= R,SX= W_3KgDDMn,+kh)T_,S̱3"&tM+-Wp2d9xFP&8)I*;i?%H!cgEMK8Vid>N=sШy蝩(sYu59#jrfG&h+њb݂=cməE:21G{Q)t,9xޠE:25[i/M^1%}#WvȜh.uzպssAMBmCfjDskJ:r t >2G SnMۑDԷ 2[""jϡ)3ZMW2[߬ÍÈCSwd[t|[߬G S5iёɮ&qSRq%ύӑq6eLfMIGn˷ύΌҨ#uM&# t 7]G&LzMIAnG_' Y%ujJӂ5)f%9>'b+23ҡ#gM pwm爘ys)eS»&J\-]7&I38CY)ILudf^kG2ECeYj>ՐǑ9pJM>c3}| kd>icuU{6a`s;MmE#.d735,L6MiR)V0&\SMf,0WБB)-H6⎁ff7&j+^OшK!LM6ȭ#nS.\ӝMV0"NѸ?H69eRMN7NfTQ>)kRGRsdqjrA#ҳ 25ud9$E|2X j"7}NDf 5嗟Rmm P&"߄'w#@&u=~}sHh_ۼg"s#ɢ9rɬ̉.jRA7# T[SG.(2AfDĚ(ɹ(R.KY3W,(|8"s"3&id氭ZP2dNd13i&zGfvJ5%{NK3c+O\k}k%;RHS-+k%s"2c :bڑB)M 21+\X&:2i?갦$<3 7yafAf- V$i5K8V2G*3,̡)kR#eǫV2SywTO"FxՂVGfei[)[I1Cлw"2"FPSHaה5iԑb!뼕37d>P*2HaԔV5iڑ-B샚bڑܷ;wnEQy2)UXdiJtH9jI6yMeFБB)ɱI6yMeFFM^֑J=HZ <ɦ2Cumʅkw#:}ݹBfkdAM4&ozvDatMJv5vI.d735YgV̊LMJMVwdr"jkr5%.d>9k#jrNG&vMfkdAMQWs:2&[M&jrK!LM֩$69tAfʌk=Ԥj}l*35Y{5W#59Afʌg\s5&Mڠ&}Ǧ2#d[uWAf j G"jr&jrj296td<56?3価Uz$2k$:|䎍dri[I\7#s yɾ^H+Pjuyy#f8.c5\R֒ʢ"sy˨>\p6zG ӕhM!s̭)u;2Q^}dSd.1Q}MVo\S_Y!sFDfqhJLqkUv}Zn2'2ykҢ#]M&lj_iEY:l"+EtY#idHL2^M&lpґeYdrzG..&͓xɼY5 ћZ7v5/?0~m2Ldiřkr剫oMtm] >˲dNdp0ss0.WFNR9Q2ETB#Ȭ贈s0rMNﱊY>̙+R>9f4czfd593Z`u s"s094yMjud>?3`fu s̥s0LҶ&;R{ɝ>?ReE}rdNdc9 D#]S֤QG2)g:(WC2Dsbԑ¨)jҴ#JGLO8I52!s9iG&jrF!fNVC2Gjb]ɦ)Mjҡ#ŚFLiBOxXs0MIM6a`s;MmE#.d735ٚ,jߘnrjkru0.d*n B s0"vܜߘd7c5x$q@fkq3l -xDZdk߂GP;S4O3y f5&wP"NѸK!G`Xs0jr5Y!y f5&wP"NѸK!G`Xs0j iUxuW Ik…vP"NѸK!OlXs0jϭ)W3"^9=|35nG&jfU:ofnOxXs0jϡ)W6<j=4.2GjД*ǖσYiZ9 ĺ&-:2d2nʵ/YTjZ9Ŝ6eLfMe:Kj 2981jJL5 rz^}JDK`,Miבɡ&^Sng`<9ݨC7s9VSI1,78&Xn̉"XfCG&ϚeU]ueaN9 ,} RxפYw_Ld/d$>s~!djCFgAejSU?e\APǦ2SQD܈O|v^ }>3VDsz"KTd13&ќF6 G̈D"n#bfFME7 d13B&Ѩ!}ĎI4*RHf3#.j트GD".d13j&_~y~"d13&U{u>"fWT7 d13q \ooy>"fF&/,Ȍ!YC'fdД5Ցzlx^\}D̼qZmMw`@o,E Y/13ozG 4I *GET.n:Ny:R5UMvdb(oOQ`)/C̉(vd&17BkdNdL6MiR)ǫfv[2 БB)I4Mƒ:(d54Zɜ,u`:(WC2DsH۔ dtgG~ixy&Mȓ7Lɩ[t ^id>n˙7nVɊjQlJj-.d*Of8jrG]MI21@1u5)OVj2T;K8oMG`fTdz w5 q)$Ȟ9?JM&ZEjHI6,UtdY$R̥̓l*3*:Ƶdmʋs#<yMeFmJ]K!'Tf$D\ <ɦ2#Q=$fyMeF&{I̱:jB2Oj3E1yMeF&q!-q)$$ vP#RHI6skJFD\ <ɦ2#skJݎL$Zq)$$ʌshJ*E\ <ɦ2Д$u"^#m032뚴dWɸ)+q)$ȞiSƫd֔uxz21jJL5 ΛOlf7]G&LzMHMvm93i5iA 3˒a5>uCȌfCG&Ϛe,P)52DfQQ>)kR,K(LWCu=d#ҳ 25 dBSjd.Qhjd9kAM"s@DfGM"%qu213&IF."A2!Pwp\#bf4D`>"fFIQ &JMHȮYFM@M^ȓ7ԬMdEGv,?fUtdnIFM@d5jr5 ͢&wT0j_L$4TdZYM$M&%jߘ&`*~cr5jܼ6 SהM&Yo#$4AMJjP&wP5Dž%.># DMP}MIG@ Mޑ)nMґuMZtddܔ$cڔj25% A5QG&LMIG@hMiבɡ&^SRZMiZ£&̲#`efCG&ϚeIAUOA #˒Y)IB&DM0`5 jA$IQ &DM0`5 jA$IQ &DM0`5 jA$IQ &t&@FM0`5 jAg|+@M0쩧R~i[`]PIQ &DM0Ȯw?l@[ endstream endobj 2613 0 obj << /Filter[/FlateDecode] /Length 1299 >> stream xuVKs6 Whr<"sv7;tAQh[*Jr'G^ ;xv K❽4:q"^"n_y߼_Bv?Bjo?jqHcލ_( ؿ[4?u]UXͰw>L'lIc*ޗ-CJX{$iGRm1I+,-ނJ8IOK(+m6YwQQJ*|)Of'2fM#(818Cp9|@M9rDHGCE/r,6fvR~;X~=z3j) 1é7"ܻ !5OHO(G-c*ߝz~u<2B s3ƍD>Z|`Rݮ2uU6f!/@(حi 0 B|Y%ཬC4C^6X ]7=d_~լ9nWt6*ǣEzυdLlp P&̻0ꒉHzEmLz[/k"ǩ!ҁJZP}ѭu$Reᔑż : 8YunRMq?t2r"@XlsH2\22 \F;ߊμZ4w-I> endobj 2615 0 obj << /Im72 2611 0 R >> endobj 2610 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2614 0 R /XObject 2615 0 R >> endobj 2618 0 obj << /Length 52 /Filter/FlateDecode /Name/Im73 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -1 -126] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 2619 0 R >> >> >> stream x33T0A(Ufz&`!SC 22)s!tg  endstream endobj 2619 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 13235 /ColorSpace/DeviceRGB /Width 610 /Height 540 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 610 /Colors 3 >> >> stream xK$Ggcy fW@@ 86n3m@`_1o@ x@ V~m@hDeUVgF<dQ=YLuf+N~򓟄R=+^K.-llȆe6)Mv?_җ>{ndSO=U6Ͱc=ӟtzذ x&;C+WEcC x&;eC H~C&?ϗ4G"w?#/F x&;e7"u!_x_WF~˗/Ͽn7lS6dC  . X{d*rs&x衇~_Wa!odlȆۆL~K_*Q&?On)>fjSTo.[ {lo|'7i Pp&;eC64ް`!Rz/w}ݣӫm7!on!7|/-xԿ`lxᣏ>:7y{dmndlȆ6!_WϿ]Ї>4;{[8~7dLڔ!u}~j`ۆ ?jgGǿ5,-ß} x&;e)CK!_/=frdcz qd?- y?_lXb^,Evmß>`8}p<?x;qRvʆlhaCn!7n/i=_ѵЭj_5-3lǣ%^H?||…AaQ|lȆ Woy7̓'/K,h#Ч vK?3խ0$~25,Y/‹|gxAɃwZv`!z)0Dꦛn2o}l>6tK8)հ>S7_ {1K;޳(}K6m ?Qo;=p,6\MvʆlXaNH23+? z4J^}sxzV=;WUst`:6lS6dò t^!I(gndNF v{a;67lS6dò ՆC^wSLL3ld0B_RaΆU+-c6j&;eC6,۰NW׼5C&4s"wj=yZG6ȆMvʆlXa Hկ2}l+f~,{ߊva!mdlp!RzիL~/i&;Wٰl&;eC6,۰NpxC~ `N3)2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 $2 q&殿?$WI&I&I&I&I&I&I&I&I&I&I&I&I&I&I&I&I&I&I&I&I&I&I&I&I&I_W)83PLk]È2 X8RkSa{< #Qlj!xPB&Eyof&Ǭ:gI@KMAlr)<83Lb\y0@&aI.< D:J53`LT{Ẁ2 1M~̀%2 >x0F&6eӘgI@eYTqqf_EggI@xG&ZMzW@d19g @-83jyLǙ&$Pcr< 4A&Zqf 2 Ti؛P3I+33MIxhL<&@d19g @-83jyLǙ&$ mxG&qfބLW3$ L̀12 Ȱ8,83`Lbl.d{qf$TGK3f$ L:J83`LԥǙd'^3$E*<dgTI@QN( O 23z$ 99 83LZbrQx3ǙUdP&zxOiѤ4gI@(9783L.zqfy,83`L<^yA&I{qf $yLǙ3d7g,I@xD&{qfdx 3$ eǙ{d ewy|>/73`LB^4s'ehLBˑǯ7axd^0Ƹ7+cE&!oxSGrų,9+ű#3\ 3c7a#<, i|cdRA*6%@B&!@|L.{]ѱ$ĥP|j|&7$jjڛH:~gul#dU AoӘ~gul$}ij}Rq=c8LB"eY"4ߙ}Ad,/˲ю9cB&Qr!h0,*/IMǙ}2Mx5lқ:~gulGIMǙ}d%<.~qf_pD KxױA&QR{rrrgxLd%/qf2 :J3ٰ74=̼`%YKO\5'h"qw+33s5 3C5pZgrf sjzLǙ$ d2v,dC+B?5qf_Z)d& dKxױ{L JIF&=MǙ}0&D5odiМǥor<؀%FFzT̤R##΄< w8c6)R+ MǙ}0@&q>fI;̎ d)J& qJe| r`|qVyYױUKI&QAr,r=cUf81ڲh]m.Τzױ%fdKE&873X şR3S93:6 7<{ ]g;.d jVG7ߙ}ULR6d $FSjz}2zfGd-d kdD&W@|5L`PW'fN<TI2LԂhy  }gvtl@A& b |\>c2w\E0~Yp:_g3O;?6 L^Udr/h\0?o̒mjfNvwcr E?麳3s3&_|b0xxdG&O[VoZ83l"gs)Ǚx<]K@Fm2<~=r #3m592;_ofex#"yf sy,,2ٯt~ti悅;6Y Ai@R,-gvtBəkw~JЬgF^=F&{}Rg޷Iƍ|?kg2ȕ9dEy`x#dRP~gM3,7Z# bip9!2953g /N;י#'?8s3{g2dh=s`J)@&(KDɏA0bf=y;qdr0mrkJE3O݈f݈ΌA)fCCex }1;<ΌD; z >]40Mȩ_&Z)iddz3c Y\Yf.XPrisf>̥Xr?;2YN*eԂ< g`;se J3%~_Be= gRL.+=XW_nZ(R*-+kJoRr?8^:!Dx)UהH|ea惔fVjd$^J3!gV~ O)A*f1j#SgM d-}R>Y;Ag dbPJLJ52i{\J&"Om;0heUμ4x?b2O 3YTEl]2qLYîg2YR`), J3pM 73/U3soL_Mp?2ř9֓\ϙ \M.+3373,Sɚc,2ō drZed 25LOs5ya3O2"M~o?5|orGA&+4 3/B&md& dre!6dg\SזPKgGx!6683L{;_'Z+ !66;3ou.Y%x"2LqYaȤ 2YLB&a)c#흙٬ac3ug2_PV^JFEz3g6#;6`RKCof>N|fR_JF#ck}#Rp5h̬IFF㍴v>"O V񧧘 UK)tkedr̬ `R72R]YfQY⍌<[fVJF2YLO3 f/ӭ91R6?Ku+K~;̜Y)/0(x#LsKR"rb̕Ի~&Lj42e2(8"r_>sA73KLt|X\Dx?oj)e2x/<5y2kxy~ɯp:Yg2dh=s)RA;A62)HKGR3'^JFLRȑEfHue3bэ@T)UYd2eWEA[Y,}1;3UҠ2QA, d mvX 63y|NlmXjf,-$.f̌ (8383c$838>2Z,f־Y83"R}7̖7$xa fָA]xIm$dxEof)yޑI^~r'0|3ft0"<%+K&y8vܑ3ǯoe^F3gSϵ♻:W_n;_o@xS#9s/^ H~w<9sjv7 wg.ܹ|f>^Awx3R+89sD5Y\p|~gaR'gbV<9+r̎A_*R=//??fZfEj,u3zwųLg_x<tD^)3YTH,5hd$>|̾AG)R+K*03H?cHpfյ;H?%S4fu:@&v>Ըw[8hg2R%kal"LݑS|.}42/%بlFF3/߁ L^U?*ͤU^0L"3:1kd$[ƙdxe2} of>٢Lu3Ys1l~59?⳴K\厊g6^9Hͥ̂LRɚEdM-<Sq&֗I x4ukɆkw(M3/UA׋qeϸ4-MiHj2dyڸߛE>k~ZfR囙>OD&wx?< _.O`2LdOdr,%l09gu:B&wOQ2qf_#dr,%l09gu:B&x#ߝ7Қx페y&}FZ;d(i8s29f~3 W#fV%8sR62~?y[:83Kpf_#dr`]e`VaL'bgvtbPJF\e`Vh ;b)3:ΤF#^&,s+Ӏ9f>Hif_#ɠR=KOϱkw#3:Q*R#v&K$zOdPXe1s`fG/kd0d[Cs` jwWv9q=sR2dTn:Q-z|@j׻vfG/4hddTnﮂXR6`79NA,mYg2rTj扚)e<Mg.⣿Z.Af2QLb4<9gΏA0bf@zG&x!h/j1ό h5l.6< $E"lLG/نǙD<^40 352N\ k~D,< G&)2i$5g5* d"68L}箆e2\kwLht=;` .2<̌Vx3$h3aɅN?q`f&H!fM]4ثr&G;3Le Rg٣Cjx"t^hK3gkGM3>R7⍌87(.Ew.L8d2ER wbṕJeڙ :TɤA##;wdP8Drs\Vj0hd$^J2c:m&({9yUf8Wl:X􃲂G1s952-e/{Rpk5sd: 3OWA2yó;yי,,d ^vd[ U7nɂ4LƝI$/3^IYfk=2 :I$.3*%Kef:2z-%2.D2//q"feLY|eL:I$dr&fA&++ îXJe2TMmPdߘj\mbӸL~!"lL(+e&drfA&kOD&wp \mdrD%23 52`L6\3[!52J\M.6d 29i\mJ2drfA&+1RrԮ\mzfmd \mzdr̠D&afdRA)fuxIFLRrȮmIAɠVJu["E3`fS?3FJȠɠPJ- CBef:K` WJh\`<3IR5E&XrnE:SUҠ2QA,9F7`5JbfQK@F֙fƒ:#̌9f2QL3}#J)5f13#jp:́d>H- IK`Alr23L“:6< $)܆ 383:G&,bfgF$$$z/^W_n ht=;rCo&T2L JIF&\jdKL72x##R*fRw)R+ dQmd (%8ɠSJL42A##RI:2yY##J 3kd$[ƙdL@fg:u%+|י\z)9 #UTEKI&j2Ylm;-%n /ʒL@1J7֔{;$ G&*hdF2 +Skd(-پXRޤ 2 2~ L -2Lrdr2 ȑ1H ㍴vI@LF fmd dr̠41(x#LHE; z ʥ$j)e2FSJTjddP(%K` WJ UJ@F*cI#`e*ci`ɨ V 63͌%K@Fm2 dIdIdIdIdIdIdIdIdIdIdIdIdIdIdIdIdIdIdIdIdIdIdIdIdId$dIdIdIdI'|+[@`SO=%~>> >I{dIdIdIdIzMx'= endstream endobj 2620 0 obj << /Filter[/FlateDecode] /Length 1328 >> stream xmVKs6 WhRyVvN2C6[I̊Ժŋ"`&i"Z>REN6ey4DeQ&e}}|ҨM:z/gIFOv)&ګW=!?Aߏ7Q$Ut[Gn2 f5e֤x*Uڢs8&CN(x=T= I+R̼ܭ дOOZ~P缁Kп4dYV]o\\ތ:AOyUίyj8fٱ rBB '{+yw:^ r~ACnl@sC֋^Mآ((k$o@V0S Y4?%_)xCi{-fl`ϰsl&Kۤ( MH(BRRVlRI;UoM9Ks/B :T)s,:AB1SnjFG;ۖ4tF3@k1- BH:IX3[GCl.ʀ\rrݖz#@.t5uw3A(tBgNrrnx{,e^ERUHBs" $Tl,].|'yD$1|0c'lG%7jmvx ȩ6-g]ˡnݬYn7#?q_nnj[7%`^ `7a52v%>աez0nv`ն|qDszb 1} Ǣ;=Thۥв*KF* "q"{z=Q-rig^dAMP]gF*֗ɀ; 7|K|`11y)N{ yXab> endobj 2622 0 obj << /Im73 2618 0 R >> endobj 2617 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2621 0 R /XObject 2622 0 R >> endobj 2625 0 obj << /Length 52 /Filter/FlateDecode /Name/Im74 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -1 -126] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 2626 0 R >> >> >> stream x33T0A(Ufz&`!SC 22)s!tg  endstream endobj 2626 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 12810 /ColorSpace/DeviceRGB /Width 610 /Height 540 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 610 /Colors 3 >> >> stream xK%Gը8gby {_@ tc7als 1̭']+3b2g`ΝIf;wU}$zꩧ^=[7<99)#aنMvʆlha?es\dH}i~:?pmdlȆ6)v/N?_+/Yp7ttGua!odl8S.\0e _Bz꩓ȭfկ~?B~&;eC64ްNِ 7"umMX+wg'>qn6=Bя~4\[3fs y韯nXG6s&;eC64ްN&7M򗿜gy?N~2dJ]~7y?8<,Yf =Kf6<޹LO1z30bgo~>}݆Mvʆlha`)RSeL~_=/~)gg>Y&n-8gMvʆlha)Rӭݔɯ}kg/=^,r_M}G7;$̆%)fZYYm6_/O-oy˙G,rJ)MvʆN[L~_?s|NNn->ZR#-^=fǟ0L|\\h-+c7a#<7$:G&!)̥`L3&6g3-d#!R*6-<7j2(%Iodw[)ޛDKFe&22 q)odQ[7Q|Xf+ 2ΑIRmd$^JD;s&e)%IT1hd~ՠ7AeL3{nd2PJ!(-CR޷IUڙ ?ILD^^-KD^;s>A@ (fy+4Xޖv4ƙ  $ JF7 ƷeQ͙ߙޤp|PB D!2!d%>!8>!#(D&W"ǑItLЦOQ if9sC$L YmYwfLHh (gyC)u`ysV[ѹL :hss&՛̾ D-P%5ꈼt;s!*Mwf_#('e̎ G&!C|5RiN8̜x<7LBԂhy CgvtnLB\Z CтF! <7d$a(}2u W ū'̝@ 3zƋ`\"^~ݘ%'jf {dV Q ƦDɰ3MI m|Bq,qfĸnG&1.[&fI J{d#RZU@d#"9k 2x 3I <8>I03X< 3 I /33Ģ/^r^g٘ن= P;}";\5\_?Ϗzm3g<3g.c6s| drPrA勋"Ȩ+78|drDiM)XPriq1XYr@3y<Ԃ< _s3W21t}pLD|MIݑRLn+]gG3<"ˊŕ;\YQ|Xsy>#29 5%_Y H|fFF{}pL`M /Uݑ̪ /=ξgE&O[8V;Y;AyꝈTw6hd$ҫ|kdr,zGOQƙ gG3#;g;|w6kdTyC8:9J;_>{7R2yl}(>d=kD+KM#ClȨOJgu>cy\VʖdM2Nw3vLef&=ξg3ˊ{L%I LYeٴJ+f8G|o {|Y!Ȥ 2922[ הPʵI&62f G8=drLdrϚ?6ˑ}y$FC&Jddr<.+ A-']mIxA&w&IxA&w&IxA&w&IxA&w'*B>!ɝ]Jdɝi~`Xʴ1ޑ3~.Ի .2q&=ξgF& Sow0)ed8;:p5xY;Rg_3"0XY_2[gV-˭9 @TW5`WY⍌g_3<"S~PJٗ[sɱ,iA jk =T)8'gC&G$X>_-gM! Gհ`qi[po2sA, n"y?^gxA&5[ W.. ה.\VÄ9}ꓟ9XZۙ˴:/(q)6[#f#232VjQ\|7ͼ Jy JDf p`/Rc/fg_DNntY:FF&;f83p&2.06ȤGOiuI+Jk@LC&|XgfrdҟA2qefD&!>83<8%2 l  cdҟ3 f/IvI6;VȤ?;/y/>ꌙkxy3;2tA~+޼I&w|.3/MGC&}ȯL/2G/#;??ŏf+>[ #v3sF!Lv->y&+o|le)lC|sce sy,)Sd_]=g2\pDE73Q\%LYiQPA<69fN\LLv2A*_[DRLLv'^Gn3)Fi!(L:B&_W:9<7QJ_dGV^" BW:_^3M4 F^zLbC*3Ro;Z {0X"ROz2m>2*ۅiY,X CjXȔf3Z0nd4l)+R=|؀,^ܢr0عA&H52Qn&SjL֬<9_od 6R1ߣ2n2ixgV@asLPjdTJLjxޞΪ晼ko?\Fxk;A&@&"gRLF&J;A*ͭ\3Y˟q&7248=[ #eҠx)d{CMJ}LwGZ2<ML>G5T&5ޤfnxn52-ey]hWL6b !P3{<7ȤLvPי,~c&o 2 4ԹA&52TDK8>B&7!:7Ȥ5LFd2Ȥ 2d3z| >!DAZIA,PϜ 3sk[fmd ^RCxA0bf=<2u#y. [?6x.h 3cFz  r$}RZST*f~X83fJ"Qe,idX V1SKFLF$=`1̓33V*M #LFn<83<8lhe,-$S0.A&Q3^A&Q/z3͎{B&xoϷ c̼q02(M\<{ IYG~s<2󻟵oH#3ϛ}bϙ=f#dROf3YAqfD8w>3!ެ d.Y)W.(|f>"ye sy,9Gx^Z,d.*M3,(93)\\%Lg#dT J%1xz@&N/tLn+הDi뙥V>Ύf&O[=] FFJˊŇepmfFFsf{^OQmd$^J5%_YάH~Oj77i ҫ_;j#K~q&>o@&=zߒ<$UJoZY\Ϭ WJ8TĠꙔjd|LD^zyꝈTwfFF"/=ξfƌT#XfRQSV2|=ј3g2z͌FFzT̤R#&dTyCi;|wfFF7~SjdTJL62<.+~gޚɗeL RT&onȨM֔xe;sA#S7).kf3tJIFFg-dLn2ԹĠx)ZdroL5<ΌL>Ǭ'ɕZ +3Y@&JfdK>i'G>w|84G2eidɡ3 5Fי,hdc&6.RhF2iL(kd( Hdr lҥMJ38LٚxiT^ dL&7{`v7~i23w}cL72e͗f6~ L 5)eid&dߙfrkf29SF2wf2P5id <.+~g&+ un7&PKWwϏL 6:7~5eO&E"3AZ܀[Dd2yed\ `X0ا٬ahfS`~CYy+?ywfL{}<82y &,K=v=A)o%#̃#s{ Rp5ڙjd8yp|)d0)˭9E)3R֜k摑9R6sA, n"y?^fj)e2x_2y l敋'•igq敱Z8w>ȔJȠɠPH[gR3'^JFLRș]m9_"Օeiw,g=RT ddɨ2]`e\ 6bfTKFLF$}2{.#f3cX2d5Iy.f 3c d&'`f]03vLʢľ} Rhld$jy\YaULKê>83[>8<")Y vqfF&!333 \ YvqfxG&ݻNއa3Vg\ct70&<{%Ό%<;ȕyc>2g|TW~ѻoOyπ9q殮=yߚ;__{|x5zI~V^B5;+\IW2ǒ|\ !Wy[#Vpq҅jp `.mn9sA s)35 7yUe q031xJ+&RL n+]g3;} dbPJLJ52&~}&zOiDAmqfGנ/RLTcIFF>A>x*-+`VifFFyf_נ#⍌JIFF{=TH*U]#Px)g3QjdTJL62 3>>`~y͠AW̾AGT䡝az9$u}j-CRt}=E;A*4hd3il"B"ͭd"ҫqf_נ#KI&`462ky0Qƙ g3;}!1kd3ilTdv!i0*o(g31kd$[^29[ /e%?L =ۚr_]t]gV8I&`TVJg2Tq8kБFk,e;%&+dr 3!d2Gr7d2^Mwf_נ#5ߘ$WIߥLD&m#j.d2ՑL6D/o?W ♷f2]8ɕ䒚+.,v?6D=.guJdL f7&lx}L6\C]Ȝݤv BנE& f2 !E?x`v7Jgn7ſ4#<a/QK}&kIdOoSa/Q?0Cg3!/QK!\L J)0ߥLD&mI%dgKRH&W"6Ȥ~3 "?@AZ9gu :iB& xD.d\dT{&a)wszytןPoV0q8kR62~?yk|PyPVJF~Ǚ}]9R|2+4v!\g3;}1(x#Lܲy2+{}4H T##̾AG3Ƞɠ\ʽ^WK=Kj9Rܚ{=tD2JK*U>UWCu0R-x##̾AGJȠɠPݟ^z {GQ/I)rkq8kR52d2ȕrK*MgP;a83K22p3gvt "UJ@F*c9%uZ>ZnIR&qfGנ/4hddTˑOp\M3,7 bipyq8kНX2d2UXP3+_g5X X gx=he,-$*~ɺY!*>o4dqgf$|UL8ŇtGfgLy*T21(z&pZ)_ O~99gin3ugypRL>jfR'R8[;ܥ?M&qlˇ'odm{L*52lP~F\shv9sϙ ;:5yLJ~]J' IgT"K )4sϙqr162CʃZ[3d&3h~ p[J;Am2iȈxDܙ+3Z\q.mFF @&ѱx.Q\ #3wɝJ3L>Ǭ񎩮jfLdJ<8FFlNN2Xk+gd3nS&_S 73Vr]Ag8GNV3Wf23nɂ4LG6 kgd0Yİ3LRɸ^rBTӰifLE932_z-2ΒwgoʛM3d2,eQp-wdL"ɕ5?J&O!x5:T&̂L L"\ &C͌Fi[&Ci)ϥLn2_#<28wRH&7jf){q)$ 53!q)$ 53!p Ka39qD&kS$*ycnrfF -Mu D K!d9RrǥLn2HJ)@&K!d9RrǥLn2Ro&9_w@ !+ 83Lj42e2(Sv<.drgFN2Jo?~"4YĘ3cFJ ڙ dM3i6agƌx) 2Jə:˛f>nqH,2Uƒt467 3/af2 b9:,P%5f>3㈂X2d2P] Afaf2dXIQz/1sDWôu3c/$|Z-o903#p#vFef:Ǚ'Z `0_نǙ?ſpdfgF$El```Q/%IHe;{=]B9>!*>o& dB)`*R=RL%&D5odD))FFzT̤R##J (52R*V&U$x@&QJpD;A*4hdD)FF$ud9f(%tάl)g2?L@f%?ڑOd:Lu̿ބR@ `);d12 ݪd12y nS1J7$t{ag{c|o2 ȑ$+=g2$NY)|oRnOD&O!-2y )d#I@LAZu Y)i$`VJF2 0@& JI#R72$_3Ƞɠ\J2 _&Z)i$8TJFLRHpMz  r$RT ddɨ24v2 bI ` bi:XHXZ2jI\ ,",",",",",",",",",",",",",",",",",",",",",",",",:' !,",",",:y_z:$Nqg?|'duL#,",",",H endstream endobj 2627 0 obj << /Filter[/FlateDecode] /Length 1378 >> stream xWOo6 Sjɒlmo:`x^n$'DqbNHH*H%? Dfʓ$cII&߭"LIV%Ͽ"+s0a<yzXI)OCcj5?%??/9e"ɒUvZC\T*K6{l |_OB$Ea7ʑ޼ u.rW#n0H)- R֨ P ZKYؽ)T̀I=~7hk݋O.B] vń<#xxac\XA{n37Gzsh Lb0HyET z<9o4%[Nc"XDA Fnpf xp|ٛ)7Rg)ڿ?4c; u~siAPH57lEyCI4&0\BKvwE_,R/vxݧ ,F) Bhq]$oQ#z7uO_b:_9 ,qiA*Zx9FœrD%g<ÓujFi!?R>nO S<rZ5Q;=ĭM6O%x^d UG}[aNAXӛ&48<^^+H YYjVvCvppኛᲛ~ p?#?~.2 <[&afR),iv[B6}lK`ivGB}=@u *cLLb|X"Pg菘2N-\>+@zT\%݋0:W|3ٰ0]ɱgDTL@ڭ 6_K.MQtRQ;˪d*:vTCyyU+P'*Jж)SUҾtG`i7z endstream endobj 2628 0 obj << /F7 38 0 R /F15 112 0 R /F12 59 0 R /F6 35 0 R >> endobj 2629 0 obj << /Im74 2625 0 R >> endobj 2624 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2628 0 R /XObject 2629 0 R >> endobj 2632 0 obj << /Filter[/FlateDecode] /Length 2055 >> stream xڥXK6m*#Q%d3SMWrH6Yr THċHD~*erLDp;I*R7O>ѡ:<]_?Oo1LI4Fwq`35f6WQ;u:϶~I/Fxm*͙9D];z) uY8elxL@S ŷc}k_ 8 R!q2R5NANd5jRȮKh^b4z;ضkl KbQΰB5n3LRDMX$ά ~o *QQWTt16Ug_lJi;[ w7pK$ bxr(-"m*N2 [^ }lɥNHҡ!d`;f(73|T2|ۺnq[-24Gwүv00v쪑Vv4۶i` Q? hN4l¥Kh~1A^Q;$xYJ"4@6mpB뚺g,U\`MOKl̓`_y; tcMgng*׺vH<[SDDV>9󢷫 cbfӐKmKdCFFk~QoI@qӶC]e}:/Ԟ"ghD*փaU1[_+)K-lg5y^RJ̄JVA}nhKbA1Z4hY|t %qwܾ)S`0;<艢i%8Ʈs5 Ov1D,TtW\"|NqB8f'U.b &|SYn>Yr5@44iQE WWЋO\ٔT'UYtY-A=|ŪC0 wCK͆ג%RYx%Y~ !H2főv(nQ1;ܶ\j~"W3Gj-Gܕ*W~QLE)(_zUiěĉ8I_bڵ ԺxͦZ$lhn1+ P_^;ǯQKw`͡O&G킚%>0|8I ,/:̏5d^LJ P`,1+o,yk98~zA)F6 K(jX2xeywҤB~@)ȹt[4sl;4klm ܣ/ 719K+Hffs Ǖ8R% ESs'=J1rVC/aE. f\~dAE^Q:?59 endstream endobj 2633 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F16 138 0 R /F6 35 0 R /F22 218 0 R /F8 41 0 R >> endobj 2631 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2633 0 R >> endobj 2636 0 obj << /Filter[/FlateDecode] /Length 2385 >> stream xڽ[K8P{qķ9  Fزa@~efKtO.q$E&JPDãH$(Q8JHey懟eLD7,S My\ǔmUMO뻛QQӖI>lmU/X/75c*|&r~S*zb nN bh~9$"Z61vPsֽTac&U맗әN&>JRZ(,d {xʢ"( Kz>VMi>i6U7.:zt64B^SS՟ Uq_+AԠ6?(2BLJYp)i9ta0G"&B%QڽI_7`hqGe>ẹ M+(aZJP ;W$<)J{}qzjEc%[)ger q P6tvAy~Gif3 .ּDVucJSn@*LkAB(.&Ȉb#9:0oT1U 1'||D~g[h~FiɅ썥0(>0 s|m]m@b额T'^;4/^| OQ<,5%8O6?r)>Z#ILx;CYjB}rIm٫K]cIgԽ6ub"LjKhwqOi[/G{7WOk ~fLYB#kZp24sO#-F12;D9mt*U1:r8 ?nKOʋ^17l4Ν,%(JOK˝*zӇ}si{5B:D%Z6J _lֈ[6T"ҧ5fRk$pzޑn٤J|~VL^0RLmtik;q;gԃ@8½C;Y--"+ kTnʝYj9 2^fS~g-%;onې2X'{`0<:^,1ӣg/@_ml+js~=KO賴* q.x:亀tHiNj# q P;^vFy{4RPfԎuЎ<(Qh!la/DTB1\cm PN&q$:mO) /KءI:99Ոj,I ]q希+N4"DT>wʭgxO8jǩKѿrmy :áwXIC}a|mU"Ni|Xy=(ݐMϦ+glQt g^9r—mdF$U#/dWɢ( a'j613;>Y'kM FLm@:S{;`iǜVP̹n"kF*EBw'2jy)wELd 3k1w#mzO|R\KuZvySlmF6'a~MC~T endstream endobj 2637 0 obj << /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 2635 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2637 0 R >> endobj 2640 0 obj << /Filter[/FlateDecode] /Length 1984 >> stream xYmo6 _~KZի%wȀ k 6`{5.%u&Oet/mWl"#x?Drd,I@&ۄ38~o_. 8Q%zh\n2!6b[ޜ|o !lo(wAʨ@"ORao۫"Ԙjy ҿf#r$DC؋qNÕ?C3NXwg :)f(U=D;Ar{ ʵCt3ꌹ*wNʐ`]C%™ܔQ]!)ix9Ts$IAu;DJOrbڔjTwMؘ+/\$IsҀ!Ta0PHմրnq2+W޽ ⷮؽObPFɃZYuO/h) -ҳEѮcA(FQΗ"w" >b'W[cٖ8 g2.Wj\B'ðn\<&tRH\xӘmP| *I~:ʷ\'d>}u}4\DZ{/j>L@fGӨ j ~K7/Ne 6 x\ Ô'NYa&\9)JFZG 'Qxh@? &CϷ׍Oa`/~sD$7f萋1juw=8Pt'NzBds$8 QDWS$P=cf{3T2^PڈEJ_ _:E^i?^ @A &N-֒V0%ZJ[ vZ?13ap@d}A4QtV)EfY@Nf[_v8@xi>w$S?!N(j|"c*T?#%93DbFC-YU hT`~jlBzՒA|xk:U',Ҋu:LK3zipnG6'R.y|Un˝k/[IQ1KjC i/>Yx/PEnX0zjh`vJ]Uma?zJC' FJTH%9%$ endstream endobj 2641 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F30 286 0 R /F20 182 0 R /F22 218 0 R /F23 221 0 R >> endobj 2639 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2641 0 R >> endobj 2644 0 obj << /Filter[/FlateDecode] /Length 2297 >> stream x[[o~`$I:U^X)k++y83rU̹_8.0¸XO"υĨfł i)8S-~.zeQZ7Ѻ/ ʙ]r W )_vZ׻_#E@9F. "̀cmS wOQ+ya&r~^oC=IЋϷ}\5f{|.U(>yhZKȂ\/{fݻuwB"Z*y~m1F*ȴZ)+BV++U*$mݶR/bvKU-D/C6"L}jyR*!V0H=sxh1smc#)!lm vmR6^ayT\[ˍ뛷!`kr EK>B^ vz["!TAr8{#o.Xب2h:a*) oF!O[ 4Sm<-wk@624,5;̫0&Xe4 v:oq9doqJŸ0@%|$U1P``G:uj68$˜ wq*q }i,iW0":pj&ʥ>j] 'f^@8^YsyjXf+Yvu#x>IRf9a;j-!U݋p:Em&ro/FVН&H@JlOcsWgwzxXPťTۄMdR^%fކ%Q]:UHjRN0*rB1` Q[ױ,Mb_6gm$uۇMo@w4Up%6QE@@&%Qnfdk,ŕI቎blWo*q {syxu;57C_2"6 []6 М>l*ssj| ̈́ZJB9ci+GC4 UAHRSU(΃:Q`ݜDsINLt-p"thUZ2ܔdGJTQjo0/I9R%L$u𘰕nz,٩1,!Lw*X4/ԬqOG;CdHumά&Le霷χGGSt9yۙ"Ս ʲ{W{pvi|ivwnW Ҁ~BnHx3e\#c 6ŞK*ϖ"  endstream endobj 2645 0 obj << /F7 38 0 R /F22 218 0 R /F30 286 0 R /F20 182 0 R /F23 221 0 R /F11 56 0 R >> endobj 2643 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2645 0 R >> endobj 2648 0 obj << /Filter[/FlateDecode] /Length 1747 >> stream xڭˮFt߯@W6HJmDɪR뭬νTpmcGjsΜ gΡ=Kɉ^8p}t B~ϫs'Ym{Lf2v+T'{U) ҙӳed?w!AE@dT1-%|mur1O]$s/Sl1Uu:P,/ss@發.t~wjLj|:TWc.^+tq8VEq%^erw!t|u,(/fZzΕ7Csl侸{pf6oykpݴnx+=P:x Nk,s?Shwv0^qgЭ79MwZa>iiw%3Ǫ,LSwNk,GRu˶R=e2:TP=rk:6??sTZfURKAew]^g'M~=[4PE Jơ6'qQuzTx`u%o}{`=VYM/^$7yfdǮu5WE.oްIŰ.Od}A0`_Adj{!"oCW]i <^DKα}%.Ϩo|y]J qJ (p2D(>$HsŐ%~s$(mQqU /tiÉP|̈́ (&Iֹjɀ @mm`'yEH3w\_Nt64+1%18UeLhj&Ŧ~ ΀ۃj>[z\1s^;P&QUÀ*U?7P7|Bǔ AT&/[E> :;Zra&35yp[S3+V(b1Чg\HX8PٽokEHr-QafرD"`iآ4&j ŁO ! <)?Ut]/|(M2TCxi'zX<UFդ*CEw8yP"IY2FO3dCK&J5O^1cT+\5t-F96b7{a|oLO2b@}م$VR;%r*28y@qTAʈ/(+@>"DVj)bkht,{`H~2aE.|=\؀O2 /;nUtu,@޷Jբ+Xb[ލSe꾟N i 7?uCl!= p !gF/a|׈Yp֓eS0t%S cٍXMKbeoeiJ٩2A0e0,uQCK00esn! AA)͔":SEv,ߟh2 %fDZE?H0i>HS atқtdhf@_# 6AxiEq|xIuXոe{6v Zub7 endstream endobj 2649 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F8 41 0 R >> endobj 2647 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2649 0 R >> endobj 2652 0 obj << /Filter[/FlateDecode] /Length 2845 >> stream xZݏ_K_Kthh^@y]HnIђ!9o(8|#Y gZmu¸\VZi]~^Ur{_։2*>l7[Z1fөvE_nO(L`W e1z]g;Ht+pS df]>o$AZ~87;ˬ]OPTnٟKtOC],O:ޢ-剕j.׻+;zEƚ,jz{'kʢ%_pt]>VMS5D\äP2z#\W*sss'B]u3*R֟<Zz jԙgv;|A:XꆫáWES]a+ocirjh >phh^ ځAl]N% )ttC'050kñM-d+ZTQaWOhq$ĆdATpgiE&I+5ITKG" b*>'7q+-+3LZ^ɬ ('Lu\6";WEM9(P4.uXWK /y8ֵYMU9C1 o䒐*2:np8Ɍq7O8MLc&)^OQ,Ytʰol aXS@Tt{vG'BD= 7@ zΡw+]*S!VŊ̧? °L=k)%Y=Z;h5Bq O0czuTE|NT,Qeͱ 1]O')>.'ɱ"19E-u!d@U.myH̭X\ɶ}CcN##Y5\I52 BCOETUN#BEi*6'{1N HpbݨQX/4j:a4K٘N.)e*x[{jcH*l$ d8\rwNzj+%ä45ݚOW8 Џ Lmd T M]2f8fϘwjWbSџ&ka𦕻b4KtAV)oY) 2<39q+b"ռ*;6f q*R7l|TJjQ5A/lEO~"ثc.yjpJ>#G%P8́)KOa"Lm"jvYn0/^}m54zڂD{AD5yJcxQq2SDu\㋾OtS(pY٪$ō6,f`1 66)D͘긍QSr;>lH.?.vL/`5#ywP_\V/m8}UWj}([i3C M͎ܶDDM+ f1a<<l~q.u=V.J&AD%D'>PX_́F<&-%]@=poDi>K?.T9G?*.jPDKIb꜎ɐ"-޻d.=(|v|tHK\NpFlMqBVK^78,6"񝕚5Qԁ+p/J1+ZylZ+%cwXqM4!K4iYv᡿/!"^'ѿԿ//ۢf{S_/k5ǿ-^͋wQch 2H^syBCF<,͡ʷ?/bۀj !VX> endobj 2651 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2653 0 R >> endobj 2656 0 obj << /Filter[/FlateDecode] /Length 1493 >> stream xڽY[o6~߯& !{HC-٦aeHJ;,ږO}<<:`1D&1JY0 #i BE9} Eixh~rv)G,JnIT򙟍ϿcbEV" 1 #"԰B4fG5Z%0h#D%BJiB*M6)(htf}l) YTjv0" ̋A)ߞ֢26分;kSTC3o|3鐙^}IP ph/Xw$a.ì}oa+\i*z]WXE1 ^?qx-Ak.{RXr¡6,+gt떆^#r =(JCWסS6>t~EQ>7:j,8pLqLC!'*bMn1g:͋{ > \EVDqQ#"( 4M?BD#IXw;a}o=|y@]Cfʸe~5#p HHr01‰jYT)WNF7MEww=;dbi\=]kfx0ԙ*u&} ,Ӭ:u,[5;^w&]D}im"S[5%A2Wjgu.f"W馮 ݎcSw/damhxu QuY]̸>];o7F+wKhٖ[ fTqu $weN؆:A([ C$6l[iN*[* ZpDO'#чGaq*[d&XVRV?dGprCg>Hq%DBYj-$+%2Hȵdw㸊l*bx&6kSj فpHvDQW*:pǕIGRʈfDZf7J>s6R= P>WŃRsL*^:Fa⍎|ܛXz2^8kjL֍11̓pzK >&uH~;71)Y{ݚrǦ\w/z"ΚUOphYml=|Ua~Ү?N"cS+R*fwZ "6K?gmM endstream endobj 2657 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R >> endobj 2655 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2657 0 R >> endobj 2660 0 obj << /Filter[/FlateDecode] /Length 1035 >> stream xW[6~pߘDjRlgAꏯ $s3A#@ P2OQgzx& L8~pB('oyzie )' ^B!Dr.-ٽ? A 6Ä*θv!',e;~1 1 ~ϲ5{ȕ51_0Qf-& q B,Ǯm.]Mt- /ų<§n?g3>1&1?*OCJ'5 zĞX熶˅Qq6paSKu:5Vt"(`fI1"n,}bx=U4F4[^ m˶mgyUdSaI/'0FlUf)D:.( lnI=-0d b)lT/YsQ[g=1YͪCtXԟW( 3(k@mUY7bAe^:$SFޒSXOnR_=D^  BW2m ȌN!Q>3/a4,g_E>մe -c`0ߡqjbږ%W&w^`b-61` 'Uc?茁[xW} * >? endstream endobj 2661 0 obj << /F7 38 0 R /F20 182 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R >> endobj 2659 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2661 0 R >> endobj 2664 0 obj << /Filter[/FlateDecode] /Length 1678 >> stream xYKoFW9ɉi{@})"eJJKI6}r(Yv曙U2O!LbdXs028]fg~$83j}O5/'W 5={דO׷U. !2 f:ř*tSs,d:lzN CqU,Q+X3@gI0H gI #y" Vw`?O;j? ?Aę-:{+y#jD[OK Hpxc[ЗH1/ZԵ9U 1y6N퉷6Uh 7 .A]4,Xu$'M>Uj^yY̪!%͝Z"qMdÜzu1_KmR8l M.*}y&-\;"r\rJa[WӾЩrV<:}X4i ԭ5jyۤJg~heQZHnW:n퇕m97̚!4*6Nعw<0G׊.;Z@G@ԺbS./{ڒLXMm_=w7Xلi}YۃbS@v7UU`](POceH]~I1U3 樄$NXSKOtI0Rݍ (#nkhA,AxGL$SU?nu63l,:ȑ5!$\~jgEYZ⹒HB}h%Fu0mSdFf{Կ DlkdM mm{Lhobj_\KZp뢮`ěnp@ݑ̉Ij/@߃$D$ܟF.ub@2u9*A0K-USUUa7DD aZGc֦dFfDMԺP8+_pAas4.@?{Y` ^[͋/fYY<ȮA먜cc]0,ؙ l!aVHRm7F`uTeyH!n@7yBusRHL>OU<]N'=G ws㷼{z|gs3YNw]N.'ոW?h* '\_,"QEkhc231Vf>^ x9ӠD]3e`聠1ݿ2 endstream endobj 2665 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F30 286 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R >> endobj 2663 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2665 0 R >> endobj 2668 0 obj << /Filter[/FlateDecode] /Length 1858 >> stream xYKoFW7)7& 6 k}) e*K% 9,YN>r?7eF mf/ᢲLSR,PeRH"y\|Wdtv9ogWoFJqΥP|b8BXart1ε֣_r6/7/qD P OFb¯WcvfϿ µi*&'<1:QM;~(cb6awZJe9b8W\z!x ! X-=4ɵ"eު3wlVE(|X`sJ(SqBʏCFGEIto'D@zJjR\\DK "}ze8_]!2+Iש-toaۂuD l415tXvno"{ S[I%m-8zc"c|^6eHr!4˸V(2 i REXABwmܲEv52jkR\Ǒs(B3|#{ rE DJ!>MI_3 Mi"PlrSr>( Ri X,!b)"Vb=%9ny o\B-2$\MJDeQD'ʫ$hR1|>mWY@pGz30w2{0 3݅W7Btf+/}|ǟm6 ^SozW`1{/<\`1m + ¾~WinvK.׷=>s@ `>#O Q AXdOI,QwA݂ A2A5CR`0<&jD 1Ca2P۪&"OY7}qӫ \pd`f J68Љ8 gB@-;x8MxNO"4^0T>_-1Я<6>{ 9Y]ᨮۡޑ ڡJOj4^6qU]̽0i;טj:pv =m_70kxZ"(y6k`hF  K!8;,- I=fHBhP5PmI*A q*"{"oM3nI0}yWxLztۧ4*Ӫ4 =x >c!:i-=uc]C~Q'懢r߿@_M|-R>TvX' e'噊Sa}N0 _v,Zg}Vb>2Tm69_< endstream endobj 2669 0 obj << /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R >> endobj 2667 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2669 0 R >> endobj 2672 0 obj << /Filter[/FlateDecode] /Length 1876 >> stream xڵYKsFWd[Vv*ڸ*(Hd[0c{F<4B@P!gLA~Ἳ='ozPt1 1-p1ޥ|(9N(w\DͪmZUH!Dy,y!!")@!1B,gi^#b/z=7`b`|,XHCâ(-jҢ$ҁ K P>|RD~bu]/̭+&SE|^].=_Η%Vb]YF2?eI*Fpp0˧0!@UpZ!mSl !l١l3*s*,hD ~J€b yҭ_ǩkChˁa \ ޶h!}hjK,ʒOsnY^$lvPfI҃!ԽJVqv5nc3U<}8\ilXoV4ėWHeǨ8}9XCf_SMFN$ K=K4F | ޺?mɐxP>}--2$d _Gqy(y =#Ko-|[k,mcwDFF_SV}`8b<*r@>T}[nXP$MxEȲ9<{C||KBZ[ O]V:b=h׺nө;n ee9/C2 x.~; gD0җ~(?8 A TҀv=3Iq@@o<#xL# n/TF8 c-',;:&G1!f7*޸,x"Fa3/a] JaÃDc#eJ%V;M%K2|}1H/ĩd)w:i~:pkGj m Clt5cOo* [Tl6UZRu4 ÅWo~d%@썑E2|ʻ&)|Be! :3r@ljbݱơ z6DgU-(u6Yv~'/6w=`|ЌB#-ЧW+EӋ"h`!6eJ%yEb\˝>g [80 %ۧ9rl+^&|A]ϧu~=.fә\eXNNyΓc2s|ӛ6cm9֟Eq-˴>y6_uQV9+or_EzYXe w/RK\3 j>,hJhTbeK:H4xdH%|ZX#-L0ƀ ZBU?ep, X-%R%rtN@g)KoJ?KA`U Q0KA8,tF DZ6Y]f[ jgiV (A4KCpx , ,N7L k2mlk=8SKج#' endstream endobj 2673 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R >> endobj 2671 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2673 0 R >> endobj 2676 0 obj << /Filter[/FlateDecode] /Length 1789 >> stream xڵY[o6~߯ Ӊo$kuk X1ty CX,ҴCER"EEIV@ "ύs( k{n(D)QH(CߥW4.wtH],('e@)q,?_Qeڢ?yEB/QB1F) {&Е 8aݮˢʛm]1.$uv|TE pClx]LǺ)&rY&Q(/նS۽Xf_}˯Qy5Z#ԗFZq[v_()]6rXbwexj'8iKRyp#b♳aC>a8&0%tahՁ\nFFI~t`R4vD0h&-]$E P#0/ҐNj8ѦR68 aұLP&I_>eG5270a"0<Y aG#i'sS NXgrsO9fr6TO^6v)CSPH슲kNA%8$Gc oZ7.a!2cf#tjyFC9#PLjd# I $)F F{ qb(†^zkvN~79`V:H @abB EC"ʹT&|0ԈDTY c2 cFi" pWioj+[MPMap)%f*a=wY\zI0Zx9% xꭼAu#heYr(z~Gtc%g \k.nN]M}&y8|\ xm=m~D}}6+4@YoBP%U|;gMA+ezyVy O^&s,Uj@@ hR7aR WaT];R.rjA-dJ d`:=d= Ptg+ +*ەrJKIwTen뵪K=wh%CvͼY#Hco:⣢`[CѪuG֊.Yq 鮁j-AVK^@.VoXawZ;lFkɝH.FYljF3 L7uEdԺS\b0)H8}]A?nlWf[R+O8 #:Y)D4p eSF8.LHB θI()n"F _ j~cZW> endobj 2675 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2677 0 R >> endobj 2680 0 obj << /Filter[/FlateDecode] /Length 2322 >> stream xZKoF`.<M&= v3@@q0ȔE [ YdZ'AvOJ(4=Xls4inwBDzn*/k1c0FQ=C,/s)C`皨<[hD@Ne]ry[wU1g *}]MXW"%eOK']q}>DXaz:KZ^sАHl_gQWqb42mfe0=t `^%Ep J"4\Jc3463N L8)me Q=?{Âi/]U`\[" L ?J~ݏ }q U9ۏ5˫ Y|dǢ̒M:CGVaDD'b,6w!L۰wA‹(Ge,D6Hrr,+z>|Zݧ;K'ݺnY4 >j ~>t&ُϫY}ExVѧc ~ tV$/Ms }C(^@' t?|4zSnrWԫ.1J((H Qb_&-\BKD-䴶P!,-\;Yn9[#e)j`e%T@ Zw/ ֊!t`Ns &: 'AI'!gys"oj)h_619uAΔLcHmeۤ.h8vFKӉJM;S ^UJe*!|=7%v vREI.l<{P`2lЎ0hjgte0 LXAt<䄨SªV FiZ1U;ܪV 0ɹưi*{9w}ӌjlݾO+C 1kVJ"'JY Q=XLx S=/V$uEI"RנI}I8OUs6& )TU&P!h̉Af'DU44qUwM XϟUpVnp;ïFt4LwgU!΂ &%ybk4Hbsmu#65=LcCV_$ 1R3FU6~I~ga[DR=tǽ(s5E^0f؆JBK*;fT ]u7oN{?p1 GWþ}=Ӣu"ˡjijnU)6܌B3m 8FzLPmd5 :wGo ;&%䲁ժ SQmͶ,C$tCi$qf be o.Xa(&S[M:o.rٹpb9{bsn~#jFs*qӬy-m"!h9"qb"XOkrKsAB [O倩X7*q+7_͎`b4 @O1%Wou|;,,d&Ad Ihܗ)el&W?<￟'&\8Vޢ|;G2bZV en$c?4HG%P6v+U#aǏSR6(.zCF9ޙ$٣}nUq:pLu=GԙJF'̮)9ދR֥LGtid(.0<`5=TXiGϺVr;XxZ_Z `Ju (z?u<]LB&3z,5k'x0sB*Y k Sf*d8KI;c$# zD TY)۵Y!jls(d>m%vDEs읿5/Safj(1F^\Ag%fpGÃ][x endstream endobj 2681 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F20 182 0 R /F23 221 0 R /F11 56 0 R /F30 286 0 R >> endobj 2679 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2681 0 R >> endobj 2684 0 obj << /Filter[/FlateDecode] /Length 2179 >> stream xڽZ[oV~_!롐J2Fh4mnou02mqW7Hts#zD 11%ZۺMg MΔRe5/jO,e)p!<"HJG 7ʩyzt&j^/G5]m>&2|=upMre+*Yfg_RM 45[=k%on{OY:/mUzvNG!omAeY{GxJt.iNKd澫˗檨,#L'DrCU/pC\OB,<"͞ ]IvI6멏޷ tRLR/HƻAԗ3^68.m̌QrOoFC`:qU򂾔i|9(0Goo [Tr"AR6ۺ:̖Z]"ۺ%2P̈ʱղ,TMdUBA^ˑ1N5Mem[clwպJ3 2׮kUZp.ڙҶ&@q HM+KIʰ`9"N} k/85ˈE\24 DC~Yn="E]הzs$RW5ULM^+t(%thJ:,,9aHVd՜, È'eQ,P B3g%>ׂ3 cEI+y`\kD4>1qMHS IQ4lĿn>1]8S)e c]:f,Iu79UPO"'vQ^##B+Tb슇~w骡 JYB^Z t)p̰pW/0G\ 0 Sξ!xU}ԗLQBHuʍßG cN뿡^e49; 'e[tfkjB43 }[bK"Nnw]">ƃMEF(`%z 5m N@ҐSCm89|Cu[RMLfcp <ҚCV ׺̌-'!tPڏ@euA}~\aA&o4RDo5b| DDZ }f<31؂pȉyu/؛`qUlvx҃MK1hcPΈ4O>'z>M]b_Xח/c]EKo 9Bi*Q?!^Ф0z:) r(әl` }P=>2F`G y! endstream endobj 2685 0 obj << /F7 38 0 R /F20 182 0 R /F22 218 0 R /F23 221 0 R /F30 286 0 R /F11 56 0 R >> endobj 2683 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2685 0 R >> endobj 2688 0 obj << /Filter[/FlateDecode] /Length 1855 >> stream xZKo6W9nňOImw.@On9֑RKߡHQDr}Er|ÙQEQp4>Ji2"2H{ EW}H\z_^dz_6C'󻬺  jq8!A_)N0i~/+ɋ8=323bW4-lFsaPM\{&~TT¨F1A)g$.CQz&bo`ck gE|z7b8VÙg-MX:Pj؅]׿7h:I9:oh% 2J8ik , [" TI kV*W*]O8=~%;f;njH/~i-rŭDƇ(%9^淙[Vz#x4{0-̪eyYis5#7COP7QTa*m-zF+ײ4sb J7Jg}RʣQIsan;i7k.MU-m/#1*h|BS$(T AhKSfOct;s'JBj%ɹ} fy9X(^+5^% =_yq/uV=o:aM),!*o~ F8[xXqXy3Mʃ|H]{# _Q)zNF!R>Qb6(m&R6Y7`ah̩ rD38Z^4\T1:+jnmĦ10m`7޸-Z*pBkՒlrGO p`ALw&@^1oef M(K,0^)NHFeI 9Nio ;3 >Vb[RFӇ֖N/N2[k$NQkq\x>x8n(42MU5)#gf)MK=>g]e/s9 } a~XΣ)||w,]Z /vP pm4~݀x|~IoMYPeQ% ڢH9s=?:X`ڇNe{wwf{j޺u8GnA&(RՔ'u*A/_@ d.opn~H'a#.;D}(@,b ՇŮۢYcU/t!r (/m[RvJlj~LH&ǙTciGX<Vwwz>/ۼ87VmrcE7WfϽ?ZbZ۪m@EY?W#_bBbYi*y> RTްҪPCNJS!Descݲ,e/μ]wXf­3W endstream endobj 2689 0 obj << /F10 53 0 R /F7 38 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R >> endobj 2687 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2689 0 R >> endobj 2692 0 obj << /Filter[/FlateDecode] /Length 1701 >> stream xY[o6~߯d+^E6v]m7aQcu΂;IntyyDw.L,K.sh&̮r]l]?L dBsE@ۋr).c_@(f*tF!NcF[55 ğhcY}\J )8 *8aU^ ]ogŢΪ-fhy_,T`F}hJeOxDź8tHHaA4ahD?u8O"9F?Or '9;F)d#Q  z0EVhŢo&b~v`% Ex7pGK+^Ax1( 0Y(Ԁy,o0{0`~_^u\:ŐX2R j@B4 : n<2d2<B1kGgءJC!Fy]Ϳd}ly(ʈ(#ANDy8$zK #qH_jx|yؑ)$+U{ Än]ܫAX8e8Re0 2> ;(N%xuL,&H#6 {,ٽwf-$; T# ٯmn^C7W@R3#dzi`@vΖմ 6!fJ@-jp{jJ/広բp &r}:Ͷ@>?W@ml^@461n]Āޙb0{m{x6wN'|K-/mEh{Ûպ;Ce 0gP]HC D@%`i(kgEI9`BRA1Er}^ Ճj;IPѥfYAױnOo&3"t1hx`Wֺ/ݗD&PNBD;T^X:[BWgh(8T牾s:5E^},}a(d 7ԃ- h/]|3if2oJDLEk/\bF׮,Λe|/ھԽ8nO<:v.tw8v_I*H`Frk33 ;LԔ2I\#4➪#Aoj>)$L!"Za31&Zf7DxmЏ x aoJiBjZ%#g%eoI >@q-S箾qn~~> endobj 2691 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2693 0 R >> endobj 2696 0 obj << /Filter[/FlateDecode] /Length 943 >> stream xV]8}_ xJێvVmڮ'XK 5f_@ҒK{mxq߃ yC {/[9YxvVXş%y\=/dKW|P94, b31QSv_ \8|ld| B2Xo?ش>mu6"%6`?k7+iA -PZ1XvoƀVCk@kH-L>"z7 @[tAO6Ո @zM8k V1UVm~y1F)\Z:V2r('E ^|, %~4^+2 c >rʅx5/ U ,IǶWP#AFTa6(^< dlC(\ Dxs} >ܙXQ0knu[ ݮO;VL!}<q, <|7A\  L&ޏ̶ےhdWIX[mN AkpN60s> endobj 2695 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2697 0 R >> endobj 2700 0 obj << /Filter[/FlateDecode] /Length 1313 >> stream xڭXn6}W\EMbQE0tEkg3C;qe=q_f'BwfOpH>,L >q?Ee1RJ]'so&Sι{I:R5w;9aCš!LKD2w 'q*|vc!)P):K弐9j;\x_?%ciyTͯ|,,9l,X8(x_.yNz}!z8R!ު.$C*ȥqd;=[uV.]zm OB~'b@Q tu#4Pr/:?oG j?r' Ad,q:UL]}i\G!N/>oNs3wæ_^)u*سQŔ"n$G[1oڅj[fBH^&";m0|BfIH] #SX?&#tXa=X VS24އAC|0У<2  lZ4Eleސ:fj;}jYċT- R٭,VeB( 9b Ca69TzڬsWū r1:??5G,.~BV.a)b5e&m ]kdJ}G_Mِ%RF{>> BzS{Ecqv,@^; usTYO(aѥQ5bS_D/55 oiߏ,!ZR1VS9 W^ aZ&4C(A;vNcpAA>bJC׮v+ݍ4"I` C>8PlQ:RZm j.R3`6MjrpP $~A8m 5ŎCrO$}8ܒבĂ.Ik+*)/^#9n|-R:C6 {z[# l!300+wE"RZq:}V^q2>Ļ_%rj‘^{{ )nc|7f4}R=`gӺG~!xXkoc/=uPN=ޛ_C4$ endstream endobj 2701 0 obj << /F7 38 0 R /F20 182 0 R /F16 138 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R >> endobj 2699 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2701 0 R >> endobj 2704 0 obj << /Filter[/FlateDecode] /Length 2594 >> stream xڍY[6~_at"/a%.MM[4^cŐ8~FY/p8|3Y*  `HE2^<& Y$qſV߾Elz#? Vnysmm"xg;,۷T.Bږ&%pQc*:QQLlM|Lu@~æd<[7ޡ1pc< ߽$a*X$brRkO MӲ uИ΂gKǁX"mCVltl哋,b>]K7+bD)r>FQ!R%qIJƸݵ:SŋQt}&*f/|Q'HMw6F%֪L^6&Cp bAL:SKe6͂vi?n;ŷ`Xpʣ?mnƖ3ueړ},p^̠/<&uBn2E†ޔCSdp HuHs2j^Bт$a@{ S}u&%y"W*$$3j\[OapV5*QXr0( s$̂ka_-:w^6E/~tGcF5> P3. )1u;ZʂcWN{!GFGڰ;)q1LhEKDWע d`uȃv$7+L ӒAcD'x- 'NU~Y-4\kquTru8,ߙnF ')9jWX3Z4Xx蒐iT``zpػfj we  ںV2,P *sa_ׇ\&Mxh|k'(z##<3=u=+ą'o }G2yoįԻq:RE9'z>0a0";-$oq P_'ПZ_)L,uLUo_fD]K[kv+ʁ7tKDt!P1'949&,7+Ž7 |i?i|/o~7?^ Ƙ{gP>!ʅlXB|W@,JORXei:ݖ1Gkj$W!@-HM_}fTA{!}E` eh뎽s4368_mԴHAHh}-U2w"W_Y^>2HPUbD3UldńT\ oKH<q+[wZB衅DwBBmc)b@ m oZ_Plð=fj&JL\T5(̽ifJQ~Q+!l/s"ۊWy`I.8馚GgbNfxzzkGa-!{v. f.NTQ8H`^Ҙ^!w1$M̮uMn y58_ġ&!G\qt.(2xF9kn7T~g`B74 iLsj |>WUT$*{2ױ}VrPr{B3V|6ocL#LYSU1&.5\$mB$k[6HH 5?pDpPŘ'& 4w-ΡQm=Yqd2J1p4<\6ԯd3`E;Ⱦ52ӰXR,)@tk+%$6߰:"Jv9f}>X/#摥x%HaσيS>#u<{kíMkPy(`0za!!*Ky7+Si][~:F0 )X &w ux HqznbBxL-/m2 Ï08L0Bd'EiBp\ Ώ,]­ȶwwk!=S]O=qzQ 3pX'@E!7GW ?2܈ZAg_s ?R~cy'1 |z;o򜦢n0DP22Y\?k`+d7Jnȓcͫm Vv3Ը$> P;cmwze񥇐u$A[HLK̩X`N29 39y|a?t)jcCiT)8 fu8pƙ m4O~8[E0~seQ*˨ z&q_ =N!(?a2Jȥ֌fU1OJlÆB58ppFD4#l=0؂#1SՎ~Hx9I"=291LX{[y$ ㋂WQ]SӲ?dxT K(@ܮq陓HȤ nAǷWt endstream endobj 2705 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F13 66 0 R >> endobj 2703 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2705 0 R >> endobj 2708 0 obj << /Filter[/FlateDecode] /Length 2626 >> stream xuY[~ Zi)Hnhk3+K %wnyx!Ùof>W}~O}PD]{:(b߫HWoybd٩8J6(ax٥iu-`ۦ 0Elv>ZN؎_pZ6ڞ4Fo+7*[xt~Eϯ웩ȾnG({8 i-9DEBqT8 'ؾr[3IrI3<0سa>8RmP38Bfd*42UsmNƸXj\{tp}uͭiч0)]aiM+=ue?;rUxBgE9҉x<3gm,"W1C2l<}ڰFf T!3y|=ac[z)Fm~n@ak]w= wixgiSȴ3v۔^Fs( uX]* fWm*1T[ni03 bD!q;}ġ(;* ( Y-gsTfj{t}XS |ykN\4{ڱ=InthdF7_B(ZL?+,6h-+z@CKur p-Nd0–X?wj; |n(7Ғ-f]mb-9o#E Mlb>؊Z=h\dX~-ue/]jRiƟ*EϪ,MlQ:K% d5̡m+[_5x31 GcF…'A 8BBRx+X~j6$]nAh|"oFuwxAaY[?lyJq.J A9dM> li"DPT*BPa&ժPA# RNprxRōp/t`8׷8Q6o;}ot$SqD93bb.@iS x60hh`'$Tq0{-x_]1aJ)ԏP4ꪼ,a @Evb(dJX&`"70 Dt}l(̋[+hlPrʲlH91N9ju~jp(̾г;l93:Tփ+>tI69}y*䯕 Jlp#nN!iGf$Iﲔs;+d-a.t>Ġ;p^Y0ĪIN@"t8@/H.$aG؈n BO1d;$Ċ*W*xw B= h{+UX`Vf?rN<8> endobj 2707 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2709 0 R >> endobj 2712 0 obj << /Filter[/FlateDecode] /Length 2356 >> stream xڽM4"{XX_xb ** Oxp̼T[-rc.jI*`AzXwѫUD|Y V)O|%~L$o~ޭKiKz?uwkͽk׿wѰ[i N9p@-/Ҡ}Wyp|,ov9X76H饇CYlS򧵈k]x>O] ]2kxa[@bZ:4ZSw869or{,-Pm4:Y|)3U6b" Ig6$wdѴ5hIGi"<- #lb,2 G"2{ 3 I(<(ͲyvV B"R5`Umd/JΚZ@Z s8˛t5&R*' *ތbcFߧCJ7wؾ!`J1+X[F7?`5Ͼn)ejڀ8T9YAqCv_%!]D@61 Tp- MaCKƞ-D=55aC@zv ơtPlZH@Dʭw v1"W 08rb73P4th}uPf+k$mOH5x2<؈GEqyk8OB~$vjF1g!):A3CA봀ތh,x1<;9BN2ϖX]p]e.F?3@ѐ /zS9_C)(9?$p2[cʢ6҉`NZ2]gpn:#p \tlEv٫)[[j)SCXCI2@M{iM|eܫ[lD&p~ʼ'qbE]uصƗ[7?V?"?fL4SW$lX|Iv3ޗsr wW^6~}Ni( ?.jrmxZ_Уc +]&g򵊜=aa'1Zk''N;&r0#BKQ5>Xgs+C;-}8ŷ_MuVs!#BcUWT`a 'I endstream endobj 2713 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R >> endobj 2711 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2713 0 R >> endobj 2716 0 obj << /Length 484 /Filter/FlateDecode /Name/Im75 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2717 0 R >> >> >> stream xTn0}WVJ=`^+E㊰$!Z aQ;fVQՊasc[g4XwEcpRh|czeU6 "\CpSCR}h#j)o ,s]=V~nO궄26' bxsqyg2'|gLGq/y8ɞ0ք&㔨9eDlC<ǭukčw)xg[-zj_= ͦ_>"D^:̵dKᲓnmdgk^:!hoElwџѾyb;q^Os'0e>V !&B{ĕ! +?ҐH1Y3{A>a(˶Ĺ|3LU} /]2nGWպf$ endstream endobj 2717 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2718 0 obj << /Filter[/FlateDecode] /Length 1473 >> stream xX[4~Wjպ[Kh\`;0ٸM`;]ϑdيumxRtt98A8 L-4<8"F(&.`!F\ zz)t ^rNa&7Y(!e"B_|6yYԋ߯~^_$4({*F0Lr<~'y ~8Iʜc.Jp I`ڶnMU!llM:3@^=}$c(b1Ў,fcѸFDI| HvuZ7o9E-Wp5^=Myp)Yyfr~:wqdfߏ\{ۥO-ja"(b nޥE CUQ巛FnVyeD/y>/DJ.6 I@"n]jy[֛RXX<ߍ]»j+Uet>q"w!.M=Edgrg>nQnkebXXuK;!#) Eރ.<}`Ha}9 bFv)m+辂l۪Q,]ZNHZJ-60¿/hM௉ T>['aI() ruNhv1JO/ܥ}Dw?|ޭ{@ T3@Μ>Rds=m6Q #90sʛY?P^l*oF #W=Adϯ>Ɨ|1ta q9w^Z%<1G`W]K]%Kf::uj#Sy+*8˳ܰԛ  "s$> endobj 2720 0 obj << /Im75 2716 0 R >> endobj 2715 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2719 0 R /XObject 2720 0 R >> endobj 2723 0 obj << /Filter[/FlateDecode] /Length 2859 >> stream xZ[:~W:$Dǒ,_U( }${;΄ӭd,ȭ[o_U`?r  eBaV}V)KL}X:v_6[_7*X׏un>=w-U”ZzR\%fV%j+Bƥvʻc7#)KJU~ Xr661*<̷(^|Izzd-,5b'&nAL)L[bah^g/03F8FۊH95 6"Ywyy#u-ta"SwhLdQtʪT;>o1glT jږ\4%+'j Kk's=tc!ԏ]7g Klf8 AA:cjQAmS4͝T~x'4)@wYOK)%BL$@ Ra V5H Kq|O"Ѹog[T"))GjCeHU6͈ޘOr9@Ro&F+Ӑ4SzT=JT/$U`O$U£ãp0 1@`Z$B C0@KJA lw_|͛&ԧ5mb:3ZghjpB;D]M[6uYBµ""Zk\ yG:dhc0x*lFQQeuEDSf!]mV56 @* Ez-p,ڻ BnMGk_g#44{՜$c + 3hhf}bWNMFvmV3n9PF@0B0=*|0gMiM~ʊT){l;di9$.0Ī,dJܘ8%apv D6/ĶRr_#YYPA|}^-ea+tɒKuΨ}E]wgW6sHH,v51`s~%Tf LV6JapOӬ&?vx%Fv>Sr5DX5خb5OU]lWN1C.oñNA >>9{iR7߸3֚6{,Ͳ6L^8sU qʔYYR"0h{_ƨhId"+$_@&,IH_.ZIDr?@_R,_ @/E?vzRK\C,~j^e8Ln w4%WIXhj E*hW1#|q9DAzpLpi)˺-X kSAA|S Rv˙'F=.Idžw$D-RF#b?h!XOm帀OX:dl>A1!-"9@{J@3x3(]>= /Bl % SGQ%edT)2ضBm5H;(~ R+%JC#t\˛rdHVMġ%A}oO*Lxán<@qh|BUa12#P(/4~4sy;8$29k/B443U1-}bQmG0qX$ӗU?]"] 9oأ};#{Ǣ {*Pʍ,f? \ڝjVW\ܖ4ᕓՇS;p!Ok&Sh=mAͱJ2є[|DͥJ B hk@B1,,W<S`ph _ Ί9&qAqҀr->"9[us\}%2s*ܝ-^kw%;[ ;3w"咬^kE߼%tt1^o[qCk߶)[;i8 J>wڇ9 BR1So1 #sx;wIDUӈړY/=ϼٽz^)iNc#1 #F \_ Aq3~K~{d[#|a K+r$lpI<)}-=> endobj 2722 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2724 0 R >> endobj 2727 0 obj << /Filter[/FlateDecode] /Length 1883 >> stream xڵkD;:輵eJZh "*'q.8]gr*;;;;qpQ]c <RH&i\0ޅJYĥPU+ ˑ) 6gn5u$P R|N9l=˲.:U(kݳ^0d@ijH9|,>,%-u>+=ޔGȬ /h+?OLp:ga ѿuLhv/1ڦ mgI FZK}B}<֙*s4e#7:ѓG0\߷mїCy\i0 Y8|D/ 12סCWlkzʰrKPl"eGG;d{@J,fMgCx'vjW4(4qm[JЧ0U$ྸ+3F,aCm55Ȟo^r{E;g[lpX2m1 EHT;FT4$G4]KaA n ΥJ9Y #=Q7EO;Ds yvQ5(tfCP xJl1ө/sq"EFvneMԭŲqYVWljb*&~}̫ΒXPW؃TPbE*Rb`U.yw57> 4(hA&p$ΰ}%dcQ%ir4ZO o3Xg CF-Erk7P@O \"ʂpS%ZQ%o`d1s LH)>fYፌ]`wh=X#6s/OMbTHL !119)d,\ yYS 0k*x*~"XWt94W{]90 ">^>{+gqѫa>NpbRa\U pùMIV$8 M:l2S,>iǍU^^g\oDʔhTa}`@'DD]oxrX/}->3i7FثND/q0O*4 :أ  綠)'*Y fU$RLgV*/ w)Oi͓^s$VFn flA pƵ_-#(s"$sze J:b(qPC*(K XvR.ABb^Em$M"X 1~:> endobj 2726 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2728 0 R >> endobj 2731 0 obj << /Filter[/FlateDecode] /Length 1601 >> stream xXKD +\pMf `aBՒCm9Ig%ۙ8qfp䎬VK_K$9.q!DLHd.`6%nIA ׃>%HdZe}y3¨^9u[5İ`M6L)":NU˭G?+DACbξq_1=Dv\lmUS7~xƺoncgM:wFuq}Ms/?ɥ侮MvHw'sU;T&(U_zf=Gc{&s:8:J5j,aR@3 I ce(T")FbךI1 XZ 3$0Uzc:^ cҿ\Y u$6HBP4'~.gIXDCe&F H#1(QU{垥|2S${eUhSL:}eރivW=;XCtN8P+[c%hB.MW^clpne.۲::l8)%vŃF\]‚\U8  E헨qt!etqLw>|AS:{_]ΝZm1]p]'HXd #&=E%u\TG 9f~y: "38q E`E7ߺEY`@v=T_\:P w0&۸x֑IO"Ό2ciNO㭫gDaNiAr9"'С_f7G-Ʀ"RMLi}"B8С];K3Z)V{YR6Q@j$e^s7Ak@b0)ȫ>r?( j+%R~ٖb}p ȭ(E^*Ǧ`x-hSSIrw1/&t -ɉ5%ƍӃ 8Hs"_l0an pڴp_%~pUY-)GQ0t~>4KGa02|ai^ Z. DHj!/^0u=fe?J:*d{alW=d~jm`־8ЖI:vgCߋl~7vh`0&:]MiS¿.PВp%C endstream endobj 2732 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F8 41 0 R /F16 138 0 R >> endobj 2730 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2732 0 R >> endobj 2735 0 obj << /Length 896 /Filter/FlateDecode /Name/Im76 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2736 0 R >> >> >> stream xVN[A ߯%]0繭DAڠZگy@j%_q왹F[etA{Q 0cW?ɪz{)-!x(tY5|zhLګr&iCGe%ޫO@Iju;-9g4xڟW7wN0I'`y܂jza3}+u1ys\rr)`C_P; "J_T⤆^Dʊ 9NprB’HߕER41jOH 9(E%ȢR;0g4ى83剾zV<[t6a)Նʑ:쯟yY=J\K,c]$p EUY`Nd(kxdguGcU M-g2sf=> endobj 2737 0 obj << /Filter[/FlateDecode] /Length 1182 >> stream xڥWK6WE*&(I!@{B+s lɕȏ%6Y 'CjffcTD/" .pIq1iEO˫7 l$Zޏ -_ !r&h XF[yvR~t!kzD E*QQvp*X)13ޡU~ 2śᱷ2&ukcsMNui݇z[wl5ݝwpc` 0>2Jy wOC(fSo:ޠ DyѴ̪'"VbvZiDH} m*;84sE8&ǔ4%ID ʳtޗM9hE \;z Rz \:8i0'ӣؾfc Kوijj DjgNutW9UWIM. 9~3-n&<Ꚇ~gM}T v RChLb]m0 +m$e!Ϊrc0V 1|^ʋ#ҷQt Y_(KK$'R, 7uӇ^eMU^;m:!,yȤsMخĢV.? x$#,([I tmv2FYfg, f"^H`JNa-6I5jAvNzV>U^#iSwBrڏ!ykE^z `X9$1RrvT4^@ۮiR)*SI8f%&܈ fj s٭t68MO0P6|`jI}Uãx]~riⰲ)2vQ]˳8)aٝ~5 .r%lJ|Us*ջ_^`[Sd#\Pթ5`404 0x'-t=$y͊ e"u@sFBh1~f 'RA'e$&ַ*. D9&k뀴wa$} 3Li^9S.eo}L m]![\Fr?cP endstream endobj 2738 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R /F8 41 0 R >> endobj 2739 0 obj << /Im76 2735 0 R >> endobj 2734 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2738 0 R /XObject 2739 0 R >> endobj 2742 0 obj << /Filter[/FlateDecode] /Length 2464 >> stream xڽZKoW ؄c/vs _d8r|4)ጴN.l㫪HmR&KI7;!I6̟7O4$6Wwa=%,\VeIQ߾lw\f[&VҤW ws))h`V eEE&Ǣ;dj薳Ns<HmXmUrr1NeUڧ%|k,D`)H;0i>5HiA94ڌAe7,e;)炓(5Z.#p_enaʟGcjEwV1R?^~,~3V,lj22 VT8^XGVȜ"Z U,`fb앀)N*k3LB(]2+v}lџX3@9Ώ:ZT򯨲GV0-HE~~fT]KhUt*1K4L)L*bst=±cUσ cֺˤ^ 襺1#`Hy)bxݩfa͜n!F}ݴ:[]%mY 49Vv F)%q1>'AW=XH=#`{m6T:E(Fޅ!ڤ6;;#.K"?ߊ 5/ Ro5[2 }Ap>z}Pݹu^cxs'jBƾnv[>r7J/+td'pUx3&i,PeK H2*N0)"=,AD.,#*N0Uyv+HVfR(Dn`ϏUד/[so$XR &%Q^%o.<~oSTS];Z+N%n[Т7ң jo9|kV/@ @65K|K{s!-WwE|bp/4 *fU! v1'C+t4džY#]qhٚۈc760BoTfr%MB-_jrO쥟ю*iਵCQ?XiCVA5Sȷfǥ"zלaiR .a:4M!_ie- '`gFo' :.1bMD 33\J Q&qǀ6TTM16 I?)9~ӐJjx8lNIK =y~_ۧ43pGTcaL]~RTBƶ`8Oi$ 05m0Ʉ"P|0`ܦAtt^CK=&&f1)ȑ4L,Rg8!]/3d$l@ʃCa{2O yˮ#N?f endstream endobj 2743 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R >> endobj 2741 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2743 0 R >> endobj 2746 0 obj << /Length 403 /Filter/FlateDecode /Name/Im77 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 2747 0 R >> >> >> stream xRN0 +rcH$i&W$>`vl(]vhNZ\6/H.aGᶁT JmٞIcibY.D'"ll." wXV@ծ&RD.5#eɈBQCAD.5\I25Hkنrg1_k1!?)54 b[EBV'  $1;sax+mB;!;مQI0N1r?;]ø~WDYņk*9^fowK{ꯋ7p7WClUU8`v}/וh %՝ȁLFծ?gPqdcQtz4"S9ѡڷ}]$-cl|_\h endstream endobj 2747 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2748 0 obj << /Filter[/FlateDecode] /Length 1448 >> stream xڵXK6WP@Ĉojڦi[zk%J8_CK)6'R#̐Q4\ 4<:D"EQJ](CtEG2P&~8F$sV a޷^%K۲j!"_6focу LBڢ$ͺC ZÊ⸪LR!&ܢ X)H\ݶ:/~vEdh8wL""nK[kzC,uQvVU%AW1ʸSnuhڼ(|6;}۴^Wp53$`5FGK01oXH)=?ayЃ®H ͆dH)7Dh?DUX KH?(DX?jf " ©D)+.tKII\IxA(Ej[oӰ_ CQ`pb%,nGpΘxCwX!E٫ $X! [SLHtKRjLyAe/ɹ0"Jb9 D+L)s=9;Kfs]{4㩉xN[OMy{rؒS;٤ӧ@Ypl8">m/,hʐ!#g,CgY&%ᘀ! U8ZWvRFIjV ҦflX-t\MM~$G/| Y*/}w l}!9Fhy , VAYJ؁BB6>}s ! 2wnf NGAʳ3\P#(!B0TSgȁu]nY3ز#(A BrCMkRE7"z%xIvǹv]> endobj 2750 0 obj << /Im77 2746 0 R >> endobj 2745 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2749 0 R /XObject 2750 0 R >> endobj 2753 0 obj << /Length 56 /Filter/FlateDecode /Name/Im78 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -215 -305] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 2754 0 R >> >> >> stream x33T0A(U`QC C= #C3cS 3|@  m endstream endobj 2754 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 786 /ColorSpace/DeviceRGB /Width 180 /Height 181 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 180 /Colors 3 >> >> stream xRPQ|^mǦs1ӽ9?S E:Mpl;_^^>U{V"8H $8Hq<>l8H $8H `$qAI$qAI$qAI$qAI$qAI$qAI$qAI$qAI$qAI$qAI$qAI$qAI$qAI$qAI$qAo:>nN#p$,>7_?9,m8nu$C7qܲ37D?7jǪE<\ds̶ͅB71 $3f^M٨q|chM1=nc>n`q|<ʐqL> a8FIǏ,ٗ fYu60^Ȧ_8Yq61^ cqq{;URv2Nq^PƉ>v?8nj8cF_1q,c19r8cF_18&?8&3Qs+WW\E$qAI$qAI$qAI$qAck7n#8H $8H $8H $8HAI$qAI$qAI$qAI$qx> stream xڭV˒48Uc= L* Q_rKX}uK;pL \{$%GO 9?;cwAf@ٸs.w\غLm'Q'il5kezf|VU֯٣h<Aaouv<](%w'|G6laV1JV g&&#ӘޖqW]_fdˮ &Bø^#J*w'Zg;`"޶Me6xN'!f{2.: ϠvuF_p%c. Vtqo) aLB\3ʻ鰾=0g r'-QUb~}| 9֚!NI=ͥtч%7MNw"||\b2wv WYd! h^0+v*~HA6 Mne;@!X6ljb6?mmhBMo&jad@vRH|~A nO۔ H/Tg$/OnQkҮA3 ;ƧK8sy]+6'?Sx> endobj 2757 0 obj << /Im78 2753 0 R >> endobj 2752 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2756 0 R /XObject 2757 0 R >> endobj 2760 0 obj << /Filter[/FlateDecode] /Length 2156 >> stream xZKoF"):7InK|́i3S Y9 A=QHVWU׻$M^~Ln'+!Iʒ]" g_*9^oR\6š+s\J)pV뢫}|w~d<X=غf8luK.[Lw Y:`3\\tЭJsޡOoQ`H?j^]~[ٷ+wy6#\s{%1Vd&0ߒ&[H#4S671*Pgֶۊ,㓡Ul*jS]S?lSem2I4)`p-8sL5.A _$J%0)H"&POA 2n >H"AeL8ʨ1B=ٰݎ¦(&b=l;qr6SB6Cip*.'C;Eo5[NjvU^%y_@dj1 {dzq߮7(N} 3b-\85C^^|>l~82}\N>sSǾK[fK]dENLn/K܍VrHd@,; ·qGh.:0T&c`^+==ЛB9;}%qJJXhN8 h:'5Nm::P\uфC?!֬88k٩7c~#ʲIqӿq5JMP5 1JT)6mGWMTvL! z4*% CGXBKx;gPvŇ`$ՈC4C{7vwpЛ&s~pDt@Gx&sTT?g$az6Tff913zt1#3iC 釧TЁ~0E8q]aҞۆT>5qdfєCH2aP-_8wȓsGnNQa5Yo/fFn/+קy14cO_!ǀ9FBX9ƠR{#RajRBj^c_L̒Lu GLh6x:y@>Y'Bt\nNP;=ˍP>@ NNISa ;i7Ŗ\6.fs"4"^B"Td^~_n}`L zC`A jd}܎V'jϡ%tHyFAy(|@Re,nKQ d/:?"Yۺ-T ^Im˕@ɲG<)3])PDcJS +M7 +8dP@b`3^ܖǟNlj?` endstream endobj 2761 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F8 41 0 R >> endobj 2759 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2761 0 R >> endobj 2764 0 obj << /Filter[/FlateDecode] /Length 1863 >> stream xڵYKoFW DK&hQȡ. ZZی%J%i;䒔9pN$Gٙo曙ev[C&JTl8*Ie~|Y*]*M!E^OŊrY2_/ߦ\!γҾ6g+/]0?+u"\UMm>[awVQRwV/(Gr) aP0A^*чfsf6m? woݾuᤛuK^7î>7!Lf1`J][C0|i%m}]ܓY[AVas1gHߛ|m7@$X%Vns7=~69|ۮ Rn[Dh\ ʛ=X nx]rCUu*],h+JD 3J9qٻ)}/\ q.X!O.RD)m+ɶ>EP=(%Ri2XQknyRP.sBZ\GI*2I zpwugS*˶zsxnuX5m=E B#7j%BKB&LԂ;̂^(0aM4A8WHK6|SfoO#0O j 9J}\W/_h|Ui2e`6>)W_Gm~PiKU!~!O~p$GG=g7{[O9nQ?8zAW3>^WeLQ)N/ AlrnnNNUmP=)u|$[L"]/+eDʪ CS(Wbw 9H GM>mw[hNY-BfKK{V*e|:(.7zdLq|?!^ endstream endobj 2765 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F16 138 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 2763 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2765 0 R >> endobj 2768 0 obj << /Filter[/FlateDecode] /Length 2366 >> stream xZKFﯨKxHO`szmKiiۢ*FY$E> Ȍ/&BQ9l'e#"M8a!Fsy7?ƛbs{LjpA!acvfRJ+ wsY첮vHMb 0A*7ɍC18A D_ 2(o]7q6Iaf!',!i0jwiKyݪYKf!B,<ëR/ݱf DŽ0WCjam.z`(SXp %XG01mʃ)eG`qw]VT> 2}lI,}攏5׸v靤|EưبM}w17&L~vYJi[ ",)5(sd\z?Oc MGS" ev4 ,1 zڮviWfm;] v)~X V 0*㞠Ո*y!anfCj w:&V)pS$ݛcዑ]ԒfҢ37BeQ; L(j+Q Sj`WPe~ʵ+HAR& m*Ewj~ΏSQ7.;Uh;\iyY`?CbiK#]1G"qfLT!IQA_"VZ,ʼGM'2CY1vogӤ2/bσ?|'/-S>CA0c>s(b3@W)X<V0?s1 6[fr"bXB ose](,-Up]|<9$m2%ucQApX̏C9WZ(=<k|ؾ|& %(J9_$lG" WBY^SJ<31J{%VY&fТO( 1iC_ D}t!Ҽ&wf/5@D0pYW}?i'ng >E{]>{%t '.0Ⱳ@.wKMpƱgd[4tPd@xP$Q!P`m,IdtpL`9q1`ے;z,hױ_N_y|_ ֈ$fOdyλp^ckK]奈#: @'5fHi_;cL!mPȳTU7+CunfQ@NZn܎9]-טUT*.ky="_,r~)/Vx%Og+" u8Bʚl\Fi, NҢ|OP:yn^?2_ rYg mO',^[/ȺAwMu^3cKj0CVlbr~&ۡ9ykӳm- JQ1 bQEYux(F*{̢$!Grn{PHlIBے$ 6NU9 o#7q/}㖎\+U`)˼:!LZY?ntɎdS!YA@a 6c;T_oӨa^M9Tly,eZ=1m^4)buK PY&0'K endstream endobj 2769 0 obj << /F7 38 0 R /F20 182 0 R /F30 286 0 R /F22 218 0 R /F23 221 0 R /F11 56 0 R /F13 66 0 R /F8 41 0 R /F16 138 0 R >> endobj 2767 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2769 0 R >> endobj 2772 0 obj << /Filter[/FlateDecode] /Length 2183 >> stream xۮ۸_a@+\un hѢ@b"mc%x%9;R%ly9>Cj0\<-giDP.E'"n^-7Q(D.֏#~$dX?eٞX?* REA;͛l\(iJc^"Xf"+(& vg1KQ8J%~ B% TR ED8rsH2OA2 6,rl¢]{nrR2 ^G wDccJ7ڌ?wDk9=\S)/h@3GDg[E!)bzqhQ.r۷U𙞴JBL*%m=uwjjNJxE!Q$o,~O!/f//f;LG:sq{5n}2< f7 O x8fл}R?ToDcgLI4dnڶqB/;g;6W$I)2"8ڥX(2!yzX^48d؛Gq0p"%c1SnQ8ՇClF,e6ll^ͲB"mf 4K $/.bcNw)s$d^`8+4zM:b]˔(1jqBU]BY&WrSrzEt^Ǥ2y,N/Y4Q+J1pT fv>9#0.JHL6A-1{MS*[+w,dXC~ m;yڊ"CF٨i4F#]zJix8%A-HrϱRxJq7<_M?4@JQ"I| tKֿ+Ä?FGR呔N+ ۡ8RJ7S"L)20*g UDTp.Ċ)1jJ}AK8as6x@g4^| \D g 5v Zmr[ vf}tv6SI\e}L Im>7 5Kh rH9J#_yR|HE} Y 9B"Ė]VtVp=Kmd$E6]%hr 烨kH;Ã쁥*l5u -q>.aaףВ,^{Ksr𺭍^ZM}xlG!5t+"~r8ǶA0piaO]4":y:B͋;fOxI[@¾dD6n5罬&qi\b~S/#4B;Ԏ#ޒ^}rHt/R }~rڪ2x.3΢*Oo휯K0À y!@G&IFz%scиssO4*yf F|N bc04)=rXʚ9ztud͠FcOPkx@`^l؍Y$P;JK۞vk܌@Jx`FYbs k'JiЛ +e2tL34/3|:7J"Np&/VF66q"ѻ߽WDɎTQ]>1o#GUC ??_7:. endstream endobj 2773 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F13 66 0 R /F8 41 0 R >> endobj 2771 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2773 0 R >> endobj 2776 0 obj << /Filter[/FlateDecode] /Length 459 >> stream x]R=s0+TB>tg&mr)8n "|ɿj?Ӓ5lGӐ;Q5mdCkNf"D~YtvFyKFɲR4x5++!Dt\VJ˺Nf胱/w?HM+DY {ZVp(7e,@#ot0:kޛL01o/hgNgL v^o,W Jqq,amRSkt=E<&0 կ2PDЄ6LMgZ/ .'6d~5HY򾬉k2l\^bRS%߯6ۄRgXM ]6f |F-ous`L$GsOMgSaj7r]Fᣆ3*v<p%ruPFW;{{3^hOݻ #t0f Fь] endstream endobj 2777 0 obj << /F7 38 0 R /F10 53 0 R >> endobj 2775 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2777 0 R >> endobj 2780 0 obj << /Filter[/FlateDecode] /Length 150 >> stream x%0D{bK{SPQX4" 7d'vb(|v~{R:%W֢vdm*ɰ˽zka R.D+~+j㐋eCN)tƸlʷ$fhGLYg ~, endstream endobj 2781 0 obj << /F2 14 0 R /F7 38 0 R >> endobj 2779 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2781 0 R >> endobj 2784 0 obj << /Filter[/FlateDecode] /Length 20 >> stream xS030PHWS\  endstream endobj 2783 0 obj << /ProcSet[/PDF/Text/ImageC] >> endobj 2787 0 obj << /Filter[/FlateDecode] /Length 2435 >> stream xڍYK WrrW-MNvjRTM{==RMb Hx|u''ٽRˣ1Ntil \l 0E*`JqQo;:;JHo0"#n$k9b7&aWw+˰I3kC LDX}~6R elʡ#ZJ̅212'f,LEV5+o(V9u$|`!ނ ū 0bKزIPBƉ$ JRO v-G!'ȣ5>?xU\!Ժ0[A$H"Q6Aml Zu*[FBש~C Qʖ"cXy _^~H{I$݌.7h1J@s55G%upI~.*][ZDGxb^ًRdQӝW9{Zݹ7[31P4˜^Vw02E5\}8,I endstream endobj 2788 0 obj << /F7 38 0 R /F8 41 0 R /F6 35 0 R /F15 112 0 R /F29 279 0 R /F16 138 0 R /F10 53 0 R >> endobj 2786 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2788 0 R >> endobj 2791 0 obj << /Length 808 /Filter/FlateDecode /Name/Im79 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R7 2792 0 R /R6 2793 0 R >> >> >> stream xVKo0 W H*z^]켵)ЬŰ?Q$+ ZF3a"% $}۾{[G}FNOqAБh R,OÊh- [-1w:;Rnøy F'F3IA7O[g)%;Nu=ez$^ZIðQɢl-܆1x$j),0Ja2g$gT 甂> l]+.o%$eF3PsfD $3iE&TiLMr( wۋO򫍝ii7oﶿR?h]\I+D V\s\18^^z}o."9[!cXӋW6wfqggWUnØ[K3TKP!M[SK,]94,Sup-FK]9W9˙Ai^U cn)L l#I[br1io3nD}N)m,ghEqPG=B,/ERz1+L ]a쭩7箰@}M7۔ 3R(Xht%8^t+Xgs&`X7^.Jh1$s qTƱ~;<;H  endstream endobj 2793 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 2792 0 obj << /Type/Font /Name/R7 /Subtype/Type1 /BaseFont/Times-Bold >> endobj 2794 0 obj << /Filter[/FlateDecode] /Length 1737 >> stream xuXK6W(kFOJʱHآRthk#ʻ΃s9~ "E!oɃ@FJm( K3%~>~/JT2x|cT\>?AҢ,(FYQ BI26UA\"JQ$΃l4 BGg~Mp˄NjPMc[z8DX`vQAa_tǴI&z8+DPCYGcq턎ZqV? Q ˞)Z[pJ+/ե rH+8=\y0--ټlrƩ`,Q$ҲMJK՗JH۠R&_(Uh* _f;Gǣx0QX0P5Oz.PD6 _6G/}"sg.y,"97Ak.qS?j4C>y{sn7 ["܌`6 F- 52J4-tiOn]0:Hl2WtH}`àZ<3bjֽh hnӰ ƍ]T!EpL-suG3?QP\(J2£9@Pn\j #^ 5݁!4="xe $@)0//&ՓM("Eߠ>Ѐ F}9ѡ0 t ,/̱':N/'v1{ȴ2 G$+/jv$"ispkPl×-,{A[F /u}]RS ePUawOdjCkv)*O0U$v<^:NevvɯGFdre + -~$iaşC1#60Gv+Wtt7 \LSj-8oKje]J<"7GU.q'w o1t|>@YKV%:8)  -|0e jd-spyF(B<9 qU 8'DZ,}0cM.w xP"_և7s57sfZTU*; WiюwkS}Y.+]"b[&l}@ d8J&ӏ P$O!hy5>33ɍnjm)Ip0 ")zWhܨ}3@\x!ҙtg'mIT흺W4kR> endobj 2796 0 obj << /Im79 2791 0 R >> endobj 2790 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2795 0 R /XObject 2796 0 R >> endobj 2799 0 obj << /Filter[/FlateDecode] /Length 2817 >> stream xڍYKWr1jD_:vlR>xsH3̿OwEu.n~@$NF?ѐo6Efq6M8Ss.&KwqVn_i[۸OLu續tMx /MdBtCG ZEt*O|PML6M< |Yľ ̋fo℈%9+!$JEg -t=c]K*R\.!VN Exq,ٻ:q#UT;'6Zȥ'#nHbQƞƻ> {2 xwD:ZbCu I_m:.?>Yû|WJFv^ EQ\CɄPB.C4&x]Lyc[$)YzRtԤC$>`t#8<2@rD5U S& W\J.Fwxy!T0t̹9bVAXίiF+,)]/i|IbZ!scJ6gQ1_pyE\8_7լɳe{3ټb:)<bE-7|*OLrUEǴUJ!n Ko-FlTZOhCa:L\&KF2zxN @Alt-e0t&#^I GN{imp%gRǪuTd}!!A_vk_v˄~ǰg(2 vZVJà[-@2⢊. _k\moY#AJ4i#  ф'e>d8^)O ZBԻLE-%sߝ%j8A$ؗھaVۆ'e78t:xiD<^ve>g ~3"R)ɓ;ye|F]ΟpWGU<0fGlo^m !7pX%hCըIgK==KFtSyT"R!VG,u FdjxT3CeZ,צ%I煱py̱2vQjeCC.6^/K)T31%/ڌ ؒQgEpؙ (lj endstream endobj 2800 0 obj << /F7 38 0 R /F10 53 0 R /F8 41 0 R >> endobj 2798 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2800 0 R >> endobj 2803 0 obj << /Filter[/FlateDecode] /Length 87 >> stream xS030PHWS@T\@XATH!WDQVp w3WԳ4SI+73Tq057Ќ Rp  endstream endobj 2804 0 obj << /F7 38 0 R >> endobj 2802 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2804 0 R >> endobj 2807 0 obj << /Filter[/FlateDecode] /Length 1683 >> stream xڍWKo6W=ZH=>Т@h=4=02c%Ϳ(VRI̐M'氡ϛw֛Fg:N.Mjݯ 7* viahnLeKgD,013<\8keSA'SKf6;qQ-uvݛ vM?G30aZoñ=wlW3-F  OPl'+kHWAD\9iEIvѣg=x3llX޴BHXT~twC3 LŽa:<@kLJVވisNa;%ɉ&ɉַ̤:>{BC; |ιUH vdtfٻ'ڃ7i"ɣ3ErSsG=% )@.NIfd6z0qTM{ eFeFCY[Ӑc~ૌGo2Ks߻^1,TK_ty[TX iOuA^eʡ<~Պ,N2Q2-,1ȓ,G9Nu8s]N2|3g%xM,wL2]2k,ۋg {3RXaU cܬKh߽ y'wUB3Wk["4BfIJ1 EOԤQ S'PTJ8Tdh!uVV۪g3H2{oc{GKs"y*MlH2p\tVƥ3l|HL'#"Upm)pxA("vyWQVE2l( - /mGn7ak` hnX9?wӹ endstream endobj 2808 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F8 41 0 R /F6 35 0 R /F10 53 0 R >> endobj 2806 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2808 0 R >> endobj 2811 0 obj << /Filter[/FlateDecode] /Length 2049 >> stream xڵXK6aXL$"2^v'H$H(=/d]dwH0Rf&i*?w}}wyCu} +Y| >\t?aH$K>tMWE0ңZănPePr..B0VtrP?>80ó5/nWaʚ] ʊ]m8r98)T8;np7WPb+]x1qo"m~/pTڦ=Y)p/WۛX]F> L[ٗ"~ Ւ}8b#Hy  l*6FƘouϒ%L˜޿)+rIg[]vRσaBȨ#7vU1Z̡Lg39bR +$x'hKTHCe+#녚}N͂lI 0S1F姉7,.\6@hH*`tֈ ^nj2݉N HvZ;bZAx fdKT<]Ber`}(~% mXs|[I+Y8|i[J!J2fk)|%XyO%b4,G޸K UQ/^Y.ꠋ4LQƮKr _E0z[Sc0-: -w~2#}D.B!0:m#=;PE) $#dA7ݚ7pZ?"R%|%uq?jhSv_7ׯ#xQ׋q*Hs˨%|%UW+ú`B+W/,<?!hUtDil@[9(.3Ęfƃ$9oߜ8f76O#7Yb{G]iso1y A$O@H/ AQUV{4^|Ų>\?H##R5#UrC,_N]o\$sI~ si^2Jy0US#~m }㻤r= B*?aWƙ޹G0JT.H':͞[ :4 3A]Z1T8 I煝s6kdgSM/ Fa.R,8<A4l@)7@-^ q9`4E`5`iYP^R,O欧% yU;t]!/Ld7 0Y N{~+MpT³d:}" &B='أ43lީgQҔ߼m7XA| __}ᇏ_q 鹚zJ~zQT *B߷3:|{ endstream endobj 2812 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R /F21 215 0 R /F20 182 0 R >> endobj 2810 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2812 0 R >> endobj 2815 0 obj << /Filter[/FlateDecode] /Length 2346 >> stream xڍn=_ 5`qD1]`L^28Cr7VFGGR*IO$E%qc6M2RDlڍL%­͗͟oɦde}rs˿uq=8øTLMbNX2:DCd{i4>n ƞp +& 4R4/A"KYZnb.-2HPE*׆ǥ=1CCɮ{("MH0D of&g27#4%"J .RY_ Fs64>UhɁv:'#W۪j? niTT5'cAa$,ѴY & ^*MѭfU2kJo7"MEiZ.ӡ;P vi@˵7 {é#[0%D:lzEi8P`xe Z=GV2+we؂fZ-L`lQgӀA tP{K#ݞ }[>oP2ϓ5vD 퐶 y2t|a}DIw6LdO.lW RGyG7JY^>?M64q ¸wBMu *1t ネ|S!'NH6YX=yλCur+$`X" ,Tf-' O0v?HAkW\E+&%s~ r{]QSdT|'&fܶv/}93rv ,Q!sN! \fƀqM鵬f:o^x:YbIPP䚆~V%24v4L9wn;\ʘ,̰L#dTXyٵ/qP1~jY&PYATIs:B~s߯ފEIқSХJb>8IhH#L@~O?. AK@:JdMQǽ%i uH/MWSDтŬ0Gz-ĥ,nlGHw<6}iȌ cHcao3iƊx%"ݍvӀ3BLaHߎ/`m&iˆ>f{~rwX} X/ %bm7d m)KEXБazFm 6&!Lj5~K/C94 Xc{316Zk ޥ<IL y"Eww61蟷hT  X-7çѶ&\Ճ+;0@WUp&ZPf'cg.BoDӑx):9eő& RF UBͨJ0EY[0.J endstream endobj 2816 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F30 286 0 R >> endobj 2814 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2816 0 R >> endobj 2819 0 obj << /Filter[/FlateDecode] /Length 2169 >> stream xڥͮ۶)܍}3",*@OES-%CK!9ٴ]ْoF")9YgOYver](Y)`}uus&lx]n]QW]]7oe+c0x_v'͡קdKF쪊Օ(XQ" VwQfK9iYɈ ti<,ɛ55`񖳮ty?>G%r`^8)] QsVg6}\ݰvqJ~?*)@lZ&wˍ0@(N̲dYUɏRkLZ$Әxz >F,o[V.(]f& N:2 Bٶ ?4(lB,YyE!hqI1fIP!NC|]ӨDsK/k!0ԸDXhL#|+"! M ivLqdM &ED%@ۄJM,qKU&Y`YI܆*K QWYK?~(qPi %n$ !w"Pe%ǿQUVOp%J&\Zn%`\2u宸H"7twK0R=j=8C%P܄KM,K]&YYI.RB]r^-#.J&\Zn%`\2u宸H"7tٵ,wfaeW2Ge*p܄JTe ]1 Dm`Rp٨I,Ȅm\D,K\YƝevCI2E:gzne leF Do¦%'l6,,_M$zC<+gNdmZ}~92 M؄J FG.w:Ћ8yV^ɹ fM)\-1 tvd{ʬ?9 i Xu dFT9r~"Zdl&gd^IdY~W$JCδ_u n羏ݠ M؄IC%f$ Ɩƙvqe~G,ךh @K0Г}ib37:8:_%TV[ Mܔ:?l9꘧y#P8(RB_<85 mh@"ީ #̰l> endobj 2818 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2820 0 R >> endobj 2823 0 obj << /Filter[/FlateDecode] /Length 2641 >> stream xڍYQ۸~0Ї5#(]^nK8hmW8[Jr6 (0,r?3"fq-/I/,fXdb8,LrW/>,~Xzċb'^/W"Uѵ6m7mo*%Ҝ"6J},iTmzUtUP5caGv<^<7TֵwD)-1}2:Cyle;j@,{>;m/VIŠK1xC!XX"z=-E)Kc-W*r/ASNe5f:͔A^j#H@ߵ#Y0sftjQwXQ#ir{EX5QtzsVMoCqR,fix6Vd9V&kJwK< EtȾrol *Fp߷ďSjsTڥ1*X}7b6E k`7&SPL`].X~o`Vg9xB4,.-$}nϭٻdb7*T9KyTc5`A'KjEg *M2k;Yj7őF[1l wRF48xFLsUnWcWWH֑4Hn(ZT-ʐokzgt2s-/qͮFxU8F҇E t((4QΫ$BMj-jNuzZ9;C/#t8Zd|ؙh^#<ަ$j(e\ض)cDa^|9w_NpPYf Hq6ʚѩzK);w"|/GD۲{G4c3yfyH%l%ykyx~cH9B!1f,ɰHXggPlA$MJN0m`2MԆɉ%1^V S$hY` iD>-|PLt{C<> Sm%g( ?6.[2#c~Ʋߟl(܆$Nb؎b)H/{D3vTB )FXԾ|[BcuӗO<*dRNUC\HC Jl֝p1tE"T[Fa0tͧ>P|}g[K^J2w,~ǻ{;6`QLH,˺z>|)dL<1PYNA\,S!:\!$ˋq"L?޽o3r;ݟ7Q[GKj| aa yb1o\a o]׫7{A{ŏ-o^B)5;DP'Aj L`UH)8IKskzM63s1<੺ }8 Ney2Z-1&> g=}09GXߘg%鯥i誺)r37s]%{UBXXOkJ}WӨ/e; GǵlٷZ} tXQ[2ȖrV+&p /d .1 YW[6sw(shNセח?7V_@\qQ?,Q8|H\< rr}4-}9-I/Tg.#oXu%{÷Z(8 Ŀ{0%`nW}S:sMr.J!tX ^6X}q,{ʺ&wZwMQ0g=Hj?,x endstream endobj 2824 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F12 59 0 R /F8 41 0 R /F13 66 0 R /F22 218 0 R /F11 56 0 R /F20 182 0 R /F17 145 0 R >> endobj 2822 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2824 0 R >> endobj 2827 0 obj << /Filter[/FlateDecode] /Length 1894 >> stream xYm4ίKV[ !CQ]]P6Y%^؉ww!!dgƏg{eYrhtr+eVe"%J*Dxo.0IפͪOzs /k0%SwoYI5fE#Vǫ:}VLHB]l6[|X1arYs Ʉ)YOJeRM@+Ӆ v=;!eΌ9v"z'οV$[k0˕Z")${çS iSϨqB[;lz~Nga}~y(q:gM7P&&lBv>ΣSh.R W6vPbk&K? ]͹3e^Ø햄.crcS Ny ^AΦ(Xȃ4QB9v>Ң9}pU:6|MX/0bbGp&ՇΉ,g`YռۯW|nuog=#@ dsU,,|Z0ԯ$t@i@h@V}e_qd%N݌ ˉ6ba8mޅEpf1"GaRbuGd ˴.đ6A7}wfС`P.;; UCto=E- r~ <_WM$#8 uˎ@$BفR\M50j9<+.3`!\  O=JG< D#fKw&^Q1|j^ 8s'GA}_a~M3h$U"HxmXZU٣Hݹb9U%Qp zx7K ㆐:!`٥@[ƃݬfx\VQtԸ{Y:E?ےL;`:O0E솑/ɉ>I ;yنq &e4Iw4IUd{ǁBÃ86΀0BUnp\BG̴K WsJ{>=燛K-Rx^Y DRjLz*)5//X]0J_/|FMezױaԅ1+q:LuS%3 XB!lnڌ%؄}9_L0r;(>{SwSKgp&gծo}wW;|OzoXS)0PCΐHpZs3*!z l]M+$h%,ZqL!Ld{P @F"$` yP! 㲹I{|fkHMEz0jj|=>̇"ǢնrZVEo%F endstream endobj 2828 0 obj << /F7 38 0 R /F20 182 0 R /F12 59 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F16 138 0 R /F13 66 0 R /F17 145 0 R >> endobj 2826 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2828 0 R >> endobj 2831 0 obj << /Filter[/FlateDecode] /Length 2123 >> stream xڵko8 #be̊G{@on..fQ(6%$')Q&} p4g! L/EfIh% l7>og|l9lUUE$jWMꦝWi*r2MgAX4b""Um[.~횲ʪ{(GZ9T?M95UjS"b.6O]mEK!ӭ`+lPn0*=*gw{|& AFDjDҗǗI NI!X*\)2dI@ˋ>UxN5% 25 JƒޕRU#H 1!(2'=DYE$r=;i )WIUC955߶oLgI\gjva{);}ҊI7zeuFOCؒMw=\`O{O(7ǐ.^jrlg57hu;lJCI}79](3j>bɑfPW (躋]b@]"ۂHmӻ1R8 ŧg߳ߓ+Ε8WBD)Kw)]tB%Cl@L^sOgn4}"M|sIQ"$?ַ>G# Wf{?C+Fg.WR}ln"<;Г5/~I1&Tyw%=vg0z#vnXZ$["˵Ye}qbpsJ4ƹK2] S ۲Yv,'b)IO䜪wJ!ʒu)zFQӛVd8hEi"[~ix 2)Zi=sަ:G eScO[ᙉdW(#:$RP &-N7\՚@N\W'u]DB6to;MKPg"PRj w#|ɁfZN%KlxFQB][Tcj 'LJҀ # 0f4z{m40111'Rhp)\ 0)yFhU5:^_@8 @vK]aީӤ|#V%q%Z#JWj?|zfnPO.)By(FP;,1:;t=h58dRۖy~ɺf]S9Nj0Ӂ>*}So˵n`^|e 5HwIF){JLy/f ևz)ԸUU?#|Hi tۢ-oo8Q0R9ޱL,N\fm ,@5{ [W$T_r1#@ z3B@IfYSJPo.z}[ɣs-+[mAb endstream endobj 2832 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F6 35 0 R /F8 41 0 R /F16 138 0 R >> endobj 2830 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2832 0 R >> endobj 2835 0 obj << /Filter[/FlateDecode] /Length 2393 >> stream xڽ]Bp_"Zs?IMN]8Ex(EH.W>Q;;j$Y,ЋIX.kY")J9:s&ūߖ:Vkn4ϗ/oVk)gv6&]lwHC6[4[]Eܔ}_|,B1.m!6Lf6T &ih&PNگI:@bۢizBK,-0Mi@G]LȌ@Gf[n 1! I94MBw]Ч'e`~U_ MUB3Wꯑġ b)r eОdr}SL L2ňNo^ޓYEZdeK*CYXf 4/#T3,c\|F.:rQ\ySʵm&"+!DFccL\}rG.\*ۆJ3igb)SگHlFm̳1&n@2 pdf! c'%#֓Brr@:ejmr&T"2c0e-hXe'oMjGem HŇQˢF8>Ehr쮈аtŎ7`B+vVh+oU߾n?CEpcOA~аǎY&W8DE؁i}Ǟ6uvķ6$E&qvX7MZ!&o/qҥЦZ°j*a>߼44d8 qoaT7MK1+7f4j/> endobj 2834 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2836 0 R >> endobj 2839 0 obj << /Filter[/FlateDecode] /Length 2137 >> stream xڍX[o6~_!,AbV"u[{rMhT蘨%y%:9s8t( ^7|="V`,A$"a s[?QP2 #x4;G v>KK ;ҼXqx{Dz)+s')2Va9ow`PlyXii`P&+eܩs"4lsksvp}W讥iwt{eÈ,$/pm!q _V7N}v dhm`t8Lk~cj3-" 7 &@.HQD[`bA>&XA7rvv!Cu=TAp%~$CY2C-8Zf3-FfwѧdIpWW"H#{wz;ZQ~"tKmP(BX3(C"YUZM^ ݵ?GPD|^)dLԚ轑_zL2V$PX`Diǘ:cs!N]C⩔0Ȇ\/cnd{4ssg R da; YXa cQ  )^+l zhh:n[dvh'~A$Eq,g5nN8?WEc]}{t§q$ra纕l gbJ]ة[mWZw9OėP'O6zWa]0V7Y{xF`UYC3}V2K][lfH+hr? @nM(&Jzg 1N4rĒx~ɔ;ૌSaMq[o6gyT ( |{3γ|<4Ӗ5wK5oQi czrh|6mψ&r*z G 0yRkxz@5(ІCѮ(X tZ@[m1+#v\0^,b;UP[:3UtCa-yP F8h+bi0НVWpHΙrC.إll|l!]e,L*No@'QpVl,:^ #+Fβtxd'hR٧G6d6Mc54r&Gs/G ES[Y<F"[W@zFe%gI͖m{nL@7:4v.@g9h /'ґ >OZG dg>8_` |9r7s^BB#+Wmq.": -}դMƧJd(c"i7K9-;w,JXZMu` _yOS((+D'8l/^PY2)։O %Tzqe_;LgSZDl.g؃?&iTJd Yi"Ґ*ރ/f endstream endobj 2840 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F12 59 0 R /F17 145 0 R >> endobj 2838 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2840 0 R >> endobj 2843 0 obj << /Filter[/FlateDecode] /Length 1576 >> stream xڭێ4Vq.4, BmiR%i;9nҦî'ߎ, gaKB /% #o"fqΕ};6r'|y,ʽw2 gO$7ku; >hIof. ՗MM^}̂( u6 `@%"O ^1@J}Y1&m۬Z!K`xS֪ T_IX@IJ`؍eWe-,eO=7D/7g]jfQ+BRk7s7/pt6~w~&%(a;;'cXd?1w`_mfte4Ж5}/?νT\xc$J$byj FQ1D؉c`; bJ邂jt+ .6եWE %vK2.XC,#,BE'(qYwm_ñcN B!`>ag,sAszgi k}[m2H;3M[-뛩Rf9Q WD?>d⿫E7ŀ>O(U珳NyOzI/)8n^t(K4lyMzu1S}r3trD &0X@&e۲io/V 4T 4'a맳Õh?Cwe G 9Ⓞz\cy a s -j1{5\HzܗmScUŔu]BYJPae0\,T"-x=r*m] ECFػHTT \kT{lH#%?GAӜ8n 鸰'>eqRÅ Tm0>boIݦ {97~~t㽭bq!!(\2),MN 4r_e]c캮;l³ A1yd4@yAJY,LGE@wI6qXFzj(Fړ^XP3KY 36!,!; &Q(mF. Cm۴uka@_ِo(AD[D4P±OW,]ӈֺ#qSr*L|1x*ScGi[Eù^d00l' >-MpV2?\J38#Nb##&\^{Օ{('RlG˯E/̱Iv5xMAi״~h5i 3Zk endstream endobj 2844 0 obj << /F7 38 0 R /F16 138 0 R /F8 41 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F13 66 0 R >> endobj 2842 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2844 0 R >> endobj 2847 0 obj << /Filter[/FlateDecode] /Length 1677 >> stream xXݏ6_V:&Mi>)[WEEaae.wcwɥ,f<7 "EM`owAMe"K&^O_(ذMl#?gbl_7 V?>}_ʼnbIDDqq%%rZHtܬ1P2+z)> Տxx2͍ɏ"²g;^4<:  ʞHwX,<Ч"A|1FnTeLjg `*ι54ןu[x3/V.nCFaFv$!F[n@F宼I)*4ngvhh]'VqkzB\‚Ms$f]&X)yM0x+Bt RqȖNX[#Uyۅ_2 RRdcT )+R7ƿjZQ 5m[^WH,@#bwE0-^|[M2 endstream endobj 2848 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F6 35 0 R /F8 41 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R >> endobj 2846 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2848 0 R >> endobj 2851 0 obj << /Filter[/FlateDecode] /Length 1939 >> stream xَ6_!(*1#MM}Z}走df3Rl6mE^ùy 3O0(΋Jo*2dp٫KYym3z?|ŒG.;^,>OhX,(/a!b+MUIYK4YLRKjpX "%N[?طKՖngjvs<"+!zQ)Lw+$r AR 2RLpPvN/Wfm{A1ib_+T?y/ |)98T FT \YF QMJ"FUW Vj7LI hT:׺DQDqk:Q1~7osFBxGrIrjF9)s$*ajCrdspn(Fk{Di=00kC/T[]mZYK l"wv~:h8ie|1Ja״L.iٔ:F+zB۪8#ؒY ZPCS)pT4VK&H6L:x[nEqsYs+-#7$R |ZVtB^K4|2xqI0j)?I=2b#LXyB8or9u/(d*ǂ/Q|e1d=O4!u$o!܊41w(_fMK&- RفmtB!C1<>6IʞS$3=PGT)q ($qg6]$2I,3Y# ! nVB+wso"h| J8m̠BT3UNvZwx c-Y* ,HN50~Dc)! =ۧ-*\@PM*!9c%RQ;(P/,[IV,03!HȮq:(*AQJQsRk, {W$|̺)@(sb7 ˣ圊g ueNChBZ`؏۪! cD *ea991qݜ 5+O--2J< ^%CP pHR!*)-UKgQkB4]{ZL wrcU k;s2вb2!0Z@߾[o:EB+EsrLPfIvhaD[cijH-U{ %wӬ$ڐੜpF3QN SzJufz]1 o4NO,P^q3n~:O뻅a7uvkUά 5hHz!)Ǭ4$+ܪp)xQZFln%O1S/FzO":FӾ4ydg&7$pk)8>E.XN,jqN855¶pcr7b' T0 bɵ#& o|Bڟ `9 cqx_/f$%LvG!sq> ,EvYeexaS#>6at0sşlk'#pF *Mr, (l>)Oczob QLlWdpak I1rOBe)_G}eq#?V8ܹ7v,6ITBu7zt4DA 9%  >k:/#Yi 21‚ #Lj,QkUJJUڥi[[l )#>ɤ{/N endstream endobj 2852 0 obj << /F7 38 0 R /F22 218 0 R /F13 66 0 R /F11 56 0 R /F20 182 0 R /F8 41 0 R /F10 53 0 R >> endobj 2850 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2852 0 R >> endobj 2855 0 obj << /Filter[/FlateDecode] /Length 1914 >> stream xYK6W(A! 6M=T֦Bmi!ȏCI^;M{$Έ7q(x+\w$›P@${F$wnw/C9/RR_ڷ5oXh7s&f6瑯6Ygf6~}6LT~=Fjh6i#)^ruWF/ķvsf8ˢ,Uu&Y$,iI1.@ ;' R2O3vPX5ފ[o1IXo;a4e,yˁ PpWK@3a`ʾ+9$X`U0$qeBXHuh  LdѰaZڬSegV#/'rI2vs9nWfHO ,_e .C^_*eycZG"p49c+ǐMvȽ]Ȑ{!c_Ț#3I17&ӝP*F>$I\$ \Wj,\horH1] ҖA}V}Vu'r(1Wщwy+܋uSso/53J[`~%h^jȟ}QAdgQ]VQF$lI1a#^gofs1 NdtWUG3؝3nfrUqc%jy㫷0ב]>9硌qN.BzlIAaN6/|!sU_M%} ?!j`8CAH' }D|Tm+5%)?S,ONqm7m  1Pp" ! 95IH#K#qd4SjSnVzF++򡼙*M^|%(H-B㑂 ԇ}u,4uߣ*Wfx@+5qW`JDm͸Kk[%#Fn2jQi'&9;kY};}I"{C1<'FO$G,rsOB@qP_OA'9v.!$#]ijArQ vY ~HŦÍϦL]8&3T I&<MsQP D-:Jwc\F&ĤK3А3jpIhhO [maΒp q{zLjƁa4ak2g => endobj 2854 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2856 0 R >> endobj 2859 0 obj << /Filter[/FlateDecode] /Length 1988 >> stream xڽX4_K"]|IHҪ $C( c-zO~Mx&^ĢȻ{i e"՞%­+'WYzW3{W/L!Olݠ ς0MM[ׁPؔb(ۆNfM6Et>;/$52~ H(f(R\YŀnM&BEc(bآ87qrԞXLsˬG;pnf,s,X8F 9gd=ϱHc,ҧy,6RBC6`up- 8hcx@%p|8RnVQ8f0U r_vh7@k/ [d^{ZTXnו}Z_$իg˹ն LcUwu'DisVUZP@е"M~Aʎoߵcf( CT,y swW鮢%L/l] |OiwV>Q COTuc)K]0'u(DDHr&Oz\6lg4aYf)?MDoXV-L"'xDj7g ^[hLX?h'§"&O<?.~pCu7Ĵ+r'L(y4sR@R:oXƏƒG]L &^<`;M5\g#.R&Ï6^kW흾n&)sKv+g/;#bi#l]2Л4|=hG|~u#y~Sky]H]2S5Ǣ܈SvMtl#0'iZ7>>.zV l*=S9hIy1iFw֠*5[tuA{Ecݮo:`KB7r5 \al .0>A0^K\eleiW3rF5kaϘ#y&ů, ʾ~ ]8Vp뱲iw@58jeټ[(p坸4Ks ɌGʭѡD2Usk K߽,c):w A#4z|pN(5 bĠܕ{!xےm |c o5i>mL;ԑ'h?UB endstream endobj 2860 0 obj << /F7 38 0 R /F20 182 0 R /F13 66 0 R /F12 59 0 R /F23 221 0 R /F22 218 0 R /F11 56 0 R /F30 286 0 R /F6 35 0 R /F16 138 0 R /F17 145 0 R /F32 356 0 R /F33 359 0 R /F26 242 0 R >> endobj 2858 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2860 0 R >> endobj 2863 0 obj << /Filter[/FlateDecode] /Length 1642 >> stream xY[o6~߯кyXnV֮6>,CtMLw%Vܦ!y.x8>}џW , ON@2)߅K;)K#g93wM3 [Qm󟞾YA0t|=#LBHxB2h2k#(FA0F V٪n")G) RHg~<`~l6y7}P$dR&M( ;Œi[Kl,IU73/9kV}21͜gc&"3*vMKݲ< hS#=inb1OX"ndM[(u90,|'fYN}6 /r%F(=Hs˓m7s] {G0#|baM3]`&I$0{'a&0Sf)JP2?-HIǠBE(4:㡗ks3(^=E}Ϊ7Èg 鳙c>;l ,%}Q-g^f&bWa[†t.˫Ӊc h^8.Ծ;ױQWZ[jP]Fn[lŰtg,cm(F}ar#TŒaD .mB dݸmvD!9ډUU_ ssOw5ԕ@?il[{(/tWk4y+<eQglfZM;_FY<;% ,U]5 m%:vZZzYhGP9zilHXPիME6 ʂ4r*Tк4t;Քyv8*I,fwyd^tNU 2RƈDA=e6$maRVzB`{i<1)N_=/=5V~vYJ>߀ϯ-ؐ&k" M_mj$ۓ`^(e6,Z\ij;P9$BmE5"PK MPrKuc4;ZJ'H}`.jaqb> # 9?+ 1.CbI=t]w7 I;chbCjĎx(wc˼]퐎pV' l€Fo}5 rC XG1Z%*"x*ܲ. $Ohda0=џ}фU/gIhyaz3.)aZy:o` >Q s_TXCnL10?}X\{`g^.V-C[ yOWn{\?3=,g> endobj 2862 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2864 0 R >> endobj 2867 0 obj << /Filter[/FlateDecode] /Length 2560 >> stream xڝk۸{pbkDQ\{@zy\>E t֢Blɐ~}ENpD3* ǀ>oOA"V&Q2*`#`꿋$O(MbӮ<[8^D9_,Wi-~jޔC6[6/wr[7QDFJT;8)/$,, lW{Eb3f&NE|=59)TÜ\~Rbquh^ -P]HlDݡn.Ė%j4\lW)N +*UR("ATf|}PA.PRPI Hpہl%oKGzDL(_63AywԹ;VÐZiK?˪m}Su_a6{xv (m2´oM&N,N%)$-eo֑Eg׳b *C.Lwzb۞^HTm]^< Lo")cHYIHHR++#2b5>¢oWlR<\#uiذ!TۙZ-/ɾbrLlѝ⾬dتچ&)н/@3 _]W3+1D{2f)8Q(uk+$*OҷWQUX&W誰&FPvՆuu?f rٓP{򦱷±W喒ݙH.ohzpG G6@`qez,nEA׵{dBXC[7y+&ooF6e'+G펻mҋQhIGQ7)=4n\]YE\]{b4AOhr LeD^vUS5T"đ3ebG./8W籈K|U#sx[^iׯQU( 3WtO~ ',L`1'bHB45QccB ZGXW"U:9= 0d}Q\IuVj~{gQOE"lmm0e$ήI#9@P GU89D,L~Gk uZߺy' x/ÆSϟ~1C*a^MCaɸ-njDZ(u%gA PShkMKbpyp4ܞB0VZ?=v)שv{ϗV1I+FO+R h媟jИH֏ *{3usVDmXqvKW d~h]_;Q$_Uj yW^3Q.c0ar\܀`a*ep+b5Tgu아aB/ZBͅDIp%QhT:gHb&KO$a p,y!ďKUv ߜz!񖔄j.+A"O(@|J)&u7%^wqHwLGcDDHXWT^zYub>STWcPsр$,XmyÛSXBC[\кԪLRv49>@=!hb9ړ֨OEO¶Ϧ$NN3Yf䦐Mh> O/ơw |8 ½J|^{kcmVkCF^<=4*s/3uqXMxͦ> (3oj)C]n|/:HUQ 0+ӄsY!`yy#Rakq]_eI|QhL8 ԞQ_[ӹ"+sW}RwvCgg˳qx9gq1)Otqyb]|h/ o{uΏ3s@s#(]Pa3Ơ_GtC2ܟRǷ)a3! :dˣ77By!kMVW\\nys#?QE endstream endobj 2868 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R /F10 53 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F6 35 0 R /F15 112 0 R /F12 59 0 R >> endobj 2866 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2868 0 R >> endobj 2871 0 obj << /Filter[/FlateDecode] /Length 2386 >> stream xڭۮܶ_/ZKKn$mS8A{<zYk΍4Hp87΅{>Oݼ4PED;{qXyշaH*]xC{>_{%_mv(op3$,~l%J/ I!^*d>u0@9SJ>Rc9"SN~l; PH*YpMȌH jٞpc:?De}CP4bBB̹d$?ly>u"6_B04cFpUp8 1tt,oGuD#|mωT:紃;&1 I~qϓ/!VyM B+F!@+U,T *wG9HGwg 35lo,޳/\% ;fBx1?qT%: Guφ4A2?__Y9r |uf7w؁se l.3=_7;;}vfk*L;[m(@*+HE|hL+)=S3kYtdȯXc_<.TZ eGTr2%aA!:L_bQ3p/; GZSw`L"ٌlkF"H6|ɟ)MA wyҤ˥~v9\䣥RKΤ'=djAcA ڒMM)ZrI о-L*pX *$*pAك CV [!&ܟjJeTT Z#ڛʰu{9Nr -]+pU,b,#IcU{/3qFBą]#e`3c4SmTpѢO9P;D*gr>@<:pb2NJ•tuр灎O=zh1c .y"#e:!Ka_35\(RYʺW<{+ e ;ȉ[m!;[C 2`)Vś 7^J۸XStu`eڙUƺ{v$.[}2a!A]XEOs! nd)t;5FS|K 66lgg+GN!u"a%O"LuɞNy͝kG';Ã˅) ~MdZ֌ɌcמyaJGlq[X"jJAt خOWtnu\W wKks`\=:|bdN[/՗N]OU+f23H%tS.bBpX.aL//2dӨb ~0gp [W]|1w DBJsFSB:ҫɖWF6ܖyنY$^@ƾD/,TQE _D܊=ܸxL!:qZG7 `!ĝ 9]oJnq U?t2!i)gk!՘YQ+j?]gzV<!]> endobj 2870 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2872 0 R >> endobj 2875 0 obj << /Filter[/FlateDecode] /Length 2347 >> stream xڥn_qQ@ xnNO&E 4p\k G+m%)Qv >q@ngd|9mLr;.77x4$( RﻧܩfBxABvE]}:my]"Ϻh5;xj[nu׍q`Yר O;6zEGʒO^X^֭2'U4ԭ/bNT7ŹvYj tn޵):eqFe;FUV⑯Hz&'>aq.Ί r̎[u}*r(&^Uw{dk@2KlK]PͩܘYB qƽU,c]5br%Υ^V[ZhҚ&jrҸ MQMwJ+ř*@ZFx5ukɇMb} T AnDh0.{)OLf^;a߫ ,MR0{oK>|M@3VcՊ(L{HL5fSz>ZIا? һ{7/L LnkКƌSH,P`T ˨~VOv@JaR*YcwľLkZōg˼wߠ "MxJ {:8aRڱw2!B:nzŖqAS NYύY \*uiaׂ -hځӈF6EwF7coGXQҐi|8; }) ƮzuOs(My*Nt@HIu3$ v6Lj]ʝjEbRt~7L_C<fBEj 8.Ix%m"<,^ ] UB!֍,Ww3Ghރ~f[fr&C7 ;_%T;\r]NGUڬJw.aEM,HW`tΚ貟/ נӪ_T:6FB"re iцv7z_TlmGt6$\ൗK{ah:h7|8W8;sNClhD&jM]Q;R듪IwjMytX;>#-Z|)I[`GfF"#Iy );imxv9+)}}n;ujr>kMI|2@w(-҄SSF%O4 H-ٜ#R#椙EޞqTяQZE5ZSpKezXb/YjzMkZ0wP޲Uu {`z*3..K?$m97MYX+>t &|T(Wqs} IڴVڇe F~8/9ӱ8BeLJo_BES81_}K-BY5IL#0VC'FU/W1O I7ylԋPG1PԦy9 "/!]%3$V Zz^À6I>~i˾&jqozgf+gacEv̚U/ dHVOSy:䠹瀀~sP7sH q` S&7W4Y&J#&z^ endstream endobj 2876 0 obj << /F7 38 0 R /F8 41 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R /F16 138 0 R >> endobj 2874 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2876 0 R >> endobj 2879 0 obj << /Filter[/FlateDecode] /Length 2650 >> stream xYKW US/=k{Op;vL0jzDAzP=EPI|,mRs \i,FIŔvO7tS2>rs%?k[͓jjn:O~zӤvZ$fo=7䁻zκ(s ;c'lm[fI5tMo| )sjr L0)e/6m*(3Vr؞1 >Et4#(Aʤ'{Y2HC?ا5}y4L(_X)Dr FTĪ%p[V-d꺺얂A$V>u&U?u4f$?5|8U|hkԢIeKhȺťJNajնV f'\'zmaro8;cRLg}*2vOtp6OV%ir4 р8?P C6` sɘ? bL,,Ձ`]P!K(@!#L2g?nwVƍV9ܛ||2N%Oϓu-f2OQҺ>,Uwo*~eg&g;B/Vu§F"6&$[bޟY;hߊ"-mVC0'KXn[2~h~] | f9]y(ĩ!0w:pZ?ٶ)} juںuXT,ҽ cK#Xhq9cܩd(_yT$$ 7 w%օ%~&8A (4`S,$:D9cW)VM$`:T.s q`[ih"Z#:RGn5x}Y ٔ+%x\{A(<ҙg,RFsLu:T\zLV6uQ?ͻ=ĄqWyPve;t_@kaÝJp/ '4q2Ug}nҎ@AIj E:S[bL(+ vPPҴ[4BN{&tΊEicJp LЃA< :_o;O}Az ƝNK4)_۳7AU@LaCӆX{Uk54&r6A1UB5I/,#Rv*g!kDZ)F43\vW=Y5 hw7iMo,衩BR ,'5'65ݲ|7@u']cv<YTV R\G'agv05;Bulp.^_8#!R2e9*&ɁʁH-5-X7;gi#T̡T3fr~ifv_+8ޯgv΃W5mP/U%[~9 o :3fk ~y^0#D쒎_9+v8r&1j*LK[&97՚Ik{7 FžpB Rd& nY;Ur["81d# (fԞ$)cv.Zq5_}MT#< m=šB"z&:470/ޫ UDt:ڔ=0:fs$y  W%L", [H@J0$Sx)2&#LĴV"4 2bfUkԽ5zOY~^&ֽ4zX9Ks҅2EN-LȱrXAf*Zl>3342I^TXzљt4E.K`WBu\Ѿ&>(BQ3~_j endstream endobj 2880 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F8 41 0 R /F13 66 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F23 221 0 R >> endobj 2878 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2880 0 R >> endobj 2883 0 obj << /Filter[/FlateDecode] /Length 2716 >> stream xڝkܶ{E<^vu4EP-\ :NVqy%q?yon74CyDHfCqZjsCIEm7#z dvDazS_۝R _owQ{q+co<˦լ>䗶dE/>HI̱:f' im~7p@BoQ($^%R櫸@&oP^/H& !"I$'!0kpADm|Z쯮EcGJ @+!M5twȽ^s.D#a ~[';l[V$b"©~<ҐFO*t`[mMQxIy[d}:H)CũCYvBd5F=4f&OR!=ZRgs"{K9iכ]a\Bp?FΕ$3˺@K'#4V9PCR pj W:8[V7\t+/ym peb.z| yNn8stZ9D~MbTxj @ hp4=YѺp.z8CGWMW,#&4@`E7B!wF fbږ/eLwE*C :R~1:g2n. $^11BG&Φ<'f|wAH$|Ul'6<@al=ut}/|_n{>qo@Ehx$<*578^Ktr*iV2 |5 <)ƠGھcꙑ?"BEؒoNUq_TKb5X27y+ODZpX3Rv<23A%ǦG _aIzwew4)al}Z-PӅ/ŵ5tGP DI_HJq @3(^M6E$C4p1{Ur7U3wwx?V*˟~۟`n\3ˀ&`qey[gkb=cL.~P\1p؃|]Vp/vvmLI峇6x95*ft-h[L$OSVfR03oOc6V@ .?Fy9bJ d+yam,=\jxlc߇ c5h 6; '{r;~ngy6trIyTS1d`)R 3ỲL;LaHBw?ɌCaG.o#(mƒK#Q璄焂4v/0c&   CYR})HAsg>rɏ5$k:G mKKm9 1n٭7i|5}zP \_g\PWE-:aX~-~~;{<%h5š&!l27/2UHo& ik;4;3C i &UT?6G* 1.1n ICg83l=1oxv̮jic[P^}a(F :Z a|PPG%|VuVoX/Be2`ZT w1qiy,tGʚX<eQx:FcZDXj ʔC%C[~T. eB|hB$ 1$O![/R0Ks],c^ؘZ P,No 82#R[ dZP'U8T q$ n.@.u[?Ygh>"B*S>X""vs!`bUH Ƞk:zC19Tqg:S_qn|-\p,|W2] #fafn5Oc 7p' e֜'f rSK 8/&|Py{"X endstream endobj 2884 0 obj << /F7 38 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F8 41 0 R /F10 53 0 R /F13 66 0 R /F4 29 0 R >> endobj 2882 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2884 0 R >> endobj 2887 0 obj << /Filter[/FlateDecode] /Length 2122 >> stream xڥXKs6ﯘJ.*Ly7KR%mmJ "9VӍ@!Y6Fw6)KAo7Y )R9oTL }\m~~.L~O~lKS﷚'Pv'I~jӤr_;jZ$vmU߾79zZκ_-ع;vnB%pI 4} uh|?3fIaO0̓MQ$eWiM~MڻٟW0I1|վmϰ<vGbYRIf+d@@҈[AŕNkH֜ƮlL˃B2"2~2MPݷ] om*m{tװ@z8ҮdFﷹH?dܜ}C4(!&%h+8\"o&쇶#$lGO LI3kp;n 2jd'Ҕ<&T[nձT43Gҝ~fd\lp±Z~ΌV|hh<@*E [/1H#HXd!R*"? X+:czoD(UY^Ǔ%朕E' hę3y[XI,s70oiv1㜄ٲ%LЮsO"9 ~HPe`nQ[2_G+h9".bˣ1҇_~yKW$՟aOO5-ŪC0|4H&P]=. k]xc:%zMTהS?O^CaI3fq߼؉RX)u샵S$y^NuWO:"]VL+ u<,r Y@L3C*a4bZL0jk"D2a] sdW +=cf "}2lY|5qPߏ0dDpUg,Xۄ|oa}qy=Yeb{tٴXHd )5ϗ E8 Nskˠձ|)] 5xs,Xa=ɩc2ʫtlEJ u xǎ eWNRX@; Xϛ[ȧ%5MK!d%F;>mf'~Qc7]>h| t- U>U^m죤f)Ud& PTM~B]a5='p\[i!=b.5+[kw/L+`q3!UqG}ZցE_f_^JU׵MՇ\>+ _lW(uema@K?tPM\fS|߻.ܕ/y6D endstream endobj 2888 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F13 66 0 R /F8 41 0 R /F6 35 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R >> endobj 2886 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2888 0 R >> endobj 2891 0 obj << /Filter[/FlateDecode] /Length 2306 >> stream xێ8(RR_' 2A !B4SЩe[9ҵ3+Oqse(=wϻ$"߅"&۝w "{vvH#8%,A}HfS~U9MyDyd0eeSi^pه, u_@h2A(7WvcY9 zy"",h vw*;@.#AWzܣUW+]g (^pOURPh$x(kdİ$%Q -pi!Ƨw!(d<6UhBq . e|<z+UyJ1ۍ;+'@O~P'M&u`ba8B~SI/\rmRhMKD iE(K o" -.L[ F$.:_:0juF]SM̀: I0|_5c$_TOgYO2n,YL~9kFqSES<-)$I iL$K)n?[ ٫FYw3Bv42>Ɖ!I^}MQ%BZ ' *0w}p:*@#X byJ"׎tj ; E$tnUm¥ShN 1z>؊Kp9Txl,ĴHܸpb\jl ]˙M|!ɔe#j;|-tZzt me WP46[]c>E}S[=vΐX~:3fx>N!};աYH%kU@ʴfzM/WC"UJ]u$KOeeTSkdtX?0Fx&r(yj23`<;`FbP4(=cK :{- :WOxZoBTwS4{и fhT}vʿ twF2^7f:5>/E_EQ@#慺]A %רUKliBoiyɥYd򣟟n]ec˚[|5'SSUZ(_ 5&,ě*4q&Wy=H: YplUkG @ӈHTO&}4Ց yЦuW3Ljd`ͨIƹ㙞xy{zuF .*;P =4vRx ( u6V؋N>NsLؗW'ehhe7-pR=ND6lj!:pvwh]zTQA  2N-` hWyixS3܋| :,?7mN*b?|V*Rꇶ},”@`at2G:,  Xјk*?JpQgX8;.xU uGh]mda)Oɇ>N)5'ވZC6xPj~!cB_R٤_}Ƙt#4fm1ŋnvYBUO(p:86 ,[U>?ӫK )H x7k7~K挧zzU1 疦1&a0o?%ҕZ% `f.1{3I bwzhӑpg,tt877\њֿD͢_{II}$;Ҹߖ)h1Bh]h> endobj 2890 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2892 0 R >> endobj 2895 0 obj << /Filter[/FlateDecode] /Length 2371 >> stream xZKWmV+Z$E=^ĉs2 nvji gvT!QZs3]ůg(7wx&H7a,H6Mc3;67?}369ɓaO 7w-Ks>_r4(!iVD48jj:ms~N\dDM(&4! jmHSƂo3H8!gA_|޲ԎvEx7ʳ<㎇mZ|2p: =OejBE5jeWM[˺=~.&ap#KW={&$1;iBp)%P8+jbփVv}=T@c9$VT6Ԫ@UYB}e}ɐï@<+jAnÜF9ۗdq. /5|V;;FzCF4ۅ|,aW엓,5[zoO/mm Eѩ%BEu10AQ. )kM 8Q*-K4 eɠ4DCH1/0:~U"[JS%% CqڌRd l 2iAʇz ]W3%r\؈TL!R+^Mf!Yˌ˔e/;waWȪ<{$pc(RxhBѰ)́RYJfWGJ F%{O0E6C͡L$ra8c]t&=>xXǗBMf ]X1oLa*FAq']A|9Nd"S;bڗQ'#W V|{q[EpNJ(>sq<3ZL$v] s2k<擵6L~I괮Ѹa\v2@a?^U)Т=\o6M}T2NIєd0așrE6l T!M8h5j? _9&!_؝aƯ38l3lZ1h]J1Jŋ-q9y2؝5OS_L9-iD8u+kR${?5bdScIv2['!`ukkx2T2]fɁ|SNhRga5vg-\c3m@b } 2Astu.gJټ.:ʑb qL[{_lfdho ߪdf'PUUu]H;a/6AQqo<BIOkQpOyH^/<ܑ[ot6xYÄJca_[@ÿl5_P_b7_8 Gk٤ѷy\ Y1Cz](Ө.]w ?C$&M 5MmBUPtU36)\$'ڥCq˩ώI%<ҵQ/ed(#E*5BX6+5mdp1g=^ pT7juc K'fI2!΃Fm,ֽt5y {2bjT.w6`J`p0vI>'~L8QĘ"j F1V 447A7sk?/]i endstream endobj 2896 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F16 138 0 R >> endobj 2894 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2896 0 R >> endobj 2899 0 obj << /Filter[/FlateDecode] /Length 2390 >> stream xڝYݏ۸_8TF<)*-pM{}-p>Z[^ %C ?3R%w(|o,fq<{G41l(~Ȅ%¿fftL6vD6oʒkų6?tE3_H)#nh|7_hF~.X+VjM6|!L*ڶhkʲh Y4v[75>&Qqdi84CiRRόS 8qЋ8C %)VEXMEˈ9=/4H"u abCTq[Dƣ\訠K/+"&mrC'۽+l|c9<ՃgC%.gk-NѪq =co!U7m 2cAZKҦG]Gqmrg} +a0ѱhFcJ}WdTxLJ[*ժ)qinX*:+&[cuВiHH)rlH*IL>\e~Z54a7lbѧ7l|׸387ʝNF?𖠳P>x=RvO4'+3(BDǕ}3c" m 19oVV|%˲s6 F٤#Js< [,K_ !R+L gʶڍ .bY (8Ḇ&JزD]fIwKb?((HDD>FTmC7E ́-N]%,"6!" CX0u;j@ v5op $ AP-aHe4>/%2!MO{KT'h$S2< KU'"{#oDf'I鐘F@JyL)nv;Aaη߸Cfkd|?6?{әY/ۑ<֐q¸†^FTlQ l$-N ֘ `AgmЬ4\f t҅?q(P)R +!> endobj 2898 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2900 0 R >> endobj 2903 0 obj << /Filter[/FlateDecode] /Length 2096 >> stream xڽXK6QlF NX"ҋse,O)Yr&`Mz~, Txb%Co*0ًHE;Ͽց<񞏎_+{xXXe=T}u8Sq1˄a';)S_cݵ]l"; Iv:PiJU7k~&g=0*<*Z}]YCO&qP ՚0}> ' OTDŽ 3W<  ~60LW2e,~7H,\bbⴑ݇j(2vuH\Y a[jC@t9?mLW.%YsV9K1ͮR6ɨI?L7s۠%:3dՙ9}'GY}׾8C_'~x$b87h[} gE~YL߳Ŗ|)z}$ O^X.\zx`#O0 t0cNۧnB؞<ч87t0ɀNSӷr} 0ԝEt )b~烙 MGG F$r9tNA]xYKJt+*8DWa=_8dd -fmḥUpwpT8.RP5(@I^t>LU<ϚL*IO(%RX2oyduu*yWoOH?Z0cm &) DsIdkD5Y7A Ld8*)*bdHc ?'M ɳi"T< TY\xī(SZgjr Bڥj f_j$SNx VwZ"<L$&`lXB*ṃH.R[2N8R%u!]S:욕 Y.8LjH@?$!vn*YG @UЖTB\"^Hn4C]r/;*:!*"*;ُO)H1DkgtWL:\ZT˸˴㇫+x7m1.__"OsQ2Lo) JwJ:Q*2ſBS%HR1{*6s]p\p}v!-Q\3wդd \EVy}J'ݬɔ /KJQiRu/ \XCS8qesL2ټn"ԋ yS\X|zq^̧7*Dv?l3`hqfOw lTf10k@SkTɼ!@ 5~Ajlێ^?rer˒K+m ?O3]f")u.8GDOWh/ %3_d>fkPߘP ?u\@b̤DuRO{S=Hmd@B"O|j;#$Fwc%_; endstream endobj 2904 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F13 66 0 R /F37 677 0 R /F8 41 0 R /F22 218 0 R >> endobj 2902 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2904 0 R >> endobj 2907 0 obj << /Filter[/FlateDecode] /Length 2270 >> stream xڵXKsܸW̑SIW^6eצb1HK<=DhXqC'fq$9t)q6L#R[53Igۖ |kDQo'sV9D_ ї( <\!/v#s2+<d\ )W$WeT/ c]:zn'|y%E%J?,7XK﫯;ʓBD3LdLڛJ$lB\P4SgAo*/zSgn1W"NnH w "92 csji<֓H5,~ qXBA6?LRg7-^"_q|-f&bgBAKSOt[OmH@܄N`.%Gǩ19WI΀A'd<[+UPo V)1 @zCz>MDgHLyl͏b@5/ L=GOiz˯Tiϼsy %M&j pNs|)N*]TcAm`K2 e j|d6_=@Ac;1l6zߚC%.6A>##u 1-][$Zo905Y.JTr&\Qsc&cZ%2t} ||K5Cπq;D^][Fگꆓqg2KDEv*T<8Y*5Jw "9H2w*[8($-]NHg.'iLߛoHM;>qmsj;z*Rв}ϻtG.9?'"~¤d:뭺)c5;28,J{u7Q/cn&KˠВ[/]L-߱㡶LD\~j*h܉"L/{ם!?CT§6?f?TR9{J5ȿ}yoҫ 1BA[HϾ { KdMxɷލ 4` 5x Y28שiDȬw3G)-B}6cc7趻ϥ7qe¯ u $@ם=ΒN} =\%[<};+<~+CyON`Zno\ɗZCߝ}6n} z> endobj 2906 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2908 0 R >> endobj 2911 0 obj << /Filter[/FlateDecode] /Length 1216 >> stream xڅVKo6 W(k-֦M"E; Ahf-CL%Ey2yl{%('E)O߯+ǨJyGIQTD}T/:7ѧ:KUݟ3.hg8ˊeT\fLE5:~~eG֨ MDܫ&g}"e0ā`Sƨ!5Q1yq&D{g{%cQyQ΢?ü_ir$X4L:r8-wqRl$iV?F%Y`tdy@+ٛF>~NQh``"NM`˰e{kŸÎT:)=ܿhB-4-/(i^`4s6,K ^YUUxE l[ ed?n6\1zpZd|vڌ˝ћeOlj~)V71 ^͚<cċ*e@r+ۜӛe5 xYdzEDH }wRO"5’ Qgu5SG8 Kz%Î #b?hŽ+FS?0/CM$.,L3:iLΖD!М^um>'ERpͣ\F D09=R NΒ#!6uH]h缈q Z#Z#1a(=zv{$xZӖeHTϼvvd&0W3IwBwcN@yi'5vA`S'`Nñ_V`SX? ɓ.+.` ~ `\ "˫<%G H!`݂tr}h> endobj 2910 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2912 0 R >> endobj 2915 0 obj << /Filter[/FlateDecode] /Length 147 >> stream x=ʽ 0ݧ1NġԪJSNw\\e'x`rhÍ;X@!^wU@\ 2 U(Z'9W: Lm]mJ)쇞2Idz; M*. endstream endobj 2916 0 obj << /F7 38 0 R >> endobj 2914 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2916 0 R >> endobj 2919 0 obj << /Filter[/FlateDecode] /Length 1973 >> stream x}XIWT̉xͰ/><11>OnԠ^띛n$RR+) t9/?S]fi8P ~;Se< P!}4xGlk<;"hgiqoƩů(n0jLd 45[δƋ03@I' W[zMBy:'UoٹAWRU굂~͌x4*pCf>W۝a 3 W212ItDoIV1)KӱIVo+7 .+._(.nXx9= LzzIM#Yem|QF)/ۤ&_:Z$8Uӈ>ٮlƹoy8_aoUm VO Y1!hN^D w' J$E,R +i9"IBT`rI0K2J(#Ü!̻rwqFyt`Q0 A;Cfx=XI"j\vA (Dhy>5O=A20 h,>; g݃?ZE,^y?>ZbC␺ = )Ҳ~̋?U*ҿYeVWQ1) xiI"x!rN*D(fa>qXbҪXHa4ݭ϶W/xcL_b -P+b)z@W5Q1@*jD3pЁjaQFwҖ^ToT${>ˆoݸ';.Zas1|w_QO//4O*j"KhS{GMU =R٭$с $m ۮdPYg5 !Dѿ1LY5C(гdZya^> -^`ߵG%@n4HTqy1W4G3LA?݇L {MvxԦC&[>XJ! -39 Xg-9A8 aXd{VxC%`$@q\d rj]|_ڷHR: *֡=%8 ;Hi} /W^-ssq h^q=jukT&؊\ca'+jD]74&>xAE֠R0Z k`c(CH8~-nC˹2c~Pc+MXoyRRKR066~Us|H"\I AXp!AV'YcdtBKɡݻ~:~ױn̪ZVSK[[D uo. n+ ?-|pcϲnhى͘#$5]foIAA{xAgeXocO% suEȠCm~i4jD~eȱmǞҿ\)n:čLja\xUB&fI^WmV.ȨÈ"(Cؽ᭹OH&_q>U_5p$HD֮{^UW*x}.a endstream endobj 2920 0 obj << /F19 171 0 R /F13 66 0 R /F10 53 0 R /F7 38 0 R /F8 41 0 R >> endobj 2918 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2920 0 R >> endobj 2923 0 obj << /Filter[/FlateDecode] /Length 2235 >> stream xڽ˒PnT2@r]>'T2=dsЊ1 IӍn䐋6~?(}ٿŸ=T d%v'R}7C+B3z&/Tq(?d!ME0nd[Iq m?!aZKWNu8Nx2WcNz8֏!ӿy.Iw_ (uS^W>n<<`T_+ i>X8 iEN"ޏ{B R2 WK4uۆje|l9YdP'O niK?:?-q ~HaҬn*|#,s"Ɨq2-Sy:.-O.,7 8o>S2 e#׿v EXqYP]V[B7Xh5TiQuSOz2B..]?<J3u~;q|!A Xcq&1J&Os)1;灅0hPW=Ɖ>Fc? pIH_{11fХQm_4mR`u Bt٦Þ5l=&v2r! (t-lc$ i1\QsȵNػmNXW^Ζn)pMPks3gRHUS/ܶ丗w/PosOl$avFw"kFS3I$b1T$Xއ'-6=օDg`}cGJֈiK$uӘ|uuǭRّK`ÖK3۔q/}2Do0w7E!e>Xgh377zխ'Sp?Nhӟ4ʶeU0ї}956͉ #QK%)R;M1aE ៽_ )*+vOhqsu&ڒ1_>3Fty饧S=C{|)wE]etwD.37W#TKML}K#JS~ endstream endobj 2924 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F21 215 0 R /F22 218 0 R /F15 112 0 R >> endobj 2922 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2924 0 R >> endobj 2927 0 obj << /Filter[/FlateDecode] /Length 2234 >> stream xYKsWhO C Hzj/$J23@S,*ۍ(eg!D_@neqBEY(VJFIޫVzejuu;Ld#6ž1t37Q5 Zl+LG~ϫ?+EqxUTȱ!LVCC6fKbdadt='Cƅx_k PD:A^5vmOuz#Ҡ Y$ǣh&,UVb`Iinɑ| ǀE&ʃ6,yL~dR%Qr\h6֌iQTӷ|Ә -sс/vX28O _mHkOl QRss*D2:z4}]3= Lz}ߜYԖP8% #?(&2l̜]ⷸBD,3-5uʋĂ%')f̘x>gI! uvM<}γ]cq\ F12RA9!bI䓉#cI0!9 jpxk8&DZM2x$,+Y藍/.9,'lW֩#]6*,#&7%TTGK}<4mAwuٔ}MHGf[[FAMmuŌ!(aj@>SX*   ]۴[: 8Q);g>%$&o]QC?~EG:ZJwGxpcam48nh_]2@HYZ|SYv'ոOĎRpK 7;n$@eWҿAH}Scp0sKdyD[Rbm.f2W{MQ`%!-.lNjBk7nْ}5qXaDN{c00LV.AQ}_]EHU6Bz'8~\zwmWj;:/4-v .z< 0Z Q3To{=rn-L0z~ހd\Էᾥ9d$dI:>yV?վ_^L~bבT ,~^XyVDS63}ze31*^+j4 wmd.5&(0캲)lj!#n`x=̨6v&9]KsR~<{'l&mEt0ZJ\đz: *._C^WhUt9YCi-Zq"+3K#)ޕ0OWHgBs//Ix[8(/3g7a74@<[mGxlib_njg茷am8jhGr6!64̅aGR>RGfg7,消hv0Jh6 N?mF6a0 I{.-1ԇSD*5b Gp#? Bib<^&a8`quve3GIƿt_-yn">Ͷ4̘:Va'N%,p+4:˻&896wq鈕 O9_E\8e޹b(. o~@]y_B:/m3CdMO^ Lk_tctc-&:ζ.0tR(dlz*iFZ+Z-i e-_TZ/DU6∎ lN}Sl:~Nhfd14x+}M9Ku4ijDF>m+'K]Ӹoi]{bwej]M3o" endstream endobj 2928 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F15 112 0 R /F21 215 0 R /F22 218 0 R /F13 66 0 R /F6 35 0 R >> endobj 2926 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2928 0 R >> endobj 2931 0 obj << /Filter[/FlateDecode] /Length 2195 >> stream xX[ܶ~MRͩnEaYE՞UV93RJ yp8׏Cy =z-BE ՞4}Ww^۟&vUO eIޞPt;?4)zGQ޶M>v]TRc>vY]_x20r*XU>tY1#b̚v%=p.P<8mC7r9fv~|(?Tڐ9VpWg,’q?< l W{ac?5ւزn*ap% ;$ey[_ fcO_Ys$.˞{Ҋe4W * )1l qz6vZZ6$bk<P}k11O(UW+|8dph?GMeC.ԩm]c Ƒ~C)41yNOJ@߽&W*/ilH^#euXG4i0ZEuA3#$AzZBKd((^eѯ$\Mij‘z^ )O:"LN`KʓV'~Nq8QEKK4,Ae8ld,MXÈbD+fş~5C-<ДO$0yBe.qlCMR :{}b:$]٦G:6Q H v\1L,:ԥ+)`'͉5jNd$iATB'yPPtL/K櫊t s}<.[_v?K"[YN'=qW=Aqe48gFbPz@jq 0>JK<lHz^̀Lj=dS?8T1&J JL3ef*;domiZ*$7iÍh_A x811qҖ,Uk[MnEYqX,ļOc14)QE scZg%͛>[@x*$-ŽO墿P ڙ][RlpmNyG<2G"̕, M#LlC9xXjO̍ыQO;{Rbo1VzT-DH56. 8wрohL I*%STA-Hͺe8TL Lh1ġL xlT׳ñ 6 qB-v0߁6fA;-4+"\ [,r]cW 1\1Wr&D<"L4eo|N.~ϺUG-u!(~S\two.Lvxjl!ޗ:aD]]a{$3Zk#zW/&\'˩RLrC AtrQI_"2Ɛ[=!p-757b#ͥ[KqbZ_ۭbNb^}'aF\xo\윎Ohjls8l7jm۽tB.}`2r1bM.f"+QTS\N&^Ӵ{M 9-S361pn[mOV4_\rU6jY6nGfDU?1Gz",Q%HLQpy7m(|?25 2;iVOa;0!>"|HfA0i8Zospl>50^m3s0 jO:b<P!c!\c 3I_!014Fnk2wcZeY!#v2vfZK xE;B{4Nlz.bS+`4v*=n<]N&_vaXNYg>Glڐd%Yc &16/~ endstream endobj 2932 0 obj << /F7 38 0 R /F8 41 0 R /F4 29 0 R /F12 59 0 R /F13 66 0 R /F21 215 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F23 221 0 R /F17 145 0 R >> endobj 2930 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2932 0 R >> endobj 2935 0 obj << /Filter[/FlateDecode] /Length 1872 >> stream xڵXm6_ASUb`l6UFIЫ* ػxl0{/ie3/gqpǏH@,A,>Hdwo/^8Y03~CۮRud7 fn/^a'f,MbI\ ";,̴W Kpf$``蕦L`TIюqydO+ R n48*WDؖa -={7X/ׇjxAp ;Y ެ`^ٖcnD<5;\s:XjغhzzNS̘wMד ٰ7v>TTQFlU]eǦnA7p1>Gރia:WND$e 33_*$_a+-|z @scP,p!3ЅW̰oZV[vm |{VOBE?_*dV[g!? ܇gBOh>&"fd8jf*bu&D4QcX@(IBm2=% *!Aгj4܀:DܑdjkFaN #a_6gPR}L,&~@v]Jy-vfnLSgEԇp'b=ydCv/ЛrW-QCHkw)>j>~M of\Z{]$^UφͰ=GM~)͹*<5bMUV5d`yS5nR_{ǯ# .]2pl OqחYF AsHL9SjvzY䆮HB^s2+Ζ֌=yS[5))Gp 0+p'/aQpѸːȪ&mFg;9o RvUAIe\8cj-f(4IbZ}rA(|ޛx(]AV$̔DvOX2EB]D`15#0gafϷJol.se_&6P>^G *a~}s.id&{ *#tW~^0zGq.mV,xxY:H??[,CV0(U 2slw)΀qM;G؏@#>%9ta)J 'MF~'jN|~Q'x`S}cd2>gi4ϔ )ԄTc.> endobj 2934 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2936 0 R >> endobj 2939 0 obj << /Filter[/FlateDecode] /Length 2147 >> stream xڭَܸ=_!$/j`EJT{gp#@,%2+CҸi⧩8ۂdoNLpu}MVTKn4c%Ot΍l<ߎ7JT+߻Vf6o54ΠRB"%r&ݕk2Z Uf+;<`wۑ4JeD5^;eL\ aO7 _ w ;=䆅{q UCξj^޸#3؜GJ:Q:hQ|H#o5(|@v%k %\<~̉`~:"зu96P1w(3x%wu"I <ڈLtoVp3{+j41yBzVLD6xe|} ؞7ɛa`P)]?Z4 .0QbH`G+~Y+%¶{a&BeNxk!Nl;:o[ވ[:.1DRΑ @ܽ4@nÕ84 +$ v&/23xI8@( 5$K a:5l#XTKҸYXU%~ a1.tӷXcע i6 R FmS=IQR&">W㑷A9H=7~*ǩ$o:Èd8Mh'b\p`ry<+06&4+;uT4.(.5厪>jl݌hZ0  3[iU| +.{^"ͷ f!7ew\.J\uM'}d]8; l lЙ),¥वd#K;9%]Α{9D^nuY{Ek &j۽5& |qL"ZWǮ_հ#cR1Ch[[ηU!wg<NoDc4xRw8ONC!|:.bp,qVV*7z?hjg.t%6EgⷬI d]}o@>CF™uHZ #?SScbx KeBbPnbR zFÑr݋ѥuMk {KQE9^\tܷ?9B-(WVXH= &D=);>+(N좛GtJWq ߤ Lwrg!F's5p2tg(A/\ofp;S[{r@nt*b7dttޖP:䄏k:2 B2J?xjɌ5Jd9stlTQf]0l#~?UD oErC%o2BMݾw.͊\J4 w/0&N§H 9v#~_'q E}F?U>JΑHgYz',+hxg*gY6=vtg&7O@4IJg@&2o.%3%UWyxZ|bp^k }GO lrh2> endobj 2938 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2940 0 R >> endobj 2943 0 obj << /Length 1221 /Filter/FlateDecode /Name/Im80 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF] >> >> stream xmW;9 u>AC0^pv6MIH[4x#>DSN%J tY?!*\9hgLQ+G**B z@.$[׎˥f'һ˪?BϬQZcHV#.,\$9N*fX\p$dq\aaգ%MYXĩ9">0f7, ۭs$1RgEBJ7_hec;zhD DWv+tY$0̨LIZumPkdXڶ] P۰LQcxW]V]6!eqJ>r]"ޑ9ؘDs憙4T1{4I^δK0H{ZF?zu+V{Lӥ=!q&fNKE hѷĆeJ0?J)8r;~# ;svV97Py~xi)$ 1kU+v Cu\Ut㫘8YJRMb _&8OBTcbk-*IdKl(]W/P 2ȦR> Əm.ڍvIY]JGyvT[Jdc!\CC>T\?$sy/ E3u5jTR! s?ub=(+h01~['yrdU団Im{u },r)JrnOha9 MOb1YͼAe]\$xZdb‰$K>?+\ ˞dO&Xϰ.6:xyH<2~^1Ts78d,n(q+\_m/)+%?H pa䪴 X/m.i|)> ?8HHm6o8%_qW,! zl~w+;7VSwг8Q^z];/sm[{x1YubʅfaEo`·ͼS~w _EH{sу$rOGb0zp,ޣ/=~wj/=c~`_'ӽ/N==u˾(YL c|1>wQ;V3ܽl|}V% endstream endobj 2944 0 obj << /Filter[/FlateDecode] /Length 1700 >> stream xڥˎ6ޯڋ y)6iSh!lS+Jn];! b3_r( nKiV2X DbXs~\?yã`Vi.U?6۪[$Bl251%Q5):>}tsFٰ1$FUgShY;}wsnX8д{3`ux{,֭#թRmgZ'P6-ĝT D ;U ivdvOǝhQlQ*HJ|9B4"' Mk*|9`3"-ll @" 6:w' [t ;WJI4An άhC],p#G O_͵T4>IE~3Vyn`ql< w9FR{vtH53+Alpj,c<~ز{*U+EȮaHN( {l"B)鴡[^]_v>4[c3- }@"&k vFa|>\ K.:^MzڱQ3_10BNZG',Nw@;N"ӬtUl@(rnZOk7.^7sf3Z6?^~SpZ#c?/{+.\\ :;l4Cmʙ6P„<x;$IIIH'Kܖb"Q+\ш]bF2{c ~~^l3:O$|~k&pY4CQ ]~A_bEkW~Ds$eהu!0.q^!sO 4KRő5&q&VIѥ2f_̩'!Zmt֡$NCrг씴Sɾ^/$dƶ endstream endobj 2945 0 obj << /F10 53 0 R /F7 38 0 R /F21 215 0 R /F13 66 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 2946 0 obj << /Im80 2943 0 R >> endobj 2942 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2945 0 R /XObject 2946 0 R >> endobj 2949 0 obj << /Filter[/FlateDecode] /Length 2146 >> stream xY[o~ﯘnQT0_6M6ih@ ,3%q"QBjY//x w|.Eq"p(q~7pwٮE",v7Kt4oN~ F޻!M3UcnfAV/Vuow0ADsGYzs ?j' w0ys?vQ_O'U'#?j4rҤSmd2(v6m;8Ń@ )~ߪӗAy Fh߱wt<T6^5ȚYwis<~ՠ/i"۵ DL wF2t= Toj첣zdǛmZ2yJ:4ԫJ.D5pmQlra 9ZI"ˮug;O/',!A8@Hxറks -.Z|H ~Gs J-h=0H> d"F2Gb7%\N_|ܦAZ #n&0+tEmp_{Tp[]u h&N)jуGKz}dVʔt&uIeVh#\ :`bXTNp=-Ϲ},,xS3LM$v7﯑{8^4H%yo')F.Lg`R_F%"Δ‰}<{۽Y%AC0 E58gFuό⫡٫ixzMQpu$+…=Q(x s0Ǚu@}@?GʯSʂO秣xm' ~ttw ыTXA,nNyY9(@ /#]~Ft~Fz #9)fy:gSQy5o;rD8Kxf䐋cٰuWlb8|^LzwvE]>&L&72Â]931#nŵsj u{qΓ` %AG u=S`.{u~3HRGT~Ǟ\wu,j"z%~ ߡ7\-t1`Jy`&l!yg+Q'.]f #'c$?AV*?!/u\- ]@@v.7Lno@@LeǍXSds7f5U{Hē=yYq3B)0a؅Ԫ! [G"jkq,lRd3ڹyںS\ul]M#6LeW2p0[i/HiH;A)*8gFF]ZIg׻6pNm`FZcIFΏpQ%Cז 4o&۷<< oRƥ\|^Fg|">Tl3:1Q!7A3h'(ߔpT m> endobj 2948 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2950 0 R >> endobj 2953 0 obj << /Filter[/FlateDecode] /Length 1872 >> stream xڵۮ6}_vǪ-۵.ú5vĨcgG"_vۋ%ER$Ej0YVr`~:,V0+χrf+Y%, WwOV~ޜl߬ת+jm{Ad[c5 FlpKhl~|*lʑ,C'"\%R؊>8ҽnjµ>M ط*ljg}[Wm@d9\SAlڜ3'$X۾XǴhh|g`H1=9bmAMR2m"ßX -QU7<: Bkáv-A=?bI#Y5uqs\ydenUi'r.cu :m>mv˵ڮn@JGڥ4e~E/JSx4X#4\.ڈ >It/m c#5reh2E \O'5BTD%5Ms2'YكYAESI e|ĕEW~pgnZaȵ$o0Miibk}R\:p4(*J$x3JKF'cpISWվsiۛ&({N6ah ?믬/ B ',at'\μX1/ Ş+ p[<-5,kLDns# 2jq  OҊ+BH?[n0P+Ħ0<a4|gJPG?i*}v &#fEO;ʔp;$0`q|1(`gRTY3q F}6EhC-g(H>gr' ^t[vâǯN "GGD1it$z<} dhgрX7 e(K~Or]^P-]ֺQs_Fb!j&2#/%263Lq 5o6 x9Dv7#Sټ=f_NAw1Mv9S?8snPlGqOYO?|ҟ&]SuTlT1f_~`ޥ4r0Y(axDIxF2|t+OzA#>l̗WF] endstream endobj 2954 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F4 29 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 2952 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2954 0 R >> endobj 2957 0 obj << /Filter[/FlateDecode] /Length 2230 >> stream xYYH~WԸC F <v%nbG^s*/p$\YT+CzZžVEV P>ޯ~*Yz؏ǥ~41]oPy21ޮqx6u1Ax?͡>W*5|)x !y:V'FA`fec.L(J3{? $NMW|CF,LW>-oŘή"P/]U"x. H@=@UpJEPVI!H(8.Vu};I0@nXmu?.YOV%qveP'8 ܧ\#>Ϯsg4P3ۜ]2)!C}ܝeS0y(%҅l< lhǡ;~oT63jtj>!~P_  [Ўg[)OqQN8iHo"ުb%#:MmpMqX6rN ݉E"\/ȯT`p7o˚q^5^"s}f€^nTeXRI"/z401X;M]#﫦fAP%N|x\1f(/F~窳w^pO`%4A^4re2cDk¬2c~sL¤ b=El5y@e\CYeMŚO᰹w4wئgg>K!X pjiuK#|O9Ćrl0mnsprmVAKKab -fN氎Eȴ*,BTa\*3UDXiu7\.gb v#Gl}H`Hp,S }ߦ2iL/9k'h̘?bi4,`}@Ƀ8LqGl"*DO|_y5\eдێr2=}]T$ZLeUڥ㏻:fIeCqp:K/.r p(d}R B=@-Uz QrTԨr/[-7Vԣɦ`-v$e#ksQΈF;X;x|dMlr)\-=Ljσ8pD^U{لD&;;q/[e}*:'[w.6䆥 r#-%Ӷ-iQ7OlUg2Q(7"j-xL U/* (S 3WWW ~.U6D؂^՘(drXazulH/?(̵Y`G|/*loectc2FH`K)/'elM endstream endobj 2958 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F11 56 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R /F10 53 0 R /F16 138 0 R /F15 112 0 R /F6 35 0 R >> endobj 2956 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2958 0 R >> endobj 2961 0 obj << /Filter[/FlateDecode] /Length 1788 >> stream xڭXY6~PS5#:&@zHi@}JZ,:v2m9Ox 曱EQp7.H#V`',.ebᾛ]W< Vf>ϙ(_ݷ_%<_5]Z$ y6*=ɫl YFk1lRީ5}mr%pK]lTqރO[s!oWIf FbO#S-U4<"@IЎT[ߓh>58Kt2<11n,,O̩{Iy8I&R+Mśk:qHԖ({O9 &; S--Dt=1Y b0&MNXqZvq5nm}LgG=;!#BaLUwnZ'0briK}( %,ؑc;3$不z9(݋sl`L8 IY0 ~uCoEB;uKm<O[}BwئԸ /'[0*xz5m&N~[҃dc#m! ]jhyB`^~ endstream endobj 2962 0 obj << /F10 53 0 R /F7 38 0 R /F21 215 0 R /F13 66 0 R /F8 41 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R >> endobj 2960 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2962 0 R >> endobj 2965 0 obj << /Filter[/FlateDecode] /Length 1998 >> stream xk{"  ^{ y-~ZXYrXg^e˷^E?=C@ w@[G "HoS/Wyn'Pܻǿ$ V0޵18j$m]ҩlew_˄O,:R!>ٍڸ> v0a' 3/ ;ILlMW۪U2 j߭L$*Ja;Ąijxa8ib#! qi%d:֑hPV2F#P=O_8ԥ@^vfF'ڀb[wc} i8&~(_4u7ѣ:>TNJg~#8;۹-{t7 e%1Z пjj,e vmY@L)^>'gNݎ{{$9Á+QLDIsw:N۹ !j7P<[W90نW f O.A0WgB"^Obx]\wɰ<זl;*3feq=HuU2_3x-5'VB2:JU5CDUM)_H?F_.Koϔ;x3:Sz NU~wvG < `i5hM6 L[:[uaY1c,L7Sl"; TP.\GNø2ra\EMgr0Ti:VG;csOSRԧ0>S0T|܀wUnrCÂ̯7O~$'&.',e/fEbYƱ2hnDQox2_OZ 9y| sd/YIVCc]iY}PT% z )'{<+Y$}E +ɩ&p|%F'oψLg"@s]FP|g vcUf| d~2w`4qx$\ζϫLí^㓖N/a\Kwag4KRԣ\" [Jh8$a@b lpۦ=~ޕ]j~7AOF"4h2UC"]AG"5C7@QpMID-G&GԴML'yr! {f 6?+9uRƜ1Fx.'F kϐPF_Wy 즵KOZ X|&qT+ue;* ſI,lÐQ%~탐| 1vDٽڣӚeKAGBME:a"-uUk0&]O6sqLI1Fض AjFxt sLS~Ǵ؏ػ]ϋ?Evd +l#/ _)C\Ήbq= f80VNnNF7upHpQ Wzy*yTkmD_ٞalj=hz8e WޗRn.JU_rDcqpVThpY;ʑ]3t`?H{T`5a*!rpQ+_JJ9Y p];0Ԏ9Fqp,?@b7^ǹS!a r-9I![qV n+ZtxW ۳[e ̨1RC8M"~<EK?88"YaWAGJY6&>fV1dT5,VpRL8 |Ӎ$˥wExj,Dҭtrvכ܍޶ť0:)|~)maD endstream endobj 2966 0 obj << /F7 38 0 R /F22 218 0 R /F20 182 0 R /F23 221 0 R /F11 56 0 R /F8 41 0 R /F13 66 0 R >> endobj 2964 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2966 0 R >> endobj 2969 0 obj << /Filter[/FlateDecode] /Length 1746 >> stream xYߏ6~_ASUb`ccHsTj[Y]8`vowi'،3ǟGsd#4;(C>q ({8o˧/(ot]/v3i\xnm侂N߭Ĉt7M,~[%@ĘK!ƪc'<=#@J "A?DZ&ou?#?KD$@pc}#ugv}k.<14S ^,ap2C?$Us([ L7+6/{fS_xy-@ ۮs%H]am^+mQ cbUN2Rq6$oե'Vwm5Pr;!E(c )>r9*8n9]y)aD!%T8?HK mt~!>hLp#\JXy?~ArIx@b(fdTdןgC/pRmr2+;Cک Dzj05E`?Hy /.]$In qwf!HA&%e|4D? W&]qvAۋJ=eV<6뼁oHHBH\sw ګAwdR)&|~_꣤UE$Ġi՜PpB%C2±\ Qo{9w35jV*igtqugfN*ʢYǢ|79nQ߇ǷBEq u@q}V8?O`z$D,Hf9w{mZv8ҕ?>Gv075q 怳_\;U&ĭ|LXc sکcľ[y&X%JrTбE s"E ёg냗U]mSy@.Sf 078P abתBi"8o(T.9rMW#F4QmC}\('|ˏOD춯Ir;$ lǃ JŎo[딇TdÈDY#/ognFz&ہ nez#(tBJGtdv9C Pr(gtH:5xǴn|݁D&"/B6",? vJyj5k5Ifծ= >&a |O{+>3$ُZ:VԟZ3DBi3i# /K0,0ڮUkӛd~Fȸi'[;_bBGjlH_V޳ endstream endobj 2970 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R >> endobj 2968 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2970 0 R >> endobj 2973 0 obj << /Filter[/FlateDecode] /Length 2147 >> stream xڝX˲+TޘWē<$q>d"h4|ˎ>O{žʂ!ov]*4_>d,=GuL{^X޻k~m?A v{жޛqxhKne}#}xe9ɉ >XWƒG: ErylN ߼7fJ iN`T&P%ZFrD&/y r6բ b7_M5e[c*N90iWnTF'A_-C\r2jrǃfd'äj*2FT,>]v/Ӥn gr-+qoNm$TC:*]kbmo& '>.8tL>ZT1 jƽː9A ҒF2ﱕe59"$cBMd~u#9ޤʋvVڝS#h3m.Y7Xc*Ƹ6o_ޠ+8zSzՉ1wx<G%UFFs4.pAh01 +, nйI/yY ֖uE9 .y"H)v:QF 8u5h3`w7eAV&𳅿:NG Rm/LwN ;u)2p}wWc b Ba#tjY? %`&e"8oGOMd1/Q5_|mUЎuOiUCj !y8mdzOKL!t W5žnYpb CXnhm' c{wqA' ^튶H'<޳ *D*W(R d8MP\>mMHE"'ef=XUeoyx#Af eE&@Ud`x_Ny@ZL<X[[wDX̂#=r0:VW<_>efVb=$Tf! <> QiH!t? / SK<%W J@AU2mIyyN. $ĹE QI#O0v6*yi3aivtl,B0 .Bk6<ٶ㱎FG-1H(YZs|%gm Bp%ASh]rer 9w'剶x3AL\ɄL( 00V:O$X-vF( %LA`+T,̲ǕOmlxl Neꌕ6E(J<~%D#G; ly,_-̿˷jd@[T8'g=y[@ oTx qm~HM0Lf/m9j˲Aϛ A{gx}zI?KѿX$~3U0 7[.V5LI2Woo)F.A\ 0*q[1_R}KEEzPqT=%Xg}c&C.jwgzB+{m # `QkA.ᯍC`xlvIK7 `.,^,0a /FA:ϴ'Gx;t> endobj 2972 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2974 0 R >> endobj 2977 0 obj << /Filter[/FlateDecode] /Length 2248 >> stream xYKoW467WS!l@!NYMª%CR۳b%E"YWһ~>=t ;HR;L}tWBwO>V^eIva$Ns[W{&Xw3*gJRBg3$W pϒ㹱gzLzl;<Њ ?p'O{%9ہ~۶*sS"[V؞#>!Ѷ{'g2%i񴇾;?+O*=\9[۠H;F%c9~rXj|I&8گ`*p8im?崔pz[~wnK$w]yf-l7edڐڝI(vf}vp ,ӌR39`UIp$0L~?d&IL!コGo0;wk0kw0!ma>"1*Oޒ˜z0y'.\ZMـR 5$_d]y +3&Ļ& #X^Ċ79(5_@ TiB];Gb݄' 1RT` yB\ FE ga12%Ԩ̍PaV/\v#{;>᰺|97Gwv28<&_H< r;"YqԛWdrەystp͞1mAF=A|i9W]LrU4?: *?LݐS%g ;&{"0LnAI+h\]s(n{9 *TiM|Q*?Q!>E@oVYa"bvGY_Mt <6e嫚7rPABHVI^2mV萎&{`Hj*|‚qDqD)1* ZAX*C=\jNeLNI@`n_ljo(8{Fe{6tfJ~x͛oO|ofH^yOԿVo+""RȱB,'v3IYkY-"`AҘ$|lO{F>Rǩkpܷ-s2[5(,ciRy "ݚA;3(0*ZK| _d#@/<(bْ\Z1_^h!,îQ  Q]x;ksdNf_u)J(Hc,%m\4N r0^w+{O* 8)ܴ[t`>EԃO%Ȅ r _g$mbrQTtZ&pbh/ GMЋ pu?WiҘm·f\謒(:uK`LǸrC-ts_!(njiT0W9!Tn^DS߼FqItA/ᘖ"RDIJa4K8lխu;oڄ,1Z/tqGBJDZjKjQJ-Ȭ>hn?L$:GΘVbg B\,;n;|`V^`2Xh'5%P A|ff^f6?a5BR#/jT6TU+gBK%9?wĨb*f7fpD4q'q؞= `uY! wͨ293"OZd!.9EP8ETϩI6ZI 5Km.Vf]31PL9zcɐ1Hd\9r`ۈBH(nnTyAp(k4  sø~(Bq|"ri-䣩U>z.Au{)w"6e|@}ACT9 Be?erzR_Lk&udbSU(P_w|FE Z endstream endobj 2978 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F21 215 0 R /F22 218 0 R /F11 56 0 R /F20 182 0 R /F8 41 0 R /F16 138 0 R /F23 221 0 R >> endobj 2976 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2978 0 R >> endobj 2981 0 obj << /Filter[/FlateDecode] /Length 1908 >> stream xڵYK6W W&&^ NLB˒#SŢ$ڒ]_$z",gG={QR-U<%S._{)K#n=LLݏW%O"?l}RJҿ],(4ZpH?7v,7+G,B1.S0à3oއ;0La\%,V2 @xYdKS5> L Vؽ?E>MLq;bwsM,v‘',.K5_lΏTWv%$)<3q&}82~3-,&x8(.#Krrd5NhΧCģGd(=CsYTz$łtp )RKAѮ_'!E-JruKCUt-=>TM4 9gד U44Nn&A0t͝Nwk*FDˍ1&% NyXxɡZN6)5TUwFg+V&q;׋jXą.@)+]@H1|z2\xK-mCE.m+ddzChggz;Ê.%aas 覥tA{\5 4(Ѷ eXZ! O7c{L"RZ"pn 88P r) 8d#4{H@yG51 ~x> endobj 2980 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2982 0 R >> endobj 2985 0 obj << /Filter[/FlateDecode] /Length 2012 >> stream xXI4+"qIiY$v Eb$ L/(qOǯe'Nf!.⥪\sEQp{$9H#V`',A2fp65xHۇ3Q~[fI2LGyHyKPYl$ "ß]݋qi>EXw" .+êlۉh,Чۉ(r8ce1(Xi;ҝtyߪIbw~9qI!) _<asYMUPZAԆ]=Md:Q IZٜfޅލZ7m%fa+z[C<׭nrƝ߮DI_Gx7fB^Rٕ`ϿBiӿ;+%Vʢ<иD\S3曹vY4XmG_܍JRk%\(Hu λj8yC4YU3/<3cP>+A.fk-mqcKDɂkh8ڧQ;`t@X0<)ơQ|X}myH4Ύt)Fß(DG 'pOԾ;U7ic!wy存Cz^bdyA#D1ЉS9T+\Q`v!(@Ԛٸ2ժcQIUyB׿B9|[CowFOc D<HyEy"w*M/HyXZ+}] Hgk'Y[_24$(k3n%}TU _%E\bdb J/oYeǝFM@9~LKѯ0a76z.A{p%v7$gySX4&znfؓ%]qM>2[su gP7yտki"zc6t6xe1.~V=Fąu,% )2 K7BGUeqr3E8͙56-+{e,J^$2 oW endstream endobj 2986 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R >> endobj 2984 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2986 0 R >> endobj 2989 0 obj << /Filter[/FlateDecode] /Length 2125 >> stream xڵYKW kgv%UJ%d f\*>1x#9:}y5QB$z%Jh/$IXtD0D|Y<GrJXqJݞ&iݞs<ۧiصvDzxW,G=XO݇'$ˢ=1X ұ\F&)1L4rHFIȱOAS&zhj(fy9t[{PJK&c C>`7RӴeSWH3::u K- $YHo6QTT:#Pw-yPX eL8⺐'ХQq_gj^{Wټ. 5Ձ%us 9=ewi-ԃ9VQR/x0rMʄ 1a۰?n+@>'xJҥ)LXtMUBR`*M񵨛ȳdFbOv={y bRcW⯃VIζʮ{vB7Zh+01iUY;ko7`}gˁ=aMom *I<_= _k/x~e_#^-^Y+ 0EgcЇ Zfa -X|m<# #:fzmy~p!ZYH]ػK0QEr~nZ`E7Jh]=r+Z7WZc7}uZUj3;U7uZ~}w{}(P$üvu [e||EsvNi2pS+dt\p՚1W) nJ3 xϤva> endobj 2988 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2990 0 R >> endobj 2993 0 obj << /Filter[/FlateDecode] /Length 1742 >> stream xڽXK6W EĈ4mRT^ %W)niᐜ|3wn>sr"ɐ;2L ;_ׯ>pIY9ϙH?gCV^Ǧq?oDUjW XfazcZ/bSfSuAll%yahy GLK~[~ؘ7UrHߩN [56W1537maDށDSHO,!s<_، $F`. ZE-?Ұ༚bdvqznŹ.^]9,ewPuڅdhp,fO\WW}G +~C L]$'.[Ƚ-!AzoW{'n-iTVNeUPԓrHBV6L!YX[&ȩJK׏ D)E" H+ @;TV U`HP) M^B2}8-oÐXk"̵j_Z_Ak"@BGC~t8KbY9N Jt ʭ >\q帧Ph|Æ6op6{vF5_mDA+-otpt.hnX,5l5gО ň>-n sq}K ˽2j3me5׵ |HԎ!MYF8P5T8pEH#|bttX9ݚ= Ԡ׳ b_fj2'bOcHIV'l7gl_:AhԊvYgԶg \c+dItb2-0`$YbӌX}C5p~(MݕgjݧRfB\(ܺjgtOOGN9 H%ΰ\^v6&vh-  {Y\dSRp"N@3q4*h<H# (c5c;_="M'd:K xi:˕X+zP<91lASoT?+վlꋰk5x ~n># O rz R^5;vsiڬH7,Wc@CD}ofT4:JFX\T?ydft)(6<@(#co]IL e&3e#z)[=eO#KQF%Ka/ Y^u3ap^d@(0u} ۭ5G]!r/{oo endstream endobj 2994 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F11 56 0 R /F16 138 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R >> endobj 2992 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2994 0 R >> endobj 2997 0 obj << /Filter[/FlateDecode] /Length 1981 >> stream xZo6_&1+>ڦ@׭T0*˶PY2$9n?$Q&-IS yERwNqOv}(U#;78q`}IvmV.HݗyLu3-_"ǺZvٿ7:3'>T%Ȇ"&y+%$6y#a:p/9lrPې&E-Q3~Έv>TrgۍL吗ˊ=( bkUU-I&nMR5Y} &1oܦ9T]}I淠7p^]Q[", BY0/DaaTsiږMVg&%\S!08Dm'U-֙:\+)6o攎="O;e} gʗË;r_y. 1A4M|ZNET+ P!0A`b cQ=X&)‚ק%#[`40l׆"?78G mVƻ2Q!]sb[ʃ6y\@SӢMp6郜< Rwi na 'qYy eg^C8j~3yҦNVHMj(SZA_=23…P:ye D;i MٹAWϾB}NAC6)Qx}k&rk9=?K:VV!( #ZohUվ۫[bW]R3zeuFy[\rOV:2;A䒼T>8/USlԋ'^lf;۰Lc$ ѮN PYۺU076_EdC KUo6Q. J5fX/jPg0S&;_wv>ާԼJ5Oay(bpg1Ibj/R=>5`.q{/~ 9Libb=%?~\FQL]5I2<2 pľ0f>/ rYSͦΒ%Zf\SXbJ@#[*DiNU`G/pGA_Hy]MtOByXБ ҳ'EX„&$@&B MkG܋01 n_qW}ıK}446t״ւzʮne'v;ݮD^XI![C$rUDҎhRlު" 7KR7rX3 -8YpWK q)W^ kr*ݞ;k"ՒԴk+ׄ`@⨝8̸B },)vJVF@XFw(#C1/258DN%G}AZ,ⓥ2;Lʽ̛]V6]'AS&7ɓ %qŽ#e;Zws-z<7 IHd>#9j,Q,;$RGQ>T^^ӅTuuYvm5iJ.sfi(zn#Mv R~[M9mנt  Pz9t]|p􎓯˪6V^ItUA_A249V:Y\Sv}[hߧ;g~7޷yy/ t?p(ʿo=R]EeX6:-zwA䍊ƧDy?jHxB(t(f eh}֧F2g#szxnD@x싯dѨ&d,/6p-3R)lg+ gXJrYkP|/A?z endstream endobj 2998 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F13 66 0 R /F30 286 0 R >> endobj 2996 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 2998 0 R >> endobj 3001 0 obj << /Filter[/FlateDecode] /Length 2197 >> stream xڅX[~pߴ1#nION Wʒ+Jg )˖wbS\&ioo^7E*jfHt&2ͯ"M-bv7{1t2=|KK$;A mp47h9p* <<^9]q_]xBS:Z @H:1юQ6U5R+S* ~ ۉ&_yS.EZ]*K<-̹1hjGSMwhΝ'y׃k2sL|Et"R8+P@ؕżR 2rE,0< -w0d` t.Fy yL">ME2BBkU4h^VР0gMȀl#UI!˥&w}a]&jXfS1~^pͪ2 > MF/6166?>@ A LTp֩1*zb.x0ol^b5wu)X4ԍkׅ( >QiEGMȍ<3q:LPE)e8i' M%"PSr4"{3a`p`).g5Yr싔nm [MlB8= ]?5*Ӆo2_!`4^_1sa;7v ՀXl]5㼾׺mǑEt1u; ۢ]Ȃ*NNp>E)#u%A2g/eyHX(B:-?&3E 툱,北V5hAgW)oݯk-@ j%,D[5Dy,[gL~72nP\Ƴ4MI v+H}n d>a+:5,SF ~RIBPW*}X1<&O$ղ|~cd,9 7EItcq#l^b!sR (eu+n/YLmo(S3j ^]ձc+a*0K%Bu,Bi8uzU~ăQn., Re ΕLEee pIȪPˊ%j"׽m7=Ln %>plu$?Dq |"(_#y(i*(Jl|VY#6hJd:~x&ꠘ[Χ34 U3M|:p0sJ"l oBK^%!=s8C @A;,C2sc3?\p }#?|Sh_ 8G:@r|ML#Mf WM7Bnpnߟo[~n~.ލ4aS.HE1 >IhNXq1mMx8 &l6 r烩cy//?}AFr%K* 5,F,X/d R>8UnDqmA<7=XPQ߭ wĥ6`Uo( +܋ϖlj/[5gU@?G(Lck zS ފ{tW+5/TP*@35s)m:D Ћ `]ZW`S.[' FԿA4 endstream endobj 3002 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F22 218 0 R /F23 221 0 R /F20 182 0 R /F30 286 0 R >> endobj 3000 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3002 0 R >> endobj 3005 0 obj << /Filter[/FlateDecode] /Length 1884 >> stream xXKo6WloZ H9Ф)RhD{Î;!\;NҠ'Rpf͓w7;3 C>Fj}NvH0^4|I{B^(C'˦·U'^[mnZYU}\R+',5xa(HY*vٯe,?p"Gbs3A\^75z#Z)j_麜.S}}~zῷM}y N~QYZ ݝn aNNso,sHp@,"%~m&]e<~`?U(+8sW'l!`B}P%L3qgS6>֨zy(e Z8b' zj;ZCf2RM <3IVYU$,}TyO_)v/X<`SS:oK Z2# Lm c G(;;vlo9h.jSfQJyNEج UNE:=nMahmkCuL([N6{^-cmZ_i!sCS-ύ??@@c<722!Rʢo5fSB/j"#435g G"w'=V/{L;l)(tc^PPBu>ʺKM43xPC볆NH3M(I}DuQxH8%f"L-p09.ܤ|rFޮ rBQMܪl , %8gp.nf:7raеfVNu{ȁc- e -K(p0PSnhl}VeG%Z'T4Ner*r@*JN.K7\)VS54rB &ʡa*iq0п -m8#4u3 Ko5 dyҤRV2O ͼwR1~/FS endstream endobj 3006 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F30 286 0 R /F6 35 0 R /F13 66 0 R >> endobj 3004 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3006 0 R >> endobj 3009 0 obj << /Filter[/FlateDecode] /Length 1269 >> stream xmVKo6WVXz?I"Ck {%bC.)8yp%It+,ety-Jdyl9.MU?em!o*6m^VLb)Ә[:b,+lXYFQe)ݧy,K mVj^&G'ӳAlY]dem݄2YZ1 xr9~m'yX9 #'h{fMbA9Y>RQ6<}Q4e-i9  Jv|FBJw7u,3祥7#P4GZi?Ѻr :]$p ى?T\<!lP:y}$4s3oqfG0'#?XGgQdhp̽ұ:3N84ѧnz IQ 2V p1計<Aa VDNvD@O~)ys Ra Yy<<և+A dF M ge@TrBP&7lW}|U'`w]*HGxF (M?G,h[!$i!:HW錚}ASj'aS'Y6u](]9@.SWlP&N> endobj 3008 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3010 0 R >> endobj 3013 0 obj << /Filter[/FlateDecode] /Length 142 >> stream x=ʽ 0ݫ8!1NġJxZAsHPUPY#Vq-T(yf!Mյ~mZG&LZZ }I7Ϝ^pI41ru>scgo-h& endstream endobj 3014 0 obj << /F7 38 0 R >> endobj 3012 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3014 0 R >> endobj 3017 0 obj << /Filter[/FlateDecode] /Length 1781 >> stream xڝWK6 Wxzg ERc4m:ifLhk+Jq %[9bS 3WevC\J*x1@H@KbYjD0.W,f F ٣j#2b+X{O]{t]@U Fp`6'/?7B~f N\"? Iw,ҧSeK۶q4 M6pZdtEZ(88*aE}쏺_odDg@L_}{0tV#}hˢheA,uGRvόۣ*? j6Z7@dFNN_z/Śg/8Ԧn:CANΔ:M@7Kn1JCWgH88rE`, X g[$X^'~ܴx'p&[Ӣ_6! bd:?0`2QQDghhy&8S*xӽ`4-"}lz!62C0蟤$lT}pU,4[ |?_ΘWWaq1c[s!?C)6o}Ļj}n*|0WCj d㿜 v34@Ɉgv)+k~4$!;~ g_mFK29c:=O6gS<1)>$}\91:(ܥl7Ι`?0=Z int i׺0<"24X SyF.0%#[T\3B7!_ֹ.uj֠hʯug2m<{ޑA| {!dD|顋K֛E`Ez9_T&s6{TH%o[[B(UY9ϷI<6'^'J@ S~DͣۄŋFyǎz.ܴ/S)]}kU2՗8w~pI`ǣ@gH X|oJb,vgՎOzVaVqJVnKb*%WӌZGŞy ٛZP-ExFtS:SqzሱLi.ۦ4w@_oqqd듏6i>ˎL20 6LчOA){58%>k_ b_N > endobj 3016 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3018 0 R >> endobj 3021 0 obj << /Filter[/FlateDecode] /Length 2287 >> stream x}r-TsS;SJ״ssEHfEv@Q: |xxxMps_/ݼmPfSƛvt쿛ͯ_Mlx#OXowQ&EbNu]$A>lwYat4ڊ\tmop"b+-O%Ǻn ( m`A)//B"#Vvgڰ#ᏵTSf7Gy繎"Ute60ᎁ|g?g#(a 6mѪBEPSxy|Tv؃Y64WZd u8j6w x{9vrvsY0|>ϟظZGP`'92g, PgcKOxoĚvooST =.`LSyN#Q&aOgM+sqIV-] h[GOϨx%`_`D^F?~L Q%EhrGX~R$[8v_mmΜSfBqz& 1M+!&H1Fw5;/9:H~C Q;5LKhD=N}c=ҢTq7Uu~ 9>S1ՏZè8e b\hyN&wq"]x>mF^#"C/ N" CL~n-8(/@y|3,al2k7RZkؒ$N ؽa@CCg#N1,˄1gzAdfzYF*FڤTRvMUA&k\% XaGۡ`,/El=ܘPBos, +󇵝oAهa·ʛ8*ET"knV_q e]"])J,#(.J1W;ԕ#.5)ʅ4LH.JO҈=:LD-Bž1-zGtcKׇ*`,!9T׎SjcXyz4NM|qzg d#Uf4D!IWSG dyb/L{hO•.4h͉ ml\x*#b23)M\la$^G *%Sgѳh&'@K K-!OsO)X,>+VV*Br-w˯KWGGޥQK[zoͫ H;ztɋ2̌\weMpTRܤ5%W }UҧB(=]87i`{*5Ҽ a14yra^a#PաAyi5JEOk2M7k% ^)25#Gc?5N8It+$Q|lFX ݕFy玫U"ȥ m<^^{7/WXEqvfK]J_ȏ$H#(s!ջ ^O4pGYU  ~ mEAp?l2xX+/+)9`rAjD}`>w\ޠy"r~zN^ij;n[;,b dduzC\]~~܄Jo60i5M§8 Uo|ʛ2DKa[Ɨ?y۔* &~US2upf^2<n~{LU y``o[Hw7^b"D!Rcv 8;og|VaXnOwQa|iv &Rӻu]?@ɓseya>3u6yn @zR s2' D#:N8WئкfsNwj:5'ځ77vzB 's~K(|}׼3VӃercbC.<y24C O endstream endobj 3022 0 obj << /F7 38 0 R /F13 66 0 R /F10 53 0 R /F4 29 0 R /F16 138 0 R >> endobj 3020 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3022 0 R >> endobj 3025 0 obj << /Filter[/FlateDecode] /Length 2455 >> stream xڥYYܸ~ϯ5J$uOM3MZP9S"u:Kgί bQW$cFV&,1B2}~`E1Ek;V$YiTmaHr$AdW<`F\XةǃjR%}N~ów0oVoi(ppK{R]mh2[PGix*f=$:vm"bVEƀD^S>hle[N퉅}[%EAqR`xTfax@==R⥹Lfi<{e#=-%Pk{<=*- "mo:2YolPi%gɺX&_VDYqd\@V@P }Pf[+v ;XvW$ wяe_ޛH_LzA2F6 LȂq&xHnȉ2!32). If`p.ƎCKpW:eז_$!M$Ȧ9" FK"|!k4"Ӎ Ceݵz,Bp~ tRt4m+e %/sn|ak ѡ\h &+U.[(;w (Xn/TI5$Nk*R$ fWӡx>1@xp6Ϭ2@v`Ƈ,[Mnɐ̱tuS _Zl`ZŋZPDFaX|SO.3/:|k3f#Ͻz¬ j_W]]|U77Uw:|tWKVE_v' ֭م?N{*~K]Sq3В7b('XA.T>(eu{e/F|U)ޓk(O g^F v4&#Lz,H^O=@Eq[?P)(..!d]-N^Mvh?U:8%}APBa./i<W& %u 1yO:P=l(8)AnFMտp->r +0<`|l|,|"?5ENĈ1X4i6tOS8˼|#& db)fI><&Q'К>ڪ"d Fʎ }j2grg:vWgXxwnꥷKmpQ;s& frLÁs<Dc=_8'cσ!t18Ňm1c(12sBK{Le+ZQG 16FFJė%ɨj( Mđ;wL& endstream endobj 3026 0 obj << /F10 53 0 R /F7 38 0 R /F4 29 0 R /F8 41 0 R /F6 35 0 R /F13 66 0 R /F21 215 0 R /F22 218 0 R /F11 56 0 R >> endobj 3024 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3026 0 R >> endobj 3029 0 obj << /Filter[/FlateDecode] /Length 2240 >> stream xM6B^df%N6Y@C 4<jK^IN:=>J><3I&=|$,{|ੀ%[ʈ;zRH&y;?xzX}6(o붇'|bHhdb)y__-J)nd[RUzt;ٰ "=&61!gT iU"EBJ<)H4:h:d s1ٞ1 6>7K Qกd:PYImJ]RE~)K\O-J_AtU]j~`PK=t#0>?4\tk!6K)1EZ0#O$a|Ce9FX=ΒSD&ga/:wΓ૖?=?ߞ[*=_clE{|X!+nQz/x>&:h52hz,.&X01$1=#pE/Q̈9L8* x3 ytd>;_}|g$x1_)lb~ށ_ωd!P:L-#0=~Nڛ!ccQ8%#ac7lŽѼJMXȞ[8&|p=F> endobj 3028 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3030 0 R >> endobj 3033 0 obj << /Filter[/FlateDecode] /Length 2396 >> stream xYK60:hsDRE,9dZڑ%/%y)ɖ<$XdU/~UE/} h:DEC*߯.yVLdbKoU$ӵ7z6(Qh^q?afU(iU/ʬ*f؟´LcqU/;4i07`j+vί-kM{cM5uax'$KӭT|Y15D~odmfWu~cT-z tێ5y: ɄCZG?-;d_@֦q7Av<?:<-KEKL|}LT)eΞipG:ɄTc]&fBK%Y,g",C9Ѐ cS.G:y=As欟'6K0YAtl r ) $H^ykډ:q;XG*oy"P0-RKI^(^ʥ#?l&bQ$dRgrRJ$mf|R 5aȼMotGVkH/`T~r;%3vFMmyQŁ @ B^4NsKOOy;o~pKs(ZX=)@P@ue3Cg>j3X1Ǔ#`*I# `=E Fe8(R}kVrs c16'M*ǹX8 ΄Xhߓgwwljun"p]]~ϐ=vKG7nBQԑxf,|hrmWYyw;㟑7VؑcaӀE\@cZCMyp$蔏w///w;{׻}~a|{1 E)HA30/#D5]"8/^Jn_?ndgq!nBI̾浮 ӥKI EiGH}r4Oi"ܨSy5 C{˙9"p;s鸍!bCpG {۹.2Ay! w!L@+3/fw~ w0*X"^#2N61y9F[K ]$5W4YXh#Qzbu^A1y߻kKwVebMEU,l+t9eQZks@޶L99t7T5e#!W12"w!m)*ք#fGjE]i"|4ʼn\MK;r64G[lKIU-C { [oL[N>`+kX x]gl!R$ $ @=v)?,Q:N|)=r{cG_p +\iN&I(m^;c9 ő&pΉsGuPa\]ԙx:][BB7!/25eB xT,9BG.ؕ́ ۼrYU$\G@=4>š|d%orX]0˧*n>1؄ >s"nzg*2[ΫW@Հ^zZحq}t}Z ޝB :=<,(:Tsn.L[gVb)nIg(fyN1`Jah?B,;)[Ԁ9f,,/Vo8NWhwթ1>}Kg.3p"zB&\>Pcۇc[;sTѴH{*g2?a8iK?boNց:qIHoC%\Hw7wL^QY'B>$e7EYyBLf7& endstream endobj 3034 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F13 66 0 R /F4 29 0 R /F6 35 0 R >> endobj 3032 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3034 0 R >> endobj 3037 0 obj << /Filter[/FlateDecode] /Length 2766 >> stream xڵYmܶ_h>t%zs\oN˻W__Vmf8 w X={DUL2b_WRH&W?v^'ejw?LVWcnat}yOj7[!ĚmWԞo͏Dz~x8#Xmd0ӄ `_w]K $XMgPZu 7<]+4M{kGydm$Yf+زȫZ9z0ѪVSʞ~MEiP0dYd^IqF*wU1Is H}rvMJuiDdqSšra4w74^mN{lUTحnlwFF UR+[ŪU,a0J,r׿' m^M/=]!VQ7|E ]6khGewdPm DՇ5]v`(+ua bkf{Lj!{''s]Q l(%ack s b{ eA4eŧ:\]<ؑ ?cN"fxTۗ,6p! 0Qe '^&E'́l]}xk_QĨrD) r8! DŽcwfD !1{z0.(ډXMtA#&֙{sڮjT?L۱Wbj$>XPi?(m !g©}g,ݫƑ y 5Jؔش6+,kCzs"݋ʐ5*QZMh@yͻUcFIO 㩲zөEU!kEå}BLʼS-̂oؘS[dٽ8SmFej=`@bT<vM?ZϰkB^8mpFq60j]o "UoztKW(˓14sAgLխhc OP͐2CÌTIf8$R,P WD6z(", );(NZ觇rjO>l8Id0Yfsv8!#|M,OP 0wa9.E^ݦ ԃ-@!si$MgμK@ ?&Ǭ=ܕYRČDvhOe"< tdùJq@0}|aT.rUW9tQ{YMW aP "&.U._{%=m'G{?%a|T:e^>͑g9ZPЃVEU6K[ GpQlA"lDRZh'ݩ#X=/-iL@ynbaf׍yD,KR0_ԡX4ݕǭ1r?a4s1eE aÿB řyKj*I,1OB]or$}R'W懶m^jC͎)*yIе*jd_Y,i9}<'A>h'&~Z)d¦5<Ǣ{Gpf$<< } endstream endobj 3038 0 obj << /F7 38 0 R /F6 35 0 R /F8 41 0 R /F16 138 0 R /F10 53 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F4 29 0 R >> endobj 3036 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3038 0 R >> endobj 3041 0 obj << /Filter[/FlateDecode] /Length 2320 >> stream xڭYm۸_KŚ)J&@.;8W[,v+o^HnBkHgpv8^/htbQ$NE h/~^|~(ίBF?TF/s6IHEL2U]RZȄ۷}܋)4RZV?**ITT#Umo0]ų%}\)msM Pm-.xaWcyeՑ a!Wl5J5RX=~iςΛJs˕% +l)r*!ew!s0qcS5L uW5h8:t֠U^CSddž. C5WA‰< TuW-$q:wθ&qhanS(m($uOYERʷmA$l]4y,Xb%y?,Y ;+Llyg*jZKߕKEh)Sʆ,\v6]UVׇ׷ݶwFc_ ǃemz~qh[B #0,Dm999}jr!? cocI̎~6U U(~M p)RD<U0M!:f>XZ.}|kzõu?Lns)"ql`lpk?uN[2aU&47&m!lt/gܽ<5$jʇJ̒0d!9IT><YܷhY%^X ^߾wwkJ k!#=As6Ӈ̔ eTT:3% 羿/g4Qq"LhLjJH|KTD,yʒe"53Y5Ud9 Qg@Jka|[7[)Bqh, 0t)UZ2#)mIv*HHP{[ ʻJ6#([]q}|uTp$@R"w| ֜j9^L +^y{5R9gJX;F`"f᫶eoWv[W}uwܻWEaC'Ew~ϫ(T}.V_ZKf" XLd =F\3<8#K0n^Lsq1JA}xb ')ձR Y<ΣOg ]rqCDFJ J oLTq_9|ֽƍͥ4|\W^'-"B G fOhoe*P讬Wj`yAcx/"SEM?{ LJ7l!Y-@7AnOn0/<X:}DATdP5`_b@\xƜ:0 f2䩞,YxyL |D*dJM@9>2@_@*/.B_%ET&Ygrn4a*1ѡ5djE c?Jtw5XvW]7vtp~)mi6=ծ=t 1I:P5lͦ: >64' M{p,a{Oq %#"h$۴,eOm# ϼM 6-k'bLmì߾ -œۊRz'Gҁԃ{If=lվ6Ix>xDY(®5{#DNoNJ|eD endstream endobj 3042 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F13 66 0 R /F4 29 0 R /F23 221 0 R /F30 286 0 R >> endobj 3040 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3042 0 R >> endobj 3045 0 obj << /Filter[/FlateDecode] /Length 2147 >> stream xڥXIWEJlL2@'t9@el8jK0'RE"v !ٽH.ԉԮX j/7]!twxKiC'qt0@E80M'> 91=we]oK˽h*-dL~JMR8Mkc? f&I@L̢iS"OvDJXN;&?h~M)OC=wXvX9v<9(\"PBNBz ˪ּ"R{ݗN[@ *]s۱'*z_QQ.*Z ?$k|Z 1\T—,.<@qp-7Y6DB@f 7=8AI?UI!1j.yiLv#jvjz^ 6QCeoQW&UHxMHr=@}r{i{ xǢX%JP W1>M٫ͱt)5,TVS:MY UB-kw8 T eHRօ{J[L7?LO24thMk Lz4K5la O%) Jfox.'~bxO}6X@O _(L;}>OjƪTDcxήcpㅂ= t086DOѱ jbI },7>VМ~vD8aW~/C\6+ ;@CZB[,`ƘUC rwz2!?gx΂uI=8[,3,i3Ly@ C/.H-:\9| 424%ZbS )Bj'HA]K>2j=)y[-D'=w5Ѫ =,ϲŦpQHڶ:^M.3Zkvis."U T(IM34"gAp!683ݝ :Qd:.y} DoL+"˜OMdY$PIj3 .t#7ׇK ['sc:ysIܞբ~؜>%p;0%# RSamڰo+)̫t [ݵM;x ]O70isYH]Te'[A_{̋nr>Pl\mh ֏sC=fN琙/#c}@ީm%~ͯ22m ȭ1<A }*,9>( S,L<K%gٕWP 6.0ə/\2iuv^<ȿtfy kY>-b=SN-y|xk5b{ѩ6%F=Q])uS"n~DٴVijp8mL@Uu6c^EL#Xպ<ޞR/ܽ9Ʒo6J/}KݿǑ1}Y?{> endobj 3044 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3046 0 R >> endobj 3049 0 obj << /Length 1155 /Filter/FlateDecode /Name/Im81 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R7 3050 0 R /R6 3051 0 R >> >> >> stream xXMsF W[S[NǦ芍Dْ\K2S%>r?(YMR| 'h R T^^*,PZhUʘR P 0PT#Ekrts)c5sWbK .Qkb:* ZN4SРk?. MK#%8QNnKG exj-#ڔ Q(I#_WB1^0R PXeMx W (_'u2^O fkNIHgRfD%EٗB+'E/#tWmO.Exͫ*+:D=cec)5[ qę92uF鲂:zؓ#نzY Y=*|YGrXu& F=Aàgt6̽>5~t-b$ ȑ3Li΂ҕ'vdjcӟ)Hnʦ5\E\MgN$)>y>KnDNxLΆ;mB${xopREɥH:Sd:(ҰZ;#Mӆ.탊&}%siƵs iqOet3L sKxb 2M[^.mFjHNGyUˑ5PHg{GO#i3@ͫ]=41@E9zl"4(D\DbzsNgQ34`]N,1E:ƈdN4?mœ}Rjq`r[550ҐyQ7["MPbnQCmŗN(nxnOqčUbMlڿ VL_p݂!GDj}Y7CFPywH29pv'mUfPU3ݶHUPÊ|˄QEC0 @v?ثn6mSFoaE=j4:m"o\|jf3}Aˌp(k.> endobj 3050 0 obj << /Type/Font /Name/R7 /Subtype/Type1 /BaseFont/Times-Bold >> endobj 3052 0 obj << /Filter[/FlateDecode] /Length 1500 >> stream xuWKs6Wprf,WnNRwIZg 8! XP$}a[(y߀$KLx(6RL|| >ln0(xI>6fZwY6g'8yL_7. !AGÙKS%Z<6}h(dI%$gz_^q3=}~1Y%3;ZWDݕH `hpV"cu]\D:x.:I.9U+Mҭl0պԴg:S=m ^Ӫ,q83Y:!Q KKykǓӊCb=tH3}K .V٩rQ9Ŏo{ `Z_C;yNOwHb.XʁH=ob],ѯCa"%# >Άb]y\Y NY|EcḀH] f_d=S@>hueiںuշoDiFG[w{:ǵع:P4p0vؾҌ3S O׏RK,jxG6"n^v4O{UͨnR3tus]:g8k3@> endobj 3054 0 obj << /Im81 3049 0 R >> endobj 3048 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3053 0 R /XObject 3054 0 R >> endobj 3057 0 obj << /Length 896 /Filter/FlateDecode /Name/Im82 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 3058 0 R >> >> >> stream xVn[9 ߯в]D#JHn `P.nPvN1GESot3S.vEӯ}L6S~N9U(INp.@~wjm4SIajbn_[߼\}^›mNjZؓSrjX'c4qtPTD .3jC$qPljn T2bC(qdf|C|,ըm13<'aq/-a+ u<+*bV̝n?4]Lk] ֚, c-b{X/{=ncx8듰8W8}PfG&%QUK-h5໤%H5g ;5D*~Q b3g5AU`.rBd('>ߧeيı%IzbR8֊+]B<<,9 6h$cHRe1``'*NP ,!Ym{ϠN*+u@ZYQ΃JAӠb*ZR(Ϩ=ҸOJ8u ]`aqQ9O^rfA/^.YPJ3gpjlS'xy Ne/EMFjL u~R^V3#|o`M"gk )}e^4'|+Jkeͳ2\NKE o͡;' %7,9O -lJBM^qhetb=bI?WV=WqB_-߷ 0YN/}ݾ\;p]{(t|W7Nnַ?6J9 endstream endobj 3058 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 3059 0 obj << /Filter[/FlateDecode] /Length 1646 >> stream xڕWKs6WPɅ2Z!צrp8&d=ӭaRE]`?oAY!H%`(Wo`Elo1E}wJI8\1v=54}Lk.:[ڒFM՚NQ9}]k<~C{P8fEb(C=WuRF 2BF%E!\3+\23X0(B\8^3'FnL[@肗/SEQ,&iZ3Ҕu7b68YF+Oh'FAkx~CX ^w*E".`.Vh\u_n9DJ^#%sCŎ C]+JIw4h@b۩.9ZqGMʮmMI}"lؐq5 ]?_5u#\wQc|9M-,@\zη*[xgI8x{h)ꕀ \&}K -z\x`Lœ {^d %l}EX"fA/mM%5ƵHV3Ɠe0Uq{Ua.Nʅ/Ơӯ ? "d> @BxC绵:9Nvbr_~J{ҔI  )-Q L _m|}G3圻n\M endstream endobj 3060 0 obj << /F7 38 0 R /F8 41 0 R /F6 35 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 3061 0 obj << /Im82 3057 0 R >> endobj 3056 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3060 0 R /XObject 3061 0 R >> endobj 3064 0 obj << /Filter[/FlateDecode] /Length 2674 >> stream xڭZQ6~_1{Lբ $r=\]-aw(30ݒ1ڹ'%ZV-6>F?~GQ>K BqȀ.76=7 KaϙH67oRyym|ʛ7}4apJJ.&<)#Ɠ'ƥVTzqh˿ / Em#- `I`0lai8GY]^Y@t Xg5ǺYϧtχL]sz5UV# Y,|[g0mlDkg4fiYK^;|K,?untTTF-ǴP7m,g=Ewp멐%1>UZfHzW,ß,Wo}G\z{hK;RY8EwDCLI0'BҾrGU?T |0)-ma$ m #0M-vh n~,wPn?C$.AHg7 ́"ܟ;zn,H181ZПB@3Q.543Ƅjcr.NHLIXWIp?<,m?S6^.-?[cgSTOhRܦbhM u}w>jPV]cˡ/lIUjgF 8rfM0;Rl'.'Jڮs薀 U=eA5 thOE`:U.7s0ED:a* ZQPFgˈP!'ZBNKkv:js&0.-d~26R邾 TwUR;|.^0?}y׻:,EP]oxwܺ)Tr!T+#C ϫW'qre[@OhMD|( Ew5{W?7w/b5"D/ueު>o8Fw)4󗠙C,C3=~̫,TYJ)8ā+7v d_onOrA>crUDRca& ѴyV5|?Nvwt{YbNr1?ԃ-I|lMgyj{QNSw%LG>ķܾol[K]'Cֆ̋T Cط֣?a}!oNDkt!O47^uDZQ.UBej@/yڴ %bl;; Vxm)2ZtYN+Yig0۹t.gnAäIgg_7l)؝}Ef;0TXт A B 5e/"2{3G^N@D2C\$,¹{2 Mk303H ,ն )2Ga@p/A½7>l4)ZЗBkS)g\t-T%:$TfyGdpk5Kzض\aP](x?eLm'ӹp{Ф.iA?EY\%x|P>XHRKm; Y`O?`+ҿ1 endstream endobj 3065 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F11 56 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F13 66 0 R >> endobj 3063 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3065 0 R >> endobj 3068 0 obj << /Filter[/FlateDecode] /Length 1825 >> stream xڵXY4~WҦ7v!m#eI{:fsdOIf$xSv]_{ 6vyIMˆEb66^O6nΙ6{qm}0ޔWfaȻq{ou};jO_L{0y]{lR'$"b<jKyLwS̽.% E72oUѷv/ i5rT ViUݓkޚHoi-ZcTWOtVVݍ;t[=\w1 8I'eX*@'TF?.ҝWiIy؊Snopy O6>\"ڌk(SKBIJh"PݝQej9ME`%,s2`qb8:c>SdNf*R q^lf.p0}T"Fo*&,aT~FyExK/8^Q/eLki\7lE0 FآY ؂y*Y̙K3%'9,Y.󂟰~znOOs+"jbAownNv= 5r7>̍?Cl!05o~L; |'dbFȤZ !F, *EfLDa 猚jX,rH|wn`f ՗ ^jDT]|L%h%U4', D2.ogՌ")f츰~XSrcka[_d|6G_wuEN}9^vӄ,M8ٕn>ێRz"Wa$n *X_j,h?ZZcPL=VYQ8!(p6t7yFuFqT( ndOR ;ѧ`B+h4,ﭏuK̡䷕O'F8YngE)9vmj\V!+ѓرk&|&-o:s> endobj 3067 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3069 0 R >> endobj 3072 0 obj << /Filter[/FlateDecode] /Length 2676 >> stream xڥYmsܶ_qm?ߒ:3%7nS;S]Ɲ:G+_,w Adg_X,n +kYm7ih7GwUzdͲ[,"M'}-f{įCו W ")^*?4⩧־NUƟbd߻#B.宰az_Y_ZTx~ y6*""˦ᯑ&[A: LAiۙ vaLc!Oh-7w'w:h| "=M ~ rhq Iӡx!i &&ReCM$_XK1hؠD''5-ʦhG#(esΡy$GW9fpnyN4~iqfJUeWz9*^ v~^]:o!Ypr`%>?Dž%xj+΂?~dkӵBT{Cu&R}>7.:c he!%A- T 2 'myf kd0?4pD'/vJG6--뵎,m,Gv#* '3uɴ6[soJ #/ynтSk`اl;ɨ`I6H`7Xix*{jq@V!٠1!%Qua-#N:vY<ܲmáhml#b;ԗD c,GP[i -|,jPDF|z'A ZLN*&c%Qߘ1AďF87: ѐ,Etkؓ%&dؕއipX`똽z=pvsE- ==&ZpI0| A~ 0AΪ a$,ykRF#@(U(5X.5!%qg k_;LMbWd}y%%F-Ǔ @·~%of;Jh4Fw72I5*,'[Z3N\v)bm"23ɴG! ,8D/u4TZLlȀs;XʏM֛R-( N+咝C}F_ ̘85'Ԙ>1}{cXnPBZA'T埅߯ʜ΍%RͥvbSЙ4fe4qO6kיHA$Y$~*fA"tq ֜j ˼'PsP,@\F8e9/;ֺ7?za4vΦ$\**,\P~JϢn cކ0|0틈ާ_ң ЫOr!O N{t37ۻ=N˳pn *n]bgHf|c12k e}gƈTES }{h钊/)ۓeoh(?9:\w® endstream endobj 3073 0 obj << /F10 53 0 R /F7 38 0 R /F4 29 0 R /F8 41 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 3071 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3073 0 R >> endobj 3076 0 obj << /Filter[/FlateDecode] /Length 2491 >> stream xڽZߏ۸~_nq y"nR!T֖m!Jr6[ IQ#Ed 7a.Kx\Kb—>Ew_,~]"aI9oP5/|LmVRʥ/X0\M[6i/uu)/u-g\*Ǽ|_mW%˴Lej_LVSZMFև)+W"Z+VmeI92XHaс2amپ3t^B/\v/Y{<*T\0C8ZdxbiOh>#9, }&^{ mgeZ~F!6sXm/4jo~/$ :NVc<{Fu>"+Q/ק׿NG|8XJ UeVWU~_sdz5XMB*d͎ n6zibU]="b&hMYXJ])-[ kl@HV~)ǒdF0!4!>y-ogQ.JTRo"ƼԎE$Pyq1  `p( D󡇏[-(?B1MIk&qΊUi lmrg+m⬏H(s3tS-0qP| hQA "R?||w?kȬ3\Cp! VdD2\@w1Erm\ι"fwҼ;<@u_2-G__fNwxmn7]gEcNow봖ӓE9.ЈTA~WGF`#=D5t$sE=ФDSF\mqLx5|!%Bt_K%bB@mP'g'O̽`>~ mE3xj/ƵHeZj3r#T,&YC"=:KwY_]̞*wZd1GDX . TR  teJ1 tDo!O8* ހuZ6@O!jaYަMWMjtO(͹HQSُ{X]I`f0') D_NuIo#}F#!;*wb3SUuHaa9-+x 6V [|œfH6 `gVVIl3C3oޚ^,^hz>i=GeY4)k~P>D#ҋr<:!=jz'漀:km"R񴑱V:6VmW[?$c1Kffк> endobj 3075 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3077 0 R >> endobj 3080 0 obj << /Filter[/FlateDecode] /Length 2210 >> stream xڕYY~ϯ`& @fo<هY8p [, ԒH-IS},)>(JG}bJ2aD(^(B~_z_?fH{py˛We!.heU6?u2QJȣd, ,5!aBH/AEWzyYEw;ZXZ37oxҐ|/׆tӄDhyL.A!!(DPxf^G+*H,&T+$ґ^XFZ i5Y QOBB/`d &2Bj[` IK@&@q2!"|s2E P|BpUE7Cߊn7y˲^)X}^˩A$K57*ߗE `ulzo7y(ЎUsjxoץ:> .diD`>ohyOnĜ>b3;Rb/ <ʶ3 /Eוv;(Z\|M|i ݚo,o,|4ϛ }]ΰ6gY~?Uדq.?[ͻ}n?~zWE}4[\LN7pJӿli*2je'vd&{bon^*?[i1/z ^+׌;m)m;OI*=EP4*Z']Ov(N #'XSdDȠVM! ChtS4LhUL1ɡRE|U ["j/(ɌĘx4v aF~q9(#,2axZn1&AJYmQ-ɛWjF4)Z,08 ^9L`A7EmA?{=L3{[9T F#\.mׇ+4ȡ,[* X,8‰F}yyVB+w>[8b` !cў. RhO"!ne }"c/oqf q]>&ڗijPE RLS X62Ryx5IRq;P#"՛i$8°3ADvOFCSlkԿku^F^;JRʗzŰr;(hh_:$TƝ0}+]v?*,8AɊ[KAwTv&0L ~퉺^̻B)9Pu*sC $POBQxWO)MDuHp6bj8 Oo7Y3J&3E8?ړX=T ( 2؏P#gmb&X{]r>W29ېP=0D<ӶD4TmJOafb6#,$ R6h˹ByjwjwYն^=H("zi> L% *0)P h= QǪs2 )0/g.l"ifrH 7O6#53&c*aXߍrжJEw]s#x~ N:uJ04t̳=qK$Gbtߟk&k3WlblE5{jc@&yL endstream endobj 3081 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R /F21 215 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 3079 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3081 0 R >> endobj 3084 0 obj << /Filter[/FlateDecode] /Length 2811 >> stream xڵZݓ _љpyŖ P{P,Lt ݺ(f'^ EcuF7 |8UHNBbFStD1Z^hNTp=Msh6=#+7¬;Z3 UAZ6v^/'Yoы3]oF/M#/vr}1ڄFS6'j5CI u -{-`XCϼn`Zrey3jZd8Oˏ `6-w˦3v͹Dsΰwik'M:xȀ;L*3,h8cIl^iS*ᚒQ܊wIWeWַ 50\c]y2SO;`v7>o+I3af4ECp (1LB_&Uћq>,fY+\ !7CPEx PԝPIbWG_%{fNM [ƙQ>3sٷ7z#9%-댇dZ*bzCDxP)#zXPKJ 3M'pf*UUTԛ:[XTށcϢ«\XG 0Dّj 겇9c !]qH^hҡZ&J4'K%f30//zU?n"Q. IH8зDydAy~g S)C5|_R^'8LyL]a:*Ύ^PtcTbhxD~4NG% g?4ylF"vl_mpW"tDMM`#q9R0DK?9 _,qtG𙿋0"N;LQ5l2rޒ8ƥH0yJEy:T]70х}T\ DHS\|,hctSiNTrnB 0w|W5TtM/0z ?5=yƬ.UF~nѡM& G<=_r ӡzߜ1b0d Fq-G0e<8dhH M7N9R jPd2IR]DoFl XOP%Vٝ_v7@&zW]SٚݲV $7rFL-ǐREw0!ԋξ>ooH бYg){F 8ih"$mfBډI>|6jO4 m  5BLYX_SNڎ]nm_oo; ' _Uss'J:|?52}ByoOgK ڢT=[Z(Mu3+%ͿLjln=ҟϠ[cO5 endstream endobj 3085 0 obj << /F7 38 0 R /F20 182 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R >> endobj 3083 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3085 0 R >> endobj 3088 0 obj << /Filter[/FlateDecode] /Length 2336 >> stream xڍXo6~whwp͝F7^ ki/a=zdAJj'C9o8\u jM8,"Wb•r/y"i =!oeUխZ|U?|0apie< +`%*FG\2pn(uQxrZtEG#1\8 qHlvgƅ:ɏD(1 EUy]-etZ޹Gt{5t*,F y"5c˯653\^lsu[1gdxesq~-Cgˍ%k}z#^槝.M\F=Wݩh%: .ǡqVp:."GRmv?L*y۲I"@PQ]Te y D,dN}5疕Ń.d[T7zՍ}`*/zNuk0P=e02sS1{} %Y4OEF|ʿ~ 1[ .滈s)Dy3a͚-̈yCP3 $Hw]S 5Qͭ":0xHa=Q0dp6_o_JL?ݙ)#iԇld/x`ndSOih+E@o^_=9 )D$w˧\)U'XdI`3OB*SO0k!G\7;}י=bِ:5쉫4Wf) Zͩo#%Pacn漊X@|Aޚ:0)7}㔖 nAa3G5Lr]m#)E1yƭ7t endstream endobj 3089 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R /F21 215 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 3087 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3089 0 R >> endobj 3092 0 obj << /Filter[/FlateDecode] /Length 2538 >> stream xYm۸_&( kHHMq-z$܇XhmyƖ\I&E|g8Dn(Jcrf8! YWZ=tRDbuZE2bpn{R;ΙHW7(\oxd:d6)e B/uhںoN_Op(cW04J.ű }^u%Yן0*;E8Z;Kmd/D:ȝھew-s*_Cd[|(n lEOQ(wTvqʌiį4Ώ_1^4h9=8U՜m6?߭7JupW %cV_OH ޥj3KKpi>T2lKTg/m^6I7yHD l7xG_smB1A>jInp;#␱>+ ZP\*Ԓa`{̚&o^$]D244Г`@hP)p zD3NTOyKz e3mU9a}C h6XQQnZYFr9S g:8ܠ'vz7/mVM@ǃՒm㞑 t-$NbYW@vmbNEN @m~ENDEe=eEiˠ*3 HR0]2+)Y;̚9,5',V/LN`KA9slABC ֦^n Gi¥#@VX35蹪'Zq&U< V&qzB i6"~М)1@e M\)Lƞ<4q0'X9ֵTGv!F3~9%=A].DW얛HjhXS*e^6KoSsnlafHoh}PйBq%mnskm\bSiTW-oQhLJsP9uN՝^ HI/7Ti:%oچя}޸L'LSPPu[4 K0>h'0D@Q$J!BJä ͗1#W_o@w{>0@S+76ȜE3K;h,[?w9%y+~ LTPxx)Up MsJ\6 ٣M(Č|n .Lqr#Da%Lbqҝ?T d5C7t%UxugmF | &k0nid:OЌ,a3FPlnӁ~@q=tii1owP_LL#zZR"޾US XaJ 'ǟo\!_yDcQ9qɓZ߼~E$KȒ[7z{G#:0؜gk(cp N'peI%&d]w-)fZ=EK!B=_5œZ_ƫāI@D/V5!TaV^(y1sg.*fg12a}/_~7,'R(&?E \ε_Þ+ $Pš8 ܚ;)6Q/TYFխCN#)}}A;{6z{[j{:=E9$7oJNAAl4BW|v6bp6>3Q%=iI8mPj|,)&~ 3@W (Pt|vؕ3֗|Kվ6>[ M&{FɖjLXl*&BEM":?=.Aswj[$eno#=;kZyږͻ_&0|&^NuCs+wB<;lOFs 'tݗx'bBSj>bLi2K z gbeğ^rw!~]Hm/ 񳒧Ùvo ^#% v%c{J^xᅄi` g3UgݚkD_~4Up7 F+%}4շ'#QzW 1H47T@}h|:}7j-y]؏ qM'.'4eJ_╓|#Ul,aN@= endstream endobj 3093 0 obj << /F7 38 0 R /F8 41 0 R /F16 138 0 R /F21 215 0 R /F22 218 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F30 286 0 R >> endobj 3091 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3093 0 R >> endobj 3096 0 obj << /Filter[/FlateDecode] /Length 2417 >> stream xڵZێ}Wkf;z $:@x6j*gYuEh4+IdVf(ں?. pnbXk_Ïf n2gEJbY(wg**[mbmS5۩YB9ӣ,%+QmIt~H09+؂OLu_W?fwo܃5~ꏭ{GjaIĸdx2iMՇmoS ^"xlS3-aN/ݏߣ>zZ<鮈UWmgtfan{j3bf:fpA\9VRZ%C`~BMu8C`MԣvOs~B%y霴àA]r`}A-uS5nEq/UMeODhT,y|ֽ=~oRNkzC{o,|| N"kUB 1lKCN_/wOñY ;8@8ϸ}T9ͅ &`S:l쪘l)_7==vCp&{jѦ5@4}GaC—1Si^C]~b4wKNd4Yg>@d  a%㓒Re9=H$/]7Wy{?{o-#s?\Qtw׼Sf8*[,3?CsqpQfTAwg(\ceTPN)8C㶍~ê y/"߭m̙ l iZ ]`:"\}\:$wh|Ec|!*L_\.J/bJjѠ -ꤜ?S o7w{ lr@r?@4Ě%@q)(j9y/Boof=z[VHI+#eeTP*vU[]~봠; Sګg.DơoOi{UW5c议~(>6 NA., HU xWZtoMgkQ_e2N&S(hI"Uvxk!8ub} \^(c{Eiqwr1 TUZ[!g[OC hO>NB{Mԋ?ӓi7Ua{YGW)8Q_kP?4|F*'˼m3oi*!-J@ѥ=4 dJIkˎ<{K> endobj 3095 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3097 0 R >> endobj 3100 0 obj << /Filter[/FlateDecode] /Length 2452 >> stream xڭێ۸_at_l`̕HIh4)E"@;}ȜYJt&ӯQ,O$<7+T-3|"Ta$U^ITü^_i͢PEc-Dq|jou1f#&uewɻڇU߿""qrA'*6DNݩ hp.:&Wl;5=~+;B%kv,YhS׹:>تYޭfٻ)/Z@K+۹[nmx;5*<Y.PY%N8zD 1!``Y:1*A!*XuҩA`V>rhC﫞G?C]O1NoEj7R3T*AMSu&*2czW%ނc%X&*.@p,2i[h Z=\OMxS6̐l߉wwie-˺x_ N# mx dDIHc`~!шY) t]ÀQQ(_Bz?C]`bczY$X \ARr-4UPA'tS(RC"xUWYxD6/y]qQ)f=#.Jj97Ra@Bb,'`.LgLwNr0d!?Qu2 gVqY9 0+6UR'@1Ydi:A^-NXj~{8 y02(Vpc+qe/xR˹x#%?sXiP Hm]t $58nudB, U[<\# 6:`~bN}Ӯᒷ5^)oʲۭ!vFJCasdg=sR[],מ$TbCXh[DnΖDDQz{g{7Ug&L{gLLUU-m]O˕Pz؝yUzWz8u.thʰ:=߲ة+&f@O]sBw=>=H%G$)LmBI鮂#&)$W||*coio "JWF(k s8J ld ![_lX=Rd3`(v< lufvS$08xp!-SN7 {Wpy ݮgp~=AwokAo4O>oWv >Vt1' AKc]9biH˲DoI"43Ys Id4=r #= $@dRoRlQJPH1|C^r!J! n6ʀcaq;C{-+QXPyoo w?3f@V Z2U,$㧘 ];(}߃nC(LJ!;7TtmrDq}vSw$;l{.#9_FPGPHH|>( ,gȔ |03d>˨*JI7(ywʾgM:@d$…)C'm+r\E;?cVybF<=KK֐clG "H*ׁU| gPx,m;R\nj9~^dK;::x õ| 1|Npt8ZŶ{p;Lu(G[]pu8#( qP7T^c> endobj 3099 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3101 0 R >> endobj 3104 0 obj << /Length 824 /Filter/FlateDecode /Name/Im83 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 3105 0 R >> >> >> stream xWMo1W u}Ep 8G!EBC$>4AMQd3~v&7W^mԳ7EpƎ^wKDփzR{T_58݁sCL;obòX)@wV5@l̳Rbc)O͏P K@B_S: %- =#uFG/Ĝ'A),~ \0K8z,)_CbGKRB˹ȢMESHV[ fdtrrH6.7NFj{eujHnjY&*Ǫ=AruHTFSr!QV9WQk$sttĮr r+[ Ū @,'M:zפ U@os(϶~x\']IE5> endobj 3106 0 obj << /Filter[/FlateDecode] /Length 1716 >> stream xuWɒ6+xDK*ĉScU` 2Y鍤f@ݍCf(  A: ,QAf:Ky|~}.ZEp_cm6yԎ&LR}t /GjA,wm-Oca<^dg!សt5&-k?(˙%gn;G4'$'2-/.k:wIfbTިUޝg9 Hq= M7z#Zdsq( O1b,~ޝ2h^ly(P(x]ӗ`go؝MIiFS4?2'| fb2R^E.gz~ʃ^x zhRR',-~'BU( zᕙ@3e1qVd5MneyL+M'U9U/fX`?'7V$BHixe女p5g%lxI " ,0?B$Z1 ¼ _ !j0Lr$:FZa@1Rg"h~G.^`sHgHD%xQ(8ĆUo'̄I#%U1/Nc"Xуk=P*]?DÖWeZꍿd#!|!T 5aQ-!'B#!ҁvbgłw#/CŪ]UdvX#1v[`iV%X+Px" yG,+<S'rwrFIs1W螶FO]YT2`V?o7n*ͱfAԈt hȔ& Pq?%,9=b,y%w&$uwsQ<"z+Eh}{Dwσ`Hd\'_pTMSwVNa[nfT'agHb ٷQĻb9_cgb]XR<i!W4RR$1I&\LQDNX}\;(Y=[Lb!/7#DKAw~y7f\Hէ ͖8+v1j~ ];sN'-᥹&U*kIV ?9q endstream endobj 3107 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F12 59 0 R /F17 145 0 R /F4 29 0 R >> endobj 3108 0 obj << /Im83 3104 0 R >> endobj 3103 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3107 0 R /XObject 3108 0 R >> endobj 3111 0 obj << /Filter[/FlateDecode] /Length 2330 >> stream xڍXI۸Wr U%q_|s<)W5)(< c. A==ק7n2FwAS ع9Ev0RQ0+WOY(aP',B1lcKe@^4 AN:}.Б܅/ZPW8| lQH#bԸh@߃A|ʶA{4 kEN`K6 oyaep?pͶL7 {Wx]4S,vAvIk1$md=稲얉UA/ s[^a E/o>i˖)Z7}FTⓐÒXnt[=U[Br"*q!fn"MԼlZ63Fp |t`f>^RY$W[& +:IV;"옐V2]WVc% G 7~}\KWOIdP\% dj/r+1Yڌ=庈c1YœZQ)͘2.0|9ԑwIy=~׽}FCKv񣚾,P<*􅔡xP<5"悦S5" #pPVdv/bҞ3rH#L^Ws؏,;8&_ & (J{ŷp ʨ ɢUQ3 +Q>] d\bc\)*$@w^Cܟ_3(%!-.=D'uq:y]A{p0,LmjÏfkşX!}eYGEWPT흍i\LL烋ཾt;s% /2%QR_KS_tПn#N%O8O_ϐHdXUO97WFPړCC;k9`F&XScEou o|YD ܃[c}eUh^R;rH˦g1V9O)e> endobj 3110 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3112 0 R >> endobj 3115 0 obj << /Filter[/FlateDecode] /Length 1377 >> stream xڕWmo6_"wYS MĭzaX:hK,j"5#yX}#xG6mcmF 5BŞa]cc|ƃ!_;84U 76KٴUļ\66)Rdouԃya+ݰr}xʔDzHȍLȹk6#˝盼[S7tJi #cym,t Op[$ CNpF3X:Z$FD|H4:X M׶_>/Łrœ/$;GLJc[jE %A@/jD}4VfC6 aznb"]`ƭ;d:Do]vܳ;oCaiJjnjM~q†,.iIŭ<׾^zG]+ʄniInZ<8yod: ACYbI^]cI9֎O* =!Ej+{1TfI˧)D)8DW}ybrӭsmscn]L|H+ڀ/Ju: (vz)HS^\Rɻ/HKE!p$= Csj%9LW,oWVTk!`7TԻ 3Um^Wz쮽 g!T*wí(w D" opViHt^ Ug9u䞔j2CBʒK/Ny+v0SD7F}Q"Cw,*2VOT٩ϖX'|!7H̛G`f,5$Ǎ9]N ^&- %HbYS(Gcd,fUhP|1;oo6⊋Ruz\2@N7V]B!>No1?͇wg6nMnM]IZj~|1N`~{Т N[N~z%װzڤƮ =~?Ycv?m l uSu\ DA/:~-.sNG;6:} qGqWFqX)荦4U80l6Zs:-UcK h]-'}'!F*4|weӷaP" _J)^PmP6XP +tlpf3젋Ӎ 7\QתuDm 6͹u׋Ͷ:RܭͧFw.xmI†+\ѽpo Ur ϰBAg;h-ȋ{HS~,_a7 endstream endobj 3116 0 obj << /F10 53 0 R /F7 38 0 R /F4 29 0 R /F8 41 0 R >> endobj 3114 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3116 0 R >> endobj 3119 0 obj << /Filter[/FlateDecode] /Length 2656 >> stream xڅ˒۸БIMUxԺ֕hׇ8 I)R!Hk&_~i6FDam~!\7Yza6M0Qcݛw l;LPO $>EǓ >j?l, ~O` wޜu{kMyTIkB=9ݪ<;Vuz'-lE9pt8YD]1g*Lcoxg^dy0GnDȘ{Ӻs=Kp>oCp <1˔B̉Wr,E!ߧ}=#&Pw-/`a < y#^vї*zqד(Lso+[QZﶅ ?D5kX'e'a[q[-ӛM8VD^FlЋhNDi.}7tGxw,`t Ԥ^c2_K-lDv[WJIw Ri#0yy=^!qw<_2fp'1\ n`'pGXIpD[m/m;Dr Jفa_j ATL~Y;$XH a::{p2Cّh=/uEpαo@`Iwo brHS-Tu8z"|3~Wbϩ] /U] !a ڶUOa_APUYWz`DkpË2艾T$2!A B6Ņ |I<KwO#u&6tNJ`6`?,f`_@Q3"P-;g=xJfQp032,Y# ɴ'˴_|{1 fH8`DD0"1꽐/9ptGYyzppөWG(o颷)kC@@*2uK2h.@ERo#y%}w*P=JTnv4`I"{XeS71@O%Ptz.&3" ]o&,u[TWC}s]مz DgI.zg+hMα00,' {{2∱x \Jpbsqv<(I EsdtjĸPֶkDaz&d-#^+u\HoGB-|$Bq`Da@бN -`SR ]Ba7G#:/ 'hӫ =lXwQ"n̰'{.)K\ VZKf=8BA8O`7v\.5fB(~47s8c7QK}&RbNoozNPKzӿAFX Ɩ^Q~>Bn\7**}ԥ Ϥ37Vd5" K$ҡ06 b|5eLTR~5tH.o-׃{Pfd7QXL'PnV# ]UQtQ|)rtry*A&RFb1ÈDBH/^ y6F0#yUd ݝ]zgCd}g*ג/\.\W `wk{@YI99<`yZQ=TšRK}@ $xiph:*1`ChޔO,7 6PCI:Swr 9T6S->"8Ke} ׵ VF*A \+ endstream endobj 3120 0 obj << /F7 38 0 R /F8 41 0 R /F4 29 0 R /F13 66 0 R /F22 218 0 R >> endobj 3118 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3120 0 R >> endobj 3123 0 obj << /Filter[/FlateDecode] /Length 2167 >> stream x}˒6-TE!A߼{[lU=`(X-IY| 9Tr!Fw~s0\V4JBǫU^+e_Wϫ?F*Wy}#?[o({ׯtĎnW!a&&(^Eʷ ((#uʶ!u lu^VaB@u0N⠰گ<m90 jN_: ܰIpY,pγ^ZpI_M'[WԵF$8 F"*pN3aʸ&oҙ0 ʞǦx҃tpaLVˮmjg SFY"lK>Mg8S6=EZ/A終y3n"0D"GisP 4 wv;uO`7kk;-/׉րeuxTݲG`ǝe!x=%ЕrsEeqǡ_P+_uJ 5Enr8n]C-ʖb'W2<&bȗCfffXaOMޭm`ɫa|׼MhbR;MnĨH&*3<@6+?3}A?L M!"b\c9 d5Laǿ<[$UϰRFz+l;T`4!hղ%s5mFǒ vSxj܊+(h z&3>SlqYOEq`Tks Jp3>E}y[HKD}x!T9(5hsnkJ+`d>xMyx 4Y4"Xnz({CI G!>GA8qu^1}բYgf¨VPсd9d"\꾬b?c- #((dE|_JL5X>XTI$hNr2R \BA>cUav*Kɔ lcYLb[ ͑IԧrgMnr )4[m %n/nA4T;K.):I3I|E`ly[܀K:"AfΊ.dT\( k;G%d_{kχ#ީ7\^@ 徴U{PK9LR'yiyg9jI,߮”+[mǵa qseHc!\JU$j<[ 0ûPƵU%"#;8l?PU&ǁ/f$C>Vŀw}ѕR(hq7^D3ַ< )_čdJ-~)ooD>6rg2pL,[{DL1:4*9_ AqGz8{|\.JG?T"5ngͅסTxP/A('aufJt6 |R< ~S&J8~1 XC6 Z>א~vu:[~oxBL'\YD pЈh !L[ɐ(Kٖ;{R yu Ak H;h |ٗ*_J^$&!R`gٶ)M?#Q6VCX /^Ξb;CzXK]:RT)F &l벺deMJ B9bI <SyFjXBJwP"QPp쮌;$a@Pbq O^R2bN>B?8CUçR:'!0w`%$X4ş<:'3anT(eÍvwгvYJ[!zsw>{d0N>fRMno?/:cYD endstream endobj 3124 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F4 29 0 R /F16 138 0 R >> endobj 3122 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3124 0 R >> endobj 3127 0 obj << /Filter[/FlateDecode] /Length 2172 >> stream xڭXKWAj+zyrH vC:LLK#*VQ/{v$,z~Ut$>ytDT"Qe* 6)_h'vE|BOzxJ\UAۆ9t5(ۍm#s 8: E!1'g^RHp l>9MLNK)O.+QN ޮsѷD`j:3о K^˛Rwkr#-uSi3..AL?z vKk'K.dPx`PN%w. (aZv)B:*Joy &Q[=ff#jpwu<a(T1ev"ccK a(d ϲ;pQWsWh [' %n<92/?u3+en$BCWH4hh) 2Q33ԁ 9킈FETJH݃p2|$3d>d$G"DGմO %f}l' DSE^LaMms5|vvWo}_R  DȬ/]W﷈׮̙[Cd țP+B-(^3=m3>ыg\~zŧCp1#}-TBEXs1]Ԑ rX<4E\~mCTƾnպ_P-(4F,$ĝA0!Ee0|zp,wexGM' 3}Hŗ9z8ƍ'$ *{AeL@(wfI`r5#B)[w=(|:/`g]F&ᘲ p*ʥ)d+5?~RXU%LV9Ev9&@'|1geuug8xT=4T%.M xl9*DmqHUtVE&u{E5rwo) /ςŁQfg"wDԅ9EUV= Âݲ|xqh,9A'P[ݼVLhC?[Ht@3H)x's@@ˡpk[jbe7ǣF*S7//WGo'e^.::TbMC \uzg٢;8 30n<nOWhŹM?'z1爿ɒ+gW=%SBK@P?5>#-@G))& H.C\j.Qpgs7U';9 tJ;)t6ᚦPi~BDd5K\eEI>O,=C ^ML|\u.OI4I 4z7km%/5",xɆ֊HCNzWLwR(L\d\Rʳ fk9߆񱕦 ƛ%BYV"[aTzǛj7/ v Xp2,h5J2!/)VSl(H)!,ʨl &lhJz4㮢t,DV#dңH _ |U`g4i (2H0R,C . v_$eOb/-Aӿ4xjM 4)D.Wv^g</hPtSx/ޅ7Mw-/K0dX{ezU*ᐹ]* s6}v,fpZRQ4"kEϿR? endstream endobj 3128 0 obj << /F7 38 0 R /F13 66 0 R /F6 35 0 R /F8 41 0 R >> endobj 3126 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3128 0 R >> endobj 3131 0 obj << /Filter[/FlateDecode] /Length 2693 >> stream x}YI۸W(UI0pm]TͼTy9QxOp ʲ(RD5z"E}>'[WytәUҩV: fӻOqT^J^qj[3\6yǻOŴ/JeND8%6H6eh74M׆?ucgĻ*zfO*iP).bU.T:(S94'i4/U#.~@.)&)e"{kh:!x~pݟ^5bg*+5t3k <ڣTYp6{T1ZԣXmp=Nlbo:5yeD8VƯ2Ҏ9V4Wq{nKU E$^7y>volW<=H dßۉG#֧ѲfW,YߦؘS`q޵-Xu|TH?[pIɯC.G28b|^ y&Ƣ#f*@Hçinɜ Ty.'nj3B^l%DRd$V" gW܈ȋugZey_OcͱdMX]e-]0C06%Aə" ^9ҹ oy Cz881d}̪)x6Vq4}=u6DΜ4`na2 A!& =NΨEk?<`W,3!!B2€аY@ֲǘpWv,2QkDmF|{;~V;f f x;|GD='JC! HP4i< bu; p* $U^02_qbF~yJnn(0[\$= LMLZ@/-#0pۭGj!|-S"7߷t}Ĕ:Z {9ihLĀ'Udž>G}T鉍AS0n4)fceGan\D$r4DӗwwhZ &@mq/֟t|l%_&&?4J~o\UіshԘ XS P!>0!>Ϥ~p82ڎd8N!Evn;_6<-;Q:$PYaڙK|*s4mqc4a2U Bdx܁4߽;ϪoT?ݼCY>=̗,KBBe\p{˦ fS=<%.NJ"Qx.\,ײGGw|{іGK Bo=Rf/z7ikAI3.%LdAyl,‹"Y[Fw:hl/fH[;k{0|*LepfHD݀O"܈x_ԯYH_߇aҽҽz w#JA/ui endstream endobj 3132 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F22 218 0 R /F13 66 0 R /F4 29 0 R /F16 138 0 R >> endobj 3130 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3132 0 R >> endobj 3135 0 obj << /Filter[/FlateDecode] /Length 1542 >> stream x}WKs6WHX0 cO2gz{HBCK{wCӋ.(aI=E~ <\,ESȓ6o8aͲ, "~Ej7zwUK/}dk(\49n*be+IrlKx^L`;(¥[AGm ;iNI$͆1x ]Ǎ:pE݅I)nZIYQGs/vy^MVf.X"pSĽ4vOFTiYbɊwo*&&?|V!*o?+/T3bYvF5亥 ⱷ&;$mȔ3vtIJ+_?eTx37Gx E޿|J$Л+: e?XGޣuNph[tv4j kWsꑳ H2MB4aO G˚I /q:)LjN{c/HpLS5C:_*+ @2(pXb Ac|4Y=O٧_. x6wTV%4)֛x 7n1҃*lAALAvTo(ل)5"I!J}!H8\ʔ'adw:ĒaY]8#"?BQV$#N*kx}zK iaC$P*>T†ؑ6X?8X>FF.ROrQ4g6R:W~^sJ 0Kz!&eUP$*Vk<,47jCYo.óJDmgzo>ʍ1prC{T;.b,!0.)nDx+9u>E> endobj 3134 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3136 0 R >> endobj 3139 0 obj << /Filter[/FlateDecode] /Length 1731 >> stream x}WK4+ré? ,Pl-TJ"Ə )~=Lf9jIݟ:^ ?l9u~Y.$p~3J_@*[y\V(*x: =Ni7xCN]I +ru9ݔ*_#5 Xju/"ănc˴Q%٫t+eQExa Rڢ=5R_][ʲut9U8[5x%Gqp#wu՝J(wgӵ,Bٙ$y1U \UF`t KG-Rn^@ay@ƨ 4p~L0 0g" nA۬,ȁ)EHH lye–0Tyt99wW9rf? I*Rm;N-yI,?C]x;]/LZ G3}FӕhVi-魯 /xyG /g=fv eV8rf~'%Zu$ *%#H$ٞC]̛l b$³9)_PrZG!6Er|P% EA:,Κ=kWGʎ>-rJ(zv,F\΄72zݏrGepWɁ5*7ߊ;gvb'X p9XgF6!zA Es)3h"O_dLGc[&oXh!̧\2%d;S],eC8:0+zhYV4&`[]\d>/UV@eA~Huq4 ]O< ea!WNc zspqvU<'S+߹c2~ J AWIs@aNv6.#YAKG+rj,ĻޫWX1_:PW',К!gL:Bz%Bq5T)$B\az}:Vonp$EJsM ʫr[HA(D9#'ɫy9sy`%Q3vwA8bP<0;N';+A'0^n`B`j5xhhD=  sy՗=s)(5LNCyj0w3(D4 JH`\Íq 畝E铲Ԅ!Q5^gSo_m+eBB;%+mz"n Ӂzmb˅r-3Zܬ\1I_Q;Cb\bZug5eY47>Xot^?2jfʑCJ I CtrkzO-W`}?m% endstream endobj 3140 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F10 53 0 R >> endobj 3138 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3140 0 R >> endobj 3143 0 obj << /Filter[/FlateDecode] /Length 1900 >> stream xڵYK6WLjGXOKqRBA8؞؞ߧmf$Oj}nBmFnW*$ _m$![+}\[=>~S%o%z,xqHOyiEp(g1ۥMVUQrkdWEt6,*|Ѵ2HP;]׺U1k]cO:ׅɊk670;sHݐuRHt9epmhՍQUMPu߻@upΟ`Et&͊jVdڬql Ĩ<~ {@l/sIbsjЯQ? f9a̪>D'%5)j4k/'QD"&ů6HF4UZǴ{7nVS8# 83->an"C4(s;-p;mz/TY[t~Zfb},'yf6܍[&> H@?@Jw!i}c5PV:Fہ@U5N-umyP`/QUkM;%ggE>~pϻf1Ǚ0tLXD最"iH)4 Gm-!L&#年!n~q?:rs@R)89qGhB  L Nƒ/8c+X<%Ɩ< ӕ(x48$!27Q߃Sps$ϻn'UIL`l&$j;MFzQFb-xܓ0!Txx8#5osxv!í707Ҟ~Lr%R T2JoI)"<m=3j$eϿ U3ged/]U {؃ə߰ $6KQEv:ۧJn.Rg#k[n-"Ӫ>+Qťe[2voI6Nz]%؇ q ׏pѥmYk\ fA?4y~cD$"b%6VQ@+ŖFL-o]{ͷ۳].s.l,-z .տ݂&b ޒ_JI/8(i^qޛKAri`0vfROApdC`0AL£1t}O_ZڀA,Dz>8s#[Yw-?F0̑pcvIb;xB9 ʯ8[>3=;2 ^M%-UV-֬Y"'@3fTb~nڛٙĪkFԥ"lTDg"P_[1vaBMpnN̻?qWF9UՍ+Oᒓ"jo'ǯMjrĺ7_~% endstream endobj 3144 0 obj << /F7 38 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R >> endobj 3142 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3144 0 R >> endobj 3147 0 obj << /Filter[/FlateDecode] /Length 2300 >> stream xkܶ{`-e$ÎnS(Cn{+VqwCr)WIΛMpsџ?Gn6Ix|sوX0pmS&rjq}3,g*~Ƒ GD< ~岦VDzI7p˙|J`B f#ie}BDzlV7=  )}G`aGp"KZۤsѓx<1OфC[6m9dl"3M4DJK/U_̨9-.|=/ER䴓#Mr"@DyT߆Sq RЧC{kZPԚi}(Z5 K#s?>?)iB)3GjUgb*?~\oy 3d9ŽO@1/y)4 TKW^P7>a"l2 ѧZsׅ]tZ*o`!Z|N²X?Oq_;S&4t\!rScFkT/| nhl$ X=}x+EEl-CZb"jL>@A rT!*caJĠ>nHN<ֶ/`eʪ'Pa@[giCUtmte]F4y1;% Qin3ٻyD1\`wꭺPH^n(%Z!iڞݞVs[uT^vѝMĝ5ˈΜV8G~jQDAQQi2(l|soJd>ҍB&t~D1\,J*l˩0XC"$K >pC{5(!M }NfCNֶ endstream endobj 3148 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R >> endobj 3146 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3148 0 R >> endobj 3151 0 obj << /Filter[/FlateDecode] /Length 2178 >> stream xZ[F~pzx\`TJ7I6mT5 F.fY}KXp.߹NB_b%|~! wŷ_DrEK鋋g˫m|b9_2byyR._,ȫ?|E0P>1nSFxbsKGXg$#iRG#~sy{'u`2D$hnb_Z/F͛ޮ^}v߇ s{._Zh /) OjMߒ *U-iHB6.se~ejˬ%%.V>YY;I5<7aWz6e@Tq0`q%QĮ(b7ƭV5Г@xee8@79SbԶ s,sbP֐$B*B(e~AAb6`r!2B iHOVFFدoFpg$J%ZU:*'1!7Y~7&!cS5)z[<(FimLl7y 2y^MB( q+@И UO~| ^8! j2c:b'ݫ?MKv* tR>f4Wu\yN%g֓Uxok)]sSWNgZr| YG('5΃ ,tϡTI%tɣB37uz INz Q Fi#M/_.0/? 4x!jks- ϋ6" ӵAa."sws_|nZ)I9R>N1c|ɐ`*[',ոpxRp'Z` c6!Sʛ,ߘ54&5= cq_Y!DUgJS@ҢXMUjcQPBꙫӇXO 4-r_b4эT$F$PL2U6!D4ofC?ei^obAzXLɂx bNGvT8IC= L?E1jp̋@;;?v($5pC%f;浞0dU:aYzSiStdF ~~*q!@if3goX FM[q윎 |Zvt>teLH,ʍ4 )uaն`yަUj6i\g\Kd.Ӈ"p12N&&u$ɇ$VU-0}1l3`d;`J}V[g,nj ^E^zw.vE\+_+ޗXuiKPڸRmJNM}X#gOCxщ|_8nyz)v[#LGeE]$ι,~z+^({ɑ4qt5rs.=5T=-~>S;U?] BBmAĩ9S"! 21@:RE`vT *=S(GgZcv9ܱR1߉7:> ,ÝwoGwWvN=5Xb@YMˬt)1<a%^N۽dd%ƞr!w'+ȳlnƙ[;u/ _Ouu"3F#6­=gySVےBk#ࡐ}n\>~hrx`}윱.֭?2|V):* 'U+(Wz#nL?wOq;R@Jq3.9uR= j bKyDҌhjPu#.#$SE^r͸9.Suxܬe?F}icKj}Q//s[s- [W 4̠*M endstream endobj 3152 0 obj << /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F31 329 0 R /F4 29 0 R >> endobj 3150 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3152 0 R >> endobj 3155 0 obj << /Filter[/FlateDecode] /Length 1957 >> stream xYYs~ϯT0*vĒOKb\ `E߻{z\S AM_], Ww+f:8 A]ǧ={bN֫Њuǰ2e1hqi׻F 4&7GC/SU|bEiL7D.Y@,f[SXhX#]\W5Hٰg2}hG#l2oF$AnڶU[]o*oJ`:&dh/Gvc>ex +&ઓ8nDW{(8KΚbˎ 9X]5+z Zz?gW38 9&Ê}a;iwg Nu 'I`UT)rFo/a 6osA"[`[fGk#8x09hNn{$qdܭ[YI:{_hFJ3 ȶ"_}cT$ۦ>Ю W^DufmQOxl>(! ǺKy}8FH<늺bAPRbRa6dr*gk0ZhkfD)lr+tivlǩOd]^01cH{nMp}NnD֍k bHp2uCBF,rPT e,ڽ#RtN@YEaEHҁ ]["䲂w;/G6/T͚ʥêц D`ڥJ1ݱQᡔ.Km_(V{^7)GEv]bcGl)@݊pa9aȦ 7{^ma>P00f2Ad'H@#ˆ(ip7_L‹O>v.(Ypٝe:+XB#'mR"ԿJ2L<aR#;}ǭ3%B2-lgD(tKГʖ^T3M4ƢKZzB_<[w~=cO1ݛ&I*?$dع y+a҅V6֧Ndɔڗ8K,dܝ-<:J)Ӊt3pѫ&z99 ڷȾg6i"k^)4 C"!ϕe/# .=C$`FSCN` 8}Ԉ}4xӊ/fhOgMJ m_B :"LKjz!78EpXLųeq FiFiؤ=4ǏOO_PS5t0e 4+>"O62z|x@ݥkE?菈 .OYsfn64}*fSf37iek* yCn:6ݝ}݋W~w+;~oliİ-&hb7J~zGW? ֗ endstream endobj 3156 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F13 66 0 R /F6 35 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R >> endobj 3154 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3156 0 R >> endobj 3159 0 obj << /Filter[/FlateDecode] /Length 2515 >> stream xڭn_I_gER $=A )'8%˖>E?3%ˊspwGqҘaĕƽ̢̣]I_1^dH>e_h|0 W֖x(n+Qke V)  yx^2{ʅ)!9%X5KFpGvrɼ9k|eDScd}H )阕eߞΣO0p8Wۊ>i@ζȧ !V0AdYd,gna5oB6)luE^|zy~^5 ΞC:*@|Ev8"xEv|2>ߟj [877I"Ӝ bgVz0EUc=DYPk[Z!wi46a$ʥ/{t:ֈ1DUHѧD){CpeZcҜERBA i0ԥP7Hپ͞_[3|#{f %jf="ǩP9osÆLĚx"'0 R}GOV՛"[2#*T:!.hbR,bv!UNJT1&v'";g"=d]i{(TlnҐ3VUGΪ9KAT_WCxɃSP(kV'qڭC~ ŧ#d*`mrE>9Vy1g".B%ꛡjfIvް}sgKZndھ:7v#HcD3 9t.2p(('iLr(t1 {pPZ1+o?EهhC@,~KSY.1eY~rؗ*$͇ÄZ:Zݱ18%t零P/j*'0"@b*/IOw$4\6Ko)S~IsI~eto̅TS9X9˗qҷ}D&Bhj}$^b%_?/ۉoEmZ,cRQ bLlRiJq;E[ySU{us5^5= U0ijdO1;H$G)*{6Gm9fPA^`BŊRj} GXLePn>|' XzGK*gyk햭4nP} Fv;Yˆw4g[.v-ۗҞ u"Wi7aC:Nw @}j3bg5 ) _N5x#ޏm`Ȳs[L}_;"j$rh$LkɛhЫZOŹb r5cTL~d4H_*eÕmkkNo5aol0 k dv˟b&`#3j5kKs1ü~0addHhi{С=ttؤ2CaR_BS?gVvy l<kaҞha2as{@E@2)].+]/Ӡ<&)UYӜc1ZMȦeAd‚WIy~Q;/[%\X"L {;e悰ohf[xH_O0kJM՘&*?¥ 苠9锽sScpȄ:׋KZȭ?ysQx~oP̞:\hD0iHaͷP+zCEF!nd h ^yh$X[&* Gev^ vn%vfGɈ '-6F~Rxf kҍhJ'Z.ѰfTc뮲a|G?oJs2TTu*Lw`[z lLwn}IAni endstream endobj 3160 0 obj << /F7 38 0 R /F20 182 0 R /F8 41 0 R /F4 29 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R /F13 66 0 R >> endobj 3158 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3160 0 R >> endobj 3163 0 obj << /Filter[/FlateDecode] /Length 2731 >> stream xڵ˒P%kD ڭ6kS+;U>dr(H,2AzvRx AQ^" /tvQE󎆯aHwϻ, di] p/G2,iCQgIK?ܥqЏa/_|)I0Mw풥9w{ Ɖ=@n"ςrit]PȃscoLm[ /L8"8cA7z ᷇}VתmT]#8,SQu5Un u0tX;[>. VS?I^O2&%䤪q mgO85ގn6]AQp4hYG2ȲII=֭3/R 88ajmԽ hXhm$LxAuˍ @"3S='ZSF ѿ$|vfԗFuh']ž{7$.l(0D J NlnUE#OvRې7ëggT͠ &U 3&z sn; rB/q o%.95~~=puۭO/sa!ʔ\x Nm_ " .0Iq;+գ>n5;bR\~x>آކ#/!bAL[mpV' 25c}d] F  vEg)U5_jV*ud>zhutx wdHHx8F|]@op^a0hI 9<#hI;]H<%sH45kbfF57=>D "!L03Zy8YZƐs}3Ghi"E\#b,:e7MK9(@[ 7_ǻ2, Z'FdF"L /e L 0M,jE3]2Kg& ele8a6+iq7p|ެC}ݧzWO=ox C5VC (AS)ݞ}u2P(I.BCeÂ<\Z2=!})0|lW7VC9HŜZ-շX$<7 Z`|aäk;X_a E|5Lqq(!vBX' Ryy|l*9Wsu)gǿlO(•ɑO<cq)øSyiXSI>ZfdFow~Yo%a{V~Wc{U8y\x#o AwE&هtʼnv(7aNgy<jTU z`yes\$k,wA{ [e7nxy7<Ρd=ؿ_-zl8ZcWG<*L%x?T ]1 Qn~B> PtYܐLUW03w#rɅbnd|ϏbC⚪Xoǡus*äȻTkRkïLzZ2.9<ͨs]xt4+aSzDz(Yz{$X=%k1n݂5[ۓ"x0TB8Ֆ1KFn.+GoxSxyyVwŁe9qoj֖ J`&\>rX͛랱[dĖ8C{߿> endobj 3162 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3164 0 R >> endobj 3167 0 obj << /Filter[/FlateDecode] /Length 2310 >> stream xڭY[۸~ڇ1ע6)d>d ˴FI{.MKEǏR,֡WKxR:{|w|p~ID:w32qq|!}/_vU|yL80 g;y5#0@4*PXH6NΛ;HF b+Ab3 7Ѧl-r&**U5\=?zPn9go ޥXD>(QWtn2RyM02ٞ7y3()РUb1J=@0O0Ф3A! v{ ˏ!r/XuVo,W,;6yʼn >leVjW&߯ձńwp<sxVyute/* %^UZ5w[U«Q5OK2o)XG`_'E{iXw)'eT"L0ci]:YҘa=ftV04؏!ZVUK2_60]Dܮ8 b@ig܋c1 4c{(9+Igv]BKJ  4_kJKHrVX Toي`Hҹ g5Ha͓V|(^%CtNEvEQ7KGp\0jwS#O8[mʳ]ڲVx֔''׃7U͢}Z[r5%4Ve2"@lѠwUB ypw,J9P^s3Qr{;ZJ}.q~ $ɂgcZ4?]n|pѸ9jPv2[-9:FG8ڮJp@j0Us);fReSSYjuFSN厫B+FZxpLy (GC[eJĵA4 18zKs$S%^3O3{!+p -1z|[PqB\_`t Efb@ZHO"xu 0!_kAZ~>>EO,&knVPrݭI ?=ֱei $mŵ }ZfZ&ƪݢH7{mf+l}솃.v^e, E;$q3q2]5a%>\Lq/c_NNmiR È7yKߪӳ5[4#_"Ev@Չ}|XBJ"F?OI!Ey+}(Y4*21jؠ=֔`0#|Gz(@†N-% b dtYoǞPQeum`P 76Yշ$`fHrgx+./G;W;SiZ SnZ^K?MQ6בՋ:}^J[ui$7&o#<@ cԝp7S2J(RH˥M3^c5, iۃ.jc8!{Ђ\L.C!\7+~\["4V$)/ t(?[5;mn:,ֳgCWb|/iYҙю0d:Q ^zֿ j_4o>C5_gONy;AG>N&{X8;R|+)W[:Trøs P[Hw\1~yc[.=Ncʩ}Cof.e_4paL?'q%tdSZhdIxlϝ^;v.s&t޽@s93W3cmҋ>9bws]rqe o$  ji2S*O [ endstream endobj 3168 0 obj << /F7 38 0 R /F20 182 0 R /F22 218 0 R /F23 221 0 R /F11 56 0 R /F8 41 0 R /F30 286 0 R >> endobj 3166 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3168 0 R >> endobj 3171 0 obj << /Filter[/FlateDecode] /Length 1877 >> stream xZo6~_b@<j>lm mˉQJdeI;Dc>:IGxwOIRU~<$"E9M&$7 1bWowWp(t>ɓ?OSʅ}7{X5ՇDD'"xn %d2! a)rp\d-b7!f3j.q;B˴,"P50bIEi\7K>@.t^{XT, \]Tl~9Y^g+jٹrdg azc쬲f.F%c,u.bVn hKh~ws7=nN wN{GBH>vuiTW_Lؑ$*u`$ׯ J TuWO6zޠ+z?dn @8kvMk8p\5ƺo8u11w*eCb}U_>3R=pW-=̖}~Ӌ'˵^Wgrd zܫv{厉~=\^lXFtG׆~6,acbos|)UU'ݏMH\Jz/ycEǨ"W]g~_/̬MOg甶Ȃ>L2pt+{TLm3@ ’X% @I#Hȶ*鰯H@ڪJR$5Z)¬;6z>ZeU^~ޕa: .>h(@p0T3(Gp9R1̑ttnE4Kxy=wc-HCU©1zvSsݰ4PT%_vEEAM{mECQ4auDp$dkhY%G]L[;]kS.z0,ܳ#|qDv؅_b> endobj 3170 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3172 0 R >> endobj 3175 0 obj << /Filter[/FlateDecode] /Length 1796 >> stream xZK6WhS bDRMR$@)⢇06D\Y@|IJG"͕^Ґx[G?sJr|#4y:rd~I/ħ|7[W]0 G?Y:UioM~}'VL#L妓hپ;|O~ecܾ1IyCM۷n9.Rլi bò<{Gn^l/lm4nya}Z霴wk]Jw!B)"g5[yz}.+!Kw @~6 bfFeqSTVJ(\Ph4!ᆵoX|3oX#.K!TGAPFXZ>WM ͪEfqqt`Z@x@֌U=tSs.(KVUL9:OA6е>!X- @e;4AWB۪zR!Uu!,'bC <2D#K -wvZ:]EÚHwqذXMv޸5 ]l &'&2[XKuY U@i jҠIj$5I6O(ef/58IwPsCCD"!γ6?t E v2ܦ0ܯt#C n@"{aW::j2@65%1~Cx(DP"EA`X}Ghrqb\ Aqnͳbj'2$.dyK+͋C6tT=V WKMΈ;ѰP$ X5Xh|Ae1gͱ QKCA2 Kx>0ޘ`y8s$U9DqIDExToqÊuW'Fmٖ);gH%'9zdW $kA;9GҥMɋ r&fW1pqI w B`OڞsHZ!Q,Y/H( ī%nuuW. c yyb| y)ғaj׿gcy/ӣ1r nj(z80-*^zV]6#(j b_af$ __p*3B7,YhgBMhG!Rdgtb1*bŖZfMBHξ{|l$%P7_I_ڕ0:Qp`dKͮawD =k?s صzJU{4Oj2B%1@}  da~'th7F[ /{?@ m_:{zn+kjH$qi+k]dݚ:Jk|8rvar|jFCTyznZ s͸D(PI1= yKz_86]yjJ0f팅_XsY~m@)ݍzd[>U /f:I[}a~XK.joW'lXwۊ퐕" Ω.a#sTH&dfl`yi9sf7ru6e5Fhym\ɚ_=R˴m<6R)L LvUmv2k^:;&:r̉L|/) endstream endobj 3176 0 obj << /F7 38 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F30 286 0 R >> endobj 3174 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3176 0 R >> endobj 3179 0 obj << /Filter[/FlateDecode] /Length 2458 >> stream xڝYKWElHكLv16d;LP[-,9= O(v=*"*z0\Mx^$b*4",tÏ:\*OqV&_wpw˵8 evnX<7ӑ3Nj$q.26W)lf"-M;aߖuln7_wͩd,:TirصgTԶCM_gk3Um߻Ogsg~E عo~ew>W.޻1߻PtM˺^3t=m{^z&l{>}5Tm{95o:W/DM]8̱Fx(Ѽrm@e8W&{Hfzdj4pqiw*~ݩq 8y]4%<eC[n7u ``P|7: UAU'diS6lnuL$ #Lymy)i}%) [0"|jx۴=) IU)wWtVU1x GR\5S, "uIޯcd·z{8CX˼j؟y\5av^BiдÊ{h~1qڔ:+F2JYύS)܊i RYQ5%z&~7`;/1SUeZ+^9e.Nh66۵]X!ZqdOqdw`]^h4E&&E'JLyWl^bqtou+HW8'q,Dl_@EY2CgPRLbKԚ\ C1 Ч40v/̽L kd#0ݎHf w͵iۧƨŵŮ67s@.Yӑeuxm2eyk^5GN <ĉ LՖ MAcf`6þkOHcVUr,&>Bp*XJ Ifj@kɬe}>AҴõgFO9E8= 9|FpH x n$Pf 61˱Ũ &jsoP,>!<'Fk kc{p* SnFn:F(K.8|dZ@Hڹ# 1kK6MFiKf$6R/ ya,0Z ;A Gg\ns8;z~35e%8Ђ0[!?({Ŗ!sܤv x8['l2#Hka[Qt+ >N-mq2\S u{lthTxW\77qHL 3B I'8R݂pK9MQQQԉ7釕Rb-QT s-i.-:hLGR2ӁQdT2찗}^IC0 oŠkث6yDN0:'HB='xIA;nt- d)FM`r3g Z%##یj[5>Y;_+#4fX[2}_ܙ yl]x7yjHm6$\RR2SsGïΖ amqwEŕ"˒3ʛPZzUj,s&+N ֔PKuyV5y pS;T `DVⰶtGKN2<(tB{4odPX,r;'\╘sQb!T)d)TqsJ:>bMQrK ?$k]ca۰7=5~ZG*/ 뮰2^? !wl^`Aو*u)U)w) Ě_9(_鿑Du8Vݟ1s,GfָIƸFJa?˼K0ڴ]tC`}W\iͩ.70 u$%ObV㕡ǿ=ZL/!EmyX3kGp/W ZM_+`W_W0 e:1T:iI.dC>('{UR endstream endobj 3180 0 obj << /F10 53 0 R /F7 38 0 R /F20 182 0 R /F8 41 0 R /F16 138 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R >> endobj 3178 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3180 0 R >> endobj 3183 0 obj << /Filter[/FlateDecode] /Length 1866 >> stream x]o6}P@jV$EY[P #1Y$:ajlŵqpel[ޕ| 6g'#Yyo6/y5\߻9 5*,pEgŜ8;`ȂLVUV5Չ"Rݦ PelaEǑW>oxξ;hVV8ΟydXn̺ 8,m='qphZ > Б(V`77Spm]Z!-߈j# =)kI>ဇAHi%ͪ'-OUVJM Tڣ1A4D[:v##iߎ2'r]z\˝c^0XN_\\z\d3,=tM FHߧ|)Aaj$nLNG#ӽ]zW4>m4^|%%%HLY{ٔk|Y*G/bpXW.q\j=yuLbŁW&〞믢UH$(bظ8OxP젝`SzyNSڑ'tŌ4<1 z Ġ?6!4k[Vǖ7ZW~檎5ċy5opڞOaTóěA[]3j (d=&uj٨%! I¡ohS>EZ5n3Nk^t/;!Mkp/"ܻz=xtz.8|z%^pN6yֽFyя ~n!(=":4}nUDBe2F~<:]ߜNN2L-ċ0!Ҏ01V⩤%bn>HV`E̫bRy  Fܻ~n+q iNIu/;fpnn tQIvNӢ=ACIQj vA=hvDu]7ǩIDGL y84v2-Q^#4>)A0&"KTyZV=nXj=Sү2i}xE$IuDb3K ; phĴ\fE^K͘@8, ̧?|Lh\7L –MC8݄viA|0*=9p$ƙ:3@3to ܶyCVCM&Ս Z+ pm?ShUm_ &Z{X^A_JP7X1^`LLYhM1z^3 ;4g|h1ha\iRzVFjqʻaoeKZĮ.F8:C=! ;>W)p G?+[ׁC*ʿ endstream endobj 3184 0 obj << /F7 38 0 R /F20 182 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F30 286 0 R >> endobj 3182 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3184 0 R >> endobj 3187 0 obj << /Filter[/FlateDecode] /Length 2079 >> stream xY[s~Pc!kNМ\&}IuqCSPxi|w #{> \,[h1[<-w,Xb|},[-mϙH_ߩZnxK R75~NۨI1 gv C󋵥ϸ4S*_WkF&ENXPW (K\5Έ~nF\{0tI3cĵWu>~ڨ-֜$07M!V.7vů]DX>bƻ'+RP{U"S6 >>Ηž9"e0n=s nV ##4xЊB-Q^*uukHJKhee,H V7gU(8„z[]ica>VƓVOeڴ|,짹`2Aכ,׃̢gBG~^!b&|q;:l,^ nLωzVu=: 7d^rXc ;b.Y"޸Q{1Nt׏TLL tܷMX{"Y:#J{ CF۫f7}`I'EF5+X-=jBrHW*tX 1\aR#,A@7X5g!G[m9w00/zct+YEjD׻K֤T@Ob0hreZE# n%gq3m[54jv)7 UYjV ::KW`?SP~LٌfnKS~ : }ER᝚>&6R4&?xoJbhKD<`IlPfEG1#Eq#vKܛM`X QS2)t$$@2<o6B8L3s Yma0+ 1î3bH]\&o\]x`LU3w2ϳVlf||؟{剹PQV'#𬪟~oU{T=̋2]/uAc9R,-at[Dy㯭^guΤaޏfj~Jԋ g<>yJl7 ft׸򣢢yrNfo\$g{ЏgQNx">SoK]q K%tޡdw|+&\;yl*4J!|MG#&Ź6.5qdd/NmN8K oHV̈́ ⱛWmrÞVkt3]|fc^ endstream endobj 3188 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R >> endobj 3186 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3188 0 R >> endobj 3191 0 obj << /Filter[/FlateDecode] /Length 1744 >> stream xX[4~W@uuKHHPT'2ώ{H2;?N<4Uy)cw| BQz$1H#T`'(".ibu{勷YP" .7vH\gbIbwY,)!e}ͫR.~ B052wʵFm""e~T~'2Mю`mirҙzprbYL +s=GmGbhb=lLH3cƵ6LRxe7Z~6l`i0*!ʦHrDg MA>_ѷˑb }5BʸY (άcKqNQBYHNrZ&\DpFa*CKjj2SR'g1@1>K*=Үl6aw "x`\Ո7zbh`7ݗ, =Aԛjνr}{hPb۩EY`9fX%d|:C$?t9Y6 `+Gzawrazƅ*kXsbm$u-0sNV`i8Zukε4c[O,܏arpcTgBHQZj(ռ8A'Yc$ ?۹x5=~fG\h;[vSytpي$˷9 fBn͓~!&atrܜDe5r妪‰󏷴^bOVN:R(4P':4=xX,]FqtV;>8R}/W{]s&pqJ}0EpU'=8. Mm:8Tem߰4r}! I1Oܱq=>ҝ'ߧ,rĞ _e+8/TcY*qAƪŒ䡕0&(?X}dʜ({tS b~[-Κl2qwl4U($ 0mؚ]$l %L䙗^haͅC1~r}) ЈѸI מGj~R}a1Nubb>ha5ajAC`E'`L^1,88C4ѩPRy(7> 5Ex\4MF"'jإ5lx9t&C37FUҦ*ICMq-شW󏦥RtƕϤʵ5\T(ZnN<5= ˺6wQhN#6^0*룖S.v5%׀b{r[퍁ޕ:&v2s;KzauQ_[Y%UR \]lXAjkzqi6{ŠȰHQ@@pҕa?= +͌{Q@:Ժ7$Ef({=X >zsЛZ )Ǝ?=!Q_Ɍ@ endstream endobj 3192 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F11 56 0 R /F30 286 0 R /F8 41 0 R >> endobj 3190 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3192 0 R >> endobj 3195 0 obj << /Filter[/FlateDecode] /Length 2378 >> stream x]q^dI\w9yB+ӶpH (˻&҇]p8||'gg-bv2`/7ܟ%,QնL$_}Z}X0>my7QE1 ÙoЕ q}ХfE,=X{ `{*xE`;茬ik-K:5HF7Uu#Lb*7yUL#cApȫچb˥HX/iY~ &c+ġڜ mQD"K&dDN/'t ՠ7a3`QGiS.H#g3''%yg[oOCnE?#@" G|䫕+c|2 \@~輠BPs7p$ÄIőA]Ii[EqCXW:=D7&qaFy!!{#@RE Y ,4kK6 m8cxup0 +`RFQ Ip0dqRfzw:XtĜlwKѠ d|-Ձ>U{Z\O U}HDQ.|r򒂋RL W|;eWu޳BvOG}onן_``"We{ 2»MHl¹ e< EI)Y">cnN@.qpGu=Ր"P,GY'Ss}|obA8O/'[G gK!xks15+S>Su]FK!j0PIDqąFTd3kyQeiv>͸WͳP@{:%ȧ)s3+}?'A+GɹxG>Џr՟?)M1]Ϟ1~Os>#o|+GyDpjk,*/J.QU`@(a>ci]PzV0|hܩ-`߷U]lhZml*D=`׆ێH~Hwh*.3HӅ>ՁٖN7˩(imk3^JyY 6M^Ӹo֕^w@w@. B @Ł2"a-e mhXǎ&#}G1 f*"),Vrm:(bqmFuS8HaZxi$-;ҟZmrOH"ό_YOm4>dVMOET|Mb#eV8eS̭ZNp­'Dk,K&mWJ({>h+9>qJ@7>H_Y=p(Ed2dQjch RܴOBN_`yg3-VQX,"/.T@AVjܵcń D0زѰT>R۰sD F"e(/{|fYްjWKx.ëBoN7._M'Au(.Z:H" #?T) D&޼O9w8dB3M̂]+&emnloP77σ.gF&nUbm?F endstream endobj 3196 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F13 66 0 R /F16 138 0 R >> endobj 3194 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3196 0 R >> endobj 3199 0 obj << /Length 482 /Filter/FlateDecode /Name/Im84 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 0 0] /Resources<< /ProcSet[/PDF/Text] /Font<< /R6 3200 0 R >> >> >> stream xSnAW19x=!+q%1kǏ$|>k `!;U5Տe/;m5_T.F&gҤ75 B[lb P"cTaN}WX}-J"8Zͳz@¡FU`qZsp~Xc?/^zz{7,o/MBA2,V ۗ~Kfel]6]Gxy?ߴ endstream endobj 3200 0 obj << /Type/Font /Name/R6 /Subtype/Type1 /BaseFont/Times-Roman >> endobj 3201 0 obj << /Filter[/FlateDecode] /Length 1611 >> stream xmWKs6WpR3CC)M$N:v:0KLPHв}bAqhyK,62X4EJH ]__n`m`{?'۷YBTohMZK)CDc]V];IJ>Z Vu?D )Ix0NbefcʕƒnaV`uzlKTr0˺K[sbc{0j4Hh#z}4&Lv;x#&Ycq\n-S&w;R1-ySr޼ȇQ/_b;є59+e~ [NVi ֓1zO$VyܻCx직K}wU]8 fԙ <5+){]:+)tew;dMqwG eqP6M]$PSebYySTkiвu7mƖ<7]DE-|E˚?4 VM$RODƐBB JTPQn@=hOU]3ugi>9A(@AN$A"Ai:Iw2rW &e/aջHBc'g tԛS9W̼-3+;aHN̥<+]r`}~XJ3 O` XJ}@ Լ,#A[5`t9iUUNRbiyaD:+) p%M+2 {Pd3pafiM_L;{"+#CM15!(S`~1{Ʊ#S-?sTKn@]TERDz²7H 7YC NNMN@6;!#'<3NUwDj21UxBgA|%b jlfQ!5y>`JCegY-%0c97«eD:M^dcy 9:|C;$v] 24|=] \vZO= y8uL>jʅ,&^BSteG*O9KK:Ԣ9MpN'?`2XNm!oCuכ~Z~nIϹ"[=PV No?/TD0t4l4yF$8_ޠBR^+덫0)7 ׻# By5΋;mu[]E4&5x'㺞<,b5ʱ. >2=Hd0<}su u=uc&6 d@_fw,WEAxcsre6H?-ir1ו.39Cu3@`rb'"j'%&)Gq>X{p)L&a@6D!Ϩ}׿Ih2pl>6^ϫu'xZ'iYt:)Vp^2 endstream endobj 3202 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R >> endobj 3203 0 obj << /Im84 3199 0 R >> endobj 3198 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3202 0 R /XObject 3203 0 R >> endobj 3206 0 obj << /Filter[/FlateDecode] /Length 2044 >> stream xYK6W[[RP4 {@X-lERLZ (zXx5͓#I|s"1*Yq0M gqwɛ^f8O-_ߦ߿^dLuS]xw+LHǥ{"=ju$]KB\Z~[Ȩ(͹Yu1paOZ܉gS~֫<[U#4/IjٟQO^g L,PQLrD{6JDԎh1dR:0ŠWNJ?Pc#$t[UN9i52q[i>-h*CA5jVkC9 ^TFxmEA4vdjٸ'X;.'m @BAD~.ǩN9sn9(}mt'wîwlOr'0NdMvQ!zZL(5UP&jgshX;Vi1WH4e.(%ŜG&  NPωb)'j9MBEڼ3͗G=(ǺyzpW,L8 Gu&!#_ ś+5 %Hv=[2+{81.Pdةc$eYBqJ̇e6Dʈ| `,'%I` (gJXI1^\% :A7|߷1Loes(`jcYC2AI+Q^J~_֠Aṅmef?tS¹qgޏ0#(a짌_˜5LeJ}m[sj/A=9ń]r򨖓c}&>ÌeIX~6ﭚ@WV8͍MTwȐ&@ ft'39f4'yќ'6szӀ-h `pk/&IvsE+ݜŸnFxO ῱l0T6{@oGr>>¼%J} bЪJro hyT+T&J$'pV KU@ ŢH8aۣc0=DqOQ-I#2~S9h=Cıȥ7Dʃn\F-blXPZ"տm# =e¹siH,̑]^D!S4 > Dom4Ծ/c}fFFǝ/we%"cCjyWAa1,W2P8`)ҽow*sD$.PMl1{x.(ܳ[zDFaztfYՍIj;123'f1/_SgyjQuB4:Ht^C9* gS?r;Q5`]"M{3cTCmܮFk`(Hsej=\jsݬËK8d.,Bׅ@izټ|jΤ]Z+k.M ԙ0/=1Ihv˟9f}u@w[}{ˋZ~kȡ3j VMsvU0ݺvXWf/؂jύMޛ̬x9\.٦cc"<77tn|h֗TIwz.Q!sGW2as3gP80 endstream endobj 3207 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F6 35 0 R >> endobj 3205 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3207 0 R >> endobj 3210 0 obj << /Filter[/FlateDecode] /Length 2407 >> stream xڵZm_nF* $-ۢrBg˻lٕݢ?>C)SRcj8g]$$Iw raE,$Ib\xxӯENr)a#e,8te9)X)pVuOf|Y153t[vv'ьd|fS=.cDꇌ*A )I:X3Vd"!y\YoH{|+]8wp͉C۲Yx2F48 ͬ2^YVk=gp#nD 3mɔD.㮬@j%{^tv$w꾺R")[pi+xyp!e*9A`|r\|QVT5̏7Uۑ~;*,'R')2l{ᛳs}ta'Fj-cRx$_QF@;FWj;֥ѾREE+mwf|Oϱl;(T[4z:vnT4U[W[QۛCqWB=U2ȗ|D}47GffHEcI]S:P؏j+XAHb$O|UKsGu+3OiK)b{4?(t^ #dRɴܮ-;KMş`i7ͱ^ rTTg:p! ȱTeaIJXQOEՌݑ \5[nkh2&3(1f{(C|Dbr=!LMLk%Pz׈b=hA!G9'qE yT̨A e$3]CXKSXy0H5]C72k;7ǖ m~5$9l0YcS"N1? եpz]|ů~2.Dh ][H|_mF>#q2F4 <*Z6;N(ָk M|tU5>BChu/>^ IQ&OPl1!-o]"uTǟ?k|*灬ULs$L0N@ʶ_<^)҉$/*z@s_w ^xNG.l.Kne2$^Ҟ6Q}Q.GrИ+hODfꤠnH KxB8WtmUg,`Q-S9zqw`P=ÐLP;֐rHc!|)ΠBkdxڵ*̶*.D[;m1ghkPkfc~l%z3t>ރvӭB!$4+{k$&b"׎~x?mβ<p`XnVBbN,I3=G}fn Yv[dtIJ<(p!Fo9I/NAӝ >WϚ$(*,~Ჿ F8Ko65fsHƻH=ܶ2Th:syFt `#՗ "Σl =(HY/VETi<8wU꾨r{鹤~/[@g:sj҄#k'@z߇qJމmj^l=*4Ů'NyTN6zusbbB/}SW x3` .xT.T+4ƫa~ )v endstream endobj 3211 0 obj << /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F16 138 0 R /F8 41 0 R >> endobj 3209 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3211 0 R >> endobj 3214 0 obj << /Filter[/FlateDecode] /Length 2460 >> stream xڵZKWP*sy9v*qd٩T蓬 P[f]{ Lw$ۙy?rv?Kl!$In& ߖD"*-o05[ՂlZW.OY 9risg_,#Y>[0A(ˀ_ƺe4'9l'q?}"jWܚKP5Д<-RI4o0tdXǚ*}{ZH&ۦ9 93P=g&و3"z7NbK Fx]a7\N?` iܭ6P ȣZ5@`,'˞x|~]$܇so>|-`t| 'ƾdWlM!S2w"'<ʻ̔YI_Oyhʘ 4S/_NJe2#F̆ 1urkq=sܖJ ypFAA,ޤSz*EI:0FS=z"?|/f AS-r(d$h6 N!ҔM\4sn-:08pJ3i2Hr\p8zz#ŕ;&DG*宏7] 3Wx ŤЁN[r2_#)oF8W\θ6wzq@Tˢ]݄gDso=ɸgt&w㧚D .J 0ի!߅d85Y DrO S+1am Wٌ4HvSS#tjOͅKKUAio>Ny{ZwvLOoC]*E R2It]Zj,4O-.SG{[Q3'_p(FowٖgOJKmuQy4бw"h58' A(CXy"P9#,N܏vN'ٴ+\$mYkwnw04扁{?psSkd`XS1gdm[lnve X[ScChDj+%inW03ʵSks.wbZ*2Z:}84.81iӕHbc.Ohcrf'a|tZqݸ<Bej7O 69R 䕚Yt Y$syZATg|Q3}e9xĠX&GT&`LV2c".m#o1kT6-\]|UXH=.7[De(iPz}WGx]Ք=W~|F.7N~t]܈0/E*T}i{2[`$7I%A\"̓A:~hkn6^=n2@%6azB ZD| NyQgL/U+)g7G@ʖLm9:c {ĐzvA͌='vm Q=וVx {8NBALm Z0ɞj Q)h=[%X^5' hĽw K!X62O}/3T)0q1n[;L ~d9 NzN) 1OTH˔a#x5d☬?J;0$UT !:}(d4hߟ\8 endstream endobj 3215 0 obj << /F10 53 0 R /F7 38 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F13 66 0 R /F8 41 0 R >> endobj 3213 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3215 0 R >> endobj 3218 0 obj << /Filter[/FlateDecode] /Length 2705 >> stream xڵZ[ܶ~غ(HJ'Ack=wujf;Ë4u.Ipf8],WV{IW+\6Ib:DzvWz\7tD~HxLE8yRFo}(S]튾jn_>%*sLf0 0M$،K7Hs |>T윻&ke2`̺4SfVk*N:BJW?lih7 R2Lp+2nl_?ʹH2&X)g^(2è<۳.=e$˅};߰2w,aɳD/;7{,\9'tՍKQcTDV-x\}W6^$n;;L>pN$[iq(P9'`7w?3pPܜ,.:*a)*gj<9Pe7+n^&`,`ǽFb YfыaZGkUo79GLf_28R#xSHؒ \11nN7r;!)4Tب_/`f*PMWyd>;qQ[CoOL8gZNLqz^WQ$#]0wpizӇaL R0M ꃏ3O!3f'Ru|!/#<٧P)@kSj RI'JY\)*+0Iv'+ຐ37^x.PV߭3aЊ1h*=Wyti (R Շ 7STVvALAqGoԢY6j j0]]t]:!gi]=@uΐ%@5zj 7dF)1O C5:e41Au`U߽6P<Œ01K1d<bHyFě=aJis9DGTX=x>>x5My/fPae# w}xIz߱ssI6Ԅ: Y410|%&GJQ+s|)5* 2}/J(Q)5 ܑ8ȑ V,thP"~ '@TZ̞ĤHn55ס>lB>j/P4MYOưMsq7h9ecHHsy :I%9ԅ]oxx1X6iUPZإ+o.}_ή:Ѿhpݞe_8f{W)Zkdav,Vqt=6!)C53fֳW{kun? ŭy1aߘ%غ(C|DFw7q.oxSi(bLc8Ʋ^h]_z}!˪ q/ҌĹ4 >MtVNDob,#YBB`GaU0?#5-=ᔤXB9d…JˮFm۬Q>R,Ȩv2K{trH4#א >)\J & */X=LSl@[=Pjr,\+*@ ՘J).vQ_htBV820 ;x b#f>%tJ.BBo$]Zn/^uSZIvn2XۧᅙCfz0djz> endobj 3217 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3219 0 R >> endobj 3222 0 obj << /Length 58 /Filter/FlateDecode /Name/Im85 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -26 -151] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 3223 0 R >> >> >> stream x33T0A(Uz`!K=c##3CSC 3|@  endstream endobj 3223 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 14956 /ColorSpace/DeviceRGB /Width 559 /Height 490 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 559 /Colors 3 >> >> stream xg}gPb[QY:Ts^ Cʩ Q9$L$MS( C EQqQ•R0*H.]NqV>ٷyc{2@uO?t3k{z晙~L3+gO?="򛚚|;ykhЋ  PEd\{Jd<_~g5JC4DC4DCYp+W|(֐F=\le܂{ҥKY0)   ]Jf.//5QRd/?^ø4DC4DC4T`d;vXC^]| _?s   Um̂nM__kK,ɸ.?µ(hh,64ZJfG?QF75ѿ˿hqq1eu7> /Dѣ{roNZm cc!!*4$#LG}\|ԧ>zh\Kh Ib&\w+??Їz,/Y>z'^$ ѐEh(vۿ-KO\Z( e\3hmE /})4}5i-ܢ')/`B foVGX4DC(o[Y 2?zzȑ#ѧAC^DΕI#!*\^|s23~ ِ=?#=LDg2/ 4m7 x54FC6$@e_u~{h ^zPe#v d$U^^[ٌFfӢ;$b#ß| cgݏhh!* d"ooc},g>mKPd$\^ӟhEa(PB>򑏄?C|ߩ?я~s -(g~F1?o!DC2 >4ZZZ]n 5LEDFl "7BFrO}SidG0ODH' fE'[cf(1U "4DCߐ̂oYɓ'F7˃=WC4-Kr;?A4B<H&3iY$PWIӡo'IEC,BC4Td,,,4ַz)PXa*%20BQ4/#,/'F aDE0[c1;c =|58v]wl*k|m!d|ioۜth)3KFie\v0#Fyhh("2 ~WUѩSzyvf0%KR?я _[3ЀFyhhVC.\_FO.֐F:Km܂O~6·OWhCZCm4DC441 ,馛d};)֐F=$} uK'aw!! d߾}2XC^,)r㯼@kQ1  !˿,̙3'(\ uxBk/Ehhh(}}2  \m6K . @u=C2`i4GF9~r~hIͅS6Dʲ% +g $eW=eJF=ז{Ϊjmm60#F5D x}Gg+'MMdYľj6]@j҇My)4m|3ӟF @(_H *3/glN[qjZ +MZ")/Jd >g+!%zqG>nwO|T=.b4*wB~yӨ3{~Y9F'e`hC\Rz?׀(th>24JMG%)fEWn.xcT0/BCq74*}҃@$ycW%J.7-NoYFCws5cBs\ݤZ$|(D_ih=")B3H/I~)I `@!C޳좹`PH4G i4Gё#Glo Ҽ4 K쩓uӁ Zq fU`˲F{4pÆȑ#y{N47lohǎY&wzlYn9'NHmį HBa߭[VwFu@:s樎"A Ҽq* nVt9{|$F_FѮ]6oկ~N .LNFiUn---=g7xO{y߾}ࠀa_V|"F3}Ś.ZX4ۂW\E@GiW ,SB:, 4EOڎp(NG\p}=SzfЄ0~NU 4f7#1eFSSSFGYi$TǕ>z|҉y+٣2"v*+F333Q~k?tst,ŨP \ߵk~`6dUeJ ў,e43dRlIyW3 nRh[}ft{ lR>{t YVQϱrF:{Or?H]wm~}IU-L#kG{/N;>6}3 lkL]JJ#ݤ~7KQk :G'ma=g vHig驓{U=$7*EэN{3O{9i )KeDх #M'M&Q87i>rs4ڢ_l;sHiyN( =u7 2>ͫ(kS "sNڍdǦ4ii.$͌%3ڨXoIFDWa|ѵŦZiFBFG3Ʈ9743gݞ4.o.B3eE=c]s|s{)+DG~Kpi[Jc7$KN!%pÆ~~IUQ4I)(h-jn>6b;czN#E\]~5XnfҨ."=9:icf2 FY%ZD?9n{QN Q\SؕUz.FeI# E~@z+~=!.ULz98uDsӨѠ(4gD9i$@ EHj#-ffܙdܫdtZ:-=^Xᮻ6u_z|_g!rUW٬NzZΔi}ؽrNJ&K~8i˕F$4ʞOϷ}bD}VH|y>\7 {e_k#t({Hd_yFy_{G2VN̯>Mw!ص/7#GolJIz"+b@҇#8b=|#9 's3cwz6Z6$;>xc?T: ,-E Ы=d{=U Fu7ׇ@j_nzmT,׼i$Ri`yN-;.puYʟw,OEPT哐) _XItϞW$ca|K{ȿg˗4 @wwS@@??KU.*۽+$(ڷo,.@re ﬣEG]$^ιXUQuonƖ4]ꂃo?t&MLSUS x")T.]\e2=aYsΞ]쓱Ew>!o0QԏL=u64n{"SOx҈4}zn!ƀN##4}:x 44I_le;/`R.ipFH#}>`i4GKL#}[#x4@$^g414`F\e (H#@>ny>1'̙kb>&it; kO„!IkO„!IkO„!,{,aA5 =.ľ 餕$AӐ,q\:I#2HO,EL{QOo*0jH##c@z+"Od\6v3H#U]C1} 0H##eAƱ= S$!4:R)I`i4G#FH#}>`i4G#FH#}>`i4G餿O{g%EХn=`$it)[]J$,H#Ki'qGIr|)DŞoβBG!` iZwpK|Du&=IcR>0RH#T]z*2f9biDy@2OH>=H&GRl94BS [mhhC4JH#;UWJǍK b}3=u`44#".OI&9 iqGo.wPl+)})i%M $ڤ 0`i4G#FH#}>`i4G#FH#}>`i4G#FH#}>`i4G#FH#}>`i4G#FH#}>`i4G#FH#}>`i4G#FH#}>`i4G|?&D] -By{LH45?z&8yrP 6?YzƏ%!\5S7mSAT]pԄ[^jYM"DžX'O=hc4" @UhۄMYSQ"0v7ܢ<FQ녳Y?PtOVQQ3 J=H#T7[VEbpՌK=⧩C5P(iH#Tw"@SB\OnPRyؼ>#P-(5cEoM?>Tx AZ..L2}T\X6ޒ7!S&27C 5!Ω^Ǎ1Ⱥ#i QiT圦O(7jmFB3i: ⥑F+6 Gˣnwfxli=buui 3i4BU͝S!,8P?W[^+MQoM[DctpO.Pip^N}z^9vl%w嬘`g .^ǡ#@*tR ?=vMrqyykqq={R]04B%nj݊ b̙9ם>rܹsϝxNq1{Ϲl%Q"nS?v[/ ]hNVZol4[l۞He\}. D/~V8qorӮp~v^ao:3罣;;ygzQsdmnEI RJOY@m#0Vfyp~^2v۫(DŽ\!g0i5ڪmFHkEcfPEH#(#>^ԯNmveC}L$KZeY$9$Jċ i#0r6<}KmƷ꛼i?9닓8?qO$"2o:nZ]s;:z-ch9=1ك'Ez PqF"Zj֖k+:ud4;i~2j;HέEV|mٶ2q# O&f7fkkVGNplq;N8x$jIM$[iȉ='g׶oi#0Z^;۵MrlN6<2fj~uɨxdSי䈮 9Xk6䋛nnyfY7L1DH#W=N0=Hb}bFx7۵zmJEPV=US|H# O_3ڵncu1=޾cy˶UgoP*'8n*. U8iHFF/50GRBM/da$yQGYze!PQ~]E^aPӢ{\0B<_CΫX5U=(OꩺO "P9~ p!~V z\K(h:*e P)*G&srW>:7f{kK#N5 r{v;Z5~pU}v]W^ߨuU}V-4GŐF|yw3v|^WޛSݫS_ И?=~xp_u/Iw<_&ԭKw0EhhZgx: &uhE[aF4K\\V $:dw0&9*Z nλs؏h̑F|~{W;gp=vyCu ޥJ#}gtE#[uҨHqwC(c,ͨwUIck[w"ʡsKro,Ut7(Q/ss#m}r h Rnj^նVU=bxG'׳,/=GvlW5 D=DMSHɨVUJ}⩍iɧ~?k}Y;2+yCunhުl^9/uBU݆U56n.IR^J^@bG77lQ-ui;w%fWo}Yp^Q_, b6_&+kf{ RUQ(&U A8oɕNRgc.S8zETWJ~ u0t)B=55TaZmZtIK"ֻHm4H#L>̕@nIL#u,dDm:Ux}qDh=%N(E#%)<~?t$&jvC_Aj }V]LAm4H#T?X Ƃ*ZSQ+{Qh>7BZOcP򮯞P7 #:ӌ^Vb} j b/ "kFc4B%^F1e ^Xυx[UENל, ?-HJNb_zW뮍&9oQv/:Cb>N~M#uم(';%Lqw=& ~hܑFcpH]~pEF^@y&ldV:ƝΓ=”4Ѹ#P!:z ]_^9ۼu{Y]!bs%W`duѠ O\qȨ"I#P-번[P\{_܊N<3:4#y@0 =D4WZdj[9/"^+$#&ijIː~xIBzV0EB)2/]m3h)2Ƅ4*(2[IQy+Fxf)rmUO_qNk1*:!֯ӨI^h7ėt 55aP(&i*H9_!9(OMg^%2?ӆA/wF~E4Bi0ڵNl1_ Unwm#۝ Čbvg:F^Y5/D$!PQ#*UN?m.R(z@akwn$DP]kkv4VNP卢'}1$*ѣ%Tp at'ǨDZ}X1N.t[HWʶ7ƽ<͂_)ф!P]%( fn v1j~m x=l~]p{=nk8$][.XCH C+*@}3QH LX^m(]Ln-&B/%gփ04B弎D&39Lf{8D^ k6 6Ѹț9\4H#T]wTOTPD+,EM?IEF{G ZB2hF{PT*ʻ O>ٯuKH F"L}𺳺4}q2,S>E4:rO: :=!R (x.5_>O҃/̠}Rv9FUit)QFf EAa})弙b jEUjy2WQUiĈK1 dDDQՐF@ۖ i[2vDQF@ mwGEIUi֝IוL}]ȡ*#LrˢHEG97ܮx)-xpi9Ayz؁ )qEH#l*qɽLP\ (JmqZ+r ZC"c͎AH#}dҐF dn=izHA!i)@,sR+c"BȂ4ʧ2H(R,$i R"+T ;鱽$Pi OYJ}(vv"? *KB(iX6K{Ґ@!LH i4?J $ד! 7Kbi4G#FH#}>`i4G#FH#}>`i4G#FH#}>`i4G#FH#}>`i4G#FH#}>`i4G#FH#}>`i4G#FH#}>`i4G#FH#}>`i4G#FH#}>it;;i@ioxitȑLaCC#FH#}4z衇%13\ċ$S4G#FH#}>`i4G#FH#}>`i4G#FH#}>`i47@yitۛz4X֕FIU= M{gk,n +Bғy*k Tz׻̯EN!O44E#>(i4ٳzo$ #rŦѮ]N8a-n׮;^|z lpL?/<5!%EO3wɭ*rZ KǎF?zhh0 DW5PP[bƒ\dǎg߿eC2~PajJ\" Ç/_Snϫ͝Psdp,т7CɡCX(FGd3 gfgg[LKL=1(P=uNYdG'k E:dm$UwAH4ҕC(d\osS( Q4BQ䭿4?E1|4B)BUnOHkSO=yݻ^"O]r :<%Tbɝ"|Py(*ߧ8z=:WO]WQo/?X\!gΎK nQ$H#l詛_\ia”~ÏPw'6}jH#\e齭5P6ͯ{n9_Vw|OGfNO^F2c4R@ߍln@2Hn(V; XpfjqIPA3 Y )ڧvOĮM$7Q:#F eʔ -kBHC"1TpLau`!dF0JS{TSO=O?'x4;;+2`J޽tB9xUV]KQ$9^NF"2BA~ (DH׎W\ F"F"T7 i1u*e8o]K=(DZ,G'*86emDŽBR*<%DY|wJmtȥ"rS/k}ᆱumY=#V'T>$IE04E=WXKK^b}Bz=d1NnYjO?|\X8cwQaP{TI0%G7һ!dnRJuE!1՗KL\5>(WMG}P.hFF@HqFȻhXI5҈ S(9bqcQL`iFԳk{U7u% ?\Jx??7Q nN"FH#}?#p0 endstream endobj 3224 0 obj << /Filter[/FlateDecode] /Length 1480 >> stream xuVKo6WE D4I&@Izhz=6d3VO$<@ )C0}IQAB 86zdP2 7 o ?n7Qzknv>_i!4N=+BNGR񢤴 6>yM$YM.jګzd2@TɃ(τ!U*1TqN6 CY&be(+vzĨҰ6ݱ)d['H +`F5gMre>#-Ztн3-eː/ih&ka+~ח -tookD[3}p(pe[>D2 E.=7:okKN.cCzH0N\fj-7: 9m6RJ-!\3-쵃X 9ԗRe|Ό2_yJ. y`l_u摽&&:Z{тj8[[$nh\4wa$RK|)AfMXJZ-IF"yZL*T*wuW!EǹH7 gNᩀ@ۦtMW# B|cNNã܃>Zɩ@2c@@jLH҂b%bT mNS:Ɔ 8[n)6Oj8JEyW{ӛ5ML,nM~ fQ@C@T*so*x y"hgl]Ѻ!$sgF2#\&q, Dp89%+VOF,FJdp ; e:;l^6噭3M#J/Z hsMp40v7x$8Axi|$2k? bR|t~V lX[df(Lo݃2WBZf#N![ ; >ZG3QtX=kmHsTbIO;hA™ ,M#.I)D<3*l֫lS=,%>V|KXoby-_"8Y"28B?'# endstream endobj 3225 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F13 66 0 R >> endobj 3226 0 obj << /Im85 3222 0 R >> endobj 3221 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3225 0 R /XObject 3226 0 R >> endobj 3229 0 obj << /Filter[/FlateDecode] /Length 1440 >> stream xڵWmo6_! DD:Hxp Q0 Pmk%OGَvo'R2׵V^na g+rԷ d.6V,we=ZoMlM4c|j~!߮}}>v(۪3U6~oH1 ;^̹}SYU>#Tgk>%툭RG)i[xYb( 5eٴL 讌TqrhDOɮgȍɋ̬wžnBq%R ]]egU '}QVjRh:]XVSt6d"(@7]t1;!4) Bbbȧro>4.2n:IƣN1)(U3AG'*'9AgB۹l~'F= endstream endobj 3230 0 obj << /F7 38 0 R /F8 41 0 R /F13 66 0 R >> endobj 3228 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3230 0 R >> endobj 3233 0 obj << /Filter[/FlateDecode] /Length 2014 >> stream xYݏ۸_! \%Yl.)r >{ZZ-_!iIQ7(p/69g~y 3?Ÿ( v^C~^.._KHyuO Kū_7ŇٜEUg-v:p!^`أqonsPoUe1\K~*;lRC?4H\XoҢP[kܑ_7iU=!SZ#5/?뺊h]oN)IP).0: G2d".T]ff+9/u;{ xb_:TP澟1'^9CG`C@f&/ a{3ISd5RT]bey>hU`yW*ׇmؕ{Cy1Oڤw5ؘ0&~Ѵ}Z p7Wj`ľG:ǺvH*VPw*]5&|h{;pU$[=)r &3ɭ~\-#8/V>"kIi4#BڔާwsפLfA (U`uÏWeUdut o FTNSE$(i@(1Xَ?jmԶqIh0DNҰ 7FSl[U&Ŭд,  ܈RzM[Ohi? 1C9g 'PMejQ _ /G/xѝɯ@pcKW*0DžT-.cHwt|ٚ/ez]`"pHDZ@[5+uafYm-gQAAA2; *B`ISApj)#M`' G?r fS~lr;`Vnpd B#+,HHBHrBxXٝg$i'bל2jRoNbk]QZN!-rnkB$/6ɡl GڞC(K1;:Mνnt'#pjLno3uC';:qwcE죕v^Pb=yaJ#ЂP#|7OHż;m~^fnWoQO?tgR/$e 5`J҂ź`Қ*pMeIxw4j}h u2ѹ -rڣ<Ĺ `b!$=2,voQx TGkFn҅ }HʥVY^N>Is!g S8nɶ>@_g;LapN"W`ʁ0j6jIeàIt2]w(8؋헗v|{!8{[ڝ*Tu76՟>f> endobj 3232 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3234 0 R >> endobj 3237 0 obj << /Filter[/FlateDecode] /Length 2210 >> stream xYm6_nq D)MMӻ PC}(vW,z2-ٛ^/}29"g bQI X.eH 1W_Wީ gyn圉veX"Fd1Sŋ>W$ yJ/~L x"Y E1 BUĆ[WTŦ/V}?*gyʤ<"ex ptw1;G 0*dY &P 1b.@ h2k*5Fm~H1'\OI\ԉf1+8?0.E _Yczϴʮg%Q[3Dm`%'3\\د͜q# #hv9%w\0!Y"\n$p|]eJdq#X?Pc0sfr%Ԟ\T]?k=sJXcԹ~?vE_}hY+ >YD|/$Fbwa/$%<ZO$%WsC6r.2X*u jNnTt-*n},Dޗ[T;2^> hkAoΈG )m-kB*Dl쳅1#z[]XBBhqն&I9(^W\6gIM7q.ԌGe=Bs{H8dz<$K](Y"@ɦ\b$t{w77d%<^,3'nɟZg1Xဲ~A,uYB QI^th*^9˲2+ltɚE_b!gQ2ێGUv^5&?Y-{&{E'jyII&Tv>X stqsC?t|wY |]P%أsP!>Y3GUԣ_7y ΠBQX,E'B֪o>y p۹"V4ՔoH1Oꐧ0Im7Rpx͂ex3dqvl5(hx*`n > ?mKbOҮzI?/ :DHixdS&:;;m,}̝fvTtT?|,{,Udq*`mõHTk%[^rMrqRwWfg%ʟ4鲢ԓ ?=M:o`G*JƖ H"~Vlmh)}$ :nipB"ItIg)Xe-T8eg^B0% AfDlӐZC1¶6L= @$J#x&_S)8'}d$[E9/^RpWR SAͩh-F2\m_mO5Zkۺ nϭ%D1A_@M endstream endobj 3238 0 obj << /F7 38 0 R /F22 218 0 R /F20 182 0 R /F23 221 0 R /F11 56 0 R /F30 286 0 R /F8 41 0 R /F13 66 0 R /F16 138 0 R /F10 53 0 R >> endobj 3236 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3238 0 R >> endobj 3241 0 obj << /Filter[/FlateDecode] /Length 2081 >> stream xYێ}W(/67Q, {Io餡Bdc=O= yUJŪST4M{ .:d)+dQ"9$J*oӤ`El|DlCuzm[co4su<8F(ƥ}TvP.cVs(Q|u{wA?W'7y_]|u-`RZ++}X9e_>]uk-N r- )Rx+WNhsF+.rhHp,0:.Ag~unZ1=ȯP,ᚳBu.W-YY,r~o7u#-U*9=uůU<ɝԎo豐1LnQO,^y9G;#a  9hOji x%H*(\4|NL( 3~_2}{sh&+) h%"LJb[ -ۣ93wAS~=vp"i iëP\ @h͖c}Rҝ b"nBRBM,|? ڛ{⻙ ex=f@m6Ryv -w)Z2V QgH/q ҂lHr/Z4,nxf/|-Jk=rwtwo+|OW7k}|=]~8$TLZD=U݋-q`URqxu=tΏP֍w2<_s[/gLv]X .%f+:]BfP#L|ETh}R>UW߾'ZrHJ]Vr hjfh!E<)ȋ`9" Ea9TTrI sYM˘2Wkt̢<6mx)׉m6ʤAPpW#od*C|K-cYO~bԛp\IeCF2b64 ђ?!q>6Zmp0@&*Iv P Y.g$+\2\̃gAµQMHWnf47d܇忰!8C +{7]{tI6sʖ`P *츲Ë6sX9D҇w^p]F] M{ҽT!2 0IyEŰ[^lלIvЃvaEI53&QҶ5Fb⨌,|DB^Ob8?RjeP:1_jؓ&ZT0`0P p!\4ʔ"9UI b+5 3Ő=\(L(JU(Ʀ1'2Sb3nV/p scč 0e 60Sz'/`q:З=o Ԯ_Þt)v&vj;yŝ{se<ӗ6)fcC@Ѥ`ٸCjbRNʨh8hTS4oK]芤NWG zPx endstream endobj 3242 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F16 138 0 R >> endobj 3240 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3242 0 R >> endobj 3245 0 obj << /Filter[/FlateDecode] /Length 2616 >> stream xZ[~pN_d`/m`n6) COsc k^Iޓ-3%{IIr.8,d8kIB:9ʹұvfoYf|E,/Lf U*?oC_R*:z1_cö^}o}P3ck21P˦~*?ջcZ6klh whVx,sV:Ouu+Ӭ_"DOsG{~86+(?[lEn8ַˮZsslxwLiY-7yܱ{2nzdm^:-`=uWK0 !cD* FxR'Rf*ZVe=G GAܡq̢{Pn:n˞B#;OP RWen~W4M[SẢxnoc-ݰZˣ!{j(T7*什վ]K Qc{*Mُ6#}ݸ@5AH8 qsCd IYD}ϣKGTuM'l#x@z;q=mLF5!yMقU]]ѮYY74Ҝm︋ 0Ě_PS$/s¯{ DF8 MN!X?XS0 Ue*q e-F XQt$3 v||JXP`V*mcn1Zw?~!vL#{5#N)w4.'{L&iI:1,PJy ~VC㦛V~OjmUUl::WAmD'mōTnzJ  ]m)/+XډTv T 7yyM4D!ݚ‰&pMQknZ$0ppV*ϘӦ?9א`tg(D (%\ 4KE,7 ~ƹA4&O-VQ2F1Ĉz^bs-($a\AA9`bԟj `B"DdW4osZ7'2Rxm0*zsre O 8!/Ƽe4tW\ɹt7MGYq%ʂ_&~4PSUP#Pbuf _Φ}qݑz#|&jY }=1<ʹx7`4zA'}w-bog-9FKm_9BqǗk|3š#sP;PKPf tbz[}鯂o%_)DZ4kdls.)4ֹ'4P,d.rcVղ[)Bv =B)2I^9PUq-Zw<-UPTZ_mX`/>x >2b"&:NBvNAg+bΗv}#pO= naB~j=/λkLU:yUZ5y2B2:sOS?}:zjIկO$q".~ˁ/ebVMɚ/n釒|^5scEV2kl'*|=L0>QLl/HͺW?Wc_oo~ȘHZqU{zG\ ؅%ZڡKy%oR0őlK%lcEOt}Ikٰ6gWu|MWwUZRy7PS}T%(dmJ.Q LJk.PWLuS2u%kS d\6ns Dw[FL.9,VOYT 9JmΨrZǧ}?v܃+VmEGqE&K Kd[WhK\zغJ֔;)(-o@>J0/Xu]gU endstream endobj 3246 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R /F4 29 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 3244 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3246 0 R >> endobj 3249 0 obj << /Filter[/FlateDecode] /Length 2540 >> stream xڥ]ܸbz}QlIH]whC=lߗi[cn e-oRPa>?'2Ԧ7t:L&tI>6b'#I5ER-3>b7?AK ZFa"2P+D50 s-\c"KF35iuGR6a™@0aVإ.Ď_鎕ʹH#ƾIԦy7XÓEe4ЖO+1e&fh7D<-JkM$wA&8"mwO/FFլ nTCA`t0G@9> llUY{9/nh DQ. =Vȗs,Zyо_4ϒ H,}) m?b}tq6>CSH_[w?4?$ ȱP%TT:]F ϧ"|}KWk.}cm[E .b7O1x=֎]AYl}W;EJ8:҉CQ|A3:J^F~TEcMGThњ;]Q!S鞡CdD+LW<( {5H "ar)eQ3J$ e^ύ=/L}Bd *_CYT?Ls, a8z+oE "_-!~u0i뵓!]0@ɣ H#b](1Qtt^Q܈8on 2A_88B3u9n;>a}_ ODCp4" ̉Rs"fdK$rNAw'ce͖/o?il}2jgam8#؂w~$:1 8TXKo^y16 `J{+Zb뱶-4;z G:.Ũ*Lp2PALjMG."8t0l=ڡ"j9 ukc7V;)ZL 6*9G%vvf]6/ݧ2X 1jJBx oJNS& Oo􋔄4UƂO_|"mch,B{#ȋ-lăNPD'4@Ʌ&(-t͹Æ;_dv`6|}!'&#D@?qe1[b{7%b$FC0zfSds(,o endstream endobj 3250 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F8 41 0 R /F12 59 0 R /F13 66 0 R /F17 145 0 R /F28 268 0 R >> endobj 3248 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3250 0 R >> endobj 3253 0 obj << /Length 59 /Filter/FlateDecode /Name/Im86 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 61 -187] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 3254 0 R >> >> >> stream x33T0A(UƦz&`!C =c#]3CC s" 3|@ =  endstream endobj 3254 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 43663 /ColorSpace/DeviceRGB /Width 736 /Height 418 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 736 /Colors 3 >> >> stream x-pdG5 h 00hpA P ` p ///7(^40 ` B` ީJU*+vsN\vܹou2Oӧ8?oqr)$|_C4_;v_s_3wᯛ/UĈ@&lŠ >99;;;>_^^.yJ|q׿^9?їe9*Ys'm _,eHC1e[`b=S0[C3b}' 24|+nnn>^\\pCϷq%)Б@a#DCua n^n9i77x} Z;vQÝϻy㫲+v4I[ sa+=lD=HTZ(p$(O.@q%@8Po rU!"7 dj1rLQreq[wYd1n7mcG='• w[m>S0Z_xzr$(T $PAx8EI&u˸ȆDƹD,lje~0fhZո<>O;7g+W{m5ܿ@M.B5QslHajBga(|IPxny7}=f1AYJ\\~^X:ip߼ %Ϡ$(8]rkm:&t021kt>z9ɥA$*n9d$]{W[7a 7 ~k2E$_-vR] ߗISx|hи8kS֬]'>q; *b -̎ϭ]]8q} ('?.Jγg.wPߏ. 0\vPuKዬ䄠đ;\rXTxjmuϋ. cPj=FDW7k#E­%jWF ѣYq\,C΂5]u_A"W(xUό*kIP~;( [n󉽿,{y涰?P1hwP2ٌ|Ϗdeʽ~_? 3U]JDOK&&)~-4g])B' "\҃7aya* +;xfmpc]اyBD21>i8 $JLfH_$[5Γb=zw,jqY640P{AOnBs&,ʿG׌uwLqq6AIoޅ>o7$+ɳ,R]+a0cOc)͐7Ehc*/y<+Tq3X'5RZ(Z67<ƒrwQ=[IfMRCQCx] X w\Ap$R(cg$buT 㕾'7] cM)hpq^9H(<Jiq􏁠_NLZ񌳃^pFy帧Ʊ+9 w!ӎ<Ώ%ؒKlƋ%'>uBC0Xoj[ Gpn!\5=o9VE S{|/0yQd Ɍbp”(x%:Wd\x,f)qـA|O2\O LBO7Q2ܵ'!Equ ʚVӢ;3P&~|~;V-cƇꅶ!Zډ7Z: |XeoY d66_-櫵謶c{ vl.Ws/AĸhpsNؓrl.~/߻A[<_|^Xc惚?G<D!mOdr킠|Ϻp /lt"qB͞f;9/6\gnЅ9Gl0Ԍ@"bjxO-z$ M4A~dUcׯDODODODOl0HP…=K9=szaN}ij%FszN9=8de6%9WWW>?IV]"ceC^pv!c'D xR~%Gy0Q0_kI$rmHlKL*怵[IZt)+"jszNQ氢\-:ĀvP"/Ij 鄕tB|:fviPũ]PW;PAEL؞ƼO[ W[|fPXퟞszN9ܜ89ȒDž ǵTA,n˄v/jѝ{e·蘩V{nvODZ6\T.F(Hr)=>Xډb չu꟞szN9? Qt. EP;(!řDNS`V%ٴ(&F5ҬE%Q5m6 l>mV` <G] *AѢMRW,S{uڃDFPlrs YJo@[KЪӔSVK|Au.eLWYh2@ą= bRYbkwIKj ڐb@-ڙEK4zmJ'MWk[H?؟YK[lF2]tL1x#W W oO-ZR߯?pZKHrjkDl,;('e.gLl}eL2[.Cc\:CA'h'SW3+Q׉U!: ZrWAT]+l$GDdlxһɄ.\aǺL2]Ern@$.ķ >}ăfB@yD( 9G75@Y >iL2]hy^62Ӈ5@?q4ç2h "AyI3 "CqkE2]L,qo\z:vP7*iYJf"0[~u!ek`qBֻL2]Ls#1IzۈW}!h j-Phqx2wtttl5g+xFPrS<94 IY C߷ HJddL2]f{d#b,PAo+H`BN i6Vd>q۬e Sl0 {ݜE\󺞮z6GjX"$=l94: lMHw6V&,t=]Oc11UK=|jy˺,cu5{4/ +\%@􉇏Unfqtz/EWt|1M$r9岈m"Cr iXt=]0BJxAi1)98?CP_3u15oowy^<1qu?egӥ7'z'ze&irZq~}8dgwP:::::::&)kG<G۩g'n%Q[аVatӲ#w+TNzJLPX8Ύ2ymW^~;ߒP;g`ova˭}މ!oZz쉞-Lȋ jUO|燓 *PU'p7{ؠL08bomq7 _ߝ}to>Lp;vԞC.dzJc&ͻ7o>_r94Wd:::^jLRA,A?$._3d0}9 ~?pCy Qvrzo?wȿ۷!f9 A=+'>'tZAIZ: ls`i|hQ|~g*1ހymV׾Q! nK[\v n =}zw`kY[L> &b]|z;!/АpwdV%dg %Cԉ l.A/?|`)q[+N;(qK6?Cy}}8dXb�uovwvB< Y9aG1o~ODO K8:E8N' ]贡?kՠy#"{'_-kqAJ}Lixɔ,wPv?}wDyGi"(C=;$N, pP79Уi(`a ("-v2[&SY6}Hq'(.J1'8OA9oF xoͿfB[`<Κ-*"H8Jr )EA*5*NxveQ|[LG#{wG;'p7?/Z{ ~4g3gʱ ןwf?;=GwP<;vODODZ>r W7?:p&?fIG45[B#.P'g=lطEha ~yͅr LCPz;{FI[&K 4edzIWz4lx%>Ƿ.?5Cwo>U\Y`lNV/9aw+젘L 8L!N>OK>ttttJQ{{q2.?-e GJ\l7a'_?b GHn﯆Z<1Aqoyj>.gOOܯ_dqx|t6Ix2| ;3n9U^]}W*܊yF20\:n~Go"嘯_A q79vhIGoA`HwPy#>۹wv%j_u&KbwzϠELN/n]|e(''onmsZ|c!:>w (D?s|.>}\='Jҝ'(5t?ww{篾}{0\jwϭu TsSYǧwF.ޞd1Ùg'ow5eQnلÓK῜9$kc|rvw/:(qW;)"<_c/U;[eȚ져ŏ> ໅AY{I|Bf_v+ $۷E9#nɟAN|N,w:::#މO8X< r{{{go(b7^X?V%iLG)pOghfIK{'U]`371]aAX#poXe7yD<<xbNId#>'xdKkXzH'e+=AdHx6vx;(w?!;!Ϣ> k,$>h[h+qNM(1L2["9fpGW>9>~ aDGwO}#@ņBN|"v?HIu4A Jd րѰ2ͳ8 g:{<;bGȄ?:R|bWJl2Ag6d]T|;ݝEV~~/p.ڇ{x?3cvO]@WzP Z'2*9In;(D3|8Zp0> W'ݳù;rw菷'_-pG>Јf8$ޑ<3uA#+Lފ($+r{'';[Rm#v=]O#C3ь(GHmc~X5jv7>v^;/3F//LŪV D9]jm ȳVR,CD3Jkj)L1ŜUEmHTR34fF*5_M fXzBOmغP*=ՁAj]0Ht8 q,i0ؖD&S,^#gɶ<4@V)BY!t=]X ǒ+9[1IEb:RkH5y"FY|`UΤDžw(?1Y7{t<6ؓ9J[]E`u ٥ JJƷfR*X,[UuBVo!Roqdޕ]O@z*7Sr% =|cY+'ֱ (j;:::::::6OGGGGGGGx A8?{'(meo&tttt78 6V~I3-IL_@PSck>8??ß(K*m:kʮ[*~"WOVxb6 el{:xv3pϿҿMkm0fn\̢_b5M5<~;:::4b=AieP8& n BO00_~{q~Aqc$4Ur&Ms JGGS5}~vBOw{__vO~! ^'@bMaM3(;x~srt~v\F#\lvw-{BP6jqvjgI玗yHG ~}xӷyc7|,T۩s`uV1[<\xvruw{vT*\4N\v !pm|6=ctj$ rO<2ׇ׹ϳp#z}: > N^1΂9uNZ=S[)>J(dT4ĥD[^;߻v~8}u<3odv]J1g;Ro$\dn_f4Mx&yzg /4ߏ03(\}a|7g/r^0]6ڋZ0rY4|0q$.kWz־œ{P9Gro2itxIhPBm_~ќ$Ìt dz;(p$ %Ǟ${$4o YUl:q_@MϠN2ZӔE͛Em)f'ݎf@̪B<+|мޕ!6>QXClCT7a:mdğ .ɓ(qhjNG'(ZoH'(OC$C3(ޞ}uq4!-vPVPjHPjwP¾H} Dxb̓Hq'\-qQ)->_[;qѦ J(1SKANm.yqqZK&N5Lx-\y+ܒ oa-wPNO^B4'Pyg=vU$sW󴫰j\&Hט+Ozib|(\~: +ķxM ۯ;GGlB>HǒLѬ3Hպl[I>={we7yj` }cKPtIr|n/!@jq;kTI4̲=(gjUvP_wNvP"A+wQBۋĥYu|A9"x6 e ;oU%f^n u!]|)gCubjx0vwlϿ^ wv~8;bxL'|}M=$(9oOPMmV;(>/%9 wPhtҁ AqG98ƺ(F&Ϡ}vPBvP ssx\4!7OiJ2ww<]==nʐ>???88} 컻&|d==O#", ҙJ:"FVE)k]bg'(/1A {nbml_ryw?zrua`'VAk+NJ=pFѪ@]:rzƐ-2p0,_L|PڞR2@IxIАuPl v)C;yx"Cui5 |U&AY1}/IHVavSYD(?O(Yj|Ӱ^;GUAIJt,Q|NNɁNEm3ʊsA?"x![-$ƉO!՟Lh~ق" &B[<^~'?o?v]{݂<_:^)QRAeʬM[<ۉ!pxP-\2&<Բ>NkA` ʲL%.xK[<5@#VJ¦%ܶ7ͰyA!>fTP;InkfHe; n;5Eai*F* *iF}ñ>{Bh#on1Yv(2ny/jk֍ 545I2]cAլ}p }2A 9{cl*m-\6b[ɂŶ~DOKN$bXL 1eO^iZRnX YJyTёC4H glfL"#Q,4 ER[M 0^D@`.ťk~1A+U\x2\g[z'z%'j JZajeRb{&>1y]Apu6e!L=qmJnN\NA"2]t V(pAaZzK?5tIlVjspfܥl9Ax4w~ 54RPi|iԠa4i) t.e24QCPe .fO[<T5vY+[FhBе.eLeb2Ke@ey^ftn|GZz4-QQ1R#-DU2]dHܩ}lVd,ghr1KG)%ˬ\sh+z#dfo7^&>Yfu󣣣r;:::::::2~+.{P::::::::l:+?$ۙJGGGGGG R گg X%Fʤ1Ҷ5KOtOMA@M[.ReGGGGGGG4x|,Xٖ9 +IZmkdaS==[1"(yU!/j+ցUbb^@uNGGG9̃˨Aʤ#A_#Z|NlzW2i>[mZA-AAZ&uttt͸YҎ?n!^!f64KDE==4-'+Զ(wBeCMZE0 @(霎.S&M)Ƶfɠ5\G[<k4#G>:"}*(nKUkdXtOtOs  &421o4m4>NVDմqbZ*\Xj㋭Cd`L,V$cPEm&G`7}R1oUd3QEmBGGGAA1fYsuuӒBPo-hmq2 jMX"=%|ti}2 ȵ_bAҞ&9A8}ޞt*s>zZ6W?RDPnq"-]1՛-Z;@ʧ%MNcbatȀol0%޿?iy –vXو9sBG ҩxͩLWf8![\`c`Þn:5l۾Bba-~HjӶix X;tdv#h+ @7 J^MiV 7OdXVA /U 7K^ Fb9Dy0A3K[<5NSQR=23\?X*NF>쩧6r$' o &Pgx' l({$gƺ8QBvV-JF2RR{e " f4X16/$'Xύwk_TռӅړ9`ٖol &`xO6dd*M3@iʺ_,y 5,A-LbݞmcQ(^s)2b>4!( B'&#W0AϠlÜ;7D^!^ñD 6G<\t/EdKqK:'bϠ]V,?q"9 }S',̋Ъۊ3(ƙ-K\5NJOYH$ N z$kU=k(㘝O{$Er@0p)ɻ mғ&2UmG7U\{QZAB'dM? _a0/Ł)Bs& !ZR"` 2VQX6@K,}NbBE&vE&jG,wPo{[4 zIzNF_Y l Xb{z9A9X(HTM'{ U Nto ~Yj,KnV>< 51FߞAd-aEwNY.M6{ C?hA4`-[I(iMm5klÜyM95.MwJNobQtBtǩ_,4v8(g*/e%*aBD3Ѻf"V(ҡ:,"B\_JUqqű5^dMF\Kj^rzlu )L$C oyM'22,%kݬ*bY2b(D\[c5QA jK _YN-%@ 4lIj-SV3XEՕNX̩Xqql 0NCAd}h_ZZ15!TNi{Z*DIO1M bAm8S<+bpT=' TMW%Ƴf􃴀tV$,m~a+ͮɀJG(/ xk4s{M3OH0^ZZ.iK0=]ǩ;HX 7I?;BIq-#JY>#JsEv /Q=afc#|_,U([4`bĶ}b C3n٪ՌidԫxM<8X5W'תlֲ(KHkP z"S\D")e҆sdf _ZzS$S\lhhFd})|Tƛ&^ëx`^Klq`'\ߔ/m`EG!~O06@~o?)oxpq.!ۊEV"\5=Sd6cJ hjTJz MHTiXUj ϠZXsET07rklʛ8P.i-l^L<>w63EJ`27>T>+2d.],?Lk^4m MXw`!1$cߝbS^lSԒ^RELEeq#@sJo WDS⣞ӽ$ǵgPWPЄUp ⣫~`祐|ڑz)b#_j3SH&uzR,\,cÇ 4+MpvP ĚJ4Bc?VSe0PVȇO0J3*‹kWCȞzzSB`jj X(MX,^*<$˭+#|!a)Co0sqڨ#c"s5  %o˰GSJhT+3&ڔH`Мt~x /aߔGc>EwB_@k"SLo V.$Vn*RVC#21U7ރa xdx1'6M ml{"?5ʚZWӣtqgm`E(paY:F=5׌@f^zתgSKUa6+267egEͱZ6dz1,UW_&hr6=d.u@w6燭㍻20fMlm`Ec)iHӍcXX=9'N; ʹ/!X0LdD 7ԟX J֪x^Lfط !Myc[f#mC~'-ҵA8{]Z#ҺkcR +F(uȫ@cPyZsM`xPkKyHTd>cpq:s/Ia*q_N;&$x?xqQWGGGGGGGGoi|Y&َ"{'vttttttlevP'd nz# UZiHKr5!4ve8vlDlkPB9a6cVi:I!jFGGGGGGk|eȢcks [~#'X#- q߶9lM- 4Uۮ1R͡3wdFO4xQ 9w#n'Y`Ffɬ{NcZ-R+^o"mЌY`MN\[6>)A7+#in-[E3I4'=c`Ӛ/vؒe[xcDiyk jNh]!^Tcx퉚`k#%G9񯉷F]`!hdIk sN-i7z^E7d?AOSCMՂxk"XL|D7pTmmcm3/VQOkiUoU-{CTU_u)^!I#$3Fڍ @#:YpoWm>5%+kKa~vu-MJ>NbN1MT(2G=e=Q?x#/>nG!kUsY`eO"m qj#UP?=cp3LcS( J=z/{?5"óiH YhCj^$+׌QRZDg3d@4 cZƎ(Vfri[aMl+}kD8RdjMd*@H!Ybka)S,ŋxIk{_MO< !۟u)vPVI75’I$Y+e>tqBfi!b2ږ,J;D0uqF&7CiS+S 0FÕdMe lC2<5";  פ 4 yM$Щbe` k2z,Ye:Thxx>5b4"l#B  ^hG"_i.ִQk.2h\B;DF~:Gh*,bsjl/ѬVAZfMFNx6kP綁F~-LEM $ƛbL Џ0VԑW^!V0oO 1ppS;+es׈Z Kji͆Dhsc;]Q Yư;ތQܕt.6pnBf+!kJ.6EavI+uF5F$-NA>KMIMP-)etzg/cJqj$xqY@O ss]V8k, A1ROh ) !cAA?\7Gwd\i|~ pH8`= I<ᐂB`M2vzl-_#=MYM`Df>TE otxI0VH4X>Q$u":wk')ڣQc5{1 k-hX@OpRrfuyoI=b?a&BNgY)ݢ҅2w}+QNQZ8̰ܮ`nj9}iUO(5k:Ypăaz9l6󉫫ҐbB-@xrtgGNfLĊ| cS9K)^ˌRaYUJ4WeUYGpC}G:qH^QϑppGh(~pZ6Bzުd*6ej4ClF&J`#[#ek(Xl #Ť]˹D /R5T׌Yu\=t)})K S,bd. G.XVY&WI%C$:'O-Hx4\$ݸ㛼l2$޿?LPk-ɏBnu;B<_ ̤ƘS8&QoęP0/W9  !4J~!p 8bz+<a1ncM!JysӀ jb&xLU &hV-AeŜ"A)l>@;͕bE5.I-6TBDȏ%6Ή &!(^clyܨh7nN9Z[Zcy$+kjI&VMC۸0C߫NfPrB5`;5|B"A_OfXc`Q]!\2?qC\/*+.51(kÿ<3%xyFH3(H%ijϠ4' ks}8*ǃa$: >Up'MWG_jiQ.*1Y#,ަ)#h) y &c~9VkAZWut(leU"zb_TIH@tG8?BPJ(6*J4g$8fc$@$D|2ӏB"&)$90Y_jV)ϠE&v`*µ?fo+Vk5r'Os2A]3i&f6[$7x#K91zH Vm>'h},(E}Q$sCy\3BMA p, J&R73X-gpvC;AX~L`)u1GƘr5"f-qe a*V,?buW%ɓ 6і4M"YLh;VzH=U4"0%!⎎3uIVAIE,Ć#.sby#ֶAa p̀j64e膈 ˿ A4!ƿ zrP$g>^xQcЋW_:e\\w>zd>)4W%Ƶ8Yýd=(cY4`]13p%o `b-60Pm[W+VAMZ&oӸ6Zѳҩw8(X%A%bW+H}͘ȱmX ;S|?x8 6'4iIMZPoq8N/OKōeA?!Λox[ⷥՂUyf??*ju?C^(.UX{}mbU,hah8Rǣ M ,.i Lq0 i@pnM=3(`>O2sd;$!.DڊS)==_!=_{<7qw%N/m*jM+oPi7kYvзZdkPe=8$TZddH9Y4RZ@hNUv&]$%(,_LW\mXc1ɈKHKH))Tc =8't Q0yclL&ba\[yio'~\ 6%HG%W;LՄ@o27G؀ %_#;NYYB !|du)n-@9X_<"Aq9qYkRD$,ɅSBp FP(łBm$W*#a.cNpHW: o eϢ2SqkGrͰ,Hݿ!4ЇvMCʺ Iǡ'O1&LEzG_1מx.xmXLqu*x1t8 „G$FEr<)vqhE7 /WNb;in!Զkfq &p Ǒ4Rw aUº.⣩5Ʉ 抋,H "uH ߯2 RXQ0EFŇfn/qЃ3:<֭6wH[bi{q]i9ΚB.QMK[dֲۭ\*BcwE|ŭU'I=9eU$,$)R%$CywQ C:ϏK%2Dw< {B4N(A%)0 O~Y/\ sEX4#1iZM׌0K_mbt gCLFn1Y4LDXpodҚpsdfe˽WLHinafC#!Yt⨡WI#Vĝx_?UTlkKD@c'RQQ#0i!bg'^` ecJ91FayAYPɆ"x x!J5 ֲ!%(@@]A>LSJ&X9LXQ&͖ iGHlΜ؉NA=V4}/͏Φ1~&ΙU- gop4#2L׫f'##+{L"qKk;4#/\uf  :7W$/5s tbsm0+" JX**じs1MP|J_+H(oj(;m4'M,mH3S[e%=WAqc-v>a{#l&TDN%~fJj~p#Ʊ97zc`¾kM ,Rb;q鏶~DmTji&⩛΋lxq a wYJn{`#MVídkoMvg_u/k|Q,cJQ,H_|o[R8?6ĕޚhfYU:w0髬PWNf1!eKY1fn<}I1Y7 B#+jyr4u+'!w\s AEm`%GKml\E?֓ |rcfhc%qj\$68ל ݗ~x(%L8G<(Π"4 *e NVK#%5%m TP-5oflgM3<i^j= 8#/("q@P9HXBϧIflsk\$ќs:fr@d>J"?)Ebv}T>DϠ5'5FO(k 7}+a'HF45,ԥ -l$H8xF)S~R`A(SI1ep .k8 ;vuG6qG,ad`qWg8 W5#nKj{830*Lj4q^"jܥ _AEv 'hNinYf#Je̻[5r op7b28-|e|#NI\!h{\&jV j)^6Mх,Ѷ)hu]pS7Ձ:Ǡtq}h'$ŷ͏N1wi^, Yx7AcQdz \{i\=Q1V UZ L#<X)Ԉh8ʘ 2CۿflȏXs&|ɁUdk8\U&h& Ʒɖƞr~ {ŦBz4q(9.&0SU#H+iL\F"FlV!6[C]5b }մyTh=M7MpZ-vH jb{Pd 繆앥ܜ Y_ꐈnKAm\X= q[ 14gz۔^6^#&G;U$+ItҸq<㩉1b3mHu׬!xQhQj #MSV a{XKq'01W' #5s.ŠV3\1YVk#GY3ib¾ddIpzl*C߬NTYɜps]d‡dsUe񲦖b!a*I'hrIUV}Y#]Vp@/E TdW5kLq$-s&@K4 0#d3NW Ql* lL[ g#^/`L6MaKd׌D, Z @bEbdwo"<Ҝf>ML|cȺټ4Rb_kC)VHwxdhlK%p֜lܴ!dd0LʋffȢb ݹʑ&L 3DЦ#cqЦtR!\#%q^>؞,p4e89sYij@jkxlwRl$Ila[& T%/z[*g jr&<9һ>Z@9Bne`PGje]G2VջBm1HY ja 2財 G;W`ZxjLxkXoQU0ǵ2~f\JW.gnCrz#\^S-!]Y &T@[͂VZuHd7 SP5c-/ kXF.K6knW)zHCn4 i>Fr82T4& /W#]ٺ hIAF p z~Ɗj`b78pg܂\UN'mb;5wL MŁFSkӳ}ă",0VOa-Z&^zĪ&:5Y2e4 `o72t씍 $B!|spvqihژ3^<#\)X%'Y^@zY&s +hc3EMtA[;~H& q1kքfchceW63񺚵 լяh@`J-&&+ESfX 3(5(dKWkf544V Tj!  \fj6!x:ɢro4c\1,Y8(ƢԄsg[<2[(VKhXav.Xqp j]'cv}%|Z\Lh@g~ 4Xeq||ggg;{ܗpH|5n-?RXWE?s^U4}"5d5FӥlXu< rUB @U# n|~nNf2LZ9qde 8Uh6matv_n-^2o*8&D@l4lxnh@*3)]Ϋ/ؐqP|v!MQXאEV9pbJj=-O 3 |ٶieٌ0YT~֦#P>d,04,3VS쵙Nkmݒd& "듰ش|2' C ٻx>ʓ) '(MNCY]bA|!F׌5 J/cH4ŖkdF/d d,2lc o~/q܎<vP) "(VwHĭ;iU!Y- eMeiYfEб.5/P@OxwP`)۸$A1w94;#흘uă}ZPdE1>,];(?u -_KzGy L83~:TϠpy ڒ!`*،k#A Vj|)9y ADme6QyԞ96s ppi}1^t`Y1.ܴqwovn>VF*KuA#JYl0M 0R&qU/jO@XiX6ͮ@kksW_ر)vPdY;E JOB7p>JM-7zxTRp ?#Bņx x`+8-Eb: Ŗ緤?E|=sb{XQ|ڂ0¹ΡO Js>O o3APn=l75Ef64h~0*$뷤C$'dH!ǭO`"+Mlqmw'+y8]0ˏݺ',md.q^&O!&AdjȊF΢*ښ="8y9\OD:`bPǕdxÚg-<ŋ87#V[Ч@qÏ~Jq`KśJxĝfd$ 0qxdſ>7qr鑗 y l=D˖S$޾Ϡ< l#gl2IW]EBCBɗ:(tLPrLɆj:lpT[<!۽f\X'D4L}p gr?G"~{Ң|Ԋy$]<~K<@<ٷL.~N׈sB>]jϠ#|zԦ,z;א׮&hZ/jQw"0O(`lgPMW)(Ů'{+V!:C6Yv܀g ¤t}6=Yt${ TKc의c JfvT/jr1貧YA[P<~|V9Ld<)΋g跤׌$_KO#2NUϳd@8nsT}olIwGGv<_*md8f/DE$lŕEjJJwxHnII/p7qu=d-.O{PqڒIҵ4s Ofr`Ux#<4{'YC dǰLeHě`b#5[j}y[</]$y Jr%8{2x;^|J.imNڕK s ;(n{Wx!2ZĞySD[]A! wX3 ҴSk2{'Jj0 N뽞ѥX d,l̾g %}>A'&#ӛ{snť/4RiƇʊsKmj+oI '+Wt}߃rrrʹ/!V|ggQE_2KA`MպZ&4Mu&,i6 VEO$A7Wn?\x`<(~GIg;"t} A!/؉"*eM!4LUK'`?!A|"TH=$Ga񉢀 ls!$Q&3(Ȓp =/ t<|i!*֯'ʼnIӌf>1Ąl &W׌y,֎g ^BWx* ţSZ?ҧ'Ӑ5cWqțm&CT t}QLL9zªd*JwWj/jg6ު)6LNytA%/!J\iAlIJmv VsٱJ~g}}MZj90lzp g-~>?::ڴ-*4xdyU@Dg"B Ŋw>>Y,ހ(Ϟx pý T'D'z1} I&i:ATNƏ:=WVm, #1WAƐdgֱ xZ#;M@y]߹%9o7gz*θA lgl6W57)nd{&jsSE:i4ha4}HA' d6+K Ʈ"ڛSv{Wϑ$擿VKؑ"=i8p #-fcem{jA9u5-G̊UYkTX,Ҝ'S)IlUNDh+x8 xfc =1ӈ G<F[ :͇z ;jUFp6/%d\"PaRd ^UA Q[jYJXÐlVsSihNia=g s4h|*@MUu;zՋI!mG01kz+Ai'nT!`ply {VM 8-*g]e)[oH/j#r!Hmh=aE~Q3{ΦrK՚G%pL i,왦ܥ#a@Z ~Hf1fzL+۴S0$cA[C~#6dsWS=Gbtx0r[yPâc2DfAޓ94"MY2s6 od4ވBPc+\8NqMVf[ղm9t:ZY IZVf6]c8s6ZķK6Ų Vur1w)d{`-D3haUul JJl4+BVf]:lK@\oȼRSLCS["ZR-, 2aQe@/-,@YR+eblayI:2ǯƗʿ!hfAVԂԾXe $+}%&v$D&ղxe(t8h U#=n3O! 5׿N&RX."`Z!1y2D`U1;7ϊ2'ۦa)t 8VPU2S`*23\V6 n>32?l"I[=`3ɲYmjc(ءg0, ަ+iMC!eTrzQJSK8([yY˫U鱖A nO-^JA/GŖi5Ęx1Fį-j4bhpm|f'mJ:M)sb۵;l!eBV\di 1hA3c4d!Zj/th6C2n:qS^jl gvv'%]$|'J vj16ZS<"%pZITyfK"6r"pCHQV@3i<~5W#pPj: tkp$kgh 3h6nnSjUP[\TM@讀%d!8) NPnﶥ̈́d=Ew[:ͲC*y@i$@kztkȆA!UD;`q_V}ѭ uT/aC5dPRG+N"\3X'N! MȣJNͪł<5`dӐ~,d=W)HBmk_4|85x[h̢UnKEX Un[DB]"c*)hB+tYxNj!pODJKHu##ܰt"jC'?IWW>>| F O%U΂ EK koQGeD@lዢ [mU.LPf4fRHJh\HەBZj (`$[ 1t;"Qj +.iTu<ˇ֦t,V V|)vR-5L!9u|U Wk_k<-:)cd2cթ?b.yYmB)*nF4u '*&ӤM+#J @CR4O$|P!~|jz?C[Tg.j6hd2Sj#.@i'#Tn)s G頌%-/JjIyMirExmvb ATAB)n^5x4S@Ls6 V(e+Q14\1VTdoיm E3x/gxiN";kH6i]d_8R@[6nLxQt 63<"kmMtT2dYk_#MA b#+G ddV Ch (=B%NݠHʬXZ~ă#M @*ngS,i>CovjADԘg dyZUt)`Ɋ:eb 32ᢪm^WEŃ_B.f&(1L1#ܝ-)\@ÍXBp+q$i6"uހnYND^!_a%?hLSNĩxR<rn.~í_:v'ifT/)`s+ QhcK"?(ߝO|ϸ5jԨQF#|MѨQF5jtO~ ÿy rş}IOƊ&uT]4z|E{`jMR;՜oCSxdN&:u?HNJj(I he9z )k}oXf3o(_ÏBL{r<|!#00#5)S g;}V5#`/K0cJgʢ"7UAN9~p`R~Jp>>wPvHzQ]OϾ}Fmݦm%Px yˬ=HͲ!XJNGjF*嫗>t~5*ߨQFDQAIKD~8ͳ]=f6$Nvf)}g2CtKL7FĩS?~u=5@iјp4j -&}|6$z`~7^r62dNP]"p :`R1E6J&(0)NMk@o1hvDYyq,Ob*b94)'Hxz(j|; F@LC'G<}xyШN- G>'(&d{m'N_:xKf 4#ߘ8Y@8i'ooWq6?~eַo2E]__;(#Kѫp=\~GןŸd܇xy(KvcÞT< JHܛ/ ObiG2fV0`B!@q2Q履.×N\\C:W_|p둍+C'׿_\\G ;_?_ y.??;uڛu_nuپ8_\deO; qޣ?x*r(>T^߮]|~rÌAE/ܔ]}wG'g.8 d"ytq9?t̻ӛO=@ =@y^)N2:J9~=?zNH(zDK-s="v1wATHWzWcp$qI1$~6]S_7p4ENP=;x!2_|%8r}}Äл݇Ï뿞9r .7;\Bԝ5K|P:YZ~<7AA Gzzܼ^qJJ<>=QKӷ=AL=@ɚѰ :o;#rQ#sڙ|93^Mx0z=:q{~W1W,Ct#@xglf>g{g8G+ ()_{ J8!yiD,K)Ф%( ۟2bۏW)Żeh>}džwP/v]|E0!OtaG*a{x΢U MJ:%dKWn)$ ȃ-1^ݙ%Y__xz 4^[<]W>ϯ>|pߦJ g\_]bxKgWw9r|˒(ɨ:t`ԳG3o%58rzzw:N}G0=@Aܼ!" z8xtt%$ɝ܇`o:=s$<)3!zq:A AG03>*;(b<fF&҃xV7*]%H_oC!ƈRCD V^#ɪ3-wnǛn@GEQNɃy:<{ʾ_ry<G6x'(#wV%NJ$glzt'"zzINxѕߢeI+OH;(}܊4(W$-<ۀGLNd4H[a?\\g/N^~vtt{G7nz4>:^nӕ 9K0չEVl~I?;_g'!EN xc#Nב~ZoS5]twPŗd;"L_Ad|OzRz<' //q+$ Y\n8&(L/U6&@m"dJ/d񄸤g(~?qAO?˕+//WmlJP?LoNjn"=A~uK^upH(QgOPzPRP(J(4> Ht<z!H|PfdOD|#:Y;#N1*"AO4=K yW:zfIHn -j a?ί#gaѶ}^ˇ;<{,=_ .u _ WqRg*%%YU|8~" 1zsjn!T$L=ZztdnL/ Q'(#-'ǧ'wլٷ'&[:>aAc߫^,{xBm`#_ +m#_%bX$Խ\fqw\M#PAl#{g-!CY<ё~8rCzG]g$բ, (H"<+ yHb=j/@_?*8ɛC$_t=.)΂𱊈WÌ| 9ƨћzuywt}ݮ~?5+{2o˦?ږH?2?VzWSٟDGˆ;>.q } " wډW2"6c*#0*?)xQwPQ3lhQԅonxBqkᆝf2B:OyU#Y„=#1+l.fD3!! /y~t o=;8Gp-*.fU`Qi b'Φ{УXTݟ1=I#>WԐBT̈LF(fsȇZٓV{Gb6CY1N%8'#FEgo|H`|ͽ~/U0p񙐍. dQGk.729iQ\%q;d1Jڑxf~ذ( ~_yoVj}]MQ̥R t^JwfLBgfS(=Š1m1cy\SN"WF6 &) ;pl0wd@' /yߋbF gPPSfnRlGL:9{i]uq&Q㚨Б.=4T.Zb]b],7gш:-|QJ5AՍh[s2YS;L}dtmL0z]b@Fc͛aKtGK/N___>U"uI5FgCuW 39oL?Q՞\L>-d5ڌpB6Kh6y#Brh9aژ]3Wx4D ' 8ps3 N2Jʟ%Ji#F/;ٿk?YV"J7ozA.,ũ0?-dFOԵ%ew6YfǞuf?RӋ1jMu5򑒜g#2S\dU#N5uն#12 Xή/Flt®k0qB[9aT= `? ߑDZLjaO iP;b"E}c P(bPzF+m>w<4"i$VE%8dQ D E![:7}<1IIگЛeRz ޟ_v^n'Rw'=PXecL -gfDxǃ]qimjVLiu $+ٻbTޕ Y stlLSX?3ALff+:4k,V]l\> ÞeR/`rH.9`W+e͖g@,ķIU؀Q\Btr3iLQŏ-Vo~xCДD&)$.{/ sG^JT0O#FlQzF 偝1{t6󞭦Bj2/@Q#zDY_,,-O HHPӗӇiƶF>BrX =g>YMB~gUִk~eOR-|]0IQIGTd*>E# endstream endobj 3255 0 obj << /Filter[/FlateDecode] /Length 989 >> stream xmr6ޯ1Cʭm֝ɤ$|}weU9a](؞7xjvbM!U- VJT;{e[mq/ܲݻyn\V׃>3gRߊiǣv}~ǾBnUٲ+F5eQMͺ6 {7 VJ%BF2ٜ] I|]z$C5t>gdG΄zHѲ\bCwY~CHQ|zzo!~2)iDY bLj~B5I">0?|;v"1f]C-4#NTz I baΝG+e%#̆iL)FYHM endstream endobj 3256 0 obj << /F7 38 0 R >> endobj 3257 0 obj << /Im86 3253 0 R >> endobj 3252 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3256 0 R /XObject 3257 0 R >> endobj 3260 0 obj << /Filter[/FlateDecode] /Length 1702 >> stream xڭ˒4W2N$r j VbqLQnIhZqo,l[$iE^0o~x;Jqi|Q!7iālA=Xy"K 8ZU 0)YyT&ser ^wkY|QF%I"J`?eO(KZYȚQ(CeQw{Df@Li35okyPaW|f,&C C2 lOTC5)!,7@A%zi'8 BB U_?cbvf{$ Iu8{^i E"{}<Ka q;sCc#F}hT9FW8+nU^J{^e zT\/wcdFELh^uj<@0[--4-u 84vV:y;#XQF$_mڪ$<ƛLZܢkke]#l,$V2X+*D%y5#P/ݙP,^ {!(GV.K2GS:Њaづo<(C+5R.v0|Zk95_AUT 6I) _:BV:q4@,)|ߟpV;<=`ULч@ zslVQ$a$X\m{D_<>RKRC' ƀ$tdhLT V`~|4ZFUXrbt('qÎkc$}A.PO!4VM9y$}\2 ,mwV%MШjqT($&*ӗ)͙WgkQQ#\jF:1&O°mJ T5vn6Phq(}8<9^cK^f?n%vٿC,' ~/8* e g/ ިb,3$澲YQm6x" !BZAj-ay^}t-z2l iJAk1avJA RV)鸷Y?Q焜 *◇W|֐wv8?)q i mRuYlh0B#vRsQ{^32.77J s9o'\tc B#{n))7t'EwR4FTzp?f nMcgϊ߱hb gaaL`rSQTNc<^|` @4iKẉ$qq^ 0_ѣLB,?M;Ҡ!S`} RFOx'j4l{ ̭5* *MC,#˃;,Ni̼҂^#$ /VL={O5R/ F * scWF0i|e+? 0pzTrQί endstream endobj 3261 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F6 35 0 R >> endobj 3259 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3261 0 R >> endobj 3264 0 obj << /Filter[/FlateDecode] /Length 2487 >> stream xڅXKsW*$9y=&ުطuSX K @h4uc 7EdE wbf&^44I4qO_]O/#{ƻ߂,׻vթnN$cl:@d @N)Uglyћ鎼re 9nUS//QaR:(&jv t(Hmwerؿis7a{51kit҈$(#jQC -"ԶiE6'SkW0QV;Г=5F &ajb{rݗ$iR^;X1Alڹ"(Ђ3U3RZziG\ GsS^ Rs`n(oT6{c+AGNp< 4}MBwo-lG1Y|%c/(*ԊcZ XQ$c ݒ|%8bk ~<i4ߣycx!ʿԏ+|| 6 9 aȞ6Ploul c,3t$K,x+D;t%cp(Ч'D8u:}`^bd < }_%(#;JPs3X.f;7%Ux];WG:R>,&;Nj@9-|2Bk9YL+p1KGwv9 @M}c UpZx'Ή-+ Ԧ4y*qj8";(w6~&SoWW<@..C:#=V@!udcфCeS0C c' "MCC R=,("`]*L}4ºl|20afIhXC.W1䱝dTtJ"ߕ/ opwDݍƆ*!rbt,_ #K7O&!c0qD! Z9Q\Nb "NڞQR78#s)l=R3O0]k t)bIj1¸{-2Z1,,2@3 d4z긕/?0 р03a'7_h =SOq1_򲘅څρ4;@n>D%\ g[.Z]nҹ'@I/1?QLDRb?Tq))?$I[ay8`)ФӎX>܀L+&讼^s0t'%2'A*ث̎ӡ+;,ҠJ V:gY!M endstream endobj 3265 0 obj << /F7 38 0 R /F13 66 0 R /F8 41 0 R >> endobj 3263 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3265 0 R >> endobj 3268 0 obj << /Filter[/FlateDecode] /Length 2383 >> stream xڍXɒ+4!JܼL9t}p"!6޹J9L$D.Dam.jMyL7,dm4 ď͗͟?>Ѧ ͂ZaF'ťQ@nm#ZlG%}GpU6t<*~pzUi;Dt NN|14Ng3t`ˮA/L 2_w>tV#m(֙BS[ǾFq!87fWʮlҾɭr=/rϢ5Uаp,9B!DqXI fmo74򻰆)IFPaٽ!Q C]ӑe``r6V w˅5T˕˘Ω ivO Ӿmw_ @HJ 6̣x(朑匇ݓP(Ncd e'f,1G8lnMYE[7WDY~@gLHs~,yر X{PYh(nN+ ~׸R _+}=7)@9+epQKS.l孇Sddt<\oe+8gl%Pl0=s$PAB@ rJb#-ET9حȐy!Y+~ V^QJ%#CˆW}[GK `̞rU fjኡJH]P֕F@/^0/R9z*^㱘^ tc'+&)aS#Mf? jN0/%5„*Ã2*DT$xǕ=\*EɦoCKvuc;2Kri%  pF%Qp0rdp';1~㷖ߴ+\ʦ8 \@tm߈O\ endstream endobj 3269 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R >> endobj 3267 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3269 0 R >> endobj 3272 0 obj << /Filter[/FlateDecode] /Length 2603 >> stream xmYK W=UOu[u;**n2zh:MH)͢(*"NRz}yҳl森́g#cc@uw?mt ]̑S뭒Fo5}]7xFYOFOx8WO< VrpA]VU)?*UzoG'_oXlGցo.+0FLx>+1쟴Z7O p.2=1Ć%AZp3H<ǂQ1/N{>NV9^7V6;I[ӻו~u};]m:E4"h99=fʁ#|#b0S˃E3IF%7A rt\U\|cYO&J[lǰJɛ G DGۑ#٦oV Y<dO;z>P&V k`' [qL͟skk_*r,8sl'1j` _nTBS.sE'*MB 0 gPTw,jxI%' NܚPv?;ak#0MӁOOfj?Ӓ )r@:QeTM^[D #`u8QJ*BhDΗS1!LT-ڂro5yFf)ʲg%7^ڧ=J@[e' #vp7Q}{W>'n e _˶6ϳNCt%> endobj 3271 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3273 0 R >> endobj 3276 0 obj << /Filter[/FlateDecode] /Length 2527 >> stream xڅYɒ+hnfcdEX#>TE6cQw @9*+kea㹞9m#>&,좽jS|dno39p8͖M[z B v9Moy 4q?FıS]b(NCZzxNSp68 v!ʜ K)N CgT^Dc?6 +?NZ}0/2rl: |AwW7M tӹ,z< |dF<]x=F3Me"7e*CE}ra싶Z'u 9 v*);aW%bAiM'{AgQUe7|]4yU:ᆽM ʋ`?XGXZshw4mAN),uT4}LOb(M} ѕu>2짍VբgQ'q/ɟT OCBdIQju-1ъ qrX`?A̾z|XKZ~G#&67jf@|J腬Ϣ]/g6_"pIc$%RsKw\ lpsAlhEHB`pt!Qt[HJخ IZfs,(T{ESQWGQؚ'$FEMyY*yD`Hvva"b M^V9 ITՐ8}g-ȏ|VG=(X^UVkWpM#<}XQKˆz#?" ":ANvOwԍb0Zb,(U 8J?J]`ҞH[_%PҜCs:^iٴLI?^1s8SI+DđV8p QL̙:[]I>ŷZ#m,)Y+W޹÷~2ay$5ɬhF['՜ް B0dJF_kJ.Zes%V^keX{R̫FX/^@3{°1V/&T}q? d]pi SD:ےsX ֈ"7x P6A$6S3KT7'+hsف:j5"g ҥ ^B;m^*2c &K8ysZHKV ?w4[L%݁x64zJu}6KL=a^N˃48$Xܴ:~܀ݘ]5&gKbq6p8 HkNR &+q{we0FH#~n0#d<4Utp}qƅ}2[Y Hjk 0 '+;>?wo%bYS>җeQ|A(Xac%js6:ĦFK:d'OyQѼ %C2ЗJNC9ET TV -K8HJ Ik!am _Yhr?T)$.F^ɢp񮰒XJfQ$^k(Ky&\iď_#Y\\ IR:?UGɫMS&LG4 ~^0zƘ4rdo\<ztphO+z;b G-Ѥqل_dly,)8ZNd:Y/JaÂq5e8?s> @WZ t/̓ɸ}1d4kڮ5@PXyakFu8^/tg3!ķ hύ6"-gSA>ڥ=)Bow,}Dt+{w8t>S͛s1>ً endstream endobj 3277 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F8 41 0 R /F6 35 0 R /F16 138 0 R >> endobj 3275 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3277 0 R >> endobj 3280 0 obj << /Filter[/FlateDecode] /Length 2516 >> stream xڥYKsܸWL岜* ÷d*eŪdUIi1Er,9oO7c8]rYʶWO+>e*V?PV+}JT#dl$IYWtzy7MuuVQ6{]?k}Maҋwa EkY8Ҫ\}8d6Ϩs.&i4!.hu'z5վ,Bu.:e=ʛƍP5E]hiV7Xɚ۔ϺW ĥϼޙ$01IC.u[InV^썂Eu Ħweڢ 5fΘsceEwhTu$C,f&^xˡe2&%AOR6}vlGl_U3X 9$'c5N9%ou㒃| œ#GX>Q]Ǣ-Oa8ZvUQz^$pNsqcIH5RSk$c7&J׭Nc{9PM[yU(b8I>' c2ߏ.l8]q*veR? iM[0v^HΈbn[lZgŌbT|O{.;s_ VRl;ȮX9L1qǔ♙ \V!XQPaz+|)1= ݚ/2-0qVbx˕?>@.I$?&ZK{g=geW0uWD؎v١ 3>-YЧ{̬ [oMWitHH(YFnca&˴戸-`zms&uΌBS(sl PQq wH5`׍>! Hrɦ_>@8+G|6Z(cpE5UӋfJz6>փv 4%.ZY(E]D ;_X<;<8< ~G[)F&E%#"r4?fBˌ{PVYn6GtqR'CIȷw7gސۢwtj*Bi¢w.e";s݋qgoݦ ] 0#1wl1pE3듢%]qǞ6u͆͡ O?j4 0Xзt-I$717o q|>ݍ_C_|Q7j.׀*U|_xyyQu}ZtTwПV?6̴E_+_Cۺ7/l|֌뀊V3Sgny^9݇l j̰h!T@-yrU|t=B\=;"A8v'ںlҼMzې:! :3qv34nx,[ИՊ]7gK}i:>g!WIi&_9{듹؎CkOZ<f5hqI7J֏G)^R3:5TL4&#*KRF%'Ą/^M;UjtZn2@e@ ī61tjWñVVL1iLʖ :#AT=~pFLR褎#G8b^5ׇh6_j6!~6tV,}#: endstream endobj 3281 0 obj << /F7 38 0 R /F6 35 0 R /F8 41 0 R /F16 138 0 R /F13 66 0 R /F4 29 0 R >> endobj 3279 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3281 0 R >> endobj 3284 0 obj << /Filter[/FlateDecode] /Length 2736 >> stream xڥXKs7ﯘ%*#7KNѱ=X>@C=a!Y9o!d7'h8H͞`A -ԻNm2:45#c䬢/HG}iZTCCS YpX"W>[pgq!c`G\*'D+`T0ʺ/MD4$g2L0JQp~Y& ?UD|~iuoW7yG̍݀RFMm~o-s hcs>֬B^26$ 9cQrGAk`okY2=FK9 i:zCkczr#!+IA8qhOOt,I]a4` sdAl LA9D% pLJ3d󕟝Cڢ8XemK0|HDi873 F$ѭ3Kb«|Y_k8̽Xa!MDkPN%JljKnx!W 5ڌ_|3ye*d'`(I۞'<\qD>@K]k[|(`u"'N!8l$ܺW %8"/&me2w2 k·6 cB:a?>(. 5Z\z  8deV0|ygT$?9}̒!LQic }y7=>˙2 p ,*bgʿ)3*Maӹ\ÈIek*#Q)cnA$%OhfيkΐHiB i5C):R endstream endobj 3285 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F6 35 0 R /F13 66 0 R /F12 59 0 R /F8 41 0 R /F17 145 0 R >> endobj 3283 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3285 0 R >> endobj 3288 0 obj << /Filter[/FlateDecode] /Length 2806 >> stream xڥr`2T sxelJ*qK\"zGrًh4F? Ql>YpT$$XdiTq<y(U2BӧPeYyqw$IB)KTXHdwXEzo2_M5ְ2 S|mA,e*dqzX38͈u42m 3;=1Mm[~1~n U םۤew'[/^8eFu[wt>ͦ_,eq7wڲ:ƭA"bD.]5x[pqhM+$V2ΥHsg,r^YI *t'0L(EZˢ@6mŤʏф&2dq dnT*wRE$bI;mQi{{{8DeEia/KR{KES1Q.Q Y1; be`F i#淭[o7LVBtJw"#O: kƣU 'rt6:8+[OD 0L3w%֣yi{^J]I04K !!S3`f}Dxn2$#Iyy,9GMXn7nt[ wdtv qs} ՙ"n]?- ƌ[ EhYAi]x1ǧ*eI}[S|{yk577+ 廩^Bə)޹c zt{q[rI4JiMud(#Q 1aw֐ty~v f' B]Ewn mRәoZ7Ik\wlZ>Ċ( i2.+ûQS{;xBȉsI_rҌHRE@=08ykPNݓe2-CpHpߒP~~DpO5}M~Svx[Ix [_*W[j7j\k ú ]PG'A c}Rfn^5`o?vyHJpb("\:CV8sd~[{FJsnd2 Zf3qa zy mNh@ ;btX]o{+wmle5`9t2N%Dΐg$#*9;PBԓp_mW%bAYqL+&1Yj^fE-jeQҍ"3 ALcqx@ҊW:#Cyߘn'C=f7>+ cFF~$Hk=SQ>p]&>JPn ~xerfOJ\@Ǣ4Q'j݀x_k$.a*9P+MWC9,{~ 2_1+=;QCH|?,S"|& oIDGr\YaGve@WeRay_o-/ȕ&cl_ޘ8O§ ~ $Z1' W"kQ MÓhsV

> endobj 3287 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3289 0 R >> endobj 3292 0 obj << /Filter[/FlateDecode] /Length 3041 >> stream x}YKs W)f|?R僳YWir@qz48M,Мg\4 4fAxY3}" 2^~ĉDn._1 _fv\웶Vq{۩Ǧ殺VT{uÏ̆~.a9+cA]f<"|G꾻2oi4 +`0j3?3CКT5#{* 2%IUm߭L$-#n{; V2O^2*`iY=Nd+q?$-&dzMo{1(b Vd͈lEDt3L֑ I,@ UtR3owiUCOZ$hfs ~ѷߎG,6NRhA1 dWl[Sj4Q1қnhj[ɨo͸QM{36X8Im#2m XQP7BN( mZ'ޡPJ7Ob3@3D]z\Jl.ccC}NC잫g:g^,, _i'#8%t}tX\zF\F؟ߪ'@7r|šGY^KzPt# tŹmцedlhE"cwf 9p~ͱ{9bߟF(81Cpϭ"X1^08U.9M?L$ vWe %3b1%lKp{یvm?%X؂BόOe{QWCgj,Ұ|HҙpCbea= mӗf  8ٿ$ر~*bQ_xJvo3YiAG4dƐz+,Xl̓]]%^krVK(o!W0nIa5ގ"ub!y_\+{ɄʈLkRjU[1Ə`a* XZuQFI;*[/029m;J5 8K@9Zn2ۙXEYqljB4"%&j5wKG_b%rr )A4LM5V`D:`1 J'7"~DB=(x=Q6]Ft.pEYիDɛ<8(wq0uof|aDQ4>Y_Ho Ss2 ~8z^= kyd9 wvyg #^JY N \NՕJԎlssaɻ?;xnjvj1WЯ|v,L0w7rVzh\/syj=8 ,R?HN1{\*ど;F!!, Ai/N o1gp;0L/6Q_zLZ9HFU{C-:hvqg\8Wݫr3h5$(~%ّPrCY;Q6eJJ~cϪD\5zPIXYNU*KTP*дH,ZBcSu}:#tV|ֹ_gYOE"z(;^Yqbe,F\ZWx$24OYD8ɒN%IJ3m~  gZ!^5D2&ؾN[Ƨ_# ݉+ $rRQ&kRzU+b+ο7S endstream endobj 3293 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F15 112 0 R /F8 41 0 R /F13 66 0 R >> endobj 3291 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3293 0 R >> endobj 3296 0 obj << /Filter[/FlateDecode] /Length 1863 >> stream xڍXK6P%TEp%8vL`,0$FšgʏO7AQ#U*4 ѯ/|uXW*Eq"pլ(qlU"]f@jg/({{6"/ q7m/:L=Uwn =.娻Цg=imxWag)x+ze_mX>l0ӈOJr}*j5^liT/k⮑NR/zt Y r Vtjkud̓@ռ{wxZ6 AK^0Ȧa{^ٵGW,CgÐ{{0g(FGQ"rR^I~R\GwZCU0~[z \؃?JqfwG=h6<+[zCOL+C?^#^ۍD]OїW>C74 "o!OS[0+UVrT睈$f/QՆV%Vzq#x,t:mҥA tzVX^3OҰiB9>.%vh aI9.V./R%ͺP-qy4.4hI6>M=ȶVD|+ժzdBJ@Up#CKy<_Ua@V CkUjs0w4H;7nFW|ڇI  p"ɎTJ6s,k{  7f-Wh~0?ɑ40#8?K@Ttqbcn#AIEeQ&y\rY*F7`h\H# Of;7wwB݊OGOwe7 FJRƘ75Y˗\.^JsBEn0E"o(C\~$ m9[w FsސwkRSc$Li64}k([\p,W= 03qr_3~ ^G6JET"M{%>[ف 3m8tHH2D+=rdiטMT7{<6r$5]yBEdD @ʣMwj ez[E{V\;i dV6l4EUbVuV6/Lc $`C"Lʑ8"aoB&\E7.:Tʿ{E6$P΀mGfK^7#z'nFj5kkΝ60 6FmI"pbVMAR;[,'h"ΈVkze 7dI ldcn8Im$ 5w,/4L(đIpG=ia!ۯ+ClF>B|BNࢅׄs mM(xdחk9Vbj{dAٮPk6@C5;jϗT\ZY ؁Zijti-Pp=@FPjN-JU/I1<톕㲆3Ng8_vswb6HE%KM0Bw> fžz :,NH9>趪i|Ɍ4hՄ7+mJ׺UrWa51NU!<[Ȇ#ـȬrQp'#ę_ˍѢ=7Ɂmu6â]鲷s?D̟ۂ 7p endstream endobj 3297 0 obj << /F7 38 0 R /F13 66 0 R /F16 138 0 R /F8 41 0 R >> endobj 3295 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3297 0 R >> endobj 3300 0 obj << /Filter[/FlateDecode] /Length 1695 >> stream xڥ˒6ޯQ\=Τ3ۦ5Õ8/@2%yb ^;xzĻxi[xBùV+ưБ(S-i4Af N,,jM vԽ  Tb) SDRfhπ&̼""b':jrvb?Le'o#S1\ `1"]~Ֆ1y) ˀ@R/iJ:\I w^4MÕBV{%vaay yq>Je"'6R#9U;邧˱*!H\vx ,Qk +  A JyRZX]SU3tF_ d_*uΊ.jg.0cSYazIwh*ՉF"KњilsH EZn~9OAvė203x!7n}Sw药:7;ʭU$ $jGXƋS ]tFiJ$NR0\44޲u qr ǬgFrV0lAfHƅWkLgAɆ ^xǴYΠQLRʖvWpbB:S'م Ax΂wJ9A8jAκ -l{`^dž.Kϵ"^>-vS+x3=)$|)'^:ͰVtaЦMg5]-DhF"!,q:qqc ՚6zi*,Pvt}W-|Nm@wXJ 7빛P)Jkt`Bʐ]+ͺ. 8YPSfΑ: Z 4TNCߝOAz245kmB^/۝pq1BbH]-n0 Zt;h`әO (1qMH[xqK 3J@?$GZ-:P3+eh ۥ*C(F~s葮qʖR뎫g>i5d&=MblTJI61: e`;6DaʁiO/oeCR.qĦ?77]`#Q̍t¦02`ϟ]1 D0.$ZD^NW?Zq ~"O24F^I~!k7Z-"4㖄,O~҉$Ydk?Jo>Zgq0MX&ĝsF4/ZZF&,i^B7g~xҥ@c9)||qNE6rYSv9LS=@$lle@$ؿT sz<1 ݍ9՞h(3P>ĵӄs7?~P r͈ÈE?s˿+/ endstream endobj 3301 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F16 138 0 R /F8 41 0 R >> endobj 3299 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3301 0 R >> endobj 3304 0 obj << /Filter[/FlateDecode] /Length 2185 >> stream xڵXKsWrYjD9MlfRS3SY5*W%Jr%Dh4npfA_/:*Z,LMnϏ7?:ˑ=UxGT媮2]0nJ4R Yt\Eݛ*"Ӎ;;lpIEzlB6lYro $0ǝ?X/V*lxrGcؒEm TUfxk]݁g%u/c׍̷lّ+yR<n-Ҭpwer陸FcE穨AE{T.-_)?ɧ+o;1JIN72گ>=H{D'NpE}ՁG{6~ 5.a*ZQvTSKPeJю_:@,3mȼY-уJ Rѽ 5M%A_ˊ=nU!9xU{%cUF1;2lY.׼{48Not X4X7 @}oܸG)ݖjI}B&iDzlF` /'lV(?]{kl:ϝz/mf{H:`yW qX|塴fNDyRpχ6RJtt`Ly jt]/`WrĮm{.L;y#d-no^=S/p WQUUǚ|yqQsr䣚y$V4pa熡!li~WԳY toR{2CM7z!!ї+S1܊\8?5za!!Sx XO(>h{mI"Nu3/^ 5 ~8U:?N8*,(ǗdHYh< ս 8lOɔrzWoH0Bc^IcIOA𓰏 odJAxY6B]3@eR\W׹=g%s20κgQVC pǬqo4:3HVXڊ( q d)dtOKy'ko7Ay\?Ƀ)z+IsNx4[]D@,/ivx 0W؝zEQd .'s* Zi6QPmi-~h_?0*$yy65?9VP\^Wˏ~g1&֊^Pe` endstream endobj 3305 0 obj << /F7 38 0 R /F16 138 0 R /F6 35 0 R /F10 53 0 R /F13 66 0 R >> endobj 3303 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3305 0 R >> endobj 3308 0 obj << /Filter[/FlateDecode] /Length 2198 >> stream xڅXKsWHUY4 6nT:!` 3|(i/PINFяDamj~&ۼm(.HmMaq* m}{f*Ѓu}|ov<2ǃ~=>\{=̲MDUPw* ㄦƣT^'c NCt{#`g~74*0Fclw`Q=e76÷Q֑eQVL'b?.dPޠ^RY)Ix&1"Z&x*r#9$Ib 4 yhߋ%Gcp@$H4LI n}]qT ~hw! Qr38 q5JW!kx̩%IKVA6ywiYKpvEZ>҉Ҙ@Y3'E/_ڽpiE3)+@oN|B{4svK?at]KWd‘~雦G5ߠ|TE|lTRiO4Re/~42 3̘#CďOI6 Hi#xpMcԅBĥ &3Zm`LRm& y&"%qN &lGh oCs2r.5[kZ-o2>r2U "#J:sw% 8fHrr{BObX2T,%x sJN(LG\\* G`V)S$潅tiHuG0{\pY`޼q< znxٮn&`w7,I,m85ݍxg*\c mLJT@3A{ЧSxV8ގf<8!iU I8TzF 3>XF!Bڡ0&|Ryg{榀=O\p&R C!0@FQH,VȜ+pur\Ty50+ nY JMMDGs2$(9.`RRQUIoŻ4QLTwЛGpBB;?s*!Ks*]X`W_W"q#pgRwM.ra^ӳA*&~ȩQ%$]"<^70O Na⠲ t4oȌ3&ycJ12\4~"LG-I龔B? )4 (dǚ9>|τ%09)_ 37RI6sy|]ī $(5?p Zݹ}*Mtd|c12"yUnן1!r'y.R_\ -T* \rUV无(} W.hʗfsc1=RÞ> endobj 3307 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3309 0 R >> endobj 3312 0 obj << /Filter[/FlateDecode] /Length 2038 >> stream xX_6 ߧ\T[xzvhwvà8Ecnw)ʉz^,Y)"D,5?CX.e  Kx_ݫY\w<{[(sXy7;uhuX !BαMK)eV2ԥ=x5jѴ;"^,4S,ԴFw(X`ϙjkӢInk^6t1x7c)1fX=#zyUl~9壭cU7*x..rX,cq> ?xPdMz,EYGֹCnE>yEVHiιW]jCD=}-bxg3L,bi;;gG; mReԿs \otud|ݛ/3LE]L\LOe>6Nb%\ rVkw. V6j]fZHtV*Wcʔ%̈_~P&e6 ^w \xX@8chqA_*Eo|.2gy2vEq!Qk]^_[ h!wi=Cیh;2D^WuURhrioPeX _DuY(jzk1E X;tnVb\*.IBZ v.;ܥ*E> endobj 3311 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3313 0 R >> endobj 3316 0 obj << /Filter[/FlateDecode] /Length 2936 >> stream xڭYK۸WDUh!ڜ8-3 `Sȓ_5ҦrE tdD>brdpLf"Ǔ$M0}NYw}3]DΒE;7;]̃s/2 >c}e}:ڪw&4.lG$Zq3eY*j!Ny !򄄘β4fYi!i\%SDfDs)SSGXzū%:^e]39k-5I?nIIQ@(~ n|Ԓ^y{ '5#q(d^Y)HAvmw5\rcIxH]2LqKNW`O a>$j݃27F%O}?h)/@ .; 9ݛ-\,pΚ$ăRpv:PF93T5ۺtJ9#ΉBp"M,QAvj􍬽x[GO}NԺ5֥1y1XYu(9j@]3TK)z)ZA-n xiS6yזL>={&y#5 _%RF!*I*k-oP 5wQOweC*djw.<>"1.Pa3 Sٸ0u15]U+yr=hJPPBaE#C-"0,۟"i4ƕ^i%/]!᪫ Fb|4y}!#GpXa/)*%ݼc렾Vµom&2a/ת*JŸX dU&0DD aCӂl"d_}ی|ЖۧK.Pԛ :-&Q; _UD wR!WPƺ%fLP@n'Xa<[qۜK pb'Hקtmypz?|M/ OuMJ`v' `rЀc">+f*~SKRg#7p %P%5˗COÞMSK(CXB 7OhƘmLA({C+"˰7Fnr~-%5k.!Njhi)#C>+ S+ 7,6t%=s}`yq" Ͼ#q:J P=Wx3FTQ7v-w)(LF8'"o~#l +q/ϓyNΓ|2qϗ19]鿝U93#o;|MlMkhpZlb 3 endstream endobj 3317 0 obj << /F10 53 0 R /F7 38 0 R /F13 66 0 R /F6 35 0 R /F8 41 0 R /F16 138 0 R >> endobj 3315 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3317 0 R >> endobj 3320 0 obj << /Filter[/FlateDecode] /Length 165 >> stream x=;0{Nh(p(Ld## DPV;q|aÙ@f\@J*o'8-wXf __1ag4Vjma^~B(ć ׄc/" >@)~]1jPq*3 &;-2 endstream endobj 3321 0 obj << /F7 38 0 R >> endobj 3319 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3321 0 R >> endobj 3324 0 obj << /Filter[/FlateDecode] /Length 1685 >> stream xڭWK6WT֎ڴ)Rȡ{@K͆RK@{ᐚ7ENaAï䋬Xo~5zY\?nhm ˿{qhM̫,߮7bU"xtp\2~|L~zBtʏ2U"zUpCG̄EepxkpyxAg&D0δ;'㮝y,O`(ISAwyAPH.0I'kZ:MPd(!0)l;戽"j\N Fwc2%k#y֫/xixh1߃0v43qAaV,XpͰSͰsOY%@ppT;FOꄍT,s6M,dFÓ-h 9sg({pу@WỲ?6~a:brt!/E1HtAd&B(LWy6FEs{]^OqaO nniy+rjS83$:&uVYPNr^{J7]8ރ.1J&$w[u#{E]"*xkb :^~xTHE)tpbIp'PKf#kviTX(pw Hyb`um &* 뎁Iiߠ ;-3'!_p ;ҩ$&U)nVTRWOZ.s^I8\3W51 a|$7EgFijD)fgM wx!mĠ@D Ou/Q팝 2d~bq8Bo|,@KKK5[S} uJZukm}3rU1Wwk;f&Υ#vt&E&p])pOat[BFHF5@u>foJ$#>(Ӌ,i6 \BsTupO,׽<9#|SSJ5J߂F*2ڤh)~ Я.%aM!NS~tUG $4? f1HD ſ`W'Q.%@ ؃re.|*a rX=0Iwu_^ endstream endobj 3325 0 obj << /F13 66 0 R /F7 38 0 R /F6 35 0 R >> endobj 3323 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3325 0 R >> endobj 3328 0 obj << /Filter[/FlateDecode] /Length 1425 >> stream xڕWr6 +4]3*zӦ3v邡hD*$}d'+Y'ߒ۟6yfmlwIgm&ӦmVj7ezcPn.뻴(xZnO^[g{WqWmM**u|o?h+/ܦ*[Jc"h2F^y`iI{Bw;1 SRɳ6g'EddmM ck4)7yw<xZ#0*(\oQHY"H4y=\Ru7Y'Ʊ dm IA2ѰTAfk+Rp͟ )&aYE.St2V 'G0am *R'F+TJSlV؞pr͖圪r; a1(<(PM:|V3 5uhդ]h{$cN8&.27WNkʡг6Ob-)}}X(V"կ 1RI?X[DW}QϯyxKΩyU^

V=cktheo=u;"Ty։+WpWU(U-THM+T.PDCu 9;[ sd"M":.odqYŒ~qJ~A-a )%:R{S@&[9}|8hQ0 |/n\~~|>Yi?'}M endstream endobj 3329 0 obj << /F10 53 0 R /F7 38 0 R >> endobj 3327 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3329 0 R >> endobj 3332 0 obj << /Filter[/FlateDecode] /Length 131 >> stream x%̻B!ާ VX`WJʂ GccN'@Vue';80e8ΙBD]`.uQ9s".泒Jql2u{^ϟ+ /LZ#Jz E'M|r#{ endstream endobj 3333 0 obj << /F2 14 0 R /F7 38 0 R >> endobj 3331 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3333 0 R >> endobj 3336 0 obj << /Filter[/FlateDecode] /Length 20 >> stream xS030PHWS\  endstream endobj 3335 0 obj << /ProcSet[/PDF/Text/ImageC] >> endobj 3339 0 obj << /Filter[/FlateDecode] /Length 1498 >> stream xڭWK6W2f%>ؠ4}PߺEȲF QڍwCɲM$Ap8͋"E>p6A$(&`sT/ #.Y: ,NЫsQb7+,ⰴt: r b4fpCWg!)(m!q6 )"L56γm_ ! SEciT u/4YɄŃ6ιҽ)yֹ}~r@[ :"2&׭-jy#HR*$(IJ]mVk".5 ޒ -}"l"aOtEg?u$5^Y]fکm>! W!ĩ--LŽOylK[;O@`,ED`SLPru ] $zwh~r@@%wGmEs<lqa߽1铻c?㽀ǭ|lwDЩ- ZC5IٖE[lˢo֭4QНvn4ў9H->jd/$514I=Tqaq,qKvzC WHŸ VdFGp /` d"8C2FF+n.]쯊.d,ŷ Cr&Fkڅ'SC89&Y?rxYp iC 8LqOOIč6.ݤ#zf)kndg{y &D]~TNH@)K5һ- "kHn_v`]kt]Eaڢ^>)KԳmq 'ƼCT=n7E%,p}F>*nx4caEOie$0grR=?3 ө/M^rbL2߁!>$Upq1LR`fNT[oaq5r@p0w endstream endobj 3340 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F10 53 0 R /F21 215 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 3338 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3340 0 R >> endobj 3343 0 obj << /Filter[/FlateDecode] /Length 2039 >> stream xZoD篰ɩo=@H'ɽGGk 7_=$*e߂}J?)KhPyp}Iux&fǍ[ݖ?F"w я".l~#~izsM ›sMb_w]VzKh'$ND'{{$f=\RyJ@;Jxt!S TB 8߉J~4 Ky4$?MBR5;顩^G:^Q YG#4zzG1\Q#:^+ w1i.g5 ![oiMv& WaLԓ6=F ##;dMr'uC\9>Mŗlp,˕e2_N If99ֹא[m<$A69h]N辛&{Uy؎Ъ*msK/LW$(_$B[ZiOK UJ(e8%L+ q6#oZC.| nQ endstream endobj 3344 0 obj << /F10 53 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F11 56 0 R /F7 38 0 R /F31 329 0 R >> endobj 3342 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3344 0 R >> endobj 3347 0 obj << /Filter[/FlateDecode] /Length 2124 >> stream xYKܸW(V'\񡗽C8,4juWRxHz^C.C6U|TW_9AĢ( K(Y X.Lއo˺*7[&M&`(Rӈo>\=5,,ʃcbJu3mɃle1wf 9pdDf8lui (SV𠫻CoEQD\Y;Lqq9}՜ll9)W)$z[w+^ek˜?]+,JM/%)<>M1V T/|*:?>Vm=xg3(I^BzV# A6NYNO0LN = j ;6 nAPZi|DYiE6ӡ7O݁28dcasI8>E3HVlF,~v1^STRZ3"—^w=+oIcK̂Ҡ‘uJșKYѿ5FOBdo8bOpc&|\y.7B#(܆?PC4)v^QΙGIM&ö9үr>C:€v+ &eFbaQGTaLs(CDZYB0⪧}"jcq*<5ױ}P zO{$_ _J\f)uի v:X2R"nZ5}u#Tw R|DJ5/Y‒#-e[QF=`7n'pL~37`{zE]4GFE'3̉vv[4;/.[X.1RCU$ PypDwLNTWSM&Jݎ2&.u{;F .¸Xh/{fy^8aWhQ|aoF4Onw)%M@"˫E P9[dh1&g:1<7a 5Uk{$!;Ֆh ?,dx0]|k\J2csy3yX4+L|RkOF/M=ˀt(xc8;&Q(IH-y\d-IAFg?9l61@i@Pcb .8mU`Fߦ?ٱ9oNK\ה{Jk 80?$y͹b|o \ endstream endobj 3348 0 obj << /F10 53 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R /F7 38 0 R /F8 41 0 R /F21 215 0 R /F13 66 0 R /F37 677 0 R >> endobj 3346 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3348 0 R >> endobj 3351 0 obj << /Filter[/FlateDecode] /Length 2150 >> stream x]ANZM/@۴Ee< %WwpHMOpgJX+՟nWJVfURb1.V~Dc\Dl<]it9S~_nZ=9\WJ*W?+YXxwpE&#M:WFÃEuE󸆕ETXl{GC&~A]sph }<^ﶟ"YŜ25!EC 9_V-JV- p9iBy!Y*-»ʞyᶇN"Y`CX{2sbd(wW99VJ'!%KvTP^p>݋ٱ: *h'*',D`|&Y)fmBϹ C{ ii?4oL-h6Z>TWp)wV_j@)b̗ /Ls 4:OÔ"]J=4=矤5O qq8C zlso'tܺIQ/ͪ✏&y#Z,^b+~|Z)&/ᏟSU(Kot_} c =Kg6\BX&839&~nzNڽҘHE_<+\l-!4oZ3\ u׵-[ :SGוּV$[ |c&ɫ fwvٕbxKrC]tqv`q4Y ,n ĵ2_7/h^s-DGእpJ5yR,<0<0{NC w)]oMXc+W"[~"(vh*` Yo`@`Kz"br=-jZhe)im;n8X> endobj 3350 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3352 0 R >> endobj 3355 0 obj << /Filter[/FlateDecode] /Length 1053 >> stream xڭVK6W(+V"H@[-6$Ptp(;@9i8{Q4v}\[VGu͊hDQ3GIˣ?ſ?@FD\B$ST]R")dnYxIZD,X*IUլ.HQV$+mHxk=Cqk$xnဲUV^Ln(ѭ'"GQ*p:ߜFw#ڪnwT;D)jʕ [$2H^2@ϨAoz٪u'tUx?wV9ɸ}P BuKj1=628+eP0_VX& Y%clYS F⭭_x蓈DODlrG m1N4ͳhm z0w5lIrN7B5Ke9hbW<ޝ>MW{8AxhK4_(Brav"d O/ߨqU5M;"դJ&%{5qipZ I/(5=4"h[Ypk#%20Z~p\uÌEƻ#\qVZҒ9?dz|7 yР5;'BV(CzhеOZe(%9' Y[e 4VIYR QVjAy^x[m= uӓ |bR thzy:* [P$it6qFc=r,Y%Cê+nr xd}y>ZV]TFjMd{ oT/Qz'Cϓ"$' jiw$o@E |!1ߋ`S/=tpFڙf5ⷲ$,: endstream endobj 3356 0 obj << /F19 171 0 R /F13 66 0 R /F7 38 0 R /F6 35 0 R /F10 53 0 R /F8 41 0 R >> endobj 3354 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3356 0 R >> endobj 3359 0 obj << /Filter[/FlateDecode] /Length 2029 >> stream xYKs6WӃ6BNi3=Է2(Ulo. A;N}0cow?<|}5'9˓b,LoXMg"*~87u9IF%"ɠog?N;M8jbJR-'\1<݉~&Q_:L(FݾΔ{z㻊=ڋ^7ESk/[jhJsf*)LҘ"Z/lu]y7Ceg겜/mu^ڮ1͹^l6jn304Mzٖ;\P5%tTئi+7.srKR4Pԙaq3bWä;XOkYϗY&dQ2n_Y)F`@H+D[^>g>X2a@g.V;62L؊Y ]P ! l8xTcL٫0$Kq Yzq!}'X0yN;q)2SxPSpt~M.}'ёnjV0#^{8ž:9#!sѱ$%!?&}OrNйcb,&b1 `BH#0Sbil =l1 Fgwqj)ef犳MumrNZx@q%< piP b!Pq0WCUiuxH!"ӑaXh.D&D8[қ`Ȏq#2eĦjvȁ~mEeoNi4.dAYK,Y$[*^ LҪjTKe!!4kJ6sK-wkxqJfk0Fped` ]GU+mcZ_s,Xc#y_^)"$f&nZ(kCqZ @B [Kz| I`"[V0`.)V@'M`6ji2Q $~ W<^)z}{?8ﱁ ?,I~<"NO~! NO.<=n:+{WSVڞnUiK sbzwsmy uw:v=hwU\,K{{c5;rɻӀ,/246mc^ڄ_Ҧ5^qGik胄V'N=5n/kz^^Jo|Q{6CmƸ0g7ZӦ6NUEUcw_{[aAq.9KN uW[UwILob3 Cqr&zTç-2 OC%֋ou=[CH_6ȫzoN[<>!yR l2xhZ/ B~S;{Nah RncW!|oQK6#gŰNe½;0OAک endstream endobj 3360 0 obj << /F10 53 0 R /F13 66 0 R /F7 38 0 R /F8 41 0 R /F16 138 0 R /F21 215 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F15 112 0 R /F30 286 0 R >> endobj 3358 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3360 0 R >> endobj 3363 0 obj << /Filter[/FlateDecode] /Length 2042 >> stream xYK60A#>ЪZ*WNެ6Tۢ<y|ߣn.ێR1mv=әyz*$P^Sm*GVcsj;-"6Xb9>zljTP`wcNHCJp؁€A2R,i6 w9u q}0 D9˂%F}"qk:Nr\G,K:P,Gy o^8j9TH' x?~ E2(M ΝBjF170' O2:1)t_tC6/P`P 8$d>{F=z%w6$CT,xΐ,;?ZIݕ\Y4E\=]RN7D@tr~2Z3!Bh֕#CftC$HFT9T(?ؘ ZLTMĥn UXDcT;s W #QHXԇ 0'w^}}dT16Ӝw*@ ? 5, n]t7YZ<ppz5|QW/^N Z r@\į񏲑CHW8 S˺jMww=T5ψ> endobj 3362 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3364 0 R >> endobj 3367 0 obj << /Filter[/FlateDecode] /Length 2388 >> stream xY[~ϯp\ik:ڝdkkSo^teO_SHs&3XU:VVIAMM$ҁD&Y/e<.MfmIPtH>ӡqfH+z:BҠJ͛^56<[.)['m/+?V874v93 ^1[\Rkae?n)Cp!9\; x(w8r< U?ohh;JGi9Z4 jh*ڼ}kqsPYO*"-T_Bbp>6&eW3gfPLd(,OC D/utthH TlViOKD$d$#(=qUj"qASGQbvhLz@lxʣL: ! )D^Alqq`eL *Y>+쌳4q)ځ),ea<ؕu!abJܡ: T=ae抮$h8'dن3rE#x`#(MG5SFegjA3W WCS޲FZS*iؾ(@mA,rD1[=Qxywڟe5š59VGֻ;Uўɾ HYV_Tc>nω:К:nsjɹpC.V- ۣ= , R L[}e <Кjx5 Y/z(=SÂԱbIJM[}a+rZYbS=bjWb)Nfn?D$Gs\xXV_x0Np<'@k _qms8mY{UpO돃//\ S^qNāTf_đ5Yc}ZyY^o s*(hrgqe;9 X!wᨄF)?: k+#P{`r;2}!Fsޟ?" ,Ly?C3V@e hL[WU50?U=ڶSDÌR[B*9.&z-n+.?ry^a1ո=5tuڥZk;O$W{ `8KV(O ImW2 tX' űJ+aj|[w$؛9" ee khD͍x»! fSD\hpkQ&_] 0-be͆39YNe$!v[^2grdLL͎VNca`4X 4`͞Q$_wn_[_mL[zq"dg4~XiEs(yv湳KUZ=fj r# WE{}sp\(8dr7Uv@9ղ&J;:{I讳:+Qc9 ,a@=5f*XƤNuW~aT0CyJG]ϸGY9:>_s:3zLڈU2zj*6߈,{x&+~x_Iv鎾7Q q#FS2mHU 01)< ל٘f6vHou|E \$HÙ<ņ}th+j70203 ~:s%l ec 1lnf_Ygcܜan b\d2m,fQ)<&՗>o'ӋL>c}Ѝ5*Ȼ酸b)`S6řD|qhΗieyq P<“#:qTq7<̔ 8ˢ|v&TI P][M?νۦ3)N{}>uPMmIr<ؕt;-@[ ҽ]l Cmar@K>ha4/˽or?zI,$#- endstream endobj 3368 0 obj << /F10 53 0 R /F11 56 0 R /F22 218 0 R /F20 182 0 R /F7 38 0 R /F8 41 0 R /F13 66 0 R /F16 138 0 R /F21 215 0 R /F12 59 0 R /F15 112 0 R /F23 221 0 R >> endobj 3366 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3368 0 R >> endobj 3371 0 obj << /Filter[/FlateDecode] /Length 2029 >> stream xYKs6WTjx &'=Vw2DДF{o.^b3ž^PBf??-GFɒ"$H㋋~X,ڴb*FP\qb)_D;'J2BB I$wjZo;'1=5콫qģ1|bkWK|[7w{[:?V]2ʫQ\=[>?wE[#X/yeb (ߴG/LiC4i t*,{umvI凛]Q#7(T.8EG!'Zwe{;(.DGFU~?Ҩ l1ͮnmq@cp[:6:(8*KCcԻz[BMШM4f&ɘ6s3ͅflUvw)IfD2;21l9\.W1'pʙ2;va#1.qH$2 6ţgsG(9J& s_-W W#+$$[St׵F˺5 GcGא/R1"d/dϦI&C#,tʸe?1Jo2 N')I#yo y?ճqͬv|' a7fo5/3:ɫoj"\%0%7yml16E]F<:7 b6yceteNP-V˝}04M"<Mj`C&;٭K{.|>8r^g0RɴCV/rٕj^G:a}J⅀Mx`R; ERM SX!4#̏rw .TŅ+Ebw2(i,HeNц쥵N;r䘛(e'Cδc2Ɲop!1 A~wdCܸŰ0]lquTx '$=hգ w`^$ ,ĝk˪qs= q`I-)TkEa2ܡWL4,wyEcyA@,Ts@'^#-]3(;ݥ$$tMF)$I8x/5$O=PyTVpmz79_ΰzh\X?%WQ>y7|N޾;+1$1y.y*7T]k{0\(z"|Q0*9.$|FTe:e5A_لb]X8v { N)(rJyWjw= 䤔Grlʉd {YE€s<:$ЅČ!舏gg#Cԧ n]S DjcIwT(MĿf-يzWV{d)Ӎ6GNU)aݦH: 4 @}[4lz9S]gs,Fe6U;wC/s:z&1X&*@v]}X]Ig|}g4c:i}0{5Yie=.4M>տ>zI4N ;ggNLuI?kOcGԽh4}w5T endstream endobj 3372 0 obj << /F10 53 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F8 41 0 R /F16 138 0 R /F21 215 0 R /F15 112 0 R /F30 286 0 R >> endobj 3370 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3372 0 R >> endobj 3375 0 obj << /Filter[/FlateDecode] /Length 1727 >> stream xXK6WCfDz}%hQ4[Z^+%CwE!)K$Ip88@z̫½LźKA۲tDhP?-|!JlO98i{cۧ2" m#[LA:ydbu3 2WY.Fq{9YaA B'uxt <6-ڱOFx1TxAKߡX{)E|Gلk> =gy_ߟ1_1$TH}@4c$S0h3̦jj\]{MɢjMCWPdP2/"1=,|5'aih^Hv.,,5~|NXv S[ÎrI@SKm5= $?,H.HB"0lxpE.4cQ,Ozo=yp,5 kyS"~͵wg)ch;*7Ԭin#e/C,iQɰz bv;ӮR W{akozvPn69w#hV'~P;;+c:#}OS͇QF;9mޯF Egg1+FڶXIZb ZxСӌ'z:eX9H!~xtߝr+PY> F٧hD~#JG&M'%3+3C &@&+ YmD1ѦFmUϻl)$QiI@ggК O SҼLƹQ# 3dzZDʴf*.oSVzrWR[.>S:ڙTydH]Jy;uZD]nɳM(5Aޯrid ؠ"AA  uV٬ӦT.J`,Ix9$eY^Dd/w" "Nsh i!pgĦݔIuALt~/Ie jLhons#3Lr!nD$i a%O .v ,{fM?HzԽ?٣;=}G.?=qeǹV蛻ȿ~z3C&7}o|ySiXU%`9CM3ڪ=-I*,j8;og.!mF endstream endobj 3376 0 obj << /F10 53 0 R /F13 66 0 R /F7 38 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 3374 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3376 0 R >> endobj 3379 0 obj << /Filter[/FlateDecode] /Length 2096 >> stream xYKܶWL*NL %%J9;eMry9rjʕߞn4@+er"h _wc6! ib,(3R,j!EۏՕ^)˧-}IK Ǽnr]4n(e[3rK(6ČD+Šg#3 o]dxc (*12o[uXe)1j\ƐRjt^^_dbj`:z?XVƘvAR^OV īۑQĢhExĄ-u._f>$ ]?SE2t.m/ Mų (Zx6TϺxfgB"p^/IY|ROj|%>=A1Fڿ" T7cqDMD7E^׮%P+A#a)BXvU6J3@D(6f ȇ̖Y)xp̑'3jbF9ܮ(S]QW-舫Tu7 w&e- PIfiarzVTM&Ew,OvMmWĕ/x,!| aYW%9-!ULWm5 2t{Pλ1!:hCϲMwv_ -(sw-?@€`mYjWƞ-NpVl`ɣiJ #V!DpW¼`%C < Gjib. 68x3lpwc,{cTVS+LO|f%@vQ?Eq2)-$svd6`>?q;@1o$3F`2cyW]Q=E{G}&7h~:saM墟Hc:}|3ٻav0]W&ǽI%:Ze(o{.@I+2k RtӜIƌ'\03g:rgȖqG*c{b.b f`|Pޝb.Ǭ+d_]^.ռF0M ;'];Br6(j|ZCI@pQk~s ҉Tx)ɣZIU_S/;`X6~Ņy~d1 [t׏2w"X#9i5JmĢL R["W-ֽZ` L?i. E>Ap]$/ms'Ɠ#wZSz0젼!~g+}!nv L} Rڍ\6Ȱ͵/5C`1ዏo_ \/v endstream endobj 3380 0 obj << /F10 53 0 R /F20 182 0 R /F22 218 0 R /F30 286 0 R /F23 221 0 R /F11 56 0 R /F13 66 0 R /F7 38 0 R /F21 215 0 R /F16 138 0 R /F8 41 0 R >> endobj 3378 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3380 0 R >> endobj 3383 0 obj << /Filter[/FlateDecode] /Length 2466 >> stream xڽZms_\frԌy׷Lәb'G˔͆"5e; "پ8b XK.z7ER swC2+Gx4Iݽh?OTs0O1yμN$IAPwZƹ%δ> P•}z"IuE2Ήv֪%$SfBL@IlN-Ϩp җhOjB 8bT3_b2ώ\OD!4ld-ㄆpl^3zqtE[!-3hˈYEhӠa8=O 3"4!r?#đMq(>Di1Đ|OP<A!{{` g`Qa5L9 -m87 ;P͠$@,RC,iꮸ-ډI~۾f3;]4ȊڜB"&F>aĦjW,u T ~V\*",#n̒p+ܹ܁W;\|̢S2ƹ2}0NGGȼã5bἽ&dQ7~S K>\O&.N*0gSA^G{k^}#W)ͬN=5Nz̀΁ZDl{g53Y Jk1qZO.P$z Zh>ho{ ~e#ٳo~2"w6 * ª/YfjQf!$) ]ip=!ǘ v⾰(ݡ{7YZgF-<9BFF]ֈş6+<4wԯvm%fS#r=^$^l2/;C:5Wewh CDB F+EE UlU=.SXwgb%3'nzlz6L,b7уG;V~ۙH͒z}ؚ;Wa+FuE ;' fJy ׁ[g3ȁiAݐpuž<*3%I{/V@&RPDT\SQ~ԋ*dΒƒL9H>~KGS"|5u K[3xtJ8p-% [h,r~ ݨ"L”$$+9Gtf מܾZ.ס3É^Kz]{CZكi 䕷ZxzZ{%Y ),Io\iCZ}3-RfLDYq臬>FKdGW;x>I`]0tl -%D`px}j`y38ɓya7/AnЫHZi>:w})sP1乕T@6|G ʒԳ)@лsWкz+l'T4A+v˶6}p359Xqc n4jXR1(fͷeNFJ~h_ͭ -n-ڗ#Z7iY_#$ʛ'3N*>嫹g~qee>gZۈ@R^!G 6vX 8mf;bOU6K<13m)ї ,3sem,XGZΉ?wqo:h״tx7ǟ,ɦDCGilǫReu7%[oŮ-֥A2#th{IT3Iȃ#Ki)eJMY$*Du(K= {R E endstream endobj 3384 0 obj << /F10 53 0 R /F7 38 0 R /F16 138 0 R /F21 215 0 R /F22 218 0 R /F12 59 0 R /F15 112 0 R /F29 279 0 R /F11 56 0 R /F23 221 0 R /F20 182 0 R /F8 41 0 R /F30 286 0 R >> endobj 3382 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3384 0 R >> endobj 3387 0 obj << /Filter[/FlateDecode] /Length 2216 >> stream xYYsF~_l f fU^z+[uFUyFb*힞)9 ꞯOy ӟy? [l%?._"b)?˶R_ \,E£O8_;s:V!gz Y(AO#'lmU+%~{xo2ziBQB3᯴mYĿYT4? _i } 7iv(6uň|V^C ;nU4* 'we oTL[tbUoJ:3na [%g~k:'OQ"!=F@ ?>Y0cUH\Jݎv^ u -wH^tQ:t! 7tS=F_Ƌb&?9Po~vP7Moǝ:_Ufݽf0aɾQD;bN tbx,aI[^i0kL瘰'Mo~u6E!CqQA8F L?ɅpvzDMD[NF;d}7qZjPMX9㣺.wOgA&9P/zG_'~$nרњDE,[CA#&M7!t;CKaS滬mU=-Xq~_ga_IVsd`Q@eJgMzLg ?r~@ S25F5v5qleVA3Œa)Wp"5e X{]vb7ynnNU_np˜Ń}f~~2¼"{Ƹ#W1ʘXS ;I)_f9>xհ0؆L/Rlu#1!_ѥ6kL endstream endobj 3388 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R /F11 56 0 R /F23 221 0 R /F22 218 0 R /F20 182 0 R /F30 286 0 R >> endobj 3386 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3388 0 R >> endobj 3391 0 obj << /Filter[/FlateDecode] /Length 1946 >> stream xڵXK60v/r`3E&;;A}9gf%C@~|EY{zsًY"Kz|g ÌgoW_Yt*2NUgHϗ:Lm~$ zedP# V ^TX3eg?:W 2&xK;&~{?G4Ax8[Fc>z8s`SY^*|^J1 dwѭNc1h|#J*z~c4j/,e*J*ӾbצuL{>Z~hňo@$ JlKdb 4НX'[Us<7!c P8op=ߠ,[g*M|]psڳhޱ}_ ۗESoNڒSo$Ohe ~94ԫG[|a}Tm{fl(0KY\IAfʖkqMն#\;]V#Fǂ`WOWxzw% %ɽ\֐bcA[6;,{)9~;TP> 'Tͮν#rImzWuα4xvgm#5gٶH._iʹiTb,@Z}EXIXcE0`dnѭ$ O~6_' Fկwy(I8mT[^$wKήu5_ :{Lzb4 Q;ն_ {تj.¬|Wp*,ۛ$*N8t4g*5a :? B8M❝up5xre 0g†|#y3W{r p4pgVxd6BБHCr;ڞ va讖OfZRh{:Xs:R0d>qGT[^'eqԹdxm]rSRak`I| wS$2ܭ!㒮X}{wLo<\ϵ x!9Uqs~\ܐw[ϠTLF8jvw[%Ɉ ]4]KX\n*H' -X֑_AO˛җJkߨ?x<PYJbj[ܣN.u;dϑxZ3ړ4ii 6kaQ U[VE:hwJF4tPFWv빴d]\9P~c ү`OpwCw#}~ANq"/a>*K.ĕr).x!,PQ6f/87WÝ Ao-s`~8urVi(JD@Q9%8ZvYvLzhp2%c @3E]V-Ft~MX$˜n l=q0h0|#Bl$.3 _[zUypPOgB3"y^*V9#B#2x-L&hJmC#Y|ZB !7 3ʸ҈4ixC+ky0XLn v OoU+S7l{q-2yIJiD6]?ﭭsqqFIJIAA"pу>Pc* /SV=@x SgjtF~CX(#K)|E3Fr؝:𣤼+CnT8- ξ1d/N'Z\2}Uo8W[7A > endobj 3390 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3392 0 R >> endobj 3395 0 obj << /Length 59 /Filter/FlateDecode /Name/Im87 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -14 -14] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 3396 0 R >> >> >> stream x33T0A(U&Fz`!S33K=#KCC=sc#8 Td5P endstream endobj 3396 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 3057 /ColorSpace/DeviceRGB /Width 204 /Height 272 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 204 /Colors 3 >> >> stream xknռ@]u. C@ 19t*s\7ߍ\?NvrN'x{{+ h$ϟ?s!}6%! hׯ V!2#*%{U}SWǏ(s ۷o݆B'$ov.qk%BCr6dk.tds/Q߿,4ܴJ򒭹iJ<ʅB"@d ׿,K\h_9HdABs9ɒj7͛7 x|>)N_R]2*?v3% TX NI/Q% Z9ϳ%%{xxXbqABF$ˉ0-q4~Oqb"wdAQ|ɒ-̱|e\Bl a!vE۔KR{%ɂc[+jM?Ed.nœ ?jL6D2q5Ma$nQ$ S,j~&LvfTKww'gp i࿻d88!aL|kSɑV,7x3*̥쓔lSЖ,p5@O285.Q-Ed4@C2QJH%nl5d\5dr*"B@M}ɺ/ty" IHv}}o4!d:H d*^|<ׯa\QӹY+ܠɒW`ad lKKdq ֔_KuɒpHeduZC3c@2!ߎفlԒl)H,?Uiw9hrePc% c52'Y.X2_0Kt2]JEh@$u A2P@$uN Iϟ?( O>M}/ $%,dBÁx޽{{zzJE2l2ɠzr\d읺(TFdF6ɊP7u !K84;F2G;>4KR , Y`(O'"hEI dIƒ1&;瞩H2?8L0̭|Hv:$sg=-Yxv\bܿ)~fŪHJVcl$ Q'0Ll@bdSJw er 3A`$,HVA5MO G2yLʵX#rG%kڴ */gԊKR%%>'dbeL6\2[2zI^'$39ɒ%ʘUj?t :H d:H d><Fi(r$r!5]jɦfdk.`wL90'Üd aQ28H d:V&chZntw,+5QɌ`ndfr2~dLdS@rSAzɒ$gB}"Y F)ɬdJ K6wkn qҘl) cA2P@$u A2P@$u A2P@$u A2P@$u$$d+)Hk< #I6 `~SIcm*ʒ 49*R]H5d= YH5d "Fd`lmɂg{oy7% Y^k$,Y5/)$|ˌμUe.mw+ a4> >> >> stream x33T0A(UƦz&`!cK33=SCC=sc#8 TdH endstream endobj 3398 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 5891 /ColorSpace/DeviceRGB /Width 482 /Height 235 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 482 /Colors 3 >> >> stream xMܶ҆id/>pNQكGރ};  {H%@@T$%TzA5IVfOÇ7oM}agohB(lzWiӐ=?MFM_xtTYέ;}hzVYiիWK[w`GVߛfz#ViKUǿ;jӃ7be^ǏZ4Ab}R8EbzUtP5.r=}8ݦ_Q;k 4ʹ6v9ڈ4~Z3/$o"iYMj/eX aЊ`ӋڴءA}٤ӭ1q(Il:|s҃ ŏD#Y={iӛ6LztH>x0yBt|w2ؔ\:\5}! fӫ ?t!(tow.!6퀣m:@ƦCɩwm:Dq,?Cmz|#>XBM'P0j127xq{nzd0M̩{&=I vR%(ÛFq;}hc7ȝǵ#ha tѣÈM8 ? -\\HScM; iVd82m:4tbqM b~mPdMky&Z.}4{`uh}Q߂TW%l4dm>)|4##lzG수Q9X+l:D/\¥OG>lX}W숨lt`>M/GS(zSNQ84ukHO+dwTHP$b vcGĸ)备<ѦX2tw|.,ysL-`C>Lh5l{r1ۇ)pXM{M\K`-ѷEAVHsX2d` k6  iB;MC2l@D`P w":`M'7[i-$.qQR^&(BD"jZ|eeBi?xtkIГ/`kMA2wQGDM==hMUsIX ,M#'.&(&g=n2{4V=*t"RPupbS妜1GĄZ ),Ma [gӵƅ^#A(il1^L1ޑ+zP_F8e4],PW(Դ6,6 UM?e-5[lA͹M/0p/W(oTO@8mC&fswM >mT'$r Qn yAdp#&sMD[-+M'%##&F+6QMdhѓ5nGN-!P{6LzZǓ> {#Sm:l}Ync.e&Qf,O6p?N猜4U.)ğ^;_P9BDaowȓj6";iqBNj3dwٵ'/BYQs2ttm<[tcZvQV](2G1aɸUd}' o+t 9rxeGyl\5'sMMz8!鸖&-pV)|\(t juv^ e#D1zߴhLF;ʣl(+6lي5h9Xe/e&۴ j6qy` iB;MC2l@D`P w":L'/ (܍ GSt-:8ͦwIfMInFgH96֬g75%,r8ÅEA䰆,M)ĞEQ6=Sr\k Lgy}dx'^M85j"Ld;ɴfrOq0FDl8׳ Z` qx)860dӛM=eL# M#"2cr&l֔Кp\?ܻ-wzǑJF)*D}|I G,`C )M ش;=A;0aQNMc 9 Đc&達s0a`vq":*d4T! Dt6 Up -oiWs}R@w'}3!Åuk;@D}\Krn7Gg Nl bGIkLcA^\,ٚ/4m!  bClv܋8u4-\9v]!@AFП֎^ŸwWfMU*U6uCئ &E o>"^Ʀ;x}F+`"Bh'9)6lX<"ָMJoKw/+f3šņM7OlZoy-M<3S66t;8cyK퓑pùr{(\(~ QPa\)"F_i Jvذ/ 7n.ylfdhJZæ;rϷʎWS#u%<3OS8 "~ "o`3AD`P w":*d4T! Dt6 Up iB;MC2l@D`P w":*d4T! Dt@nM5i@D$6M2ئ_~iߐ@DM`Ӟ!HFyLz w":hyIl-dܴ hb1l-d;=jNM 2M`n!7;56};p 6-B; B#dŦB; u3&lv@D(mT}C;CާOOa!t86262*dMwxtشF떵p]Q+#1~ǀMYjM3)p/_|ŦrMlݻw˷^ 9oOǏ×ULS30rǦOTLu B6}&:f3`",[׌ulGt,/܆_rJ\>G^1Bͼ.$6OlzM_Pvn.!5GGt(]XS菸aʏ%`ylt5qwU\+Bj+R\fش&܋y͋ n(w2p\Ng{6jۤZ M!.Vl؎>/2f}C;%} ]*7]d!6p Qt`(qlzcIyپ\Oش|2tieǒr}LgqfLz͵ʌҼnnZXS ^+ 7ŦX;u@rHf)?}ӵO +ڴi;~u8ŀKƍh.fɿƽ`+FHQOg^_u=hzU$qhxhl*Mu BzX,6 79 @6}&:f3`",lݻw'vt8LS30r6@5S30rM~M'< yF(MLM?hÇGM߿hŋbЁcwc}H endstream endobj 3399 0 obj << /Length 59 /Filter/FlateDecode /Name/Im89 /Type/XObject /Subtype/Form /BBox[0 0 2380 3368] /FormType 1 /Matrix[1 0 0 1 -14 -14] /Resources<< /ProcSet[/PDF/ImageC] /XObject<< /R6 3400 0 R >> >> >> stream x33T0A(UƦz&`!Cs #=c CC=sc#8 TdxD endstream endobj 3400 0 obj << /Type/XObject /Name/R6 /Subtype/Image /Length 2465 /ColorSpace/DeviceRGB /Width 461 /Height 101 /BitsPerComponent 8 /Filter/FlateDecode /DecodeParms<< /Predictor 15 /Columns 461 /Colors 3 >> >> stream xkT=3Y0_5nA܃{b.J-~s{te<===<<nI]?J5~zBpTMG6~g_|MR޼ytn~zDď a:}vio_M4@ȫWZ}vkz3nڪTZ}6ЂB\+!~|gR-l֝r|`?^~_/eKuNY7X1|`B3og>H[W']D:ѵ__$ݕ]> &rM{li4uz~VEݖ:gƤDYCTW-CCkgSgNg[u'HUF%+أkK؀gE7pD~/o7HG .qσo] N< 8H>> u]ž@wI:naD/䱚|_hFWag}R娉gLs%e[g!)2ugruwU00&uD>{k|V^ED{&'665Ovn?g%㳟?>7&{Ε|x#"N "*AHQ Yː@D4/@ [j St6oԌޞMi눶$,9ff*0K: lx,nI)7)>UmYA$Fdr>JBBC$E8FvMf?lVS4 #An>4{Y}5؈CY *b 9_XF@tP:Ҩ4Eh/ҴW7CaMƨQaډ\1}MEѕ*mpBٔ ,N 5>U4P`L4캞 Mi}IϢlHgeլK2'7VTm;TNFog4ea5>{ʎ\yJ#I )EC4݄=g5SrG(^>կtbHG;m?g5ˢҗeCӍh7~wUz5aaR#fQJ0z1Jn%̫)"L >;q2rHQϼz_$%4qYِI5E*+sd,C Z5" )jD4>kRh|2^l}?%J  ޛγ^> o)N})|v^kRh|2u^۬S}#N}Z35"l2Ւ@Dlvr(UprIvLHQ z%uUOM٭.75"if}G^fk!ᔯ| φ?Ӛ>s9oMp:h͞?v~ߠTV> E gݭuBYD4Zue +.,'o7.\pPYHQ 9:Rh|Y D4>}l@HQ Yː@Dೖ!E ǏO rMQDD4@g={vj`F+u{jl/|`/^0ӧOYg)KtNLY58|&C/\|&C㳘, > QLga:Y `:Y11}ro߾ga44b0g| gx žf? /,4㉁LMg1Y5H>S[ lĵÃPMXK^ߔzvW#__.kwwj$s|[ endstream endobj 3401 0 obj << /Filter[/FlateDecode] /Length 343 >> stream xڍR0O%{D ؛áu(K7p`Ono9l<4@KHAAL`EW}I*eycJRBsfL ԕEaoFꍊMiV_ccWűpTsȬED#_15J^v7?Wy(󶈕m_7mMfm-w?|( endstream endobj 3402 0 obj << /F10 53 0 R /F7 38 0 R /F8 41 0 R >> endobj 3403 0 obj << /Im87 3395 0 R /Im88 3397 0 R /Im89 3399 0 R >> endobj 3394 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3402 0 R /XObject 3403 0 R >> endobj 3406 0 obj << /Filter[/FlateDecode] /Length 123 >> stream x50{bKq]h(Q@C4̀sږ(T2TA2^\Շ4]߭Rns;ҊϜH\0BEI_> endobj 3405 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3407 0 R >> endobj 3410 0 obj << /Filter[/FlateDecode] /Length 1650 >> stream xuWKs8 iUzPvLflneKI_b9N. ~>#r>ڌj4]~HB#!0!9雪vO­'84o KoIPFpNj1>7K4ɐʰf+.kHߞ8# Zvk3~g]x/U19zP@Z=5눭'<:dӼtRxz2>pzw͹0+؟y2οd7Zug4D.B W`1;8?ٕsD*:qL`A6-*uIBwTz>tQDl-HDX+SjKg.$k{vE󗁔 jT;dV|7zbN3TNStLKCj|S~\><ܺ` M8upBY _UdhB(:n_PX,yTNwqF8#'x)cF7)g|#<$jȃ';w%P/BFO@QDvOH͏)p%OzAw> endobj 3409 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3411 0 R >> endobj 3414 0 obj << /Filter[/FlateDecode] /Length 2617 >> stream xڍXْ۶}_/*#wɳz"٪$vU&ˆ 1ۓ8/>ݍh=vN_FhG'GW؋ߍd ޙTzzBm7?G׏Z0;7@|͢p/X\*rLW TWkDRbfX UflqIF8ۍ*][1=q\6Enޕtud4<' Ui*s۶ʺUkq7u"I92tAY q$78 T¦nfFN1 !VZvZC#qq}?<^Qկ胎;QzlA{)9 "qiB޵mu H붗0@MK}V ,P+4@X~,;M΂8%Wkն9Us9 }ۺZև`  y1|5_x݂XlɊ R^=XHg<}%vS܉l:vNYՓ#C H*gۿV,]0!k*4r dw=VL> D=c^:~P*7x2̴UݜC0SϫfYPF+('о8MBė| !;P/wlֈkp-xlwYN7e E. %j,= tbo{9!ԧ4|]9I ;*RWOuaQC\1 i$t 0f\H:f{ HѢp&mJ1sGmYw7GQ$?1mDѥ%ctV0g3G⠵L˶< |3X7T@F^Z{"+$46Z[-9Jy2_XG$FTi&.vn.cPh;P(VtCDét!^m`(I^UEe'TJ&}q7YoxˢצUuVĒ<_9F}jy6]3+.@l`VC(ILun>U;T`SuAbH5 Ru C@ Uj"亯lF-#lM2vBu;:+HwWi.U SRİ cPmuf gޘ jOvvfi|d(xEO"x_>Ʈ> 9hzx6/]@3e @-WSn8?~YVٱwfO]0f_WVg8tlZ,/Lk+$s}%I[U endstream endobj 3415 0 obj << /F10 53 0 R /F7 38 0 R /F12 59 0 R >> endobj 3413 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3415 0 R >> endobj 3418 0 obj << /Filter[/FlateDecode] /Length 2279 >> stream xڕXr6}߯Są 9oۙDde&8[EK|Fu >8}0j> #ia o콽m58[_cjXO9ۊa:(J@9ܺtp>nl>TMkr8Z2kMޙB͓dOkL=WekҴ}fj rbwP R~)gʡYkc#l*4vE" V~4qV&ypiNX)  Vdp1tY*`gC|Uն},PԀA6mUd`Ϳx-ch gAj$@iV)CR$.$X(d_mnps[͈\Cci0 q{ {^*^Rtؿϩe^Hmo-3UYYN϶X禛!4z6Ga${`M(\m*0hKgՆVխǂو0BP>@@R#ժΊ›ӖymZ~'t4Df4ҾP˔4<+rjM7c36= W7xgx{g<|S,yу$8'67UGrIj{ Cs쓧&xK}A:T.^R!x6ê{z@{6 [8>*:)7&k Tw娟utT/̂w|!NUo^t9lL}0΋4qqS*TKVZlʳͲv,7!̖t'Lcv $\GFD]M!눁FAAdh?CRv<JSau#x^lK8dgYי0F/ ƗGȠ 13'J# q>\?euNMܺ: : >}? Mp'H # 舳E4EA4d/W>Dj//t(U;AsjHWؙ-jP='I.WJ̩ =E3'\K0!@=C6<X50#S&{Gn$$4p`qi:>fuJNmhdB =.6:&! 2Bsc ߱Nr@p퓳&9b,yU.65l(W+RzA ]Z4 ] *uc[O!b7s؇Ox.ktOM 3j>5hc^fW{֋7e{m1a :pt:_ؕm;zƮ|M;k3YyihQY<>#8{GP@_̾:iBe*kvg(h]gĂXR]r㘳+g 7# w5"#]7)sxST`"k\K}Ҽ~y/ƠDfH׭oD; T`l?]̋?|O¼/.gHd !3 M} 4c -hɳ1*Y=:~Cbǣs5eC&<}d@w%Dlv *.܇lZOtK,zuXlx> endobj 3417 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3419 0 R >> endobj 3422 0 obj << /Filter[/FlateDecode] /Length 969 >> stream xuUMs8(IH@ngr̮+ XvMϧ[bl)_wׯ(i#?QIKmvJi)D(x䬈*}XdbHUeuhZF]fnv$Bg_7[yFMgMp w` EfSEW5m,aryqɉ݅qd걱j٭ S]ט}%rvM%vNO ؂Y0FKoR>EF.dе5[z \4)- Od +"B\0=vRds*!U50ëݒ8o+f0A.b ,pcA2S&PilC_ :nc [P`w]799-r0=1w9яanݏ92Ʃ*|bdQw= + ICQ{. CڿYcwM[P_%EVSv`yJoMwDpg_{_W>f)ׅ,Wϟꆃ YAPǰ2fX69fCw#⥠f'J7aK ,HE|ʸFo;'!CO endstream endobj 3423 0 obj << /F10 53 0 R /F7 38 0 R >> endobj 3421 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3423 0 R >> endobj 3426 0 obj << /Filter[/FlateDecode] /Length 1318 >> stream xZs6VvގBJFmUK6s䕌xeW>|2҃l ֶ]i+L4~QO鋶Y[igжv؉t3䙗PXC/^ ~C,״ҫ0|F`TWe~"ź{Q󨲔,XY3{( D޽3Mv':9w[dƶv)'c7sbgأ\Dq玕f]-H'z6ME d`z͖Y?՞2a3\12 %"g:LH":oSleZVwK{19\֨w3rŦy| )W^lC@\zmzA2LMIg= >Bbr|/$PfS-w |zѲSr>h  :Qq!ΠBF'YU`U|j3~ZKL?u Ms9m8CxYַ-;[η&Uc2|7Xf@@7X UN%! 88'!1 pHw ib,F,d'dqtWݞ̬FG`3? s|ɫJWs]? $ hrO4BݾxsK=$#t1w6ĠrpNR۶n7=΅mQz#X) endstream endobj 3427 0 obj << /F13 66 0 R /F19 171 0 R /F7 38 0 R /F10 53 0 R >> endobj 3425 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3427 0 R >> endobj 3430 0 obj << /Filter[/FlateDecode] /Length 1944 >> stream x[KsFW航"2ofk|Krh,S@u*?>3 y5([<_w )!~]|J&5jqP$5|*lq$cdbZ,^/Yhۼ^ã8O˕T$I+?_ſd (@F:$ݗ#'yrp(c5*Y,9eʦFH ׵Mp=YWR' yr=DI^[ +)}cM~׭%EH*?c > '5٥)gDt LC ߗUy@?)K1"Б,3m)H +F{HܩDhy1ȑrOq67m~LO+D@jX[[ Wm:}#dTrH9& y5[L:,shz Ǽ^Ws51-$jė4P>s= x;K2\uO/XmmC .Zbyxȋ%SGNB*a:W3(`õY 3$9!Lb2Pȗy ?,JʯĜؐN5:Ldbab`X$vy6!ɈP(HnUφC *>Es_vuy|.0# ff\1~M5O4~s,4 &GZ5:]3VG7!W͐-M(*>ݰ|NBCࡪbu/m4q=WkrN `u!{&(VWUrck^Y^҂}S endstream endobj 3431 0 obj << /F10 53 0 R /F7 38 0 R /F19 171 0 R >> endobj 3429 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3431 0 R >> endobj 3434 0 obj << /Filter[/FlateDecode] /Length 1982 >> stream x\MsFi w#;eWV*F$LN,T~|1D% t~P]ԼD;"h~J4T$Do&ߦ52.>«7+"w/8NuZŘ%~}8>wptte #Yr %Gk*)kk՚3'5l՜5K5Ɖ[0Pǽݸ+ >HLTcu1=lRws[0g'RSYO+b賛W|4IgwlնPjf=0Bch?j_^c3ou$l64u-*/S,dLr6 2 2ʭ)RM]2 %x{Ci"hP"i~`<L,J{{a[ܔn{]',oQo߮e憝).mȲw&韇3rhwhC߼[F8i=D>Ʉ3#rH{(D"~ܜ4S2&L6;vtDt;Wiiy%9> endobj 3433 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3435 0 R >> endobj 3438 0 obj << /Filter[/FlateDecode] /Length 2053 >> stream x\KsHഁ"FL=:ؽƶosl3=U K;,i;|TG,a,軫o,ʒLGW7fI& FZ&ncX2q}C4.V/<]v+|8DÀ',HH@ {9;eIV+zw+wvVJj͵8?WGB+'9O28ؼjA&ޭ` Ԡ[ qotn2=G13fʢKH:3 t[;gIqqy%"1-|;)K/k?,{OQvH-٫!x13۱m M%Q\Rifo;ur$"+#}mzr6$bPlyE3}SOQbbQ(_W+آ YCu |C0"yw pvޑߕCgQP)c`>Xc׮C. )ԃ}>)8ϛmẔZd@K6Dp>Fu"[]+$ೠ :ΦU81hD"4s&|qJx$s?d/tTvPLz5Q,[RN|w$z+0@`Lxx0l `XFAM/껦&o}hmCM46<;l]Lm'w_v)Q303cɂ [ y5O7sn`(K{>ys{p'wWۖ)= ӁL d҄S&3)S<>K?_ER b-Ŏ\tH,!DbIQhV ! "hDcxl⮧8֍ﰐMr7 ̩J DQf1;UJÌ#kKt^p,gW&PfC!.xj杷")=<&%ހJFJ$3*[Ty(M,9kz0_E`j5?\DɵAuT3O:B۲|9vEE 3/!K lՃƣ//2j2*)1V?e ` !+zl߈1@?r9$ $>˯ʷ+0",YK۶~pN1Lr|h'*bГڇ樞A ,4=rzL r:5W49`yhִ^PoL*1FǑv z3Bl,u"CˡCu(fDmVf@ӧdhQԯwKYM;[&EeJ/oD*ǮPu3FYVi:$ZԐW RLGJ5$ătܹůfZ~H^@}&N;2 nZ^w)}w YpAvmJڿmTq0:&TmE3gF7&~lȡpzWBz/tD_%/2ڇSjaG;y5J?ޯp\6h'z"mu3B>v,x 0qD@|W7-K?ǮƼ!Qi"m]L-J08}vȭ#=X!ue-88V*c,w|Ƀq3s~x~f-mYru_;KJX < 潟Nʣ< Fwwv8aTA $s߇m Ͽn77 Ngt63X0~O("'ǫ,b endstream endobj 3439 0 obj << /F10 53 0 R /F7 38 0 R /F19 171 0 R /F8 41 0 R >> endobj 3437 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3439 0 R >> endobj 3442 0 obj << /Filter[/FlateDecode] /Length 1690 >> stream x[KFWp ȼYI%sprǧ{v_Uh4_ހD>~ GJܼ  \E7\һ՚8,Sە$aY鱩WkPaꯛ_nEocF@p ΃ޚDZk%[we$]9"Ou Eh|>'x2m$XSiaΊj+ tL&BNevnl8ݾط>}Acre\44T(<.<GL "`TEDt_ٮX&Um uzp( Vדci4@oml^v۴UZKM\r Jv-`4ޥMdE1hse?mVOTD~O >aeq'ٍie}rIxpqJ qM뺭1fJDtV 61OߦD!*2\!) ~&&lyd}"€mCřT7+C+f촫0rg]JG RԅG I睝\J$1æJSd_w(0֦=F C);\lyEj4ب 1o\0O;n\IKdEJ!TnwJu]ᇐ0m5zE S`Jyit?;e_9@S8Vrd"!_)ƍI !;]=f'` uGz`Ӟp] " -BmkzKM@FMO}03%z83N\܁m8.|^˩kMQ,D52275$p 4wp.\9*Ze&FbwlQw wG%/I5OO5@ Ӳ6<tx%a|i/]bt]CYom(gq%(/A0鬵*+n}A^Qcލ!t;~~V κQi{W+MyX-Ɵ!jo endstream endobj 3443 0 obj << /F10 53 0 R /F7 38 0 R /F19 171 0 R >> endobj 3441 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3443 0 R >> endobj 3446 0 obj << /Filter[/FlateDecode] /Length 124 >> stream x5;0EўUr\x;nA;DIHO@Jut atsN9GQ8y텒Wc}ߛJ4?kJhg5m]icxB8^ endstream endobj 3447 0 obj << /F10 53 0 R >> endobj 3445 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3447 0 R >> endobj 3450 0 obj << /Filter[/FlateDecode] /Length 1394 >> stream x[n7}W(Zp.CﮝA/h_8E7Ő8-{+p K/G;ggg(dJoBP:2:' $ㆤ5Ӗ~~K_'T:Iu-tANx\>c2'D DX"|E `$U'ܡY.yORM0܆%y[h@Txen$\,I/z_;]iN_G/I# X9>謻wp)bpEAD%Đ ˟U*9@YV3WgA p!U+Jp/<|@ dDR%d,Ps/qŹ8Y/KƗ9 U@D!ftS)#E2BQ2F :8 FW[x:*^0+TYh6fLݘQ̖migDg;OZI,/5)ъcOסI ,8* ?5wH4߅2n鈻h{+R/+UmyV}Em 7( QLC1( 2")rC@Kx& ayC^|9*ͣl@1}3Ƶv5m+q/\> W5`SjQ]ȸApR=- 'UI됲|pHM.&PN25b$!Rly,'%R2"èpx 1;ŌZLbԕ>fl6(A3 \l-׋uJΌ\=U7 ؊.l8DnbγIUv_:w7F-G nl5.߇-fyվKq }"6D۸)܌b3¢M'sg W2-|siST,5䇫6 V˭I|l[8am_I'Y@eL7(uWŸW2僜ҿtr{8uז?xٔQikǐ]^Nnfwf)d5>_&)+yY lcOfCc`7Qh" -wb~Y t)GUѠZg뾦ܦ*c1'C0՗3'N^L=TLI`\YSaK,Ɗ#x=3w㓮W^[<[ak\{_k endstream endobj 3451 0 obj << /F13 66 0 R /F19 171 0 R /F7 38 0 R /F15 112 0 R /F6 35 0 R /F8 41 0 R /F21 215 0 R /F22 218 0 R >> endobj 3449 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3451 0 R >> endobj 3454 0 obj << /Filter[/FlateDecode] /Length 2645 >> stream x\Ks8Бc~\'SmgD۬D %vK6%$-Af@8/>]ϮogEfgW"%a2oͫȪ|uZTŲXm7˵ :{췤@5[(~]#톿[B`?. ,Yyqq1%{!)K/#78ed\){X9Ҟ!XP Y@ֱ|[lmqC"C4Ȳa!HO~I!:Ek|SL2&y˸6:{fHm5V?aj2 OD'*DSFUHaebgiۓW H eCˆFe2HMy)a\9I8Dާ\m#ߴ5Ny] ̜S%XŒns8Keڅ^G Tf|0Ib@Yӭ;}RHjY}Zx2t{)~&?,?VU$N0ұ-Ԉb ͺѶ6D H'M)MqkytuY*xGjQ>F"ۓOaWTxˈЖRqŰ,%tobjQ? D!ETK X"<"nX~s^U&,{(DBIl ڳ:Vqo w pXZ=e+c2ϛ_1F(J% QjT¸19fXg}dp#N#t;? so-^G^tzuVԙ*D~n_r?F'(C@{z"Bh#D! ֽQ!*o"EHtênRql+ 4$ Lg?>|:Gm5:;EȞTos0Gq~d`<'u !(]BfR:@u& E)Qt:ɷP!2la 'g(lZ?٩J K"c!($u&r+릞q`Jhm֔I'j'$q5ށ^h@T/oUxBAzoYq s\qSbbϷg'`EL}0-a(:|Wr[Q.Fo%WERO9s1vϛm'*]NAj|PeCBVoc=D*yIlV^`a)m\HnQ~t0gBa 5aW'O"9@:v7"9R`|bi4nFcUMTS@X6 -Zg1_waE EC\ `?Nhb QmqQ,zDZ"up XU YׯFPښJuv;9sʟS'. z@"g; .|3Ep,tmlTL!l(+]g ۖF/tv҂jU_g2a!ELdij?C%XXk58Й~E.44_{倯io}cˎP)W]})CkCH/w]JUcKFidiQeU:S' (n&K<޶bYCqhtLG`4.̛y"DB &@ǁ 5.W,SI ILyt+MV*כTi֙mRsVG13d !J v I 9rb\"<1[['s@c)Bf&wĮWUC6 [É[~pS@[)͹}?o4L.7|2`$ILv//ǡKf\6uO:/c{j4R?1lЬmamDžZ#}9 &,I4W^yW  ]4M|"0GUEU$0UMU,PP}\¤:nn #%LC\,6D M_mMUn*A+::K@bOd*h%ryQq0^˷"qS3dӇL~?Q endstream endobj 3455 0 obj << /F10 53 0 R /F21 215 0 R /F7 38 0 R /F8 41 0 R /F19 171 0 R /F22 218 0 R >> endobj 3453 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3455 0 R >> endobj 3458 0 obj << /Filter[/FlateDecode] /Length 2587 >> stream x\Is8ϯБ/ש{&mDlSk~wE=^Ydž1;pXՈȁjD&? rA]SS ;D0|]EXl2#ga5?T0@u?\DqҰbMH+=*Y:It~P 'C!a\:lPrCm{T{MI,PRqҹM(ᡝQ[-Y[rn}Y KI;*m^hS;e@aZϵ<;P{i#}n&]E<@0wOZ%~TkPX/X}R~^TsoS|,m ؼ'Yq۬-vzSf~1â:Z(d6)5fN4 c#ZSP7ucTӺ'yU>CF hH~LfD!(M)Q)dT泹قP*W`9dԖIyQ&@s\Ӣ&#P31E6[.H\0/x/L>񽖤f8+%Һ]1\7l/"iIhl\?!j~c Ipæxq43isiQRfxo L'm:_'AXUǰ'0S x[MXh,*Pjg48g{n VBOOB;#i1iܫn{O?+IwIBpPAGz J3Ju" (}7 1U;%+'CxPǸemV=].@gV"R ءd;ލ iB37}6dI_6n:LfImw"—pߺuӎ_1:ʒOw^Hl:Keɨa'+@|*GRZU&_]F7R/̢>yb _tDMVHI=ͨ .> endobj 3457 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3459 0 R >> endobj 3462 0 obj << /Filter[/FlateDecode] /Length 2602 >> stream x\Ks6БZ#@uk3ݪ*mi[$jUJ H$Msٖ ~w#J(ݎ?~#GW7#IK. ?S( 2ſ%G]7i9ݔUVjV׫^{ 1z.)f4 .t߂@tUUn>ݧ8#(G\\2… .: ^RafFL` (jQ.`7Z /%4Kf _$H8dlp$ cXN۷, A'0Ah D(iF"6(Pq*3 ,(`b#}E+HhXhZ=V˳ 9|/Dp Yh<& A7!7Y5ȎX.YXKz3~(mЄmfڔ 8?w6Xw몹u! `:.N;;-&}>t>]9M̢Cl";kniި@ l|g~k%O!w _WO`Ug\41\Σi\"y5[' m>$_C9nIle+ DJT]ݶU]& .AekAʐFl|0M4A93pɤ9^EC)-@ hJ7h4!#br,+1W- PocŦv}AcK`FR\$ Z1]Q{B ~cP1p!36DZTRҚD+DZDl08%`6pRoQk9 jKޝkeF a|"Qg$3yWl\i *0E!+7 A3(<@wubHPߐ[DSҒvd)6^frSg8!LHY:MLMJ uFJQⲿ- |,zk TN`boNV2 %V];(l":B}Ȅθ99i#%Pm* ZSUoKWfΆ [%+ e?]x%Rp MwĀϸ6g J S;cZ7MVaE T KLۜCU +Y*uS(ǁd#߇:rq;t>j\K,S $,fͻx͹S:3Ʊxq&]A-4ZjSVBR9$]̝h)#$bY*s.0SU`ԠbEfŸ=LV?̨3[#l] . Al/-?.3F4m[4˵&l?3B6h<+ [)7R38|dh h^;Vne;0m +p m~d,=v2cV1dЃLPrdrIr)j ]ĝPrg0YISJjNuSwb&p粺n^_͖rME; hAƭ_7WY E\]&a{BX#2#H<0onqKzF?~-|4aXO58&Y >R-ɗju]S/@&ş;ǁ&7 Zkҿi~c^'~8;iM}Ѭɩ.V.TMu?UGAޫg`-ޓjy\2B" Q Ra:?%K@{<&+kL0B&KiqYRL^u2]?,Y*VAC8䋹@>cXG-7Blζ$6áu=  t띆y:uVe=w @4Z$j= endstream endobj 3463 0 obj << /F10 53 0 R /F8 41 0 R /F7 38 0 R /F19 171 0 R /F21 215 0 R /F22 218 0 R >> endobj 3461 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3463 0 R >> endobj 3466 0 obj << /Filter[/FlateDecode] /Length 2702 >> stream x\IsWHWEbYfRI*sheP`;M}SOx.0 xO0 $, X$?;kJ0՟"\>K(Y'$+wW D !G?7 ~Q?A$ & :6K@pFcz "}DS'<(#Wa^~ZPHCp y?7,$D"ԬwaHZ1/ݮ`9\#p=h9(2J2.;!xPy٪mR8ixciT0(ȁYgI 1]&ammC  C,ŗSExdBIp iN;(:$Z ~8syFs/){e0/gbrw<aqTo'5R_,ޒʤՏZܙ'U%E&_ !$ܜk>E>]ZiHZ8Һާ!TœkyV|X|KmV|V|K+Njǜ2MQ֘n[c&u$4ev7;VjPS\VdVz7_vvB uB  Tտ>5 kBhYEһ,^_tM'SGF+x/5ɬȕM6qCrHJ_c8ZZ>=1ݔuƪoI*ʆ`5MT 2Zc5KeeQ,Yix+ù/eSX;3pQ]S  +*#Pm^Eb͑ AL$CJ+/WfIzǏiaCz8 ;E>Rg\  w<fT}`y~,}81,CH|ɷn^'O@黤΍1%$SR6wSJO N"RHZ!DmnO_s}?c'i+\! ]fp\AjeMYoK˸맢HxVOUV}^|ݷAu󤉛)o 4h70؉! b6۱UAFlңDn KM u^Ƈ1L^9rܮsyO#iV~0!2!s =ճ>Hy#;>tx0t5|<9T*E"vhT7PHij (&ΰTK,Lu4'd"׈'2.?cvcOIx(|˻J9 9z(wg#_i# #hxZ>_kD̛)2EV<')߃VoA!"0#%Gv6.sg}oc} ~L덼0|sW֩7 T9F 4!ܶ~{51HYa/}JчC N":a}b Ǒ+;]x2>hed8z#R;ډ!Tֽ)5(5жlT_s}ZV-_EwSDR=;}_v585M"ܛ:sa:Ε/P|w#Jo_}י\'$͐Hי-?e'Qrh>:e¼bt2oՖg1&E[Mikpmy 4cκ׼o^&.VNUr9m2}| t$bgپ2[62,}v.OP0Zq3B endstream endobj 3467 0 obj << /F10 53 0 R /F21 215 0 R /F7 38 0 R /F22 218 0 R /F19 171 0 R /F8 41 0 R >> endobj 3465 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3467 0 R >> endobj 3470 0 obj << /Filter[/FlateDecode] /Length 1383 >> stream xZMo6WEj.߼CӃ6Q!8r;MqNc$G7oH'P\'&X]%˓9WXr_t6gM//ٜKUd׹2/g$+DmM &DpAkNjz f%SnU̝ulBd6F␂6?!j ^7 VzS@?LСèA}*mx|ΘNW듈mj _Av <ӘwZ]b(O2]S4&M1cԤ&TK5?sMVy[c( ¥\Lkp.7}-*tI|v.,E y5qv_Ol^Eydtm%OBfu54) m^abPWПP4Y]xRc4uv(TO]?O/u~QK[=MLQylt 2#UcF{IcZI#:vzԩT#jnQ{EѲA?n%!Ic}Y]F f< },NaH: 0a|gH~Fcdh1N9auA.(^7` tSn#IIB\5}DΧx rXrP_-R.gٞk *vRC+͸K~x׫iM#UԨ\}r?-(z_*mSFu:MO`ze6tH n̡>/r±Ls.A8dDvz"ܺykQ,=$6R4>DCx0@#kmwב,w%rSe_APvm)ypRڇY_lvHRuO٪=.*4&0g1~f#ϋu eeUtICbȪЅ􋧮o]7em=Cid ڗQZ'u-e'XIijCe81ꦴX]}F8?ef'#Abe~P  8)t/ekrdγX_W W+_fr†!\qRpm-uLuʏ)DcPBTuPA@p{־#]ȁ!AX}ɳWuBp]HlS@g*M:ݱDN N{.B>>88=W] owlXËX#\hjfm~yl/r7vE=qq <'_~jg ՎפVM/Q endstream endobj 3471 0 obj << /F10 53 0 R /F8 41 0 R /F7 38 0 R /F21 215 0 R /F22 218 0 R /F19 171 0 R >> endobj 3469 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3471 0 R >> endobj 3474 0 obj << /Filter[/FlateDecode] /Length 1010 >> stream xڍVɎ6+t fk9vA nt`% 5gOqS{IV" gYr/S^&y*&u2&Eo3K7e[!~j~btdZ;Yuh_uZUb#g%װݯOM᮶.dS8Nf Fg5Rg~W~dGi d;2aWgsdBHǑ 4`FG;CФ aW1>?l$~g6#en)SB)~ ,6=KY04/ӃC#XFDCԏG`cZv5hjsA@9 8/+F i{65zTyjE 9sUQ],#& TOh6+ I!F Th1dj S9S?}\;ܶΦ6[ La!gžAi)P陲,V?>!޼Zƨ[-lvkꅁMD~Β{ޛ1,:۟i #6eL yvb J_B&-zKz `&:kB6=ǒi-=V,魧S%bZ 2n%!M R;h"j<`Q32I>#[߰-Q%xicU#OpG)A3<"BZ6BJ$U#{Y7|Ly}q2mnxg}I w\Ҥy_ҶX0t><<`s,h%C1Hd{8b4b3cH3%׋MQU ť߂.x](LSAv:R.)ܮժ!3~rsBrbAn>AS{TZ¼4!bl Ryp\RJ׆-pdSǾ0woZʳW@t#O׳ endstream endobj 3475 0 obj << /F13 66 0 R /F7 38 0 R /F8 41 0 R /F10 53 0 R /F6 35 0 R >> endobj 3473 0 obj << /ProcSet[/PDF/Text/ImageC] /Font 3475 0 R >> endobj 9 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-29 -250 1274 754] /FontName/XSVBDA+CMBXTI10 /ItalicAngle -14.04 /StemV 107 /FontFile 8 0 R /Flags 68 >> endobj 8 0 obj << /Filter[/FlateDecode] /Length1 727 /Length2 3866 /Length3 533 /Length 4419 >> stream xg8ںǣ2ZDn3zw `FQ jD]a HDA}Z_nl&C95QHoAXfju_, p7TxeA`0Hd%eD@.F8xxxWITh@]&P E1B #^ S `0z$Pw, $gז/u]SPH7 w YX 5} !U#0Ԡ=|h G#.rB:7T ŅD#4p1ryÑs\w a+3 Uu>?b> [!zzzH( @:̼!H W?TUQEe@MTJ$%! ͑O:HBDDDJO* GzW세n3hx,?:p# edO7y;S٤%ed\)\4£p }BOԕEn>&#T{ݕ }vi&qFe2KMxMc~ui*8{?GGtTkA>Ɣdq&ܶЙ(7x`6IH0xtcA?l5AUeQ.Η E$9C"cߜR %gxlj|˶JioQ0X>몸=C16fRREDu:=7so.,Nt/mg+t"KS[ql[&$6&#kʴۻYe_1ڰ[KFe~;}0 Y:륬T a 8cǓ\_Tn2&jc 76xA4!"xñZD|gpM<>u3vHS cs-ku"i :̭(Hy.{/2ڮlUy/,zp*'P*1X-?b/f MOmVP 7' jMTH˿EXi7{ 44:Oypk,Z *NǑgJͬo-\LE=Wg@\aE-|i-hBm6>i$'ќ|^X,ƴ4܎׏c- m e4 si =`筜BI2G~(dieMo"q #ſL[`|z_Oo^i=¬$pRs]{iDEvʙ˖MK2MuЫV~Ns.Ol؍Qx>UĖƶ;ۮ K$\k!79"$\CϴšRA.X ;?"BbtelTܘ3H s7L5"t.Rm)iii5Ĵ("cPo`F`|.R_x,]8Z"(=++츊߻@U_/_y?4\n}% Ts{@n % v`pj1mU'۔4z=0"߁Ag{cWYX$@)"J+?6뻱 n c{0y&5.6Z{Ðy- аn Xsjv cy(Mi @}@sp`7\\]^0]Ƌ. nL5  "0_)+SxK%[s1TM77AQ0xj,B$J%) ׮6x_e%58۽ 4,M/i,13<9Mb|?v~PZb%_Xa)K{2=+ Mfܢ6׳ɑ0WPz2;UP"=A~ fr~w ? V.Z%);)tR[>y?%nI߇c4#P+I9uH&F˻J9~mh:2xAMk%672ab>+_(d۶PuU|\]k&S8~|wn_YhJ~s9#$U][_<܉]GWSU|{Z}ZMN(NpTu1֖1fu^Z$bn=#Ѧ Aj$aZƿ\VSpދhV/€5a/|W/s@jBmҡpv Ot0?;lc\c,d9H5g߻L&~¢~Ë=J9*LKU7E 7hwC#jcs]&_GL\;KuqC3yVP#F48kScm~ڶŽvME:3YJB bi%ŕA2\r ٥Β">YiXa=m9 {Ə(8ZΞ#2*|?R W Fg7]+vwR'2|\N&rM١GZ m) 5f7O %Ԥo2ߝ;a@m7IqlJUOr_k͌E4@EHl`l [ӴRrc w^91*<ɩ'&5ɭk ^b43$%r_tEѵwYMloJ g2zO]k<۵5wp(=81;!Z6 V2[X9Sw5'R;\^5"Pˆ s?OX{s _m8YL_b`cSUc\i+ߺ> 7p#[g1M)͛3K39r83ui&Ī#8<}-4)lw +jrw#&vta)s5hmIyӇ. b*%|5PYEHf4Bh7 dOgC*!(ڗ5q;O-&5K39:&.w|RB$ AWЮ~zat_ŮXp.}/1wBšoh糴@\X\%#UvT0!Y1Z>_Z>.xHjϟH w/IFؽ^p g8^Rq6gcn7Xtspi/qQ{I I{X818&$6}Ѿ!3̌29-ez)Il.YR/ X *2w??wrl暦X,8aXGcR!t$S*>Q7ƂxYEVɉ.yUԀ4#6#kNBo~SRfrX~H,b0(7>D,sa,mAdI@\ VwIiS6MGlr^E8ʘJ}]m'0G%w 8NPwx5Ktt!Ol'yG%tFFo+(.HrwF#@):uD rKJTc9I?ϟ1 Uu|e6*vN26~4;m$;!Jp~]" ɑKJ#65k-"t`=s47sQH&IgV 3:kؚlA@HRE1k 8q!1mRZxC_aRT[+fHQQc5u%Uxϓ2D}UUk;4蒞P]/:D' npA? endstream endobj 13 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-53 -251 1139 750] /FontName/RROFPS+CMBX12 /ItalicAngle 0 /StemV 109 /FontFile 12 0 R /Flags 4 >> endobj 12 0 obj << /Filter[/FlateDecode] /Length1 716 /Length2 6436 /Length3 533 /Length 6987 >> stream xUT!@pN@p'h[$k!w $g3vǭz5U@!rv8x8yDr<@Nnt99 7E<""<@7k /7GP_T(Xr e.Ks(Pfvbinva^@=P#@-+ DG 0E J j rsg V\jc/T^o}s:``#W>p6F fnZۃ !`fi2wE_kh'GWx9W_k 4cs_)@-A5PfKw*YYGO> O($?PXI(-$(j ?k+ȟ`Kl8єwV}bQ4C6xjdjqp~L-Q*{kEVNj5iu#Z1Cd fj/cs!m~\^UuYn؉@\PPNb1ENʁ Hjy Bu9tfnzjS]͎3fԥh7 BX"ZJ9J͛,)oifNN6"Y]z֘+̶2"rk>gy.0]$HZ/'Px^.s}\iSCҔ6͸^-=7UdХtݨ}G$'S%Dr6C`nQl+02L[w1ˀ_- uOc EzL\&ar }AmfNd,b 1!o{Z 0kΣvM[;ETXwȗ_Ab$2vU{:xCR@|]w;{63|-&)ǂ}ƍ.;Jl_,ws3kMn XD~[f3%& _HG)ؖݣ:K@$DaffrHv|CްTx9D6Tk,+?,aOM/1'4Z@ˁLfIʈԇ@}IyL6 BO5?tA0Yye ^BBl{O~%Gͯ$$T^J%V9B2xſ]X6ܹNAjHrAy -;68쭖n'Ջ8I[nsk;_' TFROT" R!e|[J<[]Yn]$gwa"I|ƀ219` tq'A^$yO I%7M :ƆVƑݯE0.*HL#rp.3GN]SktX8φdαrNY$"ц7{ӫk:|㱱ÀK_𜝆3>~\ 4Cb+鶨f~LMIPT6MC D᯹D Y~_ H/3 ץdN hql6;VPhdW3*u3ve(&| Ʈ*4-٭Zl[7QG"e1yM+Sp3jrPg+kN 4PUNQ Oat2phe"Wx~ۯȋC1Iʉm n2)tCxVN+BFsx cp՘<9zQIVn%nןSGj;Rn2klܶA3b3UJSʛ\;E"E*%xean6jlh)ed,h8XKqz!NRsZgIfcLЛE?Y[`s7,,Cxsx`E;e!kTul#nXk!6TU|b9s"ɖA3 aA**3ˑτak !j겒Ԑ7^t.$;@U/tTVJ:OVk묂m7CWRhkҼ>TZ˨9LE2l=-z\`,dz7õ$q:ӄ>d b_c\3*jx$JCh;=^;\Gr5oթp;ɕ0GZ=@_%U-9B Z:0ݻδ9O^-V3z~2#N{$Lqy<YL2g~7r~YlsY )dv+|@]FCGBLmGV%Ms2~UnhoPצiBnG2J zxmx%iCKV\  fw' 1fvi^OTkHBj!xiyrVv+5M _ŸS$J=:{7~4pUS<JI)HڋzvAIE fyD7 -Zа XX{B8}'v,|`^My'-pEUC!Lrq_r36ƕry%O}XرRTp"M+AUę&ڂj`TD{s qք{ۅ)],b& 5iR;GT!(W:"Qe+l&*fT|"aFYVbw1Egx]|I{jmiAK:h:48zQp:ۨHX#)gի_X1f`=Sz8;wnMn$«-7Oپh.ww/gvhC6EriۻHy nT^ ZE UN{r:@,:4NK???r O<}cF8+PLާb5l Sll|HUGoy!4zG* <-hHY;ѺrCU$zIழTQhNʙ891/UߙQd+  WT>ٓ%#2[8(JZL 3( cԯ^e̺Wi1nV>UKfB=IWUhEPʢ?O4vuK9oåUt x֙qH%8_=N z2 ( Z䯭6:4T-~ZsڶǏf:g'JOҏlɪBdA4~k{A^z W8R9s^sZŅ@O^LD̷.nq[ҥJ{ѐT&Cea_~?a4-5kv𧩎: IPu^cÙȓyp֤gS 0{L~R%{=p4R5 cb\ gM=K\@\]0<b\ү'e+kE+%Fh3NhB'%]TSДP,L;VG+H=JãcߦP}qj4/@e; MV./뱰T<+U%.D5XXƊX:&G>D0y}<0_Y{A>K=^;wܐa~ydE:@A$dZ.T?? o]mx$hhiIi I?uS|j JuϳUȺX,Kl"۞iL \HP⎒45P8J8,vZUTLQZðJئj/)ԣ'pj{ΖNnآH <շL#W5=b$IP#<htngt+\oz:Xr_UA(? bHw5dPOS.$"%}H܌ױeP\3T,謗jq]82,\cfJa]oq\vdɔ|1mLEEA\ifwT-`&vQѫ2"RMī5@RoV_dNJ^ O2ɞǬυ!=P"Ox'E GV<%'x&q`Tn3MgWR#sLi2;a9CRz#9MsP%0* *%!e͓{sDT9jM`.IibӸ(q.k.B>Bj^S{wWg8է\.w+ވM ]G d8? %ț]nJŰhYHS]Pc6mM# q?'<ŝ,zAK'ӓo3c@7%DaLi. X<$*m3߬‰mY]309Y 0, X>{71%"fy^? >X}q\TQn_<);ːȞe}"d]C:F"I/$w#"P;9ϘH:tYWw&s Vr&^bbs.E%>e8+SSrȂ`\8|! ޣo[| 9D{?ژNp_F7ԽBl6(ލL2DSX;7a{įqgYC6WKX_LQ}>~lJRk͗H`,Z'H>6Sy1lrH{TͲ\b@(ww-wi̒zMX9JW(6Xb!wV[<5.M.쮡{6!vP7Լ8z91 Uq6w O6#k0BiH}jcΩ&bڳ;Lyƴ>Z&%}BL;)LL&r6?=>i!%Q/KB\Q[Qb8ԑpXn<)2t8dIع*P:VͽOkZ'7J,b{,x0d_ ܴ/.?E$l yQ!3a]AO Gm~N1~L~~oxVHvgwi3rpNB˯9=E2;9A"K9$>DēGn8tnx{yӢi-FaY0FfQ50<Ǐ;eNމW*(W쵌:y';n[5hEߝaN\ +RvNY̗˽Ӎ7ܶs4q>Sd EY E4&U'QBy+@VCNvglso/bG6a=?Ifզ`U E~5 zK$fYW ,_MNX}OO??Dd/UӷxFr:62%:7\~TŎfP%dyuxzǚt@1JBTQ,(جdO@ɤ5}=<ߜEѭ~kzn|g$u Vk'~l| I.fc]|LvCy+4&q!iwS;JLeJseurh^LkÊL //D=`bD endstream endobj 25 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-34 -251 988 750] /FontName/NAFBSM+CMR12 /ItalicAngle 0 /StemV 65 /FontFile 24 0 R /Flags 4 >> endobj 24 0 obj << /Filter[/FlateDecode] /Length1 714 /Length2 6813 /Length3 533 /Length 7369 >> stream xUTk@pHpo5{C7h !k%w\]vZg=;zfը0y.6eGW@' aaQC@ LHrA~ 1 ~aj+p9CP[ B8@؂@[( 99q=!`^j@0`v.dZ.NJ iY 忍pe''-_鿴AP'epqv@@M0w6M]5 j+wPoXu؁!!o>-9ey&? Cg_k] 0csSٺ0{Ap J^ۗGPX .& `P7"@_T\o_t o-Nj)ՊcѨidF5 Y"ll w1$;Nd;LhRS2²LJtnl\,jg)_!$ʬ+Kppm;eG̓KMھlo6HN+yis[]TbF>o_?O֪:W}>[VUeőH+u-5 @JIGe2ۮ)\ PÔXWuɓ&Hz3X:%4f|Ce%+o ,{p֔Ώ;`: ^DrJ f!#TjU=_v05g>Vƙf^I9'Uitr=J6b|.C$y"N "9dY0E[&;~;1bz,n] -҇V aŀmIDS7S:j. UP3&% &懥+fjvoa'GP1urΏLǩ.QW>,xW ϣƞq+*3`ƏkQj9H nrZM:7o-`<wt0dA,zP]h.- 4e rs_c4 Y\PDj𞯘CƸFD)bx"c_tUfCz;fY*(ؿ1>kXo~X \T";f}{*1]`F'˦\dadRKjW7[0%p(4eo -ۗ7 BeMPڧL>“g!NoV0 )5]Sȵ#9VAHZB/cg;)z$1!6FijlB"5똃{z+lc^?Z~I%:`l%u&//5ų23rziy O Y)c¡!OGBVy\g.7 6߼qhS!y޵h"ɑ4]Mf?d`G2 :U`?ۺ4?BKmZv=e݊qEU/~ؼM( `8uʥBג-k _L^'|ȲG1B~Uthw޷sCoYb)1fԇxk"t@;ca 5.D_mKI.$PRogX"`έh#oEe<Fj-hiMW*ﳹ^Ä́z|jy!(+жN<7/%" TIp˭PY/?9՝*O}utBɾ,y/aۘk,q%R8S~ZF(K')НoT{GN٧,-3F!nos; \н"]$2lvie7|#Slo{"7`WyhmW^Vm_ n@߁%&5;Ha6/aഡ/\|Nn]e-1XR:i(oBXe^0(gK6^eHYY^MvFw%V J$ Ğ%|^K ua3)GgZɗZJwUvx/͸)k.BtXei>Kf_>*L?|PErWaBOx-ةhli w&2w8qW?нA"^)7e]2EwKuL'=YOS>zb>NP1֬~LuK+Ƈ$On>V$kpc8rګWJEG\`\}-Z u*ϥ+^y7 4>Ƥl157Or}>(֧$y6TS+ Ov &!I5\ әBRD2C\w^Zc f`eP+{W|ίRX/w W|v*|'Q}dy{Ppo }M~ ȮxHB(-@R_ɝ rpE|c-M)b\Y/oޟ$Ha.<$j7~=?!m巈s.Ys-z+9θ@wo8wWL,z3M, ڥT)I> שVEha .^jENCbHuBoـs*̱6\olZakpSOGf c 5uh6bēpFl g( oͤLjpxjļc`Z,&x7SstG4%1)PQ 'Ĕ$@trfjMM+o~*+>oyڶqWDW{9& 4u,{_C%D+}Rg+G`Ȟ% :\ۆ{uM$]M}ױ<~K-xgr% pC]Z> R1ء-Ǿ/Ҏr̕~ME0%ç+ݦմVD@ȝ?{Y/çӶ/;|:e}ȿKF2gT_W9l &RR~x7SlcB3=B#RIZf7dIO\Fj^/Bo%Z-xl7Z޸u\ *n{[xsa|%\ &)`TI_PVc+[lO.lꌀfb9Yu6t9QC ؕ{O+х-uAQcL A67y͋A҉vE`ax}V2?(CGKٓYJ>Ol~>r-Ϳ譐|2;u Xy7@mLň ;yZNZ FuDϊO1hҢ>: t{ 6p'qΌ^6yb=8 @dFxd{x'}&&׮^zqrm2oaچXzȻWT͋b.w4!D~(qA:;c1*Ís-Qs\NCN)hGkAmYhzZS  ĵ]| W[Dܝ;2V6Mom0\FܿrgPY-Hg<^V$+;&XBM#!:^D26ї&E S !p%ݧe#+K\ڈ}I5M65&GLݴlM/&5M OТ@BBȬaG_rrlv9ohh5Û_a Q5Bu{s~1ZHZ;D>Y=d#Q~ޓjn]|63/``pw9>Psv#kh=yg(&(ޛ*ᦄFsxi#fa訷 ȟQV):hP$SaS)=m.srsUnZi19.6\!)ض_ ,[| UAyed riӯR-6OTσ7n{3蕖 k㛉_? <&y2d tvz{Sm%dK 6ZP'ݪ`$DG8qpqJ endstream endobj 28 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[14 -250 1077 750] /FontName/BOIFIX+CMCSC10 /ItalicAngle 0 /StemV 72 /FontFile 27 0 R /Flags 4 >> endobj 27 0 obj << /Filter[/FlateDecode] /Length1 1570 /Length2 9737 /Length3 533 /Length 10663 >> stream xUX\Q֮;)w @PP8Aw%w ݂ݝy~vMc5wYkSkhIY߃l\\Um.N;'= t;]A.!!.K% /+F;yAl]L2H9 6@G*]hۀ\){{?nqh\@w;de+EGK0@_e 7_rA\^&'3jivX,84/.foth:{+P[ ݪ*`*m̥Al\mNNh?ng+ 7[@Od%֔cFyt;p@HHIr) [`C$I,TkdW,E9_[MYpޢ& ˒)gp脔'"$n bvm9ʭ[a'fg-8˸_p>#dGFu'p62`EfYdGy5p !{~׉h [↠h>%S 63~:7\fLX$|F6>Zi@γ~H 1͐b$#+-Mu$On fOy=E3)_IYQ=R w~K=]@yɾFVbχUĠ}p˖EY磻\1H#Bv:X8q6h^f`}mZSŻ@L揧5x^.w Zj-5ɊjL(E# 0- ?P ˢB"TFB?ΑP8fl9֘2KnT$vEǛoK;W;]13?|Ԝ{j2Gbe"_ aZfjvZ!Kn;x C;BfTImR_Q]xng ;r!7bJ=K*7+?K,h{kr^NSeӷ q=Fȋl视Qg[-}7jP jQ,$X~+r?|Q[3PUfGUtN:}4x2g۩b+^z@ΤRDkwqm!ڄoF4o]>XKVR+;{{59́;Wt Q}xzQ2{@=x1` bnunv|"e[T17ڦt)$\\O@d/S;ș UM +gc@(\5۲]!G G ~\UTy2g)'K7ezUNƔ(mS0 \Ԑn6}_ZA8i0yflW.j$rU1ZF >1@!mcT_!RlsY=žtKTߴ@JAuYLxa j92KA~;T"tڹAF(MT=0zVV)1JꦼbD޶|:[Ϣ5h.̥d P:s 7޳:suW=+%peVgM=Iy-mkPr9:|VEed"\d= ev0mU_1UP5lpKp6G9"gsDX /\'er'W]"M#`۱Ӥ@܌=U׊1[*b^R/FTG+?鍸SLhX#dt65ݳ,غ S_<馋΁otjkY@(ӯ}\œݞV&D9rU!x=&3 Μ:491,ډ6ML)a7uVg`fA.@jPd7 fiVҤA}c1o.g\|)Ц8dϧI6U)jAb^ /puAaXW1X F ؒ߄oz(? \?)tw{]=1PP Co߅Nv9b@Y80WAqH֢G-t '$PZp2C<gJRRW$"r =,kT8iԌ~˜gdl׻@y)E2wWWi/dYBK†ZJSE}^"WP3&UqZO6)E-D|'Q=y-h="D,bbg◷;-lMJ-=ߪǁޅZ@(KyubU¡wA~#W}K6؝WG$m7Z5w*Dhi)&,MkG˭֥ QDPp)^ 9C4ôjfZd?|~Vz`y<%Ov\wt7oW 9<`\H~*I..Tx _A+M+5Ykƴ~u쥊U3lh2 XАLf:i^ZG>%I@|=YiK&ڄ> kF@d^\OX 1cqc8C-"x+^a@Ht:q1 ;ƎIaDz&`5jGGM)'[:vBT/UX/fPfdRvn! {*毮ϒ吁5l3D|dޠ!W`_?D>K#h0N^.(x;S#:ZuW <H7Mn9.'[Wv31).f&@썑菭_HtYG|Ur >k9xggƔ>z3Sn:,տ3kQgCWef^:k*rOl"O(}{z[]7_Lp$AdDTXOUtBI_~ՊꂖjG|h?js/7N}hU!sAg@!Yx/%:6qjoTz@U}1 XԀ:78%w]a^}%as#T<>˂xWF. ~6%\bi[o +>O5y̕MOQWJsW9oCI )wWQ`ȷ3"#Egp_ލqg畭ox[ӷQ -5ϗ6hMB-BGsq"`ZE!^k{({:l`k#,Y:+Bh)NRCjğ{U\G;PM V Py$ >q kUu|_*ˆ2M-GSk'|p'ՁoF-2g?;<*ޠ "s:d%H&2\@a$zk,b"b[5;1jsF6QϽi@C.#$yL,vR"0#Cqnf/':-|vV!]ʥxΞ 9ɣ|c|K!?U }]ݨ%ˌy#Zv2Bc3mvR<0 4vwb(]0iͣf8m@~Iмڣ8P.lv|S 8kPmvsXX>p恑&u(_bj JIOQ:b= :4^0y0 dzˣ~xhr[x6yʥ#ɿBפY@U_^qEͧ'#S'Θ`g< 8"t@~3FFl@` 2Bhwʐ]~̩xkCG 'mFG 1ӏG( kWTvTSidT-܊[%QUe$lJrI|5r&-F3=,,TO_E墋}Vɤ(+:\eޠ$zlJ { |< icZ(s:|Z [ AL, Q8&cC*ϴYlښAe>ٝ;Sr7zf">,#7m-OJw Aȵ5~x4T*^~GsE,-IzHq p^$ ; 8VO"UzJjb /J2(/Ww1qu\PA[%#Xaxs>bhTC;Dq<[Uƣ`Fl p|wt6+W.2Q#%:5)2Ļ{@W]hBS@DR/gXxԪ{Џ 꺎_|3(BN0r:Uhǚ Aiim6^38xB cP)_Qi =bh}U7L }Jz&me P>ɪHeQG$bgK&#OQ{Gxk&F%g.~_E^$Vp/QkmU{+NWQlm<ƾaB0Z >3Œ&|IWU^G?ŸuogsYI]4S fȘ(aM2h Ðnt :=}^6Zn7"TϖZSX+ZxTz-nc' ? GcHWrf,1Wh]{bz,'\aΦ"md++tO׆M8iy$m_|xWf! Tn`I)}cs,1`>ݝsnpp~[f#nR @֙]i x2b6F58g%_@4gQ>v'W? 3-e|(m˶D)qdĖ/=nv誓ݵGzRDwd2pFm7vj(9:h9ʂ?T8$%"(ze٣ΑIx e4^LZ6iJyCP0h.A2, ~ñP/v0e|a$Woj41'r1q]WKu܄yw,C1a8E7scׯR0>J[=w`86,%HvXGJ ^^ N͑b($Db`v~6 M_atkv{NQSخmFǀ8\ gn[mս͙o'Sd,Oiir`ۥe;H-0\ǖ6cq]~د}8KB﹩W=mb  sfh$Yb/2dd0'(ʼf4i1<4mQ;)x^JE|Dcf?X)YhS~ЎR@ďOSG1t$S}({@q+!D?Dw}+lvaoE'N` T-));etY\İ1P!QsE#5+slI!nDź7C*k/pԇvpZ^.1FXo]BT}J'? e"15d MqP)zWâӕumX 6 }JȢޕ SD# vR @Uʠ :g@9CѪ At}Sy7jwxHQQ q`vhk蟠} Wp Bn&U0ѣ~ŰG7ٳoIvE`keX"闭qz#ONEg߆aU_ky @+-?-YQó$iRX&LkzK'=I#0e׊%-lה4-nS^BS:W+IvDFep]iӮӢex󱷛d]_|TŻ2 ]Z=@"Eh1 PC ࣬ }iODk”*[D.u0}f챑,w[_o6Y+PN'`vk )5[!iO; /.9 \I$O*]iZ{}eOrI{+.a^XȮ<$цpJYNa :` s4$ML ;\ɸL-z };2GL$ÔftZiB/;mLb!.L{kHdT; n8Z-h@QSD$CDhMA:i =z#%`co=rW#`V)~|CO>NGֹC+=a>b+=<*A{bqV"Ω]˜ҰRUH6ܫY鿺Y-<%Zba_d#Ч/܎Pݓ;ىle|^߽, ǹp+{3,va <EMbcc^8Dn3xT" yWp;gKɧfNl}Ϧdi$B]~AеWN]_ZUۮvJl1=4n&/aZ}^)~X37bqoB.:v5ժUzt-m6!?IA8|ç;1mA3fិkkֳ87OH#/0SA{ j̸M $Էj(~_?BDeP?]0g[5DD_D׀i 6_h;_D CaYpq'(\VLJ Hd=@^OEa6$Ղb~>1DF2ؓPt+ ,:0 `tg~h;fj6TC5%߃`P1"O^?ُ?ӍHJ[~D9 s;r>\xubGW=iLĮOr(M|AS(Ƚ\y͢}Vn[տap_U< RMK~K1B `ʣ`{;^HhO;wЕlr4$zGF)e5`ߎ+N"VHҧV0d?볜%OL OQqT^4JBSwĿ~I5Zusl&ho"BM2vmTˤ /%RC+$+fv2 x#zGtiX'"e|QO3Iu3ovk-Sb-r5ױd^=LJvRt`b30^G@3>QLሔLS]7_H^;.zaqOmݓ O;1l%,-&I[9%?ͮQ2MȚKle}嚮zD*NOs Db^}韟Sul.{#g ~̵CtJ3JlYp8uamRMMڵw ^Z@]̷o^y6">uMQv,)Jvs$⥴c{9PqH ԎLa <=_ݛc K#|d(*0>kp{coxm4DJtVS\ Lse7Y-)b=S&Mt PR(-`cujm+m&ռzK-hg| Qh>!?> ̞:үx/&?TTH4PtF鐆%_''d5\bˈfmFΌq+DŽ? =+ zm9UG%lB= nG#&5j{ЧqĭUv Wq1~ߢQ!Hh sv(;Hx_62vЈQKD֮}y?;oLNGL=|M=FS=5OğMq3z|4*\ATTө2\[jư$~M6r} E7ηnx m$¯_p2=U`wO34?Rcsgxy3/o"ק*%dX4gtMmzQqG8~G,`nB\@;l endstream endobj 31 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-36 -251 1103 750] /FontName/UZAHIX+CMTI12 /ItalicAngle -14.04 /StemV 63 /FontFile 30 0 R /Flags 68 >> endobj 30 0 obj << /Filter[/FlateDecode] /Length1 723 /Length2 6708 /Length3 533 /Length 7271 >> stream xUT\ݶqK w !hQE!'8  Np^d{syv|YXa>۸YCU\ἂ|E-}uA! .+" ;*P (> JI 0;{8C.q p{˟q|3@xtP7ԆWP`vRwuSrw  Ci@mqP=\Y?`gĿn.^p( fj']u8"j  SwTqڀ{-u;ɟoJ^Mݘ_o]pW-_)||ֿ+ڀa6)O*7_^a1 @PP@ .*ߍ^Pu%s!_* ߵß!APnv%=y Qvاa P5>ca r.j9X[q}rO/-ū"h?Ms}D,_&}B$Cf}ؤ_]n!:*:hzmKBRQD쏇cTl\oPkؕë6; Zu}OeK~j$q0iQ|fKSK5i֩)F+th?*N5sz5N&9( e*Ǹ\*EGV^o|_>LyJ0A|V/m0pu#HvRyW}9yl&睧%H-0WNCK5c5n >v߁A<*נ穼ZH-EE$-ND#d<몜'Lf\*q 2GfX}i .m+V(1GxrKð*_ޭ9q7Q'IVdvoØ![+klu˚"ٚcު{$jfqwۮ1WZ󳥿[~NI8eܱI-kvAW P'gm%|zl>:|~؂=>J$5T)ku;<(0hxhQꮨ ͒yl6(OTA8k&֊v\fү ]wf.7tä~Ix|LiWV~$ԐK2͜nN f{l4qU VB*^WGtv_9}igq- M(QbWt ӉMh\sd`$fFsvZe< XDOܶ@%,my aQKWlTS;,h;PK ;,J2IO0XdHKXIPo}AG>Ǫ[ՃդtJv?FG3= LD,`1f3F,~M"Srs<޾'ݚۯw}vj):G1 mP=oW<06wMqŴH"YF1BQwzݺe+g!߇H}.'crV:-':n{諵XL.hEյ{.,; ӎw1m B:1B:G ^Id{3pR}|'\z COM[u%ƒR_-}5e2U|٨II;~t[qc&]Kb@{Mr()XdՓOYIn7WGV=y'dV: NItrN5:|epQpfma&6No/(Fur#_R8[>8DP9.;(/!n&>ֳAD?&ڼ~~nE1]yn~8x1VY ¸rd^2db[:22*o'-r 'L8H7i(V^|U_1mID f~l&v 9A#P>$cϭ:oo3 Ko["2i%2Xzy&-'c>Dp2Ep9N˙QjU[Г𨤞=8JZ[t(GTBf` G:Qy?;qí#Va.dT&ujxihېM|ov=q,~(w>b%fx8 `:|0?kE~a&`O@k`>:9l޾T*A+UwE2 Iٝ;u2$P\L}7\$$L>xOqhg!9RS? ~$k7Fv$L΋^}ix0:QՎadʘDxbٖѼ8MV-eIoK!"$䣓+h" 79ǺSν`];9~r*yQ.g&$&`(t9ȇ#&))Qps?f3eJ.BlтncY=1o>\[y $fbX#U裷^6+8m3Fp $8i٬:6s!^x]@DSIԸe@pgc ?&vϘqP.?Mx=ϗۘ~ V0xf Pp6 7KѺZ‘¶^N2Q滠E#~vem wmMaٔim5#bG j:[b tJ?5jxƇ5WkRAɺ) y-K`TmV;ڇ瑨{KGa{( 5)Ṳ)k:3sQ36%T_Oi0cD2^o?엍$S.#| )-4$)>ntظ9~S1A.(@S^P;9O-jmUѹ_UrW*izmс:iv{) uǍzg]$ 2g]SFIG֠[Yrb P#'+ ~6J|mڎ"C̯ w+)l1Å1qAL4Զ!b~W򋯖% ՟/{`ni hA.'Ϡׯy`?'}4^c8^& K5veB1?cQJW5>^Js_Nh6&d/6uZihx < U2B3ścAq}k$|6[9LaF])vJ%-BON/z2a[:ɚ 4 QȰ \.#lry^d_.J~pK3l^hޥ@hQ:l0sɩmY 3a-HvEGba[+\5h*pts%| 3JC"@ ̽H̢X3Ck\0N2PϪҰB΀'/ 4;R=GDnKI~z Be>AH(Jz*?B#<%<Arh!/|>5q_ {YV\&#Jo򉐇Y"|WɄOcXw^h;F뮌fm@G[4-8?#Ce&NuF"W|RikcZ^Sʜ3H ?gM|5ť~2 ¾+1Mq2e. C/{FNʆ/BwGȩVra1BSfE]X@`0K z9abLӮA-pQ%.ASGa9fنJ#`.dt6L(SwP/ (>`pȆk XyᅸuoxHeWKoH}vIf/~ >[*cf hM5AIEP-zP 1-PFݞ;RsA`bt2U}&9I"_agD/f~a Rjc߻\,Fߎ줐{KVPyc7gNaAY^vf?6χb"deL1,+d[{${+4yu7_0D4[c$~,Syeb&~SKRԧ3V~{.LɥLN'#&p֍U_k 5]?> x;wdfK+A䛪~:J]"JxeEH6:x_ټ 8fO 誴^ *-i!9ޏTfcTb;=S +1$mpg* ڹx k;W N ޾ǫVqL7le2 ؘjXZ::1l[䦋Xć ,Εߜ7)wWiC)bV qr:7Rkg/̆c\ ݭڏhj'UJ-Jk`PnJ~q@suI"mfPmѰ&c3`ʷTG/wT ŷ,hjhCo'Mp-jjRW4(. +l|5_%*px'p`2JK?lUOAO!w֕..5=ҬWF ?`Fwfm'ׁmp583'e/QT1 H0az+%Uoѽu IJ'Nc?NS&_,rB1S _^?3 aN> endobj 33 0 obj << /Filter[/FlateDecode] /Length1 1171 /Length2 3014 /Length3 533 /Length 3788 >> stream xWiф .q|͒[4 }d}Zf<=$3ƾ gY~PTfҠen'$%ϔt cX}~2%FEK ,L+egaڸctf4KӾZ#X2Sذ(Z}]3- ( a,Cy*Z܇̺8>yׁ6tj%oCSNW̎V= n=4i;љ(yUla>rcטƗ?%z-L5_h> bMv grBl_mFˢyߓ-x;Rۉ~ܴa@rVk݌&w[Slcw?*UƵJ(歟\.A`Ll! 7f[޷]2\*J+SX k}Bal[g&-{ݗt&iOu94A'.S((=pVv1|c4O Tz5w#Ǯ4w"Ϸz H׍ uJHwKVv@зBfvz+]~b1?ђ1bunNpQ]]8y0sǃ L$YTDE&E> woůDmI'ٻókdF - x^7]w$z]}TRnr\渼Kܭׂ,Ox<|ꬃ-O Ճ.(5?!-AgV`ߤ*tz]wɽ{7>4&٪+.c3=!ILq4IvYsC|9꽵لhC!IϕUQ,K(J ~zs]6%wjrC˗;gZ3rVgmڱ«UI1-1pxE-67 uo[1k;6޹w+/BC}1+]RU$: AN+75j yJM+syh\z[38rK&[=Mꖧ_IaQ@;A AGU;11[Doׄ-_E#^k +(̘K=#dħ,1oI|} 0#f{VWqVY!oyM( /r/,>A/ |gkl5}nM>>W0 rS{ J{7a9'\14Zi#>fC%6K/Jta5.Z$箧F%2C=ē{#Stβ>6_(U<\]QL(%`DXdY,3J%e)yϋGμAozy)+>_~fF Nɣ' &ٓaͣ%Ge.Hт^h[K6 `2 J wtrL[[a]I>t?M.]q_'rUZ(ek"Z(k[Ƽ?R.;>l{K'Kf~F")yMVSWYfʕVF,dG}U`hh-*Irg=/P^ѥP٘:]B ~$)ޞBTk2{jo]TS0i;cb3i-{lh1>BG@<[@u>ۇ33W7H]4=9~XCbJVh.朊;CEШaZF ?͖kxe_<"HDHg#^(R~{d f>S{jmQjo&kWis٩/o80Y$Ud\Ёr'yևr{4,w#" ITz Z&FqzNd[-|*c)`7ԒmmKܭ/v%|9dqS\PgS͕^ya e;ͪ Xd58ڶ.f<ᓒ&U*;> endobj 36 0 obj << /Filter[/FlateDecode] /Length1 720 /Length2 18408 /Length3 533 /Length 19022 >> stream xڔs/ү;m۶m۶mϬ۶mkl=7>Q"#XI^ aoBC$*D$CN.0r3r1ssIis;x:Y[QRKI$l p41#7rȆH@D$lcC.D*gą`ni/*i;3{"M]'prߤDpx`5뿓K(+jl-m<_ HdR v1431e,a0Ut1 23q3o*oFyQyQi1,JFv.<Wm3?q a_ۙ؛Zڙٙ9/%"bML}MWY:ňؙ8Y5qurع{'3,0 PU`0 Z, S߈;nK|l!dGi68{ʜ_;H URxAf{Xd̅U0$BnmSLfAx%p aQPlhU$CWZ"hOXz(5Q! oݜ\GQ~]#vv3_|f90eԹsNU1:ȢA0ۉ[E{&6gW.=^g@=gnvN~a؉qfY9FHD@*ҷW|J,.5 &o닓JRWZ BV)Sp I-JI~l;K?=9+'Ѱd@ j~zճ\T!;DŽJ+]'Cٚxr"iV*acqb-D֬Osl7  \R66axG#5 BbV.cc]%>ÆFoa^8̊iaN fPn|1PMp:,7ng{Vi%/T]]x^6Yd9χ-3#(KVYW\86zպq\XlWj"%JzwmLs]GGY \U7tcz@1HlѸu/a\1!k$7H _1iYY^ p,ajjhFՌPEvzg\d# zov1ś&VN1no2Q SG JAzy-2ꦭțx)-Kl[]q_Kx/#]BR}& Acy]# <ѫ$^0]ƔD#CE?a*q>4ȁ1pn`:r|$XBnȬKcإe/.ѫK5s?$_#/m8͒KIfIQXY+&Q>M.!q9X iuF7/ӄIbvUI~LOBLP v+ :5 <۝:p45'혻jH g_5#ܸd%ZAћz+КRBJc:!pdՔ'UA}D_QtXNFW{)b9}S% U0zQ ā\X/uQeW"i'-{WX )lN ]grVϮz˗&ϊ^*?˦x(Q!ea)U71=^;0]{"rt?,ÌEZBX%[8[ FlOEEh)lנjdA6ej/*dYP}DNHv"?̶tk'Tt[y箬J`BVWrҥ1vXԪ%}+CC$zNp]63)gMKk%t @*8nM=Pr:cV)KPv8 m~8K+4/k}Ɏք !σ0Xc,Vy\'#r^3pѮe/'4q^W) 3 UEhu lNP'A$Da:c;jebv 5{`\QPX%ֿ$~ wDL=%znB׋VjTPrQ;n>0B'{K'B{ah 7֙5J9bPv{a.IJ˙p  h[k|M/#{Yg 65{WJN&^(k|p ~1gm?58ɂEӓnZB8ٰ BLJLg+Y'y=CM/>F~W|+@[^pNvfsG96}]yI wD>>L7NGm^$K9})׆Orl#BAn\I.9)?a \Øb:v <RX]B7(y@ brѐ񯆿q,:yq7N1>tf4X)NFռ;@ ߖ1xbN[7-DȪeWh/͕Ly ^Ko8)C#'q <>*[ı?~Ӄq*1 >TXki88?-YIq%S3-(ӫ{+_6F'Z-N:*P(<#;ۣឍwh˸uч Jg z_!Uz{uO! 쑥M ʽ wX?JU<3Fj&*]]@(( c%z"s}~U*GITtYe<[.#"ܱz LUKͫ*s:9n XJae8> g'aڬڵƙSgAsk_V)V/.?(~~XޫKoM>gd*WSYoH,pD/D0~z盚SW,5֟>哒`~@]y@2RA-BgkG<\pdI}M+)ݸl] pc_ w; hW{uA f1, ܬ_KzR( 5 ګЍIe0dQ SxNg? ~Ո舤8C!vsY^+vM|oo݉n[as61LkSjT9]uCy;U%@Whs-B=[-sgB^.韛O㮮/m+1D$ CnR@ā G 5Kڼ+)_8$UW'WS;K/&R&&8+j"8t!{y aR`WS0  k(GS~4gCD;2'bU)/D&ij21H|6zu::l]AϺ+|'G%BYUs]PI&<\~vdqʃND"l  Ȃ8_笻^%'m/A2-pw`{Z+cv n w09vO)oh-AuX7䧇74|/)gZjS}Ρ ϷbRr'EC_êF d)!{aiH!/ByJU@iۼHPFZ|\_q6ؖ,V9<7L )t\:D1JopΆVA-\ux[O@5ʾ+-88߅i2T{ߞWM &㋐j1ãy}oS9\A'ax8&?zT6B y Z64#"9366518{ĂUDǧ(x?rS '*ou* 䨖ҝ߄M%Z3rOm/-2x]13iҼRNigeӼ+sS|[@6P B+2C)U BQfuvYr{dc^9)w#f֠,s9У(I[ \O btoYw\mB:0~A IPT , r$2{{CK-#J /ad+N($@K` OeBK"uBBh+Zd%z1·#c+u}D66=XH&'hEzV!M顡|rPLtA~R"쇌=e>Hz\Hlٕ&%]_E[fg]dnNw_)Ӧw"&w(l\d-aI Y.N\iZ 6ܣ+gXlhz3f IĔbN;e Jg#kc'YRYP7~u[@=aYZ7RJvU=Gޕ;jWF1̖?rbT:PW7&=Gd?NDj DI-o:1Q Kc%ҵQ"!W -P|zw}IKu?Dhm6BV8 T4 E@%co* [e u7()6;Q{@nck[D"#h t >>'V?LQA$ύ!V:"\36rqATS`ۡKz<+oG=Z$ۻTxl &jCm0Dۊ0k,R+5'8OoqMgpntYLgwU2 ]ۣ]Nn4= S2du53V\R=mr_7i2/ :bHlZ}jD!j` I2ʥtN2>~ٻ6i b 0⦟21jYʿt e2uېRͱ  aMY][OŠcٙnZKȇI[)>[["@u-pxߨT)&o G3&ofDֹ3ZF,/ME1~-8&wtrtvlf1FAdT},/bM[JU̙( D`]< K$ Rd,ΑewE|%PD9#׈Vm2 ֶ3H^|{/DYy2Mf#'Н#ױRUŠef,s!L +pVmU*Y[s yb\깕](8:&9N5:r>^ؔ6`ޛK Lɼ6viA? d1W :;{<4~}!OӹV=Oȗ;n@6cmTa+q"ߪjAj)TuP*DQ E7(\CG5-?AJY0mI<O+RMtW>DkBs1 ci6q.gCÚ I$/x,cȃrOA/Q"Js`q&]p|_5jGSnn[Lq&s@<,@ ){=n$ϸ sRp.5qiTMИS1Ut0WMɐ(ŊZ :L2Egm<06H=_\6FK6ώ5,?5ĺԵw5nἹzK'cw]-6'f ,;#K)ϹLV-33C=kIܠO㵝.TtjRKb? (qp%uu"םD|0Y䆑(;`#t]]8 RjM^H "MTZ3FZMx XE^1fn|>_ؓ_+m)mFҭ[uݥR\6E{4pXK2*;ͲWPq,H[RlV}u[f*כ ;E`'swSVU NNgid"Dx.AsD?SNDC!8N,iͫOK ;-~f=hS%@Pvc[+/껦qǔdGy29nK&,и":kYh3'~1>*j U ]>Ġ굕 lM=P:5)Q1֯Cp^Ri3/ӏ\h^ϋQmqq 2Kt %؏6K ^OFyx ֟` /o(N_DH4+ME1ta7ϼXV䵑O>ag 1gs_V ?-!2LCloGb*_<ྼs TwƑBԚ%(rWOym\U |t4B3ߋ-%( ^jZ{%iU/H[I>v1 eaLGK x[xoItE9vyPK58R9hN\g-:1nvi}^Nu/CKiΡ\6e%|HZ/m铃T"_mkHNcw0~GH$uA& й_q4c[#&i.q/NXه0zG=T[YK)g K꘺gӑq?:<:_$ސߜ|V㉰8y|#xWXQs( ӣp":mЎBI1ۥo&v)e^=0}ln;3v9:[8`vw%bvu'~j' sg̟qkoXWjm9ݫ s2{@Y݋.y;=lmWZ1: Ȏs앟(aܽx3IMl7pvJSM=†6{Q`hu㹌+2&r+ QPR?ޥ0\g?U1Kc߰FڎOY #H]~?Bl HmSNo3 k5 lvEl:p M+6^rU W=;-UCT"|5Y,sazJ' s;vD&^ʄ f}z[r }vU33)Mޑ9C5cî'ArRu&mtwˊZV,r3M܄%K:6a@|C Q?/U!򓻣~ 6xa8ePO/tָ1c 74QCp,B`Axh_{Y#䍑'pǢg` IqT̅Z>G,(c=}Yn;Tb ΤI1<zzq=6)o 7u4.F Xu-)X @[cLY7@WPʼұӧԙ`ki_7k# }4m%q˶?bN- {2Y!XBV9 6:{"HsO p8&V}(ײR'%6 !~qe جr/R`V ?"1\uEO>ˡzXBUM $>QxM P?oOzdX܈;Yj Π1kr/э)A|K/hChËHgK|=&1N;MKJ_N ic2:=NU0#(J -J"ن/v=1%_' &(#˗g~$~-`Ϗ&ۑJv1u, _`Y)$jl-oߡ㌫B4rt$(L#Q|9e_o~3R=D6LH?I$ðԷ[+RYJqkrN`x{icHUWy&S8IER $Z'oϳ~>-eDȧuQq}zF]}Wyݍ! v#;˅7h,=@N*(I2LO0E!?b ŸDlur[&h,`ՉŅLIޠ4P 5D*E Z~)-<;R|o,ZV+hvO)WʸHY}=wz x3%0n2˂MjDˮ| Kuv-Xݿ *IƱ"YK TwفL ڜ(!9"^A#!ge{ | ɢP纳H _bKIs5 D 9O敧bp3=ɓwҊV \AA6jtڢ_"6߳ h"O}zː6(.m++$ip%>v$`6<RƐuZ7DUipfbK3Pm RΓE%|N$\ 9`!V<"~$H6!-clv#XA,pRFnH0 OAp2w6$,'&WhQ/ ?0%G/޼6!qV|ZR*Rpo닐RpfGVEק<,l|?o)>e`?v A4 L5S1_%# `옕{#ZoBfROw<5mn~[Ee<0Ţ%"~ s[2j2 c 9|1:`%#hE>?yAXb}_~maQ 2cYUP-ZM*-/ Ov%Ҟ-oFf!mO+kЏ0>uq.ellUL.yIT~쯽O 6NªPwk ,2LۜwL&K<VP*"¤:T1Tv"qCH6l7ָKກiouJsl ę0;.Q=I z#t' bPM_ w]/)N$Cd[>枨H0Nn14 "n/,fѱ}|K u -Uo^ v&_ǢUm$⛝W^6q8577uRώVijm@SI(wwA X80O!3=D@On K*6R{:F/ږXWwc51˙ "> ĎWb &+-h)?5|_$.i؜CƂtoH3ub%HH}3-jO8~%IS~9Vu6Qdn3F2/cu NOK\]+;,"S XsyG4!J F?i[} L#a/x!ّB/ޚz` 4<; @0K;O7r9.C)aI l}z94.+zQC`( |&Ի ؏nHݝXyCb ;ӛc~e+ߕj'2DžB_F$s* |z;?٨}3x+#2xdpA"rb>PLկNfZ=xSZ:vtI?/F>iĞ8}FJ[ `Kuy@qW45P~"8ڟ\.k"j9pk'cFTCDPh9G|\W~`D1[6 L5U'?oqNkqv@H7TXu3vD3?zM$ =Kc<d7ٌ*2 @x?`vSx_Z,QФQػ<1(j{Uç>TCU2F9 ?+8ny-\}[$`JFN 1Cإ1d*o},}gkg-luK/\F5߸eT?&!F9Np 3ikngqk#S}L!VY|:6\^]߰ӏdG쩥[F9/ 8H3.N/ +S5-zV56瀛2zoG91-8yvسH9u]om2-s E"qЪ_ĩob+(GI@pOS?#$+>;hMx &#. 2)!}G Lk޻J[$FJ\N>F% ş\fתOLǎp>K!ZBIm}+[-7 7Q@'aNLUa* v `?c?6>UmsF&Ȭm / BNFSЖ?˽@B9lsѥ^Si!x{}MkaSIzbh+R?'ejȧF_,"&d}ƨlNe?هp|̛LJƼ$"JWfP;kTi8ckҾ v׾UyYdt}H3W}j]a+VG!+0| <x΃f@ե -=10ꦟзz R\6֮U[o\!u YH}&>{I) "nWV2?%UwBF-R{5Ρg:izYdoԻ$ް6VnNS@u[DI ԭ:F$/>#{{=ާ;fB|N>OۢND Vz=Jϝ7fkPTG ai:Mc{13ԧ03N>AJU*(=$[hޕUݠTK\FŘEp__JP^c8s*U; 4>Pˏ'%'/$ 39'i76>#c'I`+)ddT+ȵ >m[hET|n$ ղC4_;~ʭDY-)75*PHEU^nyxF}#b3iAj-yѨTA'|a[WPUWH֪}2HM CC1gD?!GB^v9SA݈-v&HtXi PYӫCǚH0!J+՟ jHxN/ hQ?WYh-ٜQ ?q~(&4 3Ad>m4[,DAI:[uJnxm0Β &k?T,q/x ?f+NΣ8b<=; N|jp7FvSl3Ar0^D1HtF"1 J`W6qUeMEvTej~C?g?4֦Ԉ61kQF)j( T^JAG$fVJc&{}w׏cbRLeʨ3\-"Jl0v{'\r) BIp[$$qpGvTS%SFt>(sn:7ܺ{^WR ˮ&rç'CiCN[wJvRLk%xkT5 dM~wbdT'fL;K!1})`lh_RlC ;mCzĞu;w*nS'23$,(9}d@o5/ĻWw"l9yJ2r6v;m8␌вnĈ%k{RmfT)Exn14Gɉ@-nIg%[P%k[ҷ)& JcRRwNUx6a3F@7%\n!x_V@[@ CҌ{X̗_l_U.P0LWȃX:I΍}qR( +BPC q"HiܣJ$O )buWއ]EcR"wA +t(?\sM׍^@ޔx\^N>wώ_wUڶؚ,Iw#Eրr ֧K&۶ qAV$ǓLW8醏9*ɴrw|0wVu~+`ϋDKpOiLV*v­BzpCf+nI;s )F' saScP ȠіͲ{0Ed92,+} \{6+(.gQyg+WCt˪C~1ݏ)q-± JEX6֣`K3u֞F yabQM\@0f$x8>8`3u$m a[o>z6/l+ûSrD})g1xA۵yg\GL9Ah:B1bBPFZ^o0 u=~zS턎xU1e*}iRĿk= Zhk/hΓsLxQQXs%} zQf 0݊n=#R"P3()ŸKqFvbEK!0+&œ η̠aCdb|G5(|apn)o2]w0XڄyZkKRL_7ҽvWܪFw7t?LXY$K~)2Dh*0f$cQL%y ;|[}R IYJŅa>2yBܔm$<ɝQmn?Rf^UǃHYBRyZyx \w=gX4cFcTsȟ4\‡ԁFw{vh Gmӆ>`L.D6 1B`!ewxq\aUDF͓y}|GXT45"41>ESnLuݳ&#Y}%edgn_d|?Th_[J-5y;k3/,4t+I_q'Iu‡t4~r4 Q[w4$0; bOY?&>lV%5}qg6(%Ap{Vױ@oPH3<(̀uz-\{\Xb(oۧj2c%Q5ah 0DY1_ۑȳ:Q+PYAX"}52mU9H\9;>66T1#^q 2cC7Z$+T+m\jW3GBfy/4lީl5/҆ vw!U)}14?U,|bmTbzV= .e4)_3US S6o(WwxF$lHZ679hK.L`ݺg{B.%_Y>|rfVS:ֽglڔ>{ؑ8d~C[_U|P0,h/H+ޑ0قIyIj'Kd0ﹷ{ߤ3;򳘥F4TıLMF $vt0Hk$2Cp%󽾠A#WWFUt:}dHq0@Hmgbv5ت : 軡_~7}9]n endstream endobj 40 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-4 -235 731 800] /FontName/WVFCZA+CMTT10 /ItalicAngle 0 /StemV 69 /FontFile 39 0 R /Flags 4 >> endobj 39 0 obj << /Filter[/FlateDecode] /Length1 2336 /Length2 15473 /Length3 533 /Length 16751 >> stream xUXݶIpwwwww.݃w'ܝ@ @pw׷9M?}OFz* e5sGS # /@LA], GA!4qvt7qXxxX"N.VN 3/;+ bmi' bt63q(YAͬnތ@[\@WМ`nm0ZZ;1%`\2<@ 8&EG b7^qIw;;EWgM; Pp48T-)ͭU7;k3K; B֮^@sek73+;_avܿ 4%tE{\몲ӿ'_Πq1: $ͭ,o8``@Ln[NX83ll&e;b09;Mރ&w{= `2w3qyq.f@7u5u׿<&fg5`tZeJfOAPQNvPIf&P=VNVZ9%nn=7s<߯sݬ\e-{kaWH4f+/WX3#8@pS_7ӌF9Av@Mdln|dgPAɻ'[@΢{';D%M\ AwIL@N2}';\ N w(A.rQy';\ N whE@.r7\t bbbf tu:5LAx8{ 3A5/5P  */g,dm\B_R AN!H/YڭB_r Y !/g +dz_7?HȟV΋G~Ҧrt݇{HDkwgGwM)n8KV IT.36l Qqv.)%} OV_+)YLø®'9]YTuSWƨ?k@ ?k]蹊|hD-W(LRl'A#%5XBPٛ,> C ^<wv51S%;b]fpr)bmZasSYX3Yeʋ X˲ JT? 4K DŽGtɪ㹨(,lDV?XL+\غ9 TG [r5ѓ۠dcJ68c,rQOeŀ .sW5l'q]}.s:ׇPDN8uSW/EG5IiO_]KLތB8ڥ,XZ7eȳ6I]PS~QM*.]ocABN峙#f땛8W2]Ro Å/8Y)O gbv1yFcX/}l’˜EN\jO˔cTj'=)}m8C, TÓ'(zu ԡ\)$Hu53U@2Z2T₹VRǃgK%#9"8 fkos>#a脞w| 0]<JF?)~0z3IFW&H:噛e=3 ˘Ux~z%ϙ-kO~%YYR)YKf,UgE [֡VsSE2 )q%ޮkl[0~?f \ ¦kD??h Q1W ^)>SZ_7GqΛE:M&--0ى&3՜Kr\C0?``KsGeqݞj 7<>qdsD?)6Bf"0y;u=H|zLY:G¿WՈ~ToM G|zAsYʬwcFjkG +1^L?6bBPV ӝA77r#S [aWrsc\ )$ҀΛb#}tOgpOˋ[utiRHw]ȩ&T%{_C ^dCLNklҹj,0Ď%Ãɢ9ɡ dH}lZ70[4ZX>+`ŗIWȱ-XШ@td ,O\![8.QNbX6 +˾r7LVrށ8T@609cޠ8k(@ܔSD%>D'A;(W"ӏWt^]Ѽ/Z4yyM֬en vUZo_vieS]}ؕ%!H1җ_ݍ^!Rd@kGrParX973r 0`p+É<}WMfZtXxGMRw=~9S?cBx5`'cFֲr3b8Pt-T>yFaC ﯉ JtzA?#<3Ά5*,z"~J%Pӷi~d+0`\fƐg;uA3>d\C̑j %[˥BqF2d`4eH7d~Qg$yͬNfRDi? tZ594&n^Oj7{z"޸ -j%)*B-lN6QQbNQK\QY_3P["HbuC I]p)|+KciV}Σ4N(='dPJḗǞ4RY"K؃HE0!SD ݶ潘fҹ%1jXr/ 8C _>k3 |ƉN֝)uup+P?xm$#'+(^tJ.G-J53Jy7ȺM:0RE3n%§T攚k!ē~uՎiٛSGF5_am" T$[tQj'!6 tr̦Z^#R<;W25:q)O:mZ L򶟇s }A3UC߬DY&jÁ񠳯ir4Ni2M7䪀Z^j׈BKKCjH{.&aϝc* D{,ێӬ &\fpwҺ4_CG66m5`tp䏷 칛W䞴qm".3,,|UB_[?3ؑ {S^[iL#x`tF8[rg|~ڧMx EhSͥ*|KX+TӍH3p֌Tl.я1=J?bN ,bO?.$MiXoqpBA`+^Gt݆@~!3q٥ .5NӲ};r(뮩@N-UaUTx_bG?G1A R1r#8B8jmRLr6u< l.̤|D#$S0j$&V,8_N&#hh.#v?ux]H,.tD`SKcNL!mH& x!==-PO<:U{ z.F& }Ә\"qԕd8@VmoGR4cMk; A@-6(@6W/g$̈́ S-̑{TIpD1&՟ZAs_R%˅K]Mj(Fv`sQlؓT˒ 0=2[rYtòO  }9Ӗg?p, 4B{dwC+(mҜIJFՃkѩ_ p~cT:oaΒ'J$(w]F6#{X(*.'"LMv(?_SÀ}48qX$l,Gj]R'qǓ3Kg;M-޾5`e4g}0d&e,,-W ݅J"ntj)!4Ԓ}`R,[.@<w/H}GyX7`5k/3vV'"J#y <[̟̑[<vszJhǙQüx< Ǫz<KE а JAP3ъMa?dTx+,&ViW~K: = oO |X{oͫwq\9; /KfɇAZքjkyׁfP9+].Y{ uqS[ K FjwDu5GsdC׫.@6 í% VW>F8Яd枊A|4` (L,_O>-{FMٱ*~MQCRWsvnnA@^e~s Y|t,6SűT0XQ뢆;[E >W_0-7ބb\!e˸}Ty4 @9@6ɞ!GwusR+^̈v᠃C7[ھ M@g1qhpMd\<[=ou (CM+ ,.LȶFu I+Gp[J;hƕ6̳l9H9|c\%.8* 'd42iRܝvS[._3sV! mE=T0:$uMi>F*Ej<Ԅ✥mL{0Y&bUM@iZJ90G]v߽ "Ȳ.D,cg۰pHY ;cEE1V}kG%<즻60x?F=ǓfcK9d2QEb:u@0ئB<\k8DB͖3>v>;LɫKx#~mǴn}؃5u@\Z"8 ~U}T،6eZgi6u*}qG dƎ {hY:CQ޳$rQUڐ#Eg5"J9Y}GrxX 0jCUKj^8 G;΋4lռDϑ:[noM52[韴m2ۈ3j0 㫹6;WbOW]/SOP((C?;QRǗ$or#u*IWC>o' - / wY- ] [kji iּVJ|Z!~)#7F.AI3A mS1Ab}QgE"FBZQ~|cgF%ZTsfWX]d}If&RՌF#2ݟf[[Wڨ/+FMGS0#nivWM+~)o0**%oy!1\vl,ʾC|Bl 6msU˜H0N)=JI*W%Ae̽2պOg.!OHtPVsٮ{Շ>#S#M:42-|at_p0̓~;*2ƌJ'q y7JUr% 'j_hN`7OJfjO>eb)0IGT3z~m"N 3rG&K^iw1]<5T$X~#w)#l$j/}S<,S$db*y;́< !a,h)v P#!3.0fѣʞ݆Iݎ\~0Ѵtug촟m4!W$:Ѩʳ2CE9wqB5^Rod)zAGKI{ ]$sԗk2qԍ<$]ҏ},sґH8[MtIsK2'|6=?`נ"8 }5P?ǻ!heGZ?`f}P9ե=%֢-_n>wczeGg' }gA1^.W:dS|qSl ^[.MB=0cn*}LS2퀖t="-;"L99 E<9LtiHˈ1ERtP}9(Ip);ڃ3{3g?+Z$I?U+>l Ev*U,[px skj}L>!SH`U`oawnYm /VfĜg)WV Zz}{. 0M/C % 'jN]*Զ5(x'tbBJ"* xs8?0o"J!+ {Γ,sx{ b{(K5xԝlE?-?^=ќIuMA\\;T1yy4lg~DJ\"f/_mIo^L#|`rV̙l]<@}L=„T/F((K7{ ic5¿bcJ%8WqЉ5ⲓ+d v. '`_޿` }k+ԆIqـh:+}M`!RFـ Pl\HrShhS;{ "TyigӬVvA߀eKl%L}"tҿHC*oe tezox5WfSGKr =4xGwHQ 4/ZgJ$O} "'NK~g+at:~Zi, s+O4S^Ƭ6iK9ͺ1<04ǫΝ1[ëzP`MeЯq:"Bby;#CELR/A:Ek $fC7=ŧ7lNJI&HKB/8i,${[<|<F(.VH?"=Tʇ2U!CȒgF1p^l;ѹ;;lѕ -i֭k,`&`R~olLm.#,&Ii;p|mdU}Wnս`y,]a2KgFiҡ/MF6c ?s^x MzfO&tt:*dD_rX]-kاjr)OG!X[nY8$? ɭmibQ,?2Bݸ~…(*_>l.[SU=O}h^3 */Jy{'ՇY#I:"]C3 QqM1BB1vSrH;!R— SޢR[1P>&BlViCgq{"d 6^ hit,6)V|QY@ADD 6Vx@]rON 2};YHK#'!´)5(:eF"<4+'NyVϼM| Wj79u GLLA)+X/*&1^f$m^69 _QҜl|3zMj} VK ZAϱ}dCH';ȁI˶G}}S'㖿|390 x7}N&w S [#Jr=aihP\J̍5aa>q/oLk] HǗG*#.P_e$է:0A?TYE_Ri90D,& w~,R=E'`|VơO {RuK&.8IucAqO YRopM2*{j(A,GQ~_5Pޑd+Llľ۪,:ȎLLo7mM7%Wpdw|st@$gS=Nz|6;/#@h ʰZZtpNa{w_U+jeUD(9AgAV{U61 ׺q/cǥkaGku,Z4tyǑºl0 ۜ\Ǭ!-[ꆦ 5ϖ܃| З5H"@!S/& Z-F'{EÖ^&o1[![\:Q~u- ''AWTeO #j 5.Q[:8F,pߵ;A/] Y7 p7ɉYN eUD%|j[Qk1u6-Y8BDY]5.4(>+)HA4z4iH0Rm>cȖ9c~Kdt\ !ʸ}A 2}A&\[ aҸZJ}qB8?͖p,3C ]vR{h鞡gƽnnNixO!Btk3aS Z<*-ƲůT#ZCIםHxkl xt fگ֞BjHVbN(-@;~9uऩ-jz,ucY6r|^j>1|4{ؕF4RJg9^,h͠_%XSd3\~˒X"*W蛌pNo*݄:S4ìi͎fE))1.L^T],CnL{K҈E,ܿU&du.iiW9 *a=f>&/+o1N=sC<4uɄbad@28ZJDe_lDh"4sk#AS]lVdRa荥 LƴjNNXE-*J8L \А`z-YJ[sG/)i"!iR/9Igp[q.5_ؐ,18Krfv?GLʫ\kA }r,zxE _ڮgRk+IRee8O&m.88 #A7)X{-(uBahB]cx$c%8x$[ ژc f0n{=z;cHv2#_{wIc1;bH @(3A733[?ze]q/㠚6LR=UXWMh=D0 5md@M|ZӅ'\pۏ ) Vo&/t̥>t [i' PJZnقE|\)IQE,2Z96CRX-lxķIU^1_u3ؾ}W8xc{nQhpxGi)ZڜX_ 8Pή!R/+ R5n?E 5BWaxqZ^g/Έ:-Lw>2Gq+Xdr A3aiI(/>a]CjI(_Z<3|Uj8q;c7{} K=(>LtR_l`XO sG.'e :Vʓ޹gu'ֻ=l{/iIVr$G#E7J#.LaJ5lgAF3 +uW\,h~K Tmrt=&݇iT<2XG #(}&5cT~x|60lOb]{'Ehʪ PF PPy00`C O=/q &hCI&#E088mnxe\|=?.b _(:K0a2b$H] `mͫRv OPIYZu|bvY 3e+fDրD9zB""].@(vHu%5 k~%?'h#| U^SKlF 5i 2Xo\C(iY?y Q#o|5 1y5wjU?P'@p |P:y-c0I,cӲ+L?n]E%O9 >d^p_!)uz|v9}Z*\Xg$>mm ;ծ Yʓ`|‰3T1Ekހ lAT9:3#)яH9{[ kMVg p(*90ÂIWֹ<{<>{!Sϐx/lF/"㪈^*\"jTxrcO *ծh#y4_Nӷq0'( PЙJ<>,SV 5 869]̬ ۥE^b Œ`  Ϩ.n5})p$zH&4(O/]ijJK&2S9>Qn4Tp=}l{bEVV}V| q/ȒZ2=J\ .\+tG^chʔN\n[Ւxv`Tͯz^%UO<`;Z)݄KoȍJ{TeGfM &.c+*%dzK-R_-i; îq@'K_zd-OBbl@9LJabi.KTN62SP1a7[xeܚ`~8׮/6]AہKL7i _Mē ٌ;C Yv|t8֬$cΧ6{jU!VZSթ?-!a;_1YPzƂ ,tY75&' K+9"ӟa䉙7>Sr".ra~xޥkg`A7bQoϪ\0 z;R=޿?daM.0ՋbWFGŦ/FDAs>:J,N4)5~epMGGR.gS0 >_} 82L>.L Pe܋GPirdEw+^on/{Mss@d3 ܸ.囖R&%/?рS2 endstream endobj 44 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-61 -250 999 759] /FontName/OAOWHY+CMSS10 /ItalicAngle 0 /StemV 78 /FontFile 43 0 R /Flags 4 >> endobj 43 0 obj << /Filter[/FlateDecode] /Length1 717 /Length2 3470 /Length3 533 /Length 4019 >> stream xgXۚ4^% H %i&)BBbD)AQ@$E#]@:ҫt vs}\o֟}g=^I#H,/VCV`b4!(,`> tW(+C/H 87 m(K ġ;wwGlp?lH\(8BaX2xbj?[AH\ @SpBbСғE 4 뿡{ gA[O#q+,z"kwG/4KBB( xu$wFPַnvWBw d@8TO'o;"P/-p!%;6( +5[gAFR 8\'dJZD# !|#w`6a4h G 8ϖzbx0vhl@kGlo=-@? ] |1yKʒN:[wU7TJo-] zėi>|͝7 rcirelTϨ%qҏU^~xқ%<{ZQK{6Up4>kc+'qPt%3|svhƑ½?\Ti yTQtgY"}o{BgK``e W8ƶPXQ?pb*~{ gߴIjTƥK I>L1)WU}xq#İ<`&qumub(XYCJN4bמC)J]b $SꊳO1{XkSf?*.؈$l4-DsfZ6W r => ng|ezeV!|;z5XǵOo <4 DS܇AEvn|[lm젨A|t  K!Gg2H FkK:x2y u5m#Mɯa 9pȝS߄L)S>pVE3w?L?,KasMV`$Zgu4'M\S26\zEԯ(ȟ߮A0]A⢦0:q8PU:uI14R7^;gWus>vR,#23@N=Wפ ?wTW qvJPԲ: ,WAP#7G6{D*ʣ3rP6B|Q(o!=3&G#utE*oQ.w wo9̰x核yͶ hI^%G̋SQLZW :!a#=KSU|F&˚^ 1 ޞy0{ۋ C|T9UdDc=0JUBLLct~lPA/˓#/sݙGOCwv44>JvV|1ydkl% `U{=^\TN#$ ڈyg-%*?j BUffx }5V!8Q\cmd]TN$o{ &&\eV퓤E=+6Oz|%Tb~[j5rp3̙2_ 6ձ{hU¬3{ˉ}(z9_6'-)V(zJRK2oA XW!"g%%8l^ Ua3qPV *ͪC,54WChىVHP>'zйTADofLRL˾M^Vpc4c8@m_o{ R%m3W\씂: IΏ qxB7=pNҊiOCMmګeC¿T"㳟6Dv xSRʦp/m k fk)Za~eg(5:>>2紓D>Hܷ^4 .[՞dEauN9&9O{>͗k< oHl;FG}v>L}o: =._r'"8g] RN:[~6'ʐ8[]Ԅ &z>Kz`(=W+Ke8Z0>'|ksUNΩrҩNToxৌ9䶸ULg U:P;ܝ(i'`.kUJR¾b?]WB}v~tw]]g?Ќ1qq\mA z~0Јzpd~R4>%joYfAVl-KCsR6i=̗-!tekb{}&A⥓yg^ޟvoMdXn -z&Q0Cj >.y(&VeɄ#}ƖYh/纞(gxyp=ճ׮䣸 MSJ.QfMg lC#f1/|\K,pe^uaлخt 3K-c_$`څtX߭ ̀r ذ&) \/ebsjt5ߒLw$h}0ElDrXM.fIgsW%6?vdV r~ԫ"/s[N yx3;z=9FkpQM:S`ڍ Msɯ_&4Ux!ʨoiF%W /?G#qx;Η?nEe endstream endobj 52 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-163 -250 1146 969] /FontName/WDZYLH+CMTI10 /ItalicAngle -14.04 /StemV 68 /FontFile 51 0 R /Flags 68 >> endobj 51 0 obj << /Filter[/FlateDecode] /Length1 728 /Length2 18060 /Length3 533 /Length 18677 >> stream xڔSn͖S;m۶m۶m۶m;wڙ;mi:U5tX"Ļb2"E:!S{c =7#!=#04NML(0g MF(bdinBH)B*B![&&F6*&OzBB!BęP prCC31Z-K̞?Ӧ]r89EH/R*8Ml< Mf ]mllF6upu8ٛ]O89W\l,MmtLt*ZX8ؙ;?ACT[KVV,\T=#W??.9Yz0c3?lbv&v*.FvFN;S {x11ݘX ع|O+@J_YW''˿6?K t:#A59e(y7"4["*;,<Ybe/N.~bb=M<ƇeЊpQ)1} .3RKiA>Z[ke}^8ǻmg}2kf]Gdwބm\ymNף kkx ݀/Dǀ-nzlpgݺoپz$/Fdzm^^hjˍ6Q4\M w\uBw0LYƜ]5%H[j3_0n ]/tcYs|,~1k<5^?rJwU4NY`@$+t't]hkd KRt-͈"g SL.֟ [P{)JCL v17B[qMЇhC_wFљQXl#ZbDw;g4Ag|,31UV'!r OxҞ1 K%׽KM5(FTwV?' 1~vyن9-b co )hiZl⥢9I{wTA=J .Q "[얁a (ͱFNAS# 8>8EF\!zC{ Nܺ$WI_<- 9*y.9KO@m޿mNjH/ ܾPQb5|'͒nV"OUSg$F,#Vq= >I`0Ȑ+["{q B@ILAoE#?B?*g/\⃨-Ԛ١bNj lBW" _ 3u $Q-ekE3 `W%"9aԑH~GWoA&ՋtVl2pavMFϸ'[XLLMl+fbJ076o$̞s-t6A79b(昵e2ٽh6Q)WGGJs[eX,jc1<Ͽ'Vyd, ?]""PC[֪ qN`YVT _w QD6 R-I3J}J {i澦YxZ]3S1׍ؒXf4"WrZ6}|Ssnߝ8DΦ,:ߘe"[Lg~џe(lNO|xfxi-%Q7ƚPZTsj!1oKr !WvW1Cv)~X ׁaʄ36jJ6Km˰IҢ@&tuEAk s1ZU[:$C-@=9nj6UWM}PI]cQ:aV@ z&g؅H^s,  Bc-QY|| _I)>xݤ&=}r[J;Hzp.beG#$R&|S4F[Jb4[1_gLyF1yGV6Im1+i]Lm\h) S)W ҳ0e>.Mbt${|]Se]n!,_w&zVSu5T]5%EAèew E#M8=֐5²ʗ1ɔsE#:T !Ws ZX`9ɗ :Sj{: 1<8+4E4{;|ܗ ,]ܘuƟuV };Ƃ|K"F>asڃO2(XR7&̇+V\/#.pRA PWv >}h߁pz4mQRp>/㘸WA[8>PY`f!О wlߨ/4;ay+l>a2൧~OѶΠ6LuP6.;Hm/Цan!'XeV553Y-^EzE=۵Ōdn5@̲?r%^4dh#b}{zw:z7 8kXʒq"+~ \SkR0=H-0|+GdIC E5U!x @J2vId q"A1,*XOE| XߐkbC4D{ڀr&S.q YѴyzb0ơӇ$g_3S{K<0A `8mQƊ\o,6|}~+E}mz [Pޔ ]ws*8 7(]J1hU;= wI3.0c%!03- H_Xc@ʟ|#4J)GaySp25\\W6-ċo`yhpN~nϴe'yA/G_+#]I Q.EOmhÍ'ȇ>XXGM,jjCDaWx}L+28ᒱBU aq=dn xwu6h\($mģʢZO"TIWȇӄx=qC Hw5wDpᆪ=H$jMRDp1ݹ (ꩢ&8 XG-4>(oF =aRP%d^7՝ (gA"bJI-IBvϘ iNsbƇ++~63fPXZG|+lUi$vMIgG'ȐYEjzJ)Vst4FސB"a2n <|o!v*Є/X m5ŮkßǁIcPָr?T-Kh}!"#0pkGniU"liP{j&be}^QB*7;},NVmw<1IK "8zooPu!M;'0ږjʡFƼ!{I PqS}:ۆ3zeWR$T{v= U0vjq:jg ZnMbv{.J,Wzȡͪ5tK5 a ۾.`f}x/ 0_l^/Q&z7OJc#\௷a~_ǎj荍҃Kr{v_ lyhCsH3P޽S@31;%x2Q={7Lw@`ˉ:M O!iٹxk20Ա"^wҷѩvx&CG鍻dS2l4S02w72P>^ԍ.<7~p+zS P>&USo 0"6m@EԴ&*ӊBg\mcQFsI _t_*RR#dceE{ºHi/%m@4gr ז6(y{?Rji-'N#ls틿 X+`s8$vl 9cǜM5ebGyF =M3]$Kyk \Ԅ,I(Z\π+xN=PkgoU%]WONֈ4< .ڴ92e_wz <,ICJHsvaIk Yܝhxfyi": 5wns`N1ETnq<i6oe.7 qU'*G̉Z]aY oc,L YԣgUDbS iKt++cw!|σcF@ɫ`I5 r>+~C(Zwu 0 &F^[muMc-%;I/Z>- EB Tkddn/`FҮ@Ŷ , QIt̊1M2vh"ΠZlk8vV3TNl\ȫp ӣGێuO}PgBTm67d.2j~we"}>q=ߊ0dy& J`Z&&aXGCz ɿp94)a ~3emx2k> t4Q?{aP9]  U>*gV|A%q&8Sq=o]MwɸIZs"F?ɯ9=Et=cL^D&Wp#pW͏0+Px>su"_Gʉ|KY?dy!crZyo g_(U)Z̒+X5~͞o*KqV9R M̺&iI/y8s)(M5/8S[ Z?8v=KԨ§=(G ٫obDp9Lv0 +lD+誇@yH=ݻ#S;jפJ4^6R{&XßD\G?JgtS'MGW"/m|M^msE_hskB8(jQ$`4FYo{,z߇+Z\zA/]8NԠm߽M1 /ZCn=<?e-iwoIɡAh IMψI`|BCj(1P20gOp"T PfaKi=g>p՚ vLa,P*v5G3W 8K]-*%(/:E.E6\"&*h' e*q.=sNHĵHk?es $nܛN?(`AXs73TKptwZN1,T<[o xEᡠR40 /gFh0ǂT`G̫̊0pT?PhLrZkʌ͗Hrp!-=aZf2r"#PzQjȤ-٭?~TN2Sgq͠j`g2.rg$)"53%znE5`6qFzcDKÅ.35_[ٺ?GAmؒ ܓFmРX3ZnT$~5Vr ۝.'Ou͈z00'??$]WFp3 U޺:bx%e2wG5ݎ o'Yhbm+sprts7h4DA M]:nZlfTU?P[1Eknp w[dN:Kv֏i)f@q\z1ʘc 'Xl H!סü HI B6D'bmZ0oТ̋ +؊2o&a_Og*xj1f1s;ڙ*1ſ0p5*O!ry_IRYj-03|x7ɰ,3y\i U>|4DNu1|>.+pa|^aKK엧%zQzLP gP%zDz+wOi0m5zf] Z hhݯjE-p&JсY,f(;.γ_O1Gc:j:MLK萴UZWAݦn@Gӗ2~GV,=/&{PnhkhZƺZY/B˹h "/3jGL#iJ%>(xG7_^F{Oc+ H.9i7q} +q!U|1 .fIJuUEm4Kωh 3C9!tAk4 `)Yw^ j܋?yl6M@Gf5\VV%fK̋Ŀ}OhYTV8sO^sltJsajSNt3lMʒ&򣻥2Qku2rme]h)3$~0W$h_ͨӋlRKgwN}ż'rY-/&'ٖ^)59ܛ̋*nCNBPh;3Ffy B3UHyoYR.́~i{b7sH GT={O`ss7 O]EX/"s1[S1; Ĕa ◻'uiK\U++lR[OOi]t^]O9_S)b#7;#3iˮ`SXkٍ0P {󳠞HE~:37:%ZI*7NWU~W玓_`H+.sj&\"\Xϳ9"j/,xć.O;YrO F9 DWt^Hz@; O_z+#-)#4`-_d] IQ(hunujO LzS"z#차 <e,hV|Nֵٜ:XMord/3XSL ҉(%trP[3;jFd,h+=~"q>aM#O4Hq,6cEz(Bncgu6=˝e'^Mn(X-n1ڡͺeo%&oUh9 6sM~#4qTPuV*e>OzųzYXΔ"Sns&<6ݔuТm 2;E-2ULpZUw۾|iz۝uiq6; -^ gn-Mڟ(Qm;ɪ7%={M9ot1+8sZ3 ):8]It.0x3mr_LP/|dm6DVve;oL,tF70tb qqpjبzb?sWוyg"N]Ċ 6lFƏoAQ@kȇ7:W،I"bo[%.30lQ9w Y;7Â,ZJ 9}Z7j…Xe+{&Ɓ@,}_‰ff)F.yDAa[ѯ\// `ѢЎ gӲObC*6q.,$ř2$rPR ajRFVćCnxqV}6&% LvA9P/\X7J!*@"EkK?cI8.*(~/U%4A&h$LU!gmd)o*F5xޒ!49eAlHMK:kΙuY#jkgď_|T< C:#7eaA` YeZ9QnD7sjafQ/D'ja0ݩi̕% !`luѕOֈfcЯj.ggձ^3Et;Mwvr#^7ҧr' /r~] cj rA{7zә-i^e5tl"<_7 L&5nQ)c:GS`SPL1A_?Y30p_o8K\%/d&H3L7xu D  ٸɠ>e lKTDHNqB~r .8R Uiif٫Na )^lٵ QnQ`5uBm}EKtgNR֌@@V. >9ĖU|qp9\A_)U9BA4 &4*I ݄ 8:AF-c#eR{,'::NicfɒPzAa&_oq:xb;K s 芒ݳm {߫Y*!UeSۼ }NPk],jrk8o#{`*G p! "v#8ٚ&2^ .T,bC;m!hS6A6BrԄ1 `W 9LjX?Mw ~JVTb 14߅5d  8ٛ.SY}xHPQ, sG%%'v9w͂TY;+ٯY1+Wi 6 h`H{>gLHKBT̷Ng=m8ˠۢ{eZU;mrIO0$Nn.l;4#2 wr/i[yC2 HmA/if2UZ(`%9n6G9Ve%MngpQp7yh 3y\ C 1 'R)xR׍g! /G%X|&EpXg%uCW'2oFn,vfË,c}Y?21=NG-`U/7%S9~J8U85;7' ͏7H=or2cPab>#l!R 2%WT 1!K}@,cZ%eTPBG=ġ^Mc*Fe:X r62]!~ΩxBѡcRtlޑ["lo>YE'(RPB3KA,/|2`;oR4Qe*^9 (8v+ ~ @2rUXn7z}3jxiY`4JY'{x&MɱsɴZu>G\`΅(ⱱ<@P׶+ShO&vOb)YڳYvTS5& L-4 faH< 6z~EAC^K3TV{lXn+qY儭Tϫų$;ҳRždPXF6R:$ɍ`f-sl}"aP3?kD-%{x]%(f"͕4k7K掄):9?Aj9ٺS hwCLƃHpGD1XCm/:RH@3w0ځtKd D]oaֿ6)poZ|DFzX=5-<ї\:ɐp+n,c8S%3E#0E5|:S~\s 6 tz5>F ${œqd"-Ԟi8u1 %,<FKe6Bkkz>]I<(v%A{kW؟#0Z̞Ԝ@Xkyߊ(CmW:Fr̸3s8ֳ os%;t %T86H"2rD宜Xψ o{ڄ:?"F| ~A a+7NzŮ\k.Yp6s{7_A]A,G@A;;_)~n(;\m?_ԠAڇu/!gp t?SA;1N+_LmɳTaF$gU~X(&AN|d, Bu>DWS#.3,{V/dB3k1O_-]>Ƹ'uDz'*(7O\3= ޚꫵs@&;ka@3Ph`ΜNOk+' Kk_DAEB颀zRk 60*--ToD^0勇ҫ|&vH!s'2ڙv6$QcQjoj֪Uj 5ZfKjSb<#=ccEQs>Ku륪҂ӾJxĥEZ6drc},TwyIc$J*6ϑ .AqAL d4Uwޫ]{3kkZ]2KYaFҙ乱j_"N/-@,YKa`>u@ baAE%Q AVQVp͉է5B INf䲜滎 7z-f|Ep(7 h ٕb=Iǵݯ I<,zjֽ||nP8kx|:ђȣud[$w]Tɥ-7{W=`܂~xab|s&}jey?njsk~MQq>$m/7$4?n' ilo*Ԓ2v0~a 0.QgiƼAӇ[;ɏ>r&u3<4Z+5hlC7vI YQV'\'BS[Yt@H~t]ؐT##r hW@t*RvShE b<`SN}GEdх6-#-{<)wQH|Vpn~" (1ډmKYNT "!|&tE8=|]֌~B?5  wdfִ$||4r) ש_߂ypBe \^ OmJs!X#f0\YzcIg dTw{w` d8 YG ^Q6J1s?:kṃوr(MiDI?; d/ endstream endobj 55 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-39 -250 1036 750] /FontName/WCPQOA+CMR9 /ItalicAngle 0 /StemV 74 /FontFile 54 0 R /Flags 4 >> endobj 54 0 obj << /Filter[/FlateDecode] /Length1 712 /Length2 7627 /Length3 533 /Length 8181 >> stream xUT\Ѷqww www+,PPp.!`wܜs}n˞_ߚ{D%gj Rvu(h|ܼLL  +/H'&ą00 n`{/?Dr  @ u9# ؂m { ?T]\"Jz`Ki؂0x4]wseoDT+g0n^ -K ABق]Um\! R`OeVex;߶PA[GK/_K\QϘr-Rrqt\l<~TLs~Rc:E- 2ҒaC}ǟ5rU7ӌSkƬApSPJC@8dك9]eб ˬs+ׁI{zhg8?+/K;Я[v9&ݞu9j5s>25t!} &֘`VDl$HP0V>U&j^uJD'k*g0]s?;zP %ОKt p*V~K۔edW[?paԀg͘SM6'S37JmB ^zGnԯ9%1[G:ܫ)?oũm_`rZa>Rn>$\oBsOUXߑIr:#PS RuBu'zBXKSII~5E +] TON u>Fw:JclqJG_w|C=Wr80O׃G7-,$ K.3$f잤e8JQށ~dΒ/4!dro7G❍9gZ-WjZP wbF>w?ËGk6$Wtdr}g`=⾓~FZ[ #3A~u>G/o8:wf҆,>#4Q~A`0xi\Vmorr:7F1DS1u{oT<&fseyrDt+' MAGW2qzk](͏pmlf]Bis^KWQTB҄*um <??Eho/xR/ ,_~+ȅAWI#^0Kɛ}:.ۧXfti)`if1p^0GުB*Jų~?5' Ala5}a rq3j}ڷd*|OaH+V#7RyALCܻ]u%e{w!gTz79,^er@*~2=lwϾN-r0Ƭĵv(9Q9 'PuA6&Ǐ_pQ%)f'a2n-Rɋ#8(ë=0< [7kZqãL֭ ϕ\EGcsvvPnR$òqWyX302\\gpF%.Rc<'b^c&'xA~-ʄ?JyR+.OoD5]IFwvHGꆼ(J}fٓLH$fadW?4 }ҌT> '~K+; Uۏ$1GB%c͔rddE`A9dyڏzӛX}!4Ho׃.8c5qЏ>)f }Nm%hg>;&sIn]ؔxg~N%)’^ YLZ!M(`|>*>@tS%j#ƔO^,8y$:sCdAR p}7o6T2K5"^ޘx\_^aŃȸEWm{5{nuÃ{켷(3NOB JK HuzU{4Cն)wKU(p. iC71q\l$2BN$O8QEqL wύV%Q݌Ossh9 aVL!3֦yt"{ B+OGeNg%g66Xq7T.m 5X0]eS[Ûӭ-@ "Zf]j.OK9tWտAx3"O8Bhrra笐CE yCzctw_nkfXwZ]Q{*@f@Ozt_ 2u9sKF'9,$9{?(:d5=~Oh;rgVξs~DsNOrk7,uw)T;dRlr5L?&4fr @DUKz6s1~WNrWpB5 @䖹9eO}ީ-tNt\>L7:,5JUWum123`$_FL:{cCĝ UV"ߟ,ɝ5}lѱ,WlAqX.~*7Ad;ZDY έXwp+8nUޖPnKs60q*°ƛKeTB5yP} >bq~x'-8u=[T{5&t84=~) D4Wv:xb,ͻ[0#t ӵ1%Kᆬ'w[{lyJ_bIЅJ;^4?D0øړ-I07 hraW+cOxzcL{tju#F7'?Mre}$]Y ?jt.%nzJڙW:G®W)1l[$ 珈@Ӕey]4άHXMf?Tw2/4(>-;s}2j-J"uW,kHe9ـ%S!TENMOΖ";K @^,^D6vL>@eq SNнL*q,ؠJ#h+Q΀Qp>Lc n4ߣ|jsZ1(dJwbіq@RTrѨg uyp/5(h 'Ca;S<ʔ]t$)WSaM"M}Rj_oGA#b"QZq_2E=Q 1g.+Uãh~[+`z(<@@Ňc{Ȧ4N ׻Ηl6G⓾Io .˧ӏ3aA]\qBo>ȼ euʇbWyn~*(m3D@oS Jz;*=FX6k%AlrÑ13)j{SH{BDpij&ݥh4(1z݌Ff$9ZƎinS u U'XOk݆;(e-J_$>zHF?G<}3W?V=x>nwN}.̔Q/ȐR, F}lCOΤbGNjN$#w!'YJVe cՋ@jV6b4c:ZTz:TM~IiNoD?Np-v9ݔF͇% w3 FtQ%{iBmW4ؑU&he3=v4iǜo9%(hZ~FK8~`2%79_sR=5۶zepl,ڱKØL˙'nlZ_] Leme\ yRK%s vCM=Cpds ŋTS Ŵx@Stq/E ,?Ն³%HV|_a dw~T9}|?)9IZt ̜DaѴm"[_·(ݠ}pDFO~nrc+ƎNJ9_18/U׺D,FI7VˊKo?(=_jH*){JF&wv wh`"25 V1.QpM _:伿 |3Z]ޣ:O)]kFzOE"NDVm@, L0%_x6NqxSBPqʷa&:'ߍ07u@P;Oјm, e=&vηRӻ^Qvipr+t@[@&4jˮJHO{#8SnC o7?agx6oSm>6f§>0Gӧ@L%q":ep2Ы}osW>{c蔊U[Kh,RW~,923?7ZY#}9U.-N'Dj#06ۄ?O 3 Kq{R 1/fl?RdER0-P{h|^PVR ؔ )Nq0_Z95(^Gb~:Wc  j-tJ[!%`*_!Eolȣ5ϼ^pUK,)X6.Y2aU!, tW,=AdM8§Ż> ]KfnфKvsgfG)iQ $٩ԥ [6+6nH/>Ѕ0/R}N1ah_wje`>fB*QGu|˒4nJ^B' AM>[F.?9V< Mkb.A?hO1@كGX3  wCLÑ5yPtČS.aM?";fϩ|nu>m)4NfAS$;py~h\0Cdlf#kcrt}3nb]D#s3E`t%L7)J՚rqrÞ5_Ek^hxmak/ߧV!YPT"ttSiP"[ьVm- 069 # endstream endobj 58 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-27 -250 1122 750] /FontName/LAEAEM+CMR7 /ItalicAngle 0 /StemV 79 /FontFile 57 0 R /Flags 4 >> endobj 57 0 obj << /Filter[/FlateDecode] /Length1 712 /Length2 3878 /Length3 533 /Length 4429 >> stream xgXۚMzBAz3TC/")B4EzG" 4)JP^{Ιk?sͿ|<߳%%S4@qć?eb¬.xK& muW,iQq+-m xZBzۺ`hWXBkx|k-ĨI}{~#+R;EuWtz%vZٙ:anM)dXcŝ?qZݺN^/.jHóC;D-iCy?$K8GřTe,'yLq]zqNQv\wɜcG8u3$[3< ,4 /$rHgݍ bkj}>Q}Cħ`s3i)ٞma5[-(AiQԎDb%z)b#ʂFgEܘŒYec@PŸLoºTqiך@k,ف{..ڱv 'vHgotkF[ >Keyyn8҂*O*M_'6=dz Y]Nô/.[ԏz=@oAl80j|L:Uf_XG f^h܂e Ʋ{=I`Z-m}wL~?oxсT -,.6:Ct2IoŒ,`ުp2&Jg"<+і-)[}cYПRp lZABb%v5pt"f~e ?eq3>7Fjʒ/m?d՝ 0 >Use{<&qm]㼦U,.#xrMpbɅ֩BǼq?_rwIEW3?8+IБFuN6/S%>pոexlOrQNuu,g㖟)qpmqGp#J=A._-0h[[ E%vTѬZ+>`s_Ǜj^/{MM)MCkۺOÎ%yYn87RF9[[y@cDI|.; 75C@׽2iMU᏷(s#gfv{o4d >)r95hr9k@z9E=wNiTV.UOFj64Cy /JTq:73 #*:L 8Jrt d~GE1ďߟk`Įh_!df 7{H>֟`Km/Ppu Buzu<2vfnoև}薉rj=Z~L-9Egl֟Π NA}vRs[!P}/$MDxJ.v&gh#\^\T o㴭I"08Bɂ:g`9'M&f|)i?L.-ټ(&΁Plxb W=ok齬ʊzaƠKU6p10EP#[ʟzV@Rp٧ZcsW3A-6VBRXN,M h`[K6BO/T25W~"F/AKX!4[>lEů>]CGPd .t E{Ym *nBV;ln./cAxȗ߳/\RCKk'm P& } 7o#-4hb% DWd9)Z|pGՉ'ObG{s(H ?"JMDS c9~Ӝv9YɅ+lԍVpP) QJ0^݀gOlhH9yTk.MYǃ`fthȳRI44CO3ˎ0j8V,~MV,l;l•yw #wW'Wb͈K[&i`%@ ?/)1]|mt c]?۶${Θ~x\nY5W rj5zD f/6E֖vwt:;T g ɳ[t7Hlr쟠 $o]J{*=;+*(}mN\] kerDH]tA7ct{GBp6 \\WL#f?8sgԘ fsTfXRzǕz~ikQX$X-yJ`=,aB{bpYxJ\M2+;QHO·.цզ FDvEFrd"`ʷ]_劓 M˨kd+E䬖N˙lgX6|yyVuE[~spIBw2ol_-)=NaOoEߠZ>g7NUrڞz Ys1ld/5):r{v&d#X(=pՠ|^ܯYV^)}khn}Krljbsiw•o9Xt+OR'%2[NeyN&{U yBWoOZHMԹʺhyofko~0?2Rr;IՆs6g%bsT.tYr lFs[(;'3B%GYOH K6*ZɊ_h2/*t)9b|{U\~6 WqGU~+G{Ly=+:MjO++*`}ʛX9}]M=&4}a2ZUp9뒩B e9޼ , Jmx-ہk o)t'\*bl[TF|[q+RKKG _)4'2eLІ!< =yz }COE, ;2&eɏK 0S&%5)ZKҵ.}KjbM6dlc3j Pe%|$^PMzʦmm;pg˳^P.w*? u/`9_x!Mv,#d絝ܾvG;^О*^a swۍ=rrL>|"E$NIƴ endstream endobj 65 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-75 -250 1278 750] /FontName/FDKLFJ+CMBXSL10 /ItalicAngle -9.46 /StemV 120 /FontFile 64 0 R /Flags 68 >> endobj 64 0 obj << /Filter[/FlateDecode] /Length1 726 /Length2 12098 /Length3 533 /Length 12654 >> stream xctc۶m۶m'ضm۶a'N{9w̚o/W{?V9*\JC$*/*DCN.lnj p3v5!bf&v$baac!'8z9[[ZQRCI$lolmj@$ojen7*ՋHΎHS\T]̝``̬M]L-`%` ?C.rR48y[0*V3nvv Ho c{k;\͝f.4v56v3'f`ELԊ~sۼB0JI+dlhN?'f69[{21011oLfDff\""OzNv"zG;X;K311qrӶ%ssOsS\tk`C:|'$3G!ep~T2[yν\?GR%$ў !9pi+P a9{ۨ<@ -Y3ž 4A+Xn}χuFw`=|:H2H>`ő1,=!/:Apd1f ~Ex nBSN~F0)uH7rRR-c~[W>iMX1V1*e~[{;ON_iʉ?˕#:0f_~m0`m @POZ1D\t1< $1[N,|& kxz4RFhċ&N`l8֨GΎnx soG/){ߍИj^n$l+6=ǩաN4UfdNMM2l]pӵ?ks-r7<\zp=P>E=OBy.y7>12x33#|>jxUR( , +y!qhwloHPN巈2?PA%mTgT1xQgL5Ƈ6S:| QHږuJwC̤6~ $)qmA길x!˞V>B˻#n*]Nީ+GO)ͥt `5ftJ;̓|OcH$@JSj:h^x^8xczu,OqSe1_C*OgH8SbGk.ЫȎu̱Pi`?/`wdwA *Ov_ $I) ;OVyCEAZ4dBHb*23zR@Bk-0SD⛃$#-aɲ ᥋&e$1[ԙL#38NQ_z~/#P}Ji"pNpe`F#KHCۊAz. OPUfi-pO. ~iDvU_ IdmևF2iRɒs. l-ߦbzix2ΛO?W't/f_!H<+Sq1Q}~HKYnjaFUl!PC_ٞ"Ѧ2'^T |֨BEk*07e8)xv"UUiH4O{R, ~Pq~'L2K_w`z9d +jбDFǚG/,tg3/o2O e~U/CHݸ6R:H߀K!) 8]P j?fr7V:RBI|U0,-^#:^>oT3dmS%#U"%[P'*4W6RB\h!QJz[K7 $~W9dV+OɂNeSbׁ˥BS3g\qq枨IY NRxdx8inTWr*r#_Qlw>cZˈ\ a81 :tp"$$cSi.97ӆ3\4GL*X{cӰ <C׋%uflG~b3c1ڎ}9p-yάȥUS]I]A[H\3ZOM  ^b_s-w7r yQI˯b!QA;dp D̊8+Qu50V݊'Um9a$Y|Y&, ޽RoҌ]Y8 LqӅeFV^y˳BJGA6;?1CLQ41܅]o`-iD*5.[{(rZ5;@ERPW Cܸu 30=\[(bVDMNKy\4L 5K&z@14=DYy}l \iR1DcyCXQ,1Cr-ߒ,a WKUOB2\CSӛՠ]MN3M;c8.̦*s{H=>OXE}hD{)u`wßB% E튓ӱ;ѕrUJcGjnȰim\v.tD6aEȎi;_%TExO ytT ,"" .zWq^nk: [3TMH 4l}|wO3,N*+@q7o7YvLrz\ƘyE|ȿѝf xո<%^=[Q6Lkv~iA[jAz1*Sآ{sTEFo _=sx~^SC.cSЀ!e]H?ӳ2-s@)cfZxKv-bN&=PIv\HRr*>Nxc1?>3AT:A Hzp~k+VIDa%6 }9(Lt~lI6(t;҃>HTÂߴR-O7SKQ#S1cx581lE{ @fy# ޘf ZM#ܐzY!hŒ}<}VvMts&CY~y* w-2~$ _ \e((KTqS?H եⓩ vٕte]swBXذOPBy:y/T~b4U?!6 =L6]E4+ ^ ]ͯ-wd˽BIXho͔= Ԃe;PC7 3V1;'ablO8N{yY1-?t`rCpL),yto%<$ȁ_OX,Z$i6=im>4&q:v+CCOnYy"wC$ŪHg^_NzĦʧeJq[QD/mvF(VOy ̼L^T1+A6$#i LGgudzЬkylJTxj'> z= ۏ5whveL1NG74<}Hͽ.h%OR<# yXal)9E:g` 2_/3sT}t sDaS+(8O8`DdVo@+w Z ^Gs|1z{1|#~2?j>dڅHN YXZ3) e@<0!=fd2*'~""(+CN=I:*^_vW ȻsؤV)g4̾ĕ#H_E\#%(YJ(arӔ=jy4Pףp9?{P:Q5o#Ya_IsP 32phqGZML,Eh>AڌhO9ᆍו#܆P,.a{/ܫ [_=[IVWu8[T9];n:2+l+^5TL}t'F\{}y<߾6SG i+3<4_JD֟>{xj=|1A+{f>9( h=؋gҊKGNfR²,Mjjrg]Ŭ.J0/u]}0#bl '`C#mԄ(,}90㭢`˔Rю%|Mff)s}<|V`=k7Z nQlo8(OmYc8ȶ5UMx*9k[yQ($ZaI =;bGLRB'kn üOf2i-|8ֳ}U!<#yYUaVP K!(|[EpyMv٘Dy\v3Vہ6dPaRE]wb2P8k{$}L9w!yeyrCG#9 [ڗo.M5MyhkLrn81ǩ\vGUiDDàQ/>da~{1AQ6)jm ݔ9 ܆6x\7{@ b+'8O&pb-#B;;W|on?33x+3N>ٹlSN|ٵ oj+/xRSSiNĈ`{zLpIЗi"_uNڝځXo{J4QQ ҇xeJXךaR}+ yPmts9Ɩtln h$~Xan/:Y{a(Mfg3u}^5@`X9.FS\+WBӱ\A)2#ڣS Ts ¶ckO79H(lv/|-IDxZlW׎nI }Hz&~jUФ "dVVDZ^N>Af>UD &v^FBک8ˋ,+"9vUՂ\BƯr_x+g^B*! %Pz\$MrIS<OXcN_ 9' 3-IP0;O-3gz״Z4S@4 ^o"KW]O̒2UDT!-hԃB[S n-6(MoN9Yśy*&^˿t!;[l:n=nu'Z͚1"yAZ2ٟZ:01֚)9z0n'p\Zk"DK)-p2vp=B7QԟRѻ7:JX&g.\8 (t7zTL-}o{dZXd wJ߉!)_+Z%}ZѫfbkUqptqJt4`4|_s=Z`ji\-"G%<CDPjj%+Dy6m'ذЯT6=$QWkQ+.=ຕ!nF%yx?_S.!475(.YŁ`!} O-r lU1ſPXolV^ X4ps'A*a6JVB=~a -D~6.8ihᗚUDŽ uRG}Bf㻃 aYrz6J`n~ıD5EO% kUiniMPϦQ6j\PD?D&!YUmIB !ڌ-b3z"*'XcoiXB> U&6 ~ݿLUЀj͓~TE`؞yUsY?%-r9'Cö1Ix VnՃߚvS P{"z N$" l@OdҎ, AES6A>(ߋHKl"+ Eʔ&(+0&@A^ɽ.%9ge"+I1PT: !Mbc\nzH;QP6|lc% E݉f~TlQ6-V$8$HI'Q8Zg:^y(ފ9G!kp90+~dfP?䣱?^"U8:PjNq v `{E[22G@/d]7vh gٖE%Z*ֺ`&c4l~Cm88/-lF+=T:n.\JDjLd*4H# 4QDPCBN-ץ 5HRXYaA thk֔ ;j}fAdB{-5lZ"'ylIme0lqh^O;O^T9+v>1+QvP/D4YO2݂[=2(C4>6^Nf ?Sc&i2wr('ޣPhuɳb3αx ?򔲋/4kEG{Čo ?oxvlFyb˕bG#kTT U)]{[ }G4(>>%ܠ?+4Sdrf棾q|f3~y|7V7/<1[&q*=Urؙ;Wͨm1 7}'$mdbm5"CpD "׷%y+AE{sܑpN]l4J3GJ)D{6r5ݫ_/)MeSUH݈wt!},*M/w9|79W <}qvhePyIB>m2^=Z'⫒խ.N{Kq3GGMT&3g?AYDjz̝*Vŕo*9;¡EX}N4C1NvŎ7Ƣ}Ζ8̱Ip֞;\KG} 蠥C3RGP*VJYjC[XLP]Y2l@|ږcAcT GaRn Eal^~1*DH6H}m6k9^fJE>8BbE`Zے횏pE="* qo4r{Pd,KgpqrwIVD;ReA*D04u|$䏾Ӹ+/W5| faLf`kgʶ1!JEF{婳~ehǍ_~lzݲ7 `SwH&/Ymޅ+5U "{%SƢ$sh~ټaJ#;9IOgNXKC E)ťRzy% |D kT3&u#Kd8{BBgOWu_xAO :(L;W<{BN45:K @ ^H/Z0}{첳tg5K y7w$Ƀ?B=w7'UG!~r_c4kXtQ 4zujnYڽ1PIZc*H?EB E&৅dkŊX}jؽl$VF\Aǻ7@#Eb, Ӄ^xǔ3ж{'t+K F1X`L맇}xMRpku/XH Ҷ>OUb sZ',Wy]+q@d%264<[2ψ|T:/65=c! ƒBU[W[Ho܉URc2SqlCE`7Wxj9G3ME7ྫ:%B|Y*P#D͚YݶLVuVût +[[N{=P(,#Ȣ9 .NGP4[>#ғ{~(i[5Vj-`GzuD MeM(:8G$+|/9|q(ք_p$JoRRY6,>RM+N@Z)jmS޻J19S<+G#hh4$[S.6pBᨲzb+l$W||$0FG:tqd#7S «mE󖡄^!\BXF F?[R̜xyrjV_S0cP-sR.ǠZV sj8laHfC#C-ΐ1F_ط3?DfYN@-9A@D(('Ki/W.p7MrIRls.زE{J?|#x:?nGF9 [yw)p 2ҡ5*#WU*DhZ@wKB+#F!rlNPuf_1-4̧!Q+6~a @=*}lIJv^ێQO_&]N "3%8/vW#l+zŊdAӃӨG]vKGo3Y/r߲LOjFd ZM00ئ'ԬW h!|I{Q4NA+ ذO)K,ɖ_42z|Hݤ*Lvp VQ7|=_AVjDl 1͡{,Qc~ЧUbx,X=VMы9O:weupd{!exnHŷ󏕶w9ۇ!N3Uuű e;>~2{[Cp:M$$ʹ>MOl5Mc 溒p}zH%t>mX/G;*IuIiz@ۈ z'z"Z`@BFv3eIqI$xیA7eRVY1:o EZ|$1c <:4/Ta{ȃB,"Fn DR&•A1S~jZ~u]2j,35] ~3 q4C{B>BdT)WhPaV;tJOP<ܖ]7(&cO> endobj 75 0 obj << /Filter[/FlateDecode] /Length1 722 /Length2 9753 /Length3 533 /Length 10313 >> stream xUTѶqwװ \6l5wwwHp ]w9}n]?׷fCQ3ۛ\ؘ l6fV$** ' N`cZYllHHT {O' V.- dfbP4q 13@@Of@ g4gFBbc\@K?,<]trIKino-XpiW0X؂᰷upu:́Nvn "j]90L 01srK9K<* 3+ Ohg O %-qM xꪘ\4<f@=VfVVƿ? m7);3{s%@ J\Û`ccepFM;+PNO hϏ4CA12-~1\<' IBݗ˧ߖʆ%xlfwRdDeq _yBJg|K͹P^@ywY1 z%Wj!|pjkNA]7 ̚/60y1I ݎ+jE\hұN<-vNvֶ!N(E#îGi-/)Lj.yiFیF,R߱l;KFT݁9doH)u?~@Q3=~@ F_rAf#-}DIHbH\*#]{)> KznQSToR>+-L~PFd(9n y6yZ[u+>xyqw1Z:RPqúK.|8Hꠉ-6B1u$𚒜=8; 9ۄ> XL~̞,0DObz=ӢDa&i%s2mP0Ywc%8,͢ 8?hg_!v}1zϭ0g\àCԕOv7.m9ba/W4BC" ڜ@c3jGׇjFCQuƴ<*oк5xzyz(kKHG,-|& s&gġo+9V_-x.}+p`5z*o F5#Ix7ߕ';g9_ 6ev[ww1;JLT6P ~٘W|+l|ZЇ Kl@u-7Ԭ\Q')`y"gt.\P3,#eZ׬AGy/m}dT?ؤMӁ'[JD3R胠zH{ҙD 5qQ6]zgWQ~[&-)oRR{N[YTl`q^cGWIqqi k<U;~4hx:Pk~Ү>0ɋ"1 W9rsS hht ,eX m@av{u( [b!8 9@{6;xE&G)*H vbcHR珹}BQ\8j)^;:h~~\aD#vn`W :b;[H$EWfTfO5ic$7"}8gS-gJIE0,#vhpz $r|I,zHLJ+&Hϊysn+r u-y׍n{ xnK$%T[ph<@Q#K6SveQMse>lC$c䎷 0QZn΅p?ë"9ى)jK~ —9(JJ;]C*ُlBl]fjZ ;D}c.ޮF uI}OHKyTiԦ$ۜՅ^$O^~yF16lMԂW"Kyߐ 8; Kvr0%p?DH:xW|!1(r||~tg>xfc%pW&8 ܋7N"7ꇎP" DRU ;c~f\mܦi}dٵ]aۮ KV缴NxS+@Idci.º%dƂ6џ = $^J8B}SBdC:xLH0q->VG9-F@2":$^zCB5*6|!_q֜1V_5?.\֭X,*` 3U+0~oKׂ7sn[~ߐT۩Mu;pfȁLepK|uNߨ+hU\ ? vdn+s˦Tu/gq/fz Pu&-Gl VuEQJ~ݢul ntJ^gfko^Z/;Hh+,IL1R*bȒEAdNyVt3LbRաϦWr\0"y* D韷uԯ68ual1sky9+ \c81;+i?<{YS`؏H.s,#o%Iy7^X{B /:GW掻T>},I윏G5R޹e=V]V^pLJ]>=68qץcWW7A'|OP |qK-כ˨#jPOP$)>?аl oKշA\eFlRE<$Zح <{a=u%H@ῖ?1hk!<C'$g<_DSr %9.u]kJswV7#ZÓ6HIT:?h]ij5ԎuL&7N?UgYIwܕs7F#|qHXj N(C>l;F|iH؊41O+|1aCI0 Ԙ.LF%|ʜ 9F1݃L̑bbUț9O4(ۊuE3~YMrYMt}f/8&t@P/J:,ւeګGEaZr2{n..ٱoHSi(t_۔J3],?%!D=?L29vE?q]ݎe&91w!fFW踘U.e 5뙧+D,`tr˖[-2lzsq,Id*1frƎOdԌI]sgl2WuۍiP@@^J= fiRm ;oOPg׃^=Ϭ%NJǝ"Ow+WnZK|vO(Jb8^hҼ" 9+~"r5/3M×ݜU 20^;Sw 9-8|nN6ŊP`~_2W2"R}>7UL]OVYَ;|&VS)ؼެ9)(mwI<lk)*>6G닆B,\`M`VpirsOkˎseC`S" ~g Ċwg 2ڃH?GÀC[6aU?uk;<XZ sm,ZIa {))Cmn2੃)lU K[=4+F+ .Cs~d&Bd4IezΑ#` с1b |"Z\pma4ray>݀z0* DeG3aϧԳV?nW&rAJ ^p@s}&B"aXJ\K=:V9 Sndm QSE@9UK}"p)] u5|d)Bψ$݈G=͠[*]&ZRUDPJ F5aoU|z6/sa2Sg|e[~DMAGI^A*ݞ폃JkE5A ɹd )=43w2$LexZY^q|2vR:^=s.mńCX-z)ۥSaoo~ G2IJ? r}gWinSXMYy!;]ݺBted5_9*Px6 FÄO;}4zDE]WIg:趰FgUn;6_ :yDr>AhDK*Vُ43ch1&cU~5ѽ/2m"KՌ/-4Np秕h0`1ZR %5] I!fZ0(o\F F@ UT'羗]o@!G'tp1:ʃGHuq{MgʶQߤAjd=[rX;YQ1¶"gLb?q{T<"9"8?Pܕ#6$^ߢ-myIZ7F~iMyz xmj,Y+o1rނcɨip>~%PY ~nITžvȼa_ ,؂[:Rmmmt>ds H7Fa Yi[IRǶnX-k JB^!ӝ 7:E:8"C'dW5#B|qlNH$5ykI/+h1^6j:Wu- h.Ғ@jJWG$h7'P-DzuLЅaS,Xj4nOdG9 dTu&}N!SU? F\YzFg34Y<_ˡ7QiI\dqSM>BUr' Fqȯ .MM @ŀV(OclqOF; k@&vޮU].8,ZK'?a%#SE+B]w=VA'ʆ;hu\fUCh3.4~k*؊R"K9rÔ!a܅Jzʓȷ w‰>??Bߗ(X,a)=`ϖ1 =@$BWE'.YF'O;@m9Hl!R&$=x8)C8K{ȾE T9P֟ɰ{{nCo%[Vzз|HabF\fj`(Wiq< X{jfS[rw/D$#%ysDQ拉r H V$Q$*2sc/3{|dg]_H|t1 +4;I!*FPuРG"&)ydt.?h{4|7 OWAf{eR,dIh+"eQmkY/X VK9_,#țCF[6;anyhkh>i*XMU Nn.9پ6=M@M-Xߏ?ӘQ+/ dGFA5&=~?wc< Azr'jvf W8A\H[*٣0KLE5o\;]O:xS:үp_&[(|'1icJN^I 56f^VlJ5DOCaXbou 9UhjǍQOjFI&J4d6iM8ެ~dT"( -l;N4]< _0HaKk5JwYvO@4K`DEki3 S5d&B)P@Yc xkH&mb $w>FB`a۬~XNٟZ jp?##Jx %tR1%ꙹX=(C7e3BmZQϩJ "YLyFm,[UWK,[,5ۖ(VY3&*]3Z0w\T{^aڇ]=S- X4%H>ՂN8{W/~=z S7o ثlIx:#U]+e1vbneނ#P#otyveqc9A |J &~64k%m +;)Z^g Kɋzb#v׊y;9oՉdiICf@1\5؞$ZF2C :?ЄduDSsh@uI ->6fT|J%/81Ep"R1S%}kP` qffE4sisפH]|}G.RkR^.ao>991QBc=~ ՘]HHN ՅIsE|ZP2F!JC.Q^7`7i3 ߝkY %3>rlJXq8MNi"6*HFB3Ok77h:aXZz΍[CNvHQ+d;9ræ~ϸoOZ ;ZywسЩa=UyPMv[KɍxFcbi@^mm!xH%uUΏAm,5?1H3‡իLRhu3"kH&fK(sy|O h)J5/:wȌ &*E H\6W uyG!f9`7Y Z&#$n/$!9LBvjhwfoûڋp2&K iډ}2xFnWVBhJ=ak˸HsYK~y)êJ}'cU"p@?ga3όɟ89X f"`4^~w1[P^е0пP_G\4^?RxDRkM 12HG'8}q0^Wb&jd~H#V"U=h=͟D#1py> endobj 110 0 obj << /Filter[/FlateDecode] /Length1 1508 /Length2 10137 /Length3 533 /Length 11048 >> stream xUX_qw Cà3{pw $Aa}I۽gٙ~TU]tS3Z8]YX⊊,k$jjqg+s,||HHqG/g+kW8?YQ0hj 517m^L(Pt:-XY63=?Z[9ג; @/Sz= `DbVrxXͥ@ %S?5el +`tTm)-l*j 1L,qZظ[,MA.Łn6y0k}US{W /G OM+ `lvވ zoLo%,XVn)8f@׿#<f Od 6+yY.6VRxY̮r8ߚ;?!^*> ˟[7m;#qۚz3ToySQCo譋zCo`<,[gVh /d_ctl7yۿiہf 47 ǿM/|p 4\7 ׿m4(7+YyoV^sadg0q37'ioppn@{}^bK/ 4G gY*JlJX%cH{MH{%8BʥΧݎw" }殚~b>ǝih 剧38ݵ̛*[ǥ QN ~1_U]y}?F WǶ|g' N2jlT׳"f6ơH${G /)9yVdy1^ވ$1˗)T*ٓwXun9J+Vڃ'z tW&]aUA&1[O}zm〨MDz!&f"^2bصd j憎qCLSΣ#$D`a C'^*&ݪ|1au66 j/h!X! ;2 @HqHeRKMOIar }>Իݽ@{y)B@';Dނ޿Ѧs@=KUzQQ)Hy \g؜ZJxa/ sCU@Rm4jơ/ّnչP>yp7Pdoh&ȵOA6%k-^NDt=ZXܙz j1;-[E yBT ՟'a|t(QUnl&':WZ4`V]h;6cjp+&\\cލ-u$N[R!bEL5 f&gh?|uYͳdDf%<*~evSrƇ`c F<]p`rˇZdYL&+r"a5_fY3S-wYp09ɑDao8m\&5W8'2" TuQ)}I"Ra%=ݣ"cl^T;!S^}*\_Wgn1rҵJnL Mrk;9 X14G`݊utQ ѽ49?/?"1 |"# >M32|dȫ Fq*d,Sӏa*9 Y6 ˲)5sOaV-t(Dh<@VUd 9}Y;*b~5+|?%Fm2c_ĶB~Bl^ٌwX(9dbʛc*:3mv佮ޕz`Ew=rÃt,UF:c1%1oi7@Ha6lI.+֚.J;s"8xxeo@cW*^Yiei#59dDja,0C9,ia8Tr&(Y-EWWf̐%# XXćU]gύjz:bBFE쥭ȁ]{J=fSr{7UQFW})|` sO?TCL "ݦɶl &, Gc`tOCΟ}Bn|2j`#,꯮ʱk➘q&W7 SL+D}L&&wޱؑ s5|o=e qQ)yF7%BOwUr"2G!ZY4dȞL@|? bk-GJ^" }W+G*!m9xʉIU `TDѻMJŸ4kYOSq{4 떭cR׋b3FʷVA#+־߲580H#Cq9YF1oPd(G12E,B7!Ku+M<3i_NanУ9XRr7E]&{ OswnJ5XMxэSOoT֘3lm3r.3hLaF5$O" G^׮CS>#uh4du槔8>)%1Ŭ<:c!rp І-:iL"mSaHqܤ[28uqUC)˞韟iz+ɲ<{)%lԶ' X-r!EcvO GY'd+1ٴՈA~lh98Ƃp>:;:4Qm+ o0zv,~v -|^V+,s%{wV$ 1 ʹW!"{%zאZ6 20:0;jtqH#OM~ʘmajo;׉49{"U͌26-CV*LqIR&-yX&䭁س>k Ppx._~'>Hn mO<Vq~M"%JEVS)i2/fVAbC뺡BD9$ц,#O IDPF Vٛ3"Ř[KbY15c_nz΂f˂o-rZck!Dst⎊[>7el&a!%!kR'_S/+PwIH;9QЏ_4gZaM)^ڳF߬\U \lW\ab9cADIČuO Adu,O'5 [搜;,n8o"5Ҵ*t HS疭)<ݰ4pUT8u9[!4)7 _7r?HTJ,x.H-SzB og>FMTDG@l,XUUy=DNDZMՂHڶ9g̋PD8YJa/.%brE6+ QcQ/(&CSUWwix;Rꥸ$e!9U{c2 eWzV(/Ű.~_} 䚏_Y*JkxEҵff6eʨb>4[\NZDnqi9?,f򡮄adPeߧr 7X6$\ hw!vZJ[՛bQJZFwwYp^-P0cHBr&KV7W=aeQs{Sc"y eJFAx)S`F#$QT3؝W4:eđNjK-8XجmmִtZ *wfe>gۑ`z_qH8k3!~2~UNmA)J( kE˼[zQ,UcBSu$Kp* l3 ktfZ PV*Ē憸Usvy~ݦ)cDWD]7Hb+3IJE홑'ZzEU[i'+˩z{ʻֻ{I`N6ir"8G.Ew0_91 NŐ x Lؓu> ˶ WN.mN aU|a\vۭ)لglK*ۼ"s0 i?oͣV*IW,)m+dNG5剡}ɱ݆uS>pK V@8лG-a*bkҽFhؿ: 4XT=OWDpΙ21k͎͂ 4DD^2H--R֬ Lf=h_~-9@Jijjߞ6/QRK W0%Rtwo[Ck("9s@fy@̱8*6QzHrgq(U§!ycTPPkd槭j ar#cwB "3Z T|>HIUE8.? (kGAÊ I&>1N}8Ϳ:nrxڤ]kw$HPp{ELLZ텻lrE^N9ę4(F 0 IsBץQL^boy*QmЬ}KP> Ebt[ێ$ oadE +;e8lōbٵZzQ zf'{j͉WPH~V48''g%+NfHӤӍWTe;&>)2BAZG+F J *5\67tߺUyX_ZrnDmP:L` [2h_"=Q;*Z!|DAj HBaHeTKmQDk"Xٰbhߥ#kìKQV򟋼45 K SëSzoG|Hs&5bsUOsE,W <ԁsvA%᪃MAύxaKx:ر{3ު2UpU.emCL @<W@Cqdj~[|b)7&1&R8y.l+G`I$6: -SO=?] DԿeM8(1P VoY7DgRvYvoͪ k Ͽ)| -;/ޘ(- q '+@*X %{% C?OZ ÂtJ/Αɰϣ4%rxG/Sv8ӱ EoRd}q8<n"=v u,[mޣ.R!!⯻XSu+K6)N&AVx'κ ѷL󖯤|sƥ09z9 SXU4[1Gqd6] R쐈QTȠp( ˏNdVU" <Õ& N@)vVP)@ll I&%g[9FW |quó %q]saO`U.Zm8 76A5 [o;-[}AQYC ̼:H2teҋ)Ű͓jB8'!`vwB8P7yvK3NG#@_ $^^Lմ9Fd&&P8EQR?#dk{ϣ׋Y]2\ð۟d.C V!ҬO{MOlrt݆{ŸWW? c#"j}%7aV)+CFKюb-~7s[huĚØ`}WDz|;4|UqK3/JGɶOyd1])J]5*z $^r[__'L͈)Ď`h>RY|5v/ڛzQWo>3HRU<d *.y6Do1@G0HB`Ecwin#+0]ZIр]\V%Q#U#$.CdY_/~*챗)?<9M,u-"=ZI7'l.V(xq>tPNQ~\_i1Gp)VޑuWF̀K`նoF/5 fS\|#s';f4CǓrWjE˨j'*jlP*&bX=\s%`#^ݷEy1da1TWǒuL JwrK Q `AQLľ^"5%&#'/: U: YmÉs?h.{JoRcGqR8!}| Yy~?Yؚ݇-ɪ"34ƮTg)jYNs54y)׻jobuPENF4B h4+N1׹ \f3s5x@MOWaï{mפ0?}u6{'o!;C[pUcQݝU\`Yp@?jב@hr?;$|RTꐫyh❝k*f+CR?k 76G 4m |!7BW~ >F-=U{k6퉷–F#P`ܜJ?ŷn3$NP7GTjNҬu& ].kQΪI|ssԊn<Qd~!<&<3W׉zw>rx$-@[`x'l ?B 4l5nIo":u8hlB}}^1qvps}~(,kQssa1˲hʺu3YiR}bbZ_S(cjFaB~@!{2}k~K^?D?f5'Z(Dq[^jqٴ8NԁJ1vkVWmU2kS׻0¨⨣@R[7w-00[]n!XdӚgJ¿K]:ī( W O~qT(ND ~ jR'la8.nPDܿr~ڶ/)ۤ竲=})j[]˜BXeQPB G* XSS2jIU]@),n2#C15q/ |wS%i \"2 Dp1.IQخE^2[u{ {1KJd[e"}iN_vLLs) 2]/:96T*K5HKPX:JY 3ozrjT&ěWB?O|)w xO#_j5[2ʴd??Ο#[Um\0kFJC=?$e *cMnۣ)yy1IbUAp+73_+~vIYE-#4D qLBZX9eG۝2"kƜtI&n '2w5 8 @:1= +ؾA8Qs$^=]ZldL+(N2`O΅K݆> =lTE^rC}Q+^3.+#OWDם"xߵ&@D0Ĩ5\@~zx۩tH1 @7Oa(.Z=G IYo GJnkqIc5B``Bh5"dZ>L>NWQ"Oe\`:2R\ўQ AB2:=ċo@+A|5\h܏ߐ}ɬSIqyqM %d\w)MU֔Jv ګſ]pb4ڪ|Lue"ɣ.  ңZ]u 0#uK]T的{Q -3Q_kh] MnGpxBw(NP>n2rja-Z~. %7e1uZv9FC>b%Ni]\x.jBQT?4uvu:!!Xz endstream endobj 137 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-301 -250 1164 946] /FontName/JOLAEB+CMBX10 /ItalicAngle 0 /StemV 114 /FontFile 136 0 R /Flags 4 >> endobj 136 0 obj << /Filter[/FlateDecode] /Length1 721 /Length2 12177 /Length3 533 /Length 12736 >> stream xUP%o拻4pk/]wwwwwkqw31{͉swTśO="TIQT΅ */0"N K{;1)/ aj⟋ȉ@ wt4pЈK5u4A.lƖ.L @_8TLMLMXX&.#SsK;aIۙ#m%7S'4&ibog 015C`V?4/\\Fd/A6Saob71uRMom]@6v6,%,=LM,]-f gML+?7|QUYڹy:/8Yzt̗?K7q;c{K;s dJDË#FV ';TY:J8@ 'ƮNNv. <f _UYfcPIE^ȯi}=O[ڞwÈF57;F]פ 7c`d܂zxbk D!'t DCzLF`J6s7ʳKX6{Mx"Ҋq{]irYx_˔FWxw:8"3w# <}'ECȊHU> Oۙ1rgMNMEQݙȝQcYZ,刑AV .54X$I'^2D{sVZ#eUPŻ7BR|g>2DfL M&_&dEbmڙyQK>/OIlOc,'\7+ _"lTT.W;<~mi6@kcDEMPJw۠fd˻٬9Z q8{a{]0iA6Bv5Ć2b:|}ɲ[WN^ĝLH]-}zWgcm.!9H7iW1^AjsJ֒+&[mԢ,~3%1-`H|pB6XJsq&-y:ef'.C ;AIj=jp̄U%['d0A#X @wo Ӏֵd7 0#QzSqPi6BIp s Cw`fF&65^WPɔp"l-B G~z>//1F {p:;sYG}./~4oiqgz+L`yU5qvl6 .p.\*d%vL["|Cy,G9 o~2TنW]]:LskZ󧠌Ml>/Ѿ>%35Kq6/ ]jpY\С2!W`5J+s{!pz۾yr~@jwǺzޤMOZ.\#F>d=/?CGrU5Ga`LuGȋ !e] ж4eCǒLh2ZRS5^!"E!l\+[ϕp)4۰8~`h0HF^˯߾rM'3G<\O<^`pF4%^d=pXA}·6{ʜᗕ= riShyY;AеF6>(ÎLVy; (ddn̳ܯox[%6oGq*ȧ.{(v0 ˫f;HQ4kک$Ghj}B΅.? u'>/7H(exc=+xpuijʧ/UR_GO\nYeacn>-G+PDeQN2I'=1 #%ql DTh[S<-UE4iT 2#R,k16]>"|Ɨ!s4`hp/LI3 \MWQ esWOv\E'[Oł32i .`d<~fVrh;md%3T4tߊiU;a*2y]0k2E(ghUBwSwNqgtMva}Y͔:_ea%Nw Y`Usq$ݯތ92UuTR? zq,>VZ\ۭo3y\l#' @mWB<^f]Œj-pS!nTD"[uo&{h鈟# 6v$#D 7R˺76GueDYq.lVnhf?MW|O|U2cy6p:9]=^i\`[a~81hMm۰Ӭۃh@q0WWX(w4oq{}x=Z"A7ȕT3UU9nEjdrPHHޡX`B` l/U%~JI[J$Y;Lbbrm7s>\g_|]GuA}3qu|AL֏Z"mFc 1x~Ub?(!gPVc|}ۘK5UpWnM_>i1 LV +R4ü(@#'.2R~ϐ1ŖI5EIp&mK*#ц1"Hf@A#yYcĻV7 |Mͧfχgh"vC ZfY~GJ8;P%c:KpK0D14 w|Z}YȋNpex"o.5ϮxzřGnmHӘfEHi*hE"ș1 DE1_ٶѺNq e}atfm/?0躋 ZH0;K=}F;o$@ai BI-/ZѨHܘ]WuG[g)Y+-5\Sr}#?V9?I%Ϟ%91xZ4Wy|] c<3gy蹙*f)1+/<IYMWpZ9P Y[loC;PO,o?h:EAI^^w .=Rrjuʊi.9O3tO5Xۙ6WcHYT)\r4caE`ǙiuH:M[4T{ ( ,|<^0# 93^ XC_p>-u۞y6e "j4,޲ ΢0jfRĈ`.UiO&PK5~bsSl$ 2C l">`[znʪ%7D2nA W 7sTpunM"]rhqjH#X"` V=]峅%ӈZli뜢UZ(fmKC\͕/RnYɡ|LmR@#g Jd+^ı)X|U@SCBQ`t:Akg|?š=&ʡaHQyzyOISJ5{%ڧlH2*iggY%LW0t'y&>}1/޾9v;>[f*8՛<ԟ}W:"!RnKJK2NBw Ձ¤ "vZ//k;N5ͣ,6<1_ (l`Z${Tt{57RA56L&I!tk665͕){[ ٮ/rM:Q۶ ]Gh?~h3 (aCk.kc2}J+t7KYecK`ϕ_uN^?2XVj<*p9IldY <Ꮗ(? cEj&>p =`} XW_ nu_Mֳ@.1ۓ _t6PQA:pPJܦ ?Wk†KUU2ʃ< -7XP8IqUr=g>B sx˟f گ3MݣA^ ?1| "{ѦMcRL+(tWY~iJX\"^#ѽ&63@ݢ#hrlإ!RX؝7KT~[Ǟn"3Y6vQڜ'CvJ h}DadOw9 dC?Tz}*`05M 5c/UEV-P YĵaCAN"= K]6Α=F: q^J 25l&fvg)0֎0ggO+lMvD J9.z. L)7L;T5/$2fJ hEBQ{W(w{?ugɗ0Ppk'H-E(6Fc|P?e:3jL:+ʸPQ@6`:@/vUYS>dF?ҹJF#^-.NVYL\u@V W$餇3o 9gwa)"[}IXQ"eQbDn5fa' @P] 4hf ?lHuZԮfA4ˇ铺dG^{)orfE".s6$c  |uf`b'1+ӐSyzKvO7hJeaK[<1[/hw1qd I$:FAUVd//5o([SC+6 fz4HS}=}=_eȍMɭTNv}dj$$#nY%;;;2c}-t/֮"Uׅo E"cNoUsKf\ΈXEhjqn Gk?HJ迶}oY+3DrP{F1bUgFV x#g)+먗4̣^8O =5@E$]NNH#H+X<+鴶!ς'?tr3]=G0fm)JϯfH!&_Xdzk~7[V o,nL0㻓PHI5ھ\fl "'dxMBYXͨP&hUԤg+p=aސ7^Fp,Ee49k?*:s.> ]G2$KVx7F/lxGU ;hpC<$fӿDAU:T .sqDe%=u[ʇ$'{gj!oE Ў%Uy2i#̗%951jH"Ut\4ǚ;'Bhr&gvyNkֳ-:%C}?Jdz{tw4G-5[8㬕4z]lJ LIyZ柾[B!Wޅ~4촮ޤ=c/ ;ץ]ܓxo^#uX&) 0KvUpP}ᥚFamreKm_(E#"b-M Q!,sͬc'cd8mϯ1orr}@ Y("9%Qy(lv Z~.iOG$a; lmo27s!̯h¸_Y H`%JZ;'1VH*PzH{+5OMKeCp=ZfӋP:Q} IP"Iv~1}V M=q ins;׬Ki =&j;n(`}O4. 엪}Z-J_pϖ*U+='1v%v-8Eqط+jֹ4R0egvK, +V`V@6-)qn}Tt~V0y.\9ӦXT\VU+ |_QJ|ƨ5T5Ѡ~!dDTs 1/(}QwE 7A^&if #SW{b E@ ²fLmː(_uL{NQMqs)~R'F,MƮQ[jYO .vg>˪~v?8jlSQT*㔫"AE?c6={^.&ݎAFlm{TR"~8WZ!e>)cuEQI+6ZNfḯwhȋ&0P(fHu~?TzooGHn3I0-&ߔ~r~0&)#URn"yXg1 -r9֔~Qo#+|[zA3,B{{ YV2{-M@w Udc 2 l.G ma;rhnP"}xJ)-ok0 HT@ p.k ɻ O9èhkBOX h&5xK-JԢ~+]j6B7\eے\GYN -Ɔ=r*TK#,έ =|evT%4~ÿ1XޅxG`#q˦sa0, H>:Qg~6hP1Arees>q1?ba*sP ;a?ݡ%Foh.o謀rzfY` 'դr\c".ZD GU;w#:pD{9 vfH8χ`Ra?Ү葃'dXCJWE8=ND'Ѝnq;>#O˜ٝ)9Ueci8XpNI34Ãu,3%{9EΩ['sbo +5q߽y?_͞e9P'i vtZpcb޳kgeqɼ=yȓ 1 |ClEFa2 ,Y(UB+}GUB8Wڧӡ N5N`|he6ޱ̵N Ac;N^x:5r!9&Q Yf O Nq!2TL#lլ HDoFr\!ݞP;5f| 7*&|+RYx- .Z (wÕzp$?#'|`YKPn{fڛ2$P"*uCk!*7t$/X`ܱgmZ:K-%UBE.4vZur (3~-HTkq{V: Z}:xZvrs$BQ|7HHAj׼ sNb4~끞r`UaQ;P u}yx:X4 t_,A#ƯRV)[cKOЧwg(voEK@=9 CdO_3 nA%ϟ^ifQ(ASVWS<)xo"L aWNIhi >Q y&kS6y?6xU]"Ft{Z> y_ ݯ`z;dІN?*ԩ-hE,56:E _ҳkrdZwhhu]t^rAZ /Xdۥ#h޾Nͤe(RB9W#5]rZPs}m)#զR qk1z(;v:P7dVt#Hǒq;GD,mkA%slFX?@$(rk(V1;b&NǢb1 㗩-Cj@P<| /(yCAe&@~I(XXK0OφK|mƶlh.n. *bg-="e~黅*Al pZ@4"In=nۍ`Vt{ l"诛)_*~3Qa<籎ۏBkr:R &goBȪKJPg|v0'[AlS٭V؂69NSڻ0 Ruعj".oxGwpECe NM`q,ߒG#~b- ]qzdW dj>X<\.81aWK*λw>c&~3ӽr,Z,~b֬7΁ L<&`R٠#A=lNˈuSyU!,slě|"(w o*/j*l}!Sp4Ĺije;1p0b p7 2&8iQ)4ˍc*$!{>扡BnU;Fس}*NT9ĸ\y<<`90{uuGQJ XhfVN6õ{e1{c)|FWHty@_Ă ͔EYV!~+lqc'<5 r W3? _y24=[Ix:g)Z\7xFq[^Fիc} f奭,F)Pk, 528[ػk gѠ2aUw;ʜz0B97yj~{u%~$'&9-xr!o3_Tei4t#hK3sJJAڒlkIFz͎Fљ2&#:Tt W݋.F]: ,ܓ!PǝL9Gw^Twyֻ\fSl5^o(vyw#,KM"jXSwIؙs*ai\'#bDukg0GӚ _˜[FF4*w_' mLAN. 'kJ endstream endobj 144 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-36 -250 1070 750] /FontName/CSDHUI+CMR8 /ItalicAngle 0 /StemV 76 /FontFile 143 0 R /Flags 4 >> endobj 143 0 obj << /Filter[/FlateDecode] /Length1 712 /Length2 12745 /Length3 533 /Length 13300 >> stream xStf;[Ol۶mzb'۶mRmWwu}mߝv漙?~~9* *`f`#'u17Z;:ya7K   bmiPR+ lobmj7Z#bjlPu46z1vv P1w5wq77ccfY&pBvppm_!wsTpR4st[1*8?,X/T]\N_[y_qG{'7 @ῧjMGv֦v]%=͔V c;W;?e74?1%ckTWm8.֞]&&&yK%`hf` P;O$"Cga8L~Dukg7si1;'L\\n_?17475#}4zL5[zT9;v o&􌸩r<,:f0JugY2=iKD=| ܰ3!LF ĢQn!!%.,e׺"J+jx"ψ2OJ4P 1huPFeRo)/[wtX |$DeE}î);{^N7_ɚ{^2gW 38> tDz\o)%Dx俔 FdG6ф6 vю>[uɆzRe P]gnL}[p`)s7d.8ZIIhgg٩tj.dKS\;(GGBIg {("bE|C qCj4r]_7uCV/ 3pWRBX \Q caG)-KumыIKtbaK%w&q̟aH鶎ptӕNJ>?w<'7mO|8A2Xó4|mH\%:Q\&]NRJR\&-3:Y}H Mp$94.\ I,XHmMgh. ̦{Q h / 4y/ɫ!*bg ɔfGڢf ד.Vj4fp*3 tb RʛZ%j|]EU~W7{rKVGPlhioZ[!' ,{)4*/nn<ܰ2! `jb({HlOWb~^OBDK!t22jSR. }ɊQC$;ZJ&W$ T"fJNt ܤY]?'b\(ls@i[N< ^Z=*^=WC`P RjcڲkAAx9tRNG1t(wgX1 t(o/mH!Q \b-~>ܱT\*Uܥ4nvbXHiw=պAi~tB6?oM4D^^rv()3tSHUZ¼hu.6N<(dE7F0ʩ*$"E( {BzDap\mcKl4\zq=ۃɐ)&CB^{kE/Ӣ),ʦJ<GxTl?x3CuqH6i5D8o x@ /XŖjZQwn a t)H \s̃ 0j=w#njfiLPKlcrq^hhv]"5m5bt% # !+|˧WVku1u'RTF$c~CXEqDrj8Q)EگY6` g`diz}QxPSf[CIJ*6{gGէ%_ky.-Xu`Ѯ1ۣl j@n}Q1R2jCT{#W$?MI_gs$-ـLH'+b, `#*~Z6`njY3Wd 3|#B*w)އ̰]秨G7=WPaBMsLg_Y7NuSu[o652i!BX~Dw#s R{0}ăIֺDa%niF#YR(ͿYYYrJ87 20}dn-wI3$/t7įg.vG괲 >.!uaS [ ҰXzpz}oI>lBsTfռ<8au?ŬL1vJ0bkvd t.p&*Mi]\߬TLwK (@б\x8v{Ow:Oǒ?&O^NYxF00~A{%9c;qzkQfz|KOCLmyӝqj ] xغ[YwD%]L쵽|:D 徖hHsPxL]䉚*PѐEh0-'TP+ o>v$̉#23kZ0k9֠۳spwZEHg>{et"ɘd~vt,X>ޚ=*XLdBԵ~$;_VFIe][فFȭճ{@/hi4ζ{TاUCSkz7w[C\Oʋ$YU } b뙇]MݺRQ?+qoBz}DP*4w|P_xF*A+ X}Lw4@tճ/R<{i 8tzc 2sg/(Ga9_ MpuH(TXEE9%%OSKCx;HO '_Mo&}*Nmȼ]Y[}]n 9a[x&=~h0;ď"7}ȆcwA0B8@ѻ8ar$hҸR[0(~!N[6)(~_bdms,^ R|˂AS4wC<[Qo@4SՊSI;R Յc]OZk;9ė%zQ{M“(mS-}(UbŒed$/PVى5{LdMHؒ ~[eN.aL ߲ݦ2GǼUPb "Et&MW;/NN$>#7PF=ýgOv4Gzjt"-R{\W,?=I~V8pQ-TM!.Ӑ3af Zc~n}93]| G}A@>(Gǣ<) "4ҕI$. xzHEH9h`m,}كe k9j.סzCPhOmO}.+c֪vzǓ)Ĉ¿m }u1lW2.Zl?Ǽ`޹2h'XR]tj$&p(EE?t%-O]x1DAPdTvwDr}FvqZMi_50{fAauǩ/ LI7jX~49tS,w|!eJ ҥo! \[,J{ UZ̴n>oc鱦<fw[@e$^٠{4I"lb `&X{@g%/rdtIM%Z^{3Ìc~ERR;U](K '@q<]̈Q nk 0D9b%xUt8as,FnF<rg^dry$;YD:9ege-KPcoM<@jBaj6Lآ a l!G`Pq)>ٜE{,#_ H[ _v`LBLQUb7.4jMH{Z]R[t>swIHGz ϰC !Je n\.lǂt#p@u]u 9l^s:/}rdH=vO`+% u].w(r rRRm.m+TbNT嬾֭R,,O5%DT4υd$G9Qnm#/Ւ?L:`~NFP$] A o5BY1ZJyO[h/:(!uAIaK@jvTbV&Qo'|[a-b<:fmXO ^6Ca7f؃ W8ߙ2vma=2Jݒ ]ߢOEi>RW%PJ#7TE_;F VSd3'6j!R?&'vO5ciqFHE&A> Uˊpk\tΏ_`sjc3O(-7$R=xgL3dos e_g\(aeͶ1Ilnh%tRL[4q:oŧj:o2Єo/͐.MwEMXsjw@^FFwXA%<ݰjv=uBCճ] X"qu7fKP.G<@@!1ԇ6I)A+?k)ccF( F]Ck*qE ƾK.'K6/d g)]=}JGf_n1lBlPUͮ%]^[(3bI͛v{X2o iNP#* <=B8LŌ Udwng5f-5*_Q#v1J 6rM_5{bz4wT*zMϨ؂A*npso!< Vv4el%@-:lvj'FDS%Uޛqy}˩-\ԯi!:h+oa('Ȁm4R:VHEhsWh_fDj\$&;?CH{^¸rF=%V!P\5ӼF,ԲZfu4.gҶ)8L`z`⼤t5"Hy*2+78X1i@,hޤ0o39wsFV@>ǞbX4-6U u~kE_o(\}9 P֎ؔ6 8$wo(Ts-6T/ 2^x~,;)ΖdG5QJC˞|IM$DL Lˌ>Nd)ɻ!%r䛷^$d ODnX}Ȩ K|K+u&xo~QQ?-tg\Bd7 v"{ 9 Doat 8ș QJa+(.>ֱhΗ(|i+$=gBYrRޠZ<5}rk%/9Nu5\Ǒ!'YOTwqeGqSC >LAQL\ԴmBMMΗeTNEط-ߣO8r$<>ᮑ%&5ϫuÄ9uppYKx.ج͡lD1qʕ3(?oХvk}P=2θ&'PXq& LϯQefUf#ԣETW!ׅ[2S6ˊxh歒ɐ|b,Tw( Lnt^tN1XfU{K+X9'9m|6qH$FrzN63%Cf0CXQM^㞪g5L^08\4fScN7~h4v ~辉 ݶebGFyᕒvlsɗLgM5hWqe(&nl.~gyQ`!Y76} -X]"P<49We!R=:p7IŋjޘZu.H+R_dbbGAIN] ApϏc%C&7h(;B!m:RIks E痔Zv!,=mWɂ퟈rYls=| ݉arnE;@VKwD|Amr=B&T|X{ӿqPlċNN琮6M`!h^2'ƒ@սtڭa Ck4$jMH7/&樤*#e:ewa͓DtBu:(Rm?0jПm]*&DOR1> 1&$kV݁~ [dDX+Rm?zZiv-sDD &X.3UoVUhy\lE?G]XqT0vzzRB.A95 eŋ>;嚠}{4#zg}!Gp9]Vc׻>GɢB),C$LҶ ^ݦ QTNa;U\O'?0, ]!(%㘞'D3 /:y PK2a1_?u3l? udN|*u>Rhk: R=_pAx2~.#77m-Aq0[<8[Lf=lyc=}ĸaӑ5}삹1n97pTƴ j)V2q"c 3Έ'%}BC85,^7괩8,5:eF1y5G˶1 B(T}:g G)%Hred7\Lȩ攎[@(N6g4>aYZc3'21Ap(K'_5x6ɘZ",@=xPahjI1Mj)ߨs5ͬ\Yݻ/qNg衚*! ~ ~y OnEG]ÏLm4eStMT'|<Ya(m #8amo&́kpvdHQO3}Jy& ScS];T 57'Fױ#$d21/]t w>\,4/9_OVд-ު-4ʡ6󠒲5nh9M75[o++[!_UA^a ܆ji^L{Dt;Pآn“䎥*}6Jteb5F ҧ$20&[%!uOߵC ^L)7N&\x)mo\% j]t,{1N21 ~@6#03.jbff9A{ q4Kz%j>Ę0T!wQR2}>FɁiKLM'\@Tt*٢-6/a+V@2)p971'9Htհ4pBu֜qj\Yz[wSy£Ѥ ?v2j <Q2rc>1QwmBW{Pr 8R-'ysV5WK/.sGh|t5(4֜2埃smb_= Ufخ lJ5¡ݹl|ghP0ERp=Bn}H* P_N0|)mU )M)a( K>mnvbp>?{_C=X#ZicZepPWa!E.;[O Z4^,ޅ= ~AoSgצ=%#<?-='/VDje;[$9. cɳj;x䈛u9xgDR—Ñ!7!knibOzd}BÇ[ŵ+E:ww燹aID$R='ўrZ %׬̤ e=TWu :Jaޫ4]l"! NK'<▖ [*>5.|?0[)3NYu,|#uzMk{x =$Ja|x|& =]:aT3d;2- 0KIDj{兖4<GwyYK)OU隉9sˡcKLlrȒO'pzD%dTϛ΀#{,K֟`ףT4X@DRK^ͩ|*_5$̏V})fS߹k;K4Yed=N a6/k)GҜ韉7XMk1ʙkZAITqKH4.sAp4sc1c)݈ptoVrN*a$EPEye=(47\AnY@\̒bxcVqcXMz;i|<[ۡ*57Qs͈ q.dr4#o9uj_邹egBFGn[B`t|AR8Zu~ \U$fkҷ;yT x__vn9;CP쯿 LkT#MtLitY)j Cߟ} E$:Q* Hѕ22y4J^ d絹/GBxuZΛj h{_miD~$Zԫ,˘ܮɂz`Xw}j$4=~Ōvv;+oo TI "'% HL'I]^xjs=-dt9OwP:5fcz> wQ)3WɈ3˸w1oMGQS~YD5tFFO&i}أ1܇Z;$en?L^853C-E gev)j FS!*V.q-vL3%|A UDΔBLt?yq 'XR~LT,Kث%}}lenPx't Ƒx5ymԵ$0TӢDW g(q3*[.!fenSb]MW9l?^V KV VR^^*zi;ȣMܮ 7_3ڶ@W :.U^< yfJu@~klھXءΛXp˂mi7,ЧO*+.4u>5$5iaf1qnJ_$YӗyƂFJ^5rS*H o2ƭġҢnQ^X̴ j)mc8QpʹH QBYbIGR8% *H`5P!KҍMMuց5s%E(vC&X,+E4ø|: >ɠx,.A>ffevNӞA͘kT8PS$1 ]jJd|pC vߧ$+nYU|'z%X8PJ+3:n)bZ(8RY#cnno)=;UL060jqokdK*mJZòN;|F}-YU̫lRrsOI|%P?=ojVyDk1y6{˳K49|)' R}s_܌ڽ^aά1u?HH]P6ZUGw5Ye<֑Wc=R]D0˹<Lұ|;=1RAx*F|r:GqRni`037v:M endstream endobj 155 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-62 -250 1123 750] /FontName/TZLDMS+CMSL10 /ItalicAngle -9.46 /StemV 79 /FontFile 154 0 R /Flags 68 >> endobj 154 0 obj << /Filter[/FlateDecode] /Length1 722 /Length2 2760 /Length3 533 /Length 3309 >> stream xy!D ҕå1DHqqaH! X?`1h087q@>ڟ2:V HsJǔhw`f>ǫaYX կzf(?o}7!>h ') 5$qn:wo c8Eyc~WPr0AZ`욣pU/> 8BPرՐ74XP4T/ʫyE5h4Zp~CBTD 2;q:ˉ~s,ǔm#c:t2Q^mop|~Ъ.5 ea%&$%?M(Q[69m+(MDإl6}Il^3ZU478Blv4gu¬M8ٯfG.`v"Oyz!JW"f H{f0K#,;eLaQ&{tۼ I;ݸ DH+'YKyyK1ITAd[َw 8%@95AtMѬ_*K+_^z~Z/,J{ƛW+cakQ!x=i-)i~TK}H3׻()vmzW2]fFI%Դ3ܰ,6&/bthimb};Ķ~Yڣ%Xg)'Hv+UR,{ߞquF])l=ОpU/M8Q7Ewf|mTnzV`!n@|vUi/Eu%]ioff]fk_Vk1s.7騨%Z#/v'ϷinfVRٽq"Aܐq9=3{w4x#!L+$]j Rٔw=:@=۵MTD)'L}$1<y뵥|[uN< Tx_5Ś<'W6JO_"E}srcx(< mR%.~X~\ ԝ{,|4ONwf[W2=('@Ij;o]hdS?NmY$>[[fpE}i!]LJWe U*hf6_a}6p 5eR60A}fWc= y%p`T27+k]yX56i)a2i0ݾsaҞ1љj'p&,w󮐏E*bYu XcW vN'0>?NQeh y da TK)W$/+:2|}e'9|MOjPn8in9E΅Luߜ-YU%by`sX \ b:o;+MҴF-vy{"]i@35Z &Z˜{%غ.lʾ\<9q%&^d:/ǘRlS5NL13/y| / Vi,O/QHndz~aԹ c+Nǽ7dӓJ{I Aя=n|A.x.Yp7lKz7eM yPTpTbNDѼU)r[O}o|bh״WAڅŦ~ěhiN^o)r1=uQ> X(6mD}ZȦ g.2X23R@yYtQnǵ K}ںNOɤXe*<}[y6ʁՌ Oۼ2otuL(n8VʓΥ.> endobj 169 0 obj << /Filter[/FlateDecode] /Length1 714 /Length2 7596 /Length3 533 /Length 8150 >> stream xUT\ѶBAp P \CAݥ n 7眾G/=G?oͽtJ`[# S .d@w6 (D'@+/ nk`nj038l o1 jklVV<P8B!`6ttNN 0۠I/d-g_._N&_J 1AgW/1å@ǜ[dmn? vNP@ qw7ȿ!`s'@AVƢ6Vǿ$sG)sWXjl0Y9BClwnD`WVe+WS dnUsYsx]:lf6ƶ`sS*d9KPbb\\֏<O<>O^O "#ObWY 1FO 27@rmx9KetN gI M9ۼEHÑRd#p([^0F͍bdMgZ2juAlSǒ㡏z ΁ڵ_Q5>x/+Yڇvw[g%]Y2 njmn5 ^LK[nktgǝ\Ov.LWgnSF< f2Qi2w{RhyNn0_'޾˼ hc5uYx|1PBd{H>n*I|Tb@Rʊf0~㬄G\VZe[L+6xw[#j:$P4"Eu(ے/ʼn/S5D@%*==f$k/cV,-3FO;Tߚj:A[W>hCEayFq –J׷2\x } (Oam3BlacB '^0ύ J78\wo\ y% 3;~3\)BN9B^kfê;TtA_]?|O^\[k|\NIItytI?~Ȥv~I- +7ϐX4g9Yx7(Yv 9Cܘ߼pLΒ|J@ 3/x!Bxw=N6)Uudá1rb^Xbg-)ԫnزC"o24;(P}J Jz(S8Al%`]5OwW+_|p8J>%8B{**^ndIp3Y++ UM䞙B{Me3 _KS4pyV:|yV[L7~D%ל5Fwy:b";OnP$/V v'4mS'PX&|`AYY Lȱ%؉!T-4I0_N3?|P֞bw.rh1v芹MD+"GYǺ8fP_WB~d%7r*qmzn &wLbq}> " PoﶳvvbTÎoLȽ-+{ LbcRhަ<~ PG #TzS?)W櫌g*W}2?吇}@yM i|f/W3IH3/\ipv{<}d̀-Mݲʖk@+v|gZ8oy3 !dM]=_K^rmbަϥYL&d}/gՆ\-/ȹd`!ꒁwWJ4;T- LDP |`ڒw;fKj4#Z2=c 2F:W7F=gЊEyj(ek۔36fMi\AZrk/3d65aV;0!1BT6Kbj 3'ɉ^SaDm^!z~z+u9ȒTKRh;N,L]}F=߈m +iѬ1y\iuIP`L mxi(E[2լb*kmaG 1I7dFhyn}XȅQbȂq\srCi -G<-5xM_UYN^ _4&*BL#( c eKr1=oKѻlnB.hOg}IO.(C+TF@{ {T@U~0)pRmqkkwfXm X:LЍr O*)/L^h |L~ڎIA!٢utH`^, jAvx7F*|)elJι'wOr#~EgPB(^w&ّ\b;g:df\7_;]-hmUpu*@ՅMcpr+}I#_k),n7_)_b/}{Ӹ@[vf8DYYyOp.tJd44w{U#}2d قܺ=nϙo%-Mnvl0滏 'Gz9?ucO~k*289W\>*S|-V"q5%]e^؈= w>ꪑVW5*3[* ȮyfevfT.7"ΪzrߋVϧSߕ'/i;^_4Gl3)E{uH3qHSަ t2,{y9 ) q3m|l>UBt>R@grW_nH8 t/%n݊,{A;4~d,8vgG']10 sd8dV'In;]u$;~mjƯB^ٍ֠#Cs#iaĮ*O>G,+ |3)bء" S0yEäi`Ƃy8$zجnAΨ?2hk z+ۄ*mP1L ҁ{ tz8"& [ېi܉)JæAK}ufmk;%gbWv0ri/zwƑ/ BolcqNqs'߇kč1dz 03hot"4Tb+ eQf#vсF3΃qch/Z,jۦ6Ng!O NTK% |@KN;6] |H`#ٯy.YDq٘3¢G~k ԧT]NY\eW팍7=|PTC[|M1kK|lsio&'=Ḱڂ`eC/'s%1E+nS$`/$l%?{~ "TV1lݐ_ScU2. h,M+el3=cM¯.؜!aC!'w4Iӗn}R>ڑED+mrj9Py !(±t.nE 1ڕ>|U$_xҩQW?MNܯ8 ɉ;jݿwf0>tng$> }I ƏZD0(SB?yMT*^)APRceUrGZo/Ƨ ^dqxh0›%?քZӂ)&ufg"ߴzOtJGެW$SuEڠi 9Ɣ~N}11k*Jbfƌ;,õ\J(B $@~oq|9ť~DWψm@wYXYLGiJe*]{D6Bcs'6JyדZũi2sk{~[eAuE"WSAw54i>aaR=gebL 7"BWÃc@B[;Gu >^ یؘ= ["C7x *(Ӓ -҄T n&Rg\F^@ւbȚ KB3F 4sH?]y<:-ˮsii,8oїLAEs$`O3a8dyT=K2eŢO.kI :iFAR &Vj] y/ƙEDINe+w栺?G95js`&x=Uuܜ&c>c?)lӮzk՘L_8* 2 M!.^̟ cy|>ii|vrwV./hՊtߥygJ׽q8 #>%ߛi)iX h%9e wk(pgW\7Sz07l ,4| o *WrP]0JG2a@1oyIb/5|xpsvƧe:e$A ~=RFR;TS[8xr)X>$ܙmR1`瞶ӊl8hpvݞ+Ja%1r8G1',N9=3yЃ;'l(4.GK@m`ӕM ΨmD"IF!9cJnٷZɫr*Ǵ>3Wǧ1|:q]*a#Mq܏ZR]ʶ >ȴ{T"`sFs;mƖ=;. A,W-a왐49ppznz 1uɪU=qetFG G$ngL%\ 'aԴ1O Bv_؜e( VfPLz_Z0sl[:ԯ=!;1Tٞ&~-ߏB*V٧.*W1ezn"N"Fтq!*CUOFVaufi;ևD<_`Ƿ?oo.E1h|Seoo>MF[:)V DhD'M"V&IYg˵f/26,ª+͎x>tz?3~dC]sw(Ȇ5=麾?/?i,(F&U6jç yn~+keS]VVS:tbxoDSx=.~v ڀ%p]Q`%x!|ΡQSz5ˉu,sȏ&ϦZ5gJ0XcB(Ku\VI # $ۣqE|I\ZLiXͯ!*@8tpəo0=#fzҶԆo* 0iLI,KT8‚03`sS<$!_?hmWƠOE$vA+ԶPf?>ZRn:WV2R#erdgJa8]]nDȪ 5(f:]ǘBUѝ\:74Cس.{B8`-z*r^/ӹh/)j*G?1kz"Zo1pQc[9N~ 85%b4~#ClxI/ѹGTi$gcnLSGot{xUP3Yw6̉|19r (?yO'p;V0CvfDίӃ#%s@''Ds۔;#Tx4$~t<Ѷj^f(K3#&Q II'j-)7Bsr@#vXY' lsշpbYΆx %K-|s)*a[XP"8qŵ a:>$b=c :vyl#c! L~/< lZhG9)L8- :1n.vbebsWݳGEu{ 7`}]xa21\q ; o`GTgvMRoI\Y$7qD;2q@3 !agԷtQ/n#š>k,_'xȶǹ/ALmF鐺|a~1S>0@>(p]E?37LT"vO#|X,;^V($Q TJ/☦)蟧#{+~c8NjdO>Lz\ZL c<xCW|sWMcQ%t,pӻX*䏤 S|CGxD BK<4yq*%CjZk~.sf i&qx& Ո@?F9<œ;Dpxl@"NܡnG/n(VV§P=wq X]%wȹltAkyPÖD^#,aӇnLTICFoUR[T_ X2-ͼpfQ3B*꘢+@vda '3 (, utjOU'IuIEUէ(H밝<Ͻe⑫er_^?c+jk rDG(D endstream endobj 181 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-6 -233 542 698] /FontName/TDBRFU+CMTT9 /ItalicAngle 0 /StemV 74 /FontFile 180 0 R /Flags 4 >> endobj 180 0 obj << /Filter[/FlateDecode] /Length1 2370 /Length2 14498 /Length3 533 /Length 15791 >> stream xUX]Ͷw [,`!Hn5w';Mw̚UU5yO,nfguffga T'ggaCz^pJ.l<\<\HH%=At%:LJgK S5)BN.nmM+Nj@'+Ќ dLn"$oknGɝ|24EJA10_wRj7ͩ pfN #[ò[} rx5L4WI0ְÃl|֍@y>/z=(֎S&O^諗f} OfezC ;-n1~74 yѼcLL T $B]p%6,A{5X[wQr"fsj?̱0q s62{L8*8Vn$3|F^U0hHǵaa!y DK6ec?o)~!葚ˠhBO~R`CC8 W$nK~^ ϫ^`\tdzU/_>bxw8&|DƅVWg{`A{mgzΈZ NFG]ҵJPBц9Jӭ5q  r}PK[ckIO΃FdpJHioH|Ԕ>yr7JݸV]н;95t{?X\ KγDªfߏ!Qvm 3W= =7<&SB1F]7ġfс!wCe)}d{_3' F"]Jpr[ XX@-t˥EX¯)1-!spgW,Shtee̾W{5,먀?t؊WD6;4je389wF?MV|٤7|㠇rybL0h^zGm>菀u&!z\/k -uULxďpcZ n ty=~Jqn[D+ a.1m]9]N/8n^-+<EbJ]!%>FAXyve+)Rg&PRn28 28GޯQ̅'z7}a'C\;{2c#f |nV{wj"݄Ɯ}b_cpL^$#73G/zlw iinW?^ 9MG3">zE՟ 'WjJ40yM׻0\"{T od`#իYd]i-ѕf/"1}Wtu\P{ ZfЩW<1w4_a>%7;}.K#8}],+EvzjH{8GĿf/U'7KjWK1ėznwG?F|L2~\hqDbl~sEQ11m(GuCC?a2G&;/"tc޵%ʣ^ MA$ 4u$iA @d׈{ SU~,+)k5Q_74\9iq0z#uH]p7+Kc)8dcˊVs FWLgKS%LH$f\wNYQJ:m&dH!wR\ F,CINg2`GZ2 3'oS@!Z/9uPDe)6? %u е Gf뮮(yH+)$ KᵉA2iW' ])qcs o6"PR+B:h%2KO,HKWed-?R iTpv)_NcVIeU4AY G,\1R= ݼЭ px]}f\IkX Ʉ߲3ֳ9?8ךh,cϪa`lq!!ߥgd->e(GTӥsb-]:_ovq !yQ2xd+< n"wM*W4L^V2~Kx4%O2/FcE&|wAͺKsU'֍Q-b,=8wF٨-`)0YCgؕ'(T[aq;x&e*I-'$L7Tj7͙O˥ɟԺT 6E9XXgy7S圽af$r4զ$Lv-< $5(P(XRϏB&$6Ss4W8e!0:Qr#UYNVD$ܜ-ʖGڏ%[:לŅ[BWuοamzUI.qY ,pn^ w-G5'SquqO,ĒyZ9UH79R b˟D&Np3]%\.DsU1HbU)h-U'sc ,߮`涻J,|!%~=8Pyj?ӍnjӥzBߔ4ttsOj;e a|k5<Ơx"j\Z5\#s:h+ % 5K &zI(b )9>LS`)fy-l=+6/wND|LچwU#K;@Fm+Zp 3YUwN A{g"eeBjэlb6`j6vV|NbdiOTQW@H]epufZ Q63V+Ә߻{_:* ]07zT!d 3Ƅ)l}kQ/%CXXYDj "y^>ŐC(,5^Jۘ>0v#q;v5llFxY5] Әz;#BwG' -IDD]r~,C>>"Fb+9?ͣ-T><ڵ旸8G?7Ba-I7`>Y8JV9Uy&#yg>( .T LBfs3?d5VBޭ -.09]K{[>ݥFk WrںckBQ} #jaOVqFk= B<+F{KEm^u֏X"%~_|c0(R#9+fĘ=p Al)Ÿ=w H`Q]5*?Q2o?*J!|s|OvwHjVϠP' s^ұUlY͓( 4i$U1lpԀ[fp5t e,H4M^Tq.u,/t*~mB:oHY0I%QL xQ\[u!^( ,#-&=C @N>#}ٖRR°?c~7B&^in,R-2amy8ȨD` %oS^il!!O/1+R4a=2YȎڮQTaw&(δtp\S_ Y놚4i#I8pPMFuG|wTUh}(9B5;n0n  ⲑ]Rև_F'Z:.7sY[|c=Dd, G[-`lc) k(A ɱpz6nh>wzGS[0-:d懻Dgg5 fK&CdCeO%R'#]" H.9/} V+k3ڏZɂv\pdlRW`2zoh2 ^{_%vpfk-5 sK sa3֔V7i35j̆vٖ旦GY|!JwRB lR<V?0n1q{;cMn)A)n> VίgUE @ʓ0 je aAStħӐÞo®=$4FZXqi, <Ѩ~@fB[~Ʒuٕn,wU4胎wW1_mAfjXC2⠃jd@\,CAyi A5!Ah歷[ܧj)TkWԒRs`RVm} %1BȪKPhY20:KDd647Q"WՐ)O> /3daL@3wSr-̵ Nj d֭@]r63\%shb6r(FiV)l"x f,'/jV,/J~UMnz&xZz@njCX9HYUT_FMAZAP1a߬濾 {b^̘R{| lf؊2K|M/vGhm#TbsEp \lĨ] B8nCL*2aV[DeZ ?v" Uq0\G~Q@IVɊ-?zWۡ* N3OHgmZ|qN4eDbS \CV}TşAZa,}L8A",m@ N1hP:Y J%tY>h}jG!8. uT,`q]]$W$iRRxEp_"!^cPiVξ} :`6N t2IZwBWn [ ~ y^{<"gvBɸ=@.b|"Ar(d"2 ̜3 ?Fռ~<Q"~v#i*ki`y(E_{eH.dnvxֿ< k7`|.^H&,l97 >ڄ:G}E?!L|l^,>ɲtRcv XHptWFw/1mc7jC|2xr,Fcc_{܃xm3@.ferKUe(ᇷ#^k^˝y@2mJ*cOF`LU i{鬾8U 7ոE}ܑC@tZs칤)&Y~G[7{)x_x+1/wx)6wvL|97l9z% {!wXW/[uźOG|(":Ͳq tբSKb8L8"vihNqJRp-d@{ N !6*] ׿姅>瘾Qq"M/W “iG>x^24Ա$b[u> ^J;I@얅6$|}@ܖ@Æ%2lIנZRe+mO nJw^o _>fJ+Ȭ|G-$ %e^d8VtI7C[7Ე,E:HM:5cJݨ`}7eq?E5_v?Cªx?cƛZ @=tَD|rɄ{QpbSw^@џiKQ'ϫ 9Wxpw<8<[Jv^ϮąuY}W ۖzWWsɏ&%=f/gEQٙY9ok떔ߍ_nC:" IWmؔ*\?G 5q'?pXFy98ȡ0^XE Dk2׹4mty trWY }9=WKüoC5?jwAyW rZD$ӝCZW((Z#-Pu$@{0gDEtgJAg?F{4H@-`$Hű>c"RKn:2S,00QhXż82ll3|ٟPb-];_rHiՓуw>٥޷2п="DYW;vؒE¨MX>U&zQ/5)D31-jL ˯dczUflLXQz֞fUq]B‹ r\}dn*gw+hlߨ~ ƝA~ s#gI[QKC%6kV-2<^ 쭓81WI{}|flN(XWCr*9t+C?Y8f %LiTJ_dњ+㐥/~"ȒN9,Z.ŲHB'LZII qmyjjKٵ'LOCvκ[+;լxtթ[ ձ[EDS:tFե*wذky%@ғm,Qڟ76uJXQ$ÕLO?~Mӹ4"tQ!;VsRahFW&3@]>MpO$JZޫ>}j6ƖըүtDэ W#v5:>' ı o Tm ՐK٘yomx݌_ [h&59>I&F=4Ѫvɭ1\ Ӭ &bcCEj&.HS߭DE5F}I9v!maq*ɈHVC3P8UU o&*탋e QF8#Gn41%8x"b_VsȜBiў}$)4!YdoD"o/d𲬾i w p;`ޗs2 JDuL}!1D6q=G(XkNϳw{8k4+?Լ>7TU]6;dklڧnX>trWVнDQ )=˰\QLS 5.PPĭگɒ ?g"7JEZ.mڬfI] b;ᬎ'8t..r *xlwGGB:M;/ Wf7;Kx=J%ϡ^OS~Z1ItJAE L1*n\uY ql3cXvQX>E]L؉!kKK+٠lW=NWfcӚ!2Tft!c}VL ΚxF.Xyp 2͕ ۴XB1̖7E jȝE2cM¡&I7Y"i#':U#QQ"t[&*7g.B|򁭆)m8X>Tt0%}Sf+aU[@J墉Ye\XZ!'R( s¼4P3ȱjW˴59U_c=}b!ZJ_&G)F"67^8??BQrC#FNge=%*9({1Ҙ8Y1A՛qd`Y!}mjV[[B/*Rⴉi6ִ)5yjPb'+Atr GsLu]ew<2ӳc͚0t}A¢X}@\ w %S"K-6wKvŭ=2(h R<uڅ{lOmv2"H4UhwFH[ EFߜkԻcVc8vmcՄ 2}gkx1aI`KsMHł5-9)S@B%.%AFǣfe$MEc)}foMf2l 4 Qg722QvB0JXJAoׯݟI7>h{3U1) eת贈cdo JCԃ?U%NAdoUQF=7-"¶vGLg0tBt0cض 5܆Y_800es%p6,(rEͿ.*D` v+W[;x}0}|Iʄ 3ݎa@iűR 9C4E;ptD fHŔģP+?bQDPSH՜|QiaȕS{647xzN x*(_HNt%O݃;yf IN"ߊ]QhmL.Eqm-fi1Z}`rǨna݋°&(+V})̘\X1Sw$ gí|MAcxTme b M 2+ɱjz#Iը pbB(jlY~eH2'Y\N@DA>=ȷ6au hA'qOjƓ'* Ŀ/bb˄?ҸvjkZ0B;?]a!ǝRe(԰!n6u/NlŐR5B.ْ 4*_dtNRr8ґg٧$U q!lԂ`{=yWtŢHzkqHy@AiMֆ?JI19DWO|Ig2}e֓JiXDNj8"dI[ZbVFv-29m*k=ɫoBQ?}OQ$ Y=}Ĉ!Iw *;/c8ڄFd1)ƕY.}2آ W-W* Uzi} ;/5;z?\!?xgI%2Fӹ~͝d\H28w4doȳ OC^z]ئU%RaCC!B3y;=({0PsW$V*Vߔ6Gܖ,yNq7z" =ioSby[l圱52>3'i[N]YRJI}[XXTk.qHJMɖaGG]#oѲ$p 6@W3KYӟ,.b])Jr:4%U}kՖ)ʺ;2s T\U6*%gmQT*|@(͎=@|+'{L`@Œ^ln  R 6h>Һ뱥NuBKS^+U}X܀u_9,`v=b8Ɓs]0ZuiVQ#rhX*05L$>={|yН+ $7960o时a|k]7(SpG(|˂dbcCf=_)˅w4) @ZE`W]mW âޡX F`xy8]_ Sk  CpZ endstream endobj 214 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-24 -232 556 706] /FontName/DPIQRP+TeX-cmbtt10 /ItalicAngle 0 /StemV 80 /FontFile 213 0 R /Flags 4 >> endobj 213 0 obj << /Filter[/FlateDecode] /Length1 1735 /Length2 15945 /Length3 532 /Length 17007 >> stream xleT]˶ww5u,CCpw׏}ιg{ߏ0^F1fZD^N ntcg&VY::2132~JLV{1-ܕ֊B h,bcm ::29;Ĺ e3?c"6nf6#+(ɿ~9O1ICl 0Lo`mn&k` *'+NVr427Z-8}N5:20l_B٨$Q́ysGs 1#S6373w#sqsWRSg mVnOAT^JAQysO32Z-H'hJNl`oo؃r6SmMla:,, VXl>ohFt6eNbkOUgdVFb[{ `? \ b쟑9m^oӐ߳>8_?s~65*8W*7q3M~DO/b%OӧgN<Ӓyӧ33s=C{#K.{G;3YYגWm?ӆ?.gaOkOOOSVOWv7tetea72p~p~p~04q% cfa&f8ٽš47>a+p>\$sٸeC>vƣ)W@#n5u}tY$.)b(%7º,'pMk DP{~T}iC 3I<L?H'~F5G8L(7gCM&r tba{t^Am.eynQmijGy;|N@rv`-Wj[[R B]'ςE]1)6p WaL\"o?3vJ0Z8+:ݘҵa:R:o/._rs V-c>=D&Zn Qbv (˕YSlῺP+%י?2eȮCAs(a#Xvz- h鋑Ieឿu}qWJ`d%pwr|1 hR\rs)MXΔ @e82"q`!J)H>!S j;+F8_ɞIWcoC)q伿)ZF+3J*a骝;Y+q%H.O];C#Pņ*`G1؍:Xub䄋KUZ~ttL+hrA;R?ĉ\fA( ?ݞ̽JoaܐcJi_I_3_TgG׏GwqwchblfP4Dع} rA­R׍|TH){:^}/tb%9v,۵O KCHsWµö܅MQych7.T!UxV4(4Oh wFm/sT'Y.;ʮۻ57&v>W^Og#ODJxV<ÔHA04u@G}!V^)f|$S }GZHdNP6\h%ߧb" d*bpPRAAb+uB^']s͚YY49_Emhn\6n,2l ckMfdL=v1RB /?&=1Ys2A\rx{:C&ޥo;N}I~"di/ ުe#1m cM!,ɲh.Ao 1P)CثLly=}sD,:_EZ;;WtPj.7QcAʟ&A`NXNjlslb9a]87ܑva:@<b7}g|~rɃ~;k>ML:#Tc!n%ebܛd 1BK~=AV/;qtce5bm|s PX^宀>yuv6l4bx⳥hӪ`3"4, '>p ,ayY $RIqWG/f~ TD( jkjPe 7;7(B|= !&d wfᜍ) ٌ']b@S .JAkO&GZNWTePKv,$XAUɽo*IUku]S޵qXU%pD,Lj"{N~/]jd_$}^t!S| { k5Yi*7]"l[) d`BGkH6Q7ڕm@26 Ӛ3*%@(!ABO,iqӇ_PGNR  fyp|SVJ( Th!J>:ۘ3|]RsV #A"e7[G [W2 $U L0DN),#{69Hbl įrvD\V,#vʬ )s+1o2 {zKt$~|ىK_y}[bU|l}M\a9*b& n9(a0!/%bsRx"t*slծz/5>3WPT2t S=dXdOd-=}Sc!4yeS\Fȷ"AglܓAQ!(FD|WŴ>[OE4&8 P sZ뚶_p)42 j&זv*2z9yjY? Er4 :FT{4U2EMݗ/*bHnӮ24BN_onp @VΈ{1zW eq<ϐGXn?T۳z:aKZL*)@L͕dqcyn ܷy,25 Q'yqeD=&T[ݎ$)ZWwj3*v8`(8"E#0s>X#D_4Ȱaf?O[t嵪 ~Zvt=6>%':^^ $:Y*u\/h7*FDL4s?%ˆ<̿C$ sE+ w4ѮQe|} GLf{G;fCQSb\oEHyjE -bCYpAqrӛ`]uAWXAB+̷նXgaLlڮ/1g07h)5?xw`TпtcgXIi}O;mvY[_>D4)~:P#g>-f3}8r.iXkn^/۴Ƅ\@-4䑯4JҦHڛrL0 QOiGf0w %)5nQ&rZ"~W)_ƅά@|u{A/^//%k5OP # /E %@L<=|g)'7,5 v۩hXopq*TtWtܰXe$"DƐ]7Dvw`;f͆r"Xo}kYّe2Sm>acա :Zzó:f>%;~Ê'"M2ނ̼ #_38":۶CeXzg/HW~ b+Դ|lb4o\N[GAַ1:bSVߗaX(bvWrE ^HA~(xw;6e$Ohd#D`>SPle|;agq#}`׶R2ҏ-=ZpM겢Sޛ`٦A@ j!S"RqY~g+_|~m_,Y٭-tEsनbFS(J fs*qv\9]]z|lBi৾J4cL'):1ewLyr6 !|ΑʉD&.kƫ|C8mv;AIKx KM@Kj# üVnPo^ B٘R*1$o3Z  ,k7R|sDD!&/*r)MWV\P;nxK,ݺ*k yoX"Z89}c,FrΚLp0q3ru}}SvD鱐+9!-MS[KpȄV̷`h-οr2bؐ;њ`Ee>|(W&#c=fdgykL:kKT8qFF^`xq$YM8'-Fw=xil"rF_Ɨ٠%D&}ը!͌ 5L:c\J_0 VNԂNwbrd`Qܬ8`irDHprҙ&M !dz5!çHcد$\D`lL2J'1* ѹExA4o'H)kNmaVF(6b$0zG|eR|}}PN&>9pa \ZA6H[1ythjfd;vBxM]Zv24gq"TZB"!5PV-/͔,Qܖ}P#4DΤԛͩ[q* ٟ MgIB4▪R1˔w,/ 6>xˁhNsc@k܋ac@nnGcP5O^ёԑoF@%\_hň?_aTU 5~L` O8r3$l&gw^`E]ƗyE PA*(_j[YΩrj7{x8~&/I{$ ,0Y_Stc@Q$mӶ9AIg`q"\ "Gī`FPSQG l&>iH&>||yirMK`Y6к5p" :C&b|Ʀi3F*~/r԰}):/żloYf&\ze\ҵq.>(f 5s y[O}E&p)\u^jiu;Ӂ*?n8*hb Qp𒀵{SְqB+CWljWv H8SP!Pa8_)\ ;Gȳe7?Lң{|AmYHA=rEf+WZ"quK +7Aw1jm7lIq-ȝ.#q'O}e0u#%aL Atjll𻕝G*/@܎G(U4QOxt##N*s?ROm䮪r: 84 _.E%a97&XyH쓧h,!*\Upg?~ׇ-Uӓ& i%1Ъy nBJ 8T.+$QQ8i I.WFLW5!7mH;;с^Xg.dl"SK 7,E{¯fJ#ːui`']f CY 2t)"{xr,|*4#qfu4Ň;I(oYIg18A|h^vN50 Qۇ.:BB,&Tx~SQiE95ab;*XlgC菒x'nۙ:yW2`w4!9.`p C|Сo,SHu zCbZCC˸vdK됗σ -h\ҢEg񀂮Ci1i\!=)oX,KXŻuZOk*Lԋnޒ=q|[sev~Bѐ!LWWgfȏ&tL+:>SB|I ˱xJMi<Ś!I0t$+K{aݷ&_.(nj5Ÿ׋cvPF:UM,Z) jb\/O'm 툋ƍr1 _~TRrѺQřNtUѵclJT7hW /01'a5Yz@QFTF-V `;3J}a:KՏAXݍDŽYic\g8ҽi;<&B ѐbNGw UapT`A<4";E83{3rʰHd( 0r.);S`#02x ;Bk!(??˟\ %R* u$VBe(<<ˤj5F x|^LY1&_9d0+ၢܞ9yPA0c,+n ak~G@ca[=cNНvXeYtgi-!95Dw; XA@-C4 Ӡ@Qh~fo~H2'U6Ԑ$ l V ˺\|#ObAlL፼y}mMM.8u2-eQlۅPMmc2"WH5hMBO eď71!z~6'1~dkU]}Ug1R,CO몐%lR#ɸqh=elJgez=&. f,WsfipۯS۞  n\tJpOHM 1ٰ&W36c2\tp^ex=EI1^Zv}_\ȸ"u~LGj}RSE6Eߞ\qƕċꨨR`qk6H:޹NwjU<SVvpp}I ]x|\ߔl{ E`.cD> \]`脘Ϫd )l8_G; !+UKBhy2_TfW<;feB фd4/糒0 NFtnc\;[ 5awcJ WٌmM痨ޯۊg#C.6YɝtܔU/Oquŧåų;Ӏ\-8Q(}jZoAu " 5q?o%XX.cBtaP'\ͱj>t{:@_hPm\CP`@@ȑ5jyæ5FL--|hVp}*7Qĕ4ȘsIO) Q %Id<$Ak\6Q|@X_Z%7U ^]ɻ.f•rJuT*0Cq}H3rDSV&"KmoS]{1Wj|E\,Ki00 Wo[cd1(.=&O V7{AKIi$z^'~şfsU @I䛓zqPg/.ՈyY9%_=JrN[i*A1aChQ48r;K$*im =LiW5=8dзԛ6t7[fIsbU*JP UcѾ0:TţqAޖmu&K eZh۴i3d=qLEd&0e7r]اcc)>n__ƻ# &7] ׏Js.hc'uF0jχ?!} ^Pǣ_ †"\ռ Zl2K&>59͝Qꑾ=wddz5Qd9(ÕBp4 @$JN'RS,\ `-LZsD-q]%a>/5*{{1k%Iֈp 3#ON'%hSMTusRD.,nI`v CGN*X6m! ͖-`')Yd}Q+wM;u.dFssz8hrKc i $I+O$ g7L|[xStJ .IvP]~lqC~W?*Bm*t:0n̦ν1/^De:.4 9Jw~rxs:>I'{n>X>HoM`y5 K3ݒx= n _ "U8lޘCT&>I"E+/]9Tp[j#0-8h0U0]GEQ]D#k< ,EIdRhz.7F)a2{P)9 abSdM >;<d4tZěO4k :1v?*$9q +lh^˼G5T/'o'ܢM ,m@N{!Asq m*8ߌQO"nm9Sq-Wn%듖i:܅ɱy :W8R+)Em|9Wnh l 5+GJ앣5dϊ",Vuf.CgwD : 1P,@9Ԧ c6o`K ggNhZ䐁EkГ VJ*'zbhf=if@Y l69Oamry릳Y4-M,b!hGJrϣEOa6;@KF|{C[ƂlPz/Y%ꭷM&nAhZ].YҺ\4 RTkV:,Zݯ}Ԓ3Nc@0Xɚ]|gghEI:_Po-+w;_PV{U09}Hb_1F FP J2!6 CYVZ ۥrG|ЫJ1f)@JF8trY7uM9M+?ya8$DQSw#F28հL̲cplsv࿫NwhcdW2F+زa@IƷʮ\*[l nQ"c8e{054b=T&i93j_~s/9TjiZ,@8[DC~|q0Q1aOYJ."7q0G=i)^wa|V q'z,} w(!Ij_`2/ G:EkϾl" %KlIL2|ȟZE_8߷Zﻣq~kNˆXϩoQ٦ȦABeA9}aT3I>P'~J65eFg$6b9 )%UB*)iDFeF@,)Ox;)=VoHTu,K\=vcO؇Hm_ig2ZgfUa$CV fG{>~XkU;V'e?8HGRm؇sAύ4Xu S hU"w".leų?rC2m*->*]:i N\w|A0d)q3a !?.ӖGQ<6($_WVIvv̵EM!FQL:QY1 i_{ZIv>4H8,5$tl.=HL%R8 7J9Zv6՝Pc Q

.eɐr 9m+mw)d9 bɮٵװmeZ[m*r "4v AdpHc*j&27tHk7`gEc#DZtRGsK Rn+*CJe(])uBnނx̉Ү}{*YqV#(-t*F0.4QԢ%rU2b tX닂p1+(FT3QV*?訣[58H?4-%|IQ1HDBR.ȧk{^aKH;oyi[w7I# c*呬h}hϺ@(Pk pN[biKy㘯ʑF^V V?IB\AxLZP.iE&4)g>c^<&,fGAn˻^XN) 8aie\Aapͤ3SnC. i^5TuXҔEv'? ^Ce7_R@B =W{%2;;1LF%m xbE¯yyhӦharhw'یY(N< $'Bn\₷3A,2%d'Q[`P8nGޠg?. XtKR(Gs IxK8^fn '2 l/!<SA#)<ƯPI+;o(t! s;d ;2 = ^Yilz%y#\-c֞rB}ܟ9rWڴJ;c89z,:%4Q"tӥGS?}-Nj~q~w S{E^f }667|5_oZƥ=[;uܱëϕ_*Xs\̪@3^qf&kxF3e ,S0cˍVyM.c'/rdh]aʮhx77[wls~ +LP7p0, HIM,*M,&u endstream endobj 217 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[11 -233 669 696] /FontName/SUHVBD+CMITT10 /ItalicAngle -14.04 /StemV 69 /FontFile 216 0 R /Flags 68 >> endobj 216 0 obj << /Filter[/FlateDecode] /Length1 1958 /Length2 12746 /Length3 533 /Length 13844 >> stream xUXͶ$@'4Cpk&ksss 77UU7_hm 6N4 ! eez-=<)F ``8 l,![;wsS3'?Ylk @ h *bd`P52:VVq(.@cZxxhjnO-acgr:8 OJ` 4Ed )No V+ 5:T5dsTH a`gwQ h,oddprp+ 1"KNIE\UP^ m?b? j@tt"6F6FV;ðU*A:]@ ِ 4)a|mp?Lcٞ+vrV[u XPN~XdTo`TGs/ar<عu|n}p}8m얰4T)5W`{]|[k.^v>wLSKj3suXm3{CUco y/ x^G ܯL "NQr ^c~ C5kVcHݧyzQ-"$V-5AeKނ扎%QO6+:gj߰cЪ+lo׿#To)ⓃNŕca >l1U9P DbͼۼQa a.$=#:&iUzG̗|ϟCy(;s"S{VBij0e% :* >QBƫF/oM-˄{eLڛua R  ى)WeIfm[al|Mgwf+G^3/6G)Sa:1#" 2 )JJUU3\L93=3ZliSzt-Fwh}zP" دnb) co:|9B#P+IcX'ma᰿0DvP !%vĀ-=J=CБ M?됽l.7zZ3Tr%C?!U>֘ksĚojGP t.T)$;8 B܂k51wDgH8^oMv S`L8L y\ݨ KޜOlJDLXmEQ%v|>AC%7#*#g& k"ۚ5EKG;96Mٛ79+|4W&z"ʉ˒4Xu71qɓe\ad+eEZ]g}bkMi,{(t27l9U} GyvA?u(Un5 ;͛\4}eܼS4O7/>BBUZ V vVJ3.\L^=/Ax]+ʑه rVe)DKvey=p߻ +nB.Ep0&ղ'#*pNi#M4e&m.8v<${8x"3Ia>>$̿B;٠W~c.59YES\QW}^Unr7e[GW,3 (jZb-0t@T1} +JG=*C(*,À &iZaW'pl/&Mf -P 5eψ3ga&~%B] Rط2:XC.AQbtxDؐ!"Mo]Xc8=%'NNJOo p;>[hI}'ygs*/ +8lKuyLbO,yĀe{K] po,gꤋ}z:ޅ֎CUYl9<~an6s`r̹S7j)R0ƍ:ъGj`A5q6ş9 qksvC8K{3l0 C/`p]!;{go{z~y:x;ϣWE&vA(AHY27jX#>N`CrISԊVpu$}B/-΢)I<2">aSZa)K)K섷JqUChxM<,vg!n{&U`O$ړx,F &&mė/uMz<ۋ犓(O,ߎpy/1< L:&Nkʹg` \ŭkUBlhiΙ@< ~&Dgi#MڴTIΈ;[eNҨ9I;QmR5sˤ5Ep`ޞ~T `5B<-`c8hFg?$z ,GfK}v^$ !ebb^'Ɲxhy(7Us wN)MtҀ]9tU,oiQ(: ,9${yS,&ɟlS:3c-ݑ9VDڿ,2C`G`V_ kZ8!SƱnn o)}(U zAz墳.$d! $W WBҌ̙t7v=WZ\W6V8M>gjPYxU\] Y_""H,KU^1-'7¤zV~=cB^yBԮdOPQ UZ,~Sݼt7ThlK۷%e`rvl{\x:ɷKa Rd^K dMRӴ_`?_OUu^^&>h`ͭuKZy,wFV*.O6Q ֦sJ`tm&!OMTnRY.T4?b/Sc=G|^`-2uZ1m)I> EryhZ#晦E23mhu GbL,$ʼ/M's%g__-< IGʵJ#ĩcFc`&SF\>$Xۿ==U/~bFĘ(<;2EKܑo6\-aU/}u2랋wx'%3Id0{5 ڡJ2gVfk1*+jQ k|uVkkגfqO{`*&|Ƀá.DB0AGp=ݥ8/(½d36M9j3|:*Mޗ$^U;=jt xzh6NK#h®%=&+?D6ۈt0?Y$s]]"M\; 3Z7? YHnϤT{݅(j@ƥ.7OsuX="x2$a0I2&+85:zIO9DIU\ImNe!jHU 䕯s]A*ջr޳RFHaNNN6d%#]p,ӡW6੨?oo*ӣZڥ>3`L ׵ &"(#Z"i8S!VGzo#SLG!&spBA!ys}UpDhpiChF {]HUX!N4%"f*:5--lsIV Šj4Ԡ$5KM(F&4Sp0J(yb \5d1eqYyhr%OkBp)X`7'FZG_5i <{0p*.ɦ!eP9&!=yޝzIF!ĦI~- fw&L~m!I8` ʷPqVz ٟ ULVL:hlyDPFAGg9#ZTɎ} <)NYEȿn'b  !=f b3MN"d60MR10w9/ 2G^ձeV OQ g3yOa_E#QsMBr+\W&<65zvIO~]*sZeaXIwbl mLr<0@ٯ3W^-:tm*b~`H4fЖ:OW;7BM5Sb$-w[(Vq~-xN3>6%4Dǟ*qKo& b p#NPdd8vw/tN<'Zhb~:I~fWD eeq{O5f pTs{WM"(!ʑ*tU>#ש1~*YADLE~9藵B],*BeH3x7m\A* ̙RuU~, >\DtmbN6#oEb׺H7[zuغG+Pui0b &5ztt?;e!6pl-o&k--`>T>]8%m߰5+I+ETō۔95.W^ gU Z~F*f*Dsfrwsa.j:])u|.}h4j9P`F?^&#&~' ދw^|sl,{U?SmtU]1+w>s ߌ`jp>8'b8HP,nxq85L]$ z鴰RdSGQt(UƸ:Qp8mЋurV!y"!0}di6Ej~E`̳% 4܍<KDԨ*+B%P5#R`Xk??$$ "\B5*_NX{:M"9AdQC:rKv-~m5v8 U*U'f #M)7V+|*-xlf1=*hs5!)~K> m<&(sJsXW_GNHIm$ )纬EژA ػ0C/7 N|-}ct{d~E"$u\UعQ1 - tѤPOmbm?t99Ħ0X34}~=C+Qa J8kNϏsrWRd<[m]9:=x&a/=4 HK`^6  p }w^Tg9%X": rg)NCtU 9N$1~_-ڈ_ Bb1HZ-#Xc 2@[s…js}bZfJB܎ܜQFaS6JbKi}tİ2Ul2|(.tck+6lz,3;SU&N>ltet>b6T'")|>pymjr]W_'Y)+ qҘPl=~ n5CoI &qz{&{#9ft@ݝZ/%0hArZ qT/- ++IC?~a',C뢬Afq22| `Q@I;%kb8&ipG|Z@n;;YlP㱅O9W5x;4k~Qp]Gѻquސb6̚QxmWg vY޽axʥ! 8g t)Fbۦpo:s~G•]y*-_86]񷃯"Z=[ac(ș<A|zڟ szXL}. :,ǍƮ>7"$) E9ŭ[pqXY~$k %I 5ip!ĐִS_-)|@öT"?P:O2N.#wP .pu-n'a&<3d<,nuK'Iҭ`f!'ZcqRj:DG]fcCc B̓IE8!0`T\"ujh>u(#n?:}0jzJin4 #| Dt#Q&=YDEkp y |:c#NSؿ"U Y\kl -1:) c줞IwaeSHե`|D([8&gLGi8a{k!ց w6uQhGbHqT2gY ʟo 4,)|Z:wQ{CdAyŰ:GƁCӞJy;%H@ƨ+dTǕwz~ nV^H3yЍgõ&uBEaTcyL\7!Hy\M}c\)\Ldj-H~GO K^$ bTM>7jI70OKc!#p7QZl R iisW]k+dDw4j--L-Мѥ2jUw;b va̘ fD3|V-p,w*}E-Q8T憐AI%wẁؖfRqdu u@x|5kM ]Cz. 5d@~7+)ӿ+j۫lq~B]O/q񆑸]7170u€)A[~kfvb䕁撻bn2;bu )TrEN CҤ9;ȪNJ_9 cƦRi :%Ϸ BωǝBN|"opl5qkuyjŲہT8-'1&oh!R lNqXZ4duow\"=W袜CRPɀ. 7uVΖqWbݏUqQKK~ 7+'""ekK8LXjyF_qMV`ru|NFا`>#_6?j~3o3af: T_;f?>2stQ])nɸ\ƣ'B_#VS^=R &AdJ1F?Ub2-D= gMϾ‹UDiQob*Bm=KbϜ~Laڢ@'![t>BuxCHUH8GҌe`t yEwʡ_ M,o0iѦGsLaZ-/yHz~C]20 %VyVXNǟ~iE;ΔJC+"Ew~v"IӓCf |*<=LSYb#%6yRr*'ʑqU34V;.ӟT?0€`~dnR6n6dAI``@&k`=f` 1=8N?,يr_E- Ki64b_k$G ;{8tffp[@.] x,M؅,Ujq%8zVHȨ>ETf%u3Ɩ唪K˧8!Kx?KwAΠ./OCh + 8$pGVq2B^PA83~N(y''+rj8.=@{,g@C}oy*18(ڱG2'Kvt N9XX{ `O{jJٜ#(^"5p wFD!|Fm Πg2׍)j SllTGԕm\J^pZ*t/Ѥa}"_뜍/cpcUXN矗e6=>JߒF5'!6_1(Mַ+|!p,d#}2 >=E}Ynӑ" q(2QEX\nJuyQ|˳OBFF?nd쑸U{ XEt, Nj15^_%UH2^-_g&Q,я6qhn¶$:J0Um=oZ`eCJh֑'M,I \tX:$T,>1;\)p[j B7vuur,'F3,<|Ö{soS IF~KD5"Lkn^s=%/7sNFZ? p Ĭǒd},A.9R'FMC*~Ыc!4njA3apIﱎwApQ-v׉Q=gPLs+ad>]ThZ 󘦎q_M/_79E?y9]{w]m(Q!Ϸy9PfE=_8ˉXj}Cvrm̡'ؐ@\Y_͞Dy̛\yozCn9%sV9FiJ*uxeirʟ[F;1:I^$6UʆZ.blc!Lz-9%ґpt3zD kFskF6#]WZC5&#-{,m/u`vo/VGj˂Ѿ1nE< G2~qHDYd\Ek=|Ȑ\ N|nC[Gf|lj9 ^x͕`Kꢉ <2գU͚ §U¨~-l ]kH2a9 Ş_8|F[Fʼnђ^ B$QH>{KQ2'$wY$| 1iqGN«Jt_]Bbə@'ƨXy7! ᚉjK.C"8kaAee郼3[cjg>+T_.#W:pBU=%mie[L/+n43:9)' M-g 0gt=O}{'SnG;ÊOō:5!Q_i%ZUc~^/8HX\zJ=.AnqqC'cAUF@n{n"A?%sU[U 3(33z_ mfzqTX~Ѷ$(ԴJ̡om}qX<$ɿ__q:s#r/_@ֻoB:?~dJyai}B1"gu`%V[*W2O۟\'m=Jf ?:=Oy6vY=Vau觱1[L'M$/h|4<}FMdÚyCz*R3?S,D-D#+%<ضw[ endstream endobj 220 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-30 -233 560 707] /FontName/DWCQAR+TeX-cmbtt9 /ItalicAngle 0 /StemV 80 /FontFile 219 0 R /Flags 4 >> endobj 219 0 obj << /Filter[/FlateDecode] /Length1 1122 /Length2 7506 /Length3 532 /Length 8277 >> stream xu\]))if``aFDinFDB@@}?y׵kogȯ`f UusE FP3~;[4ZZ*z@큶@;TpG`1  vh;w~ s 7 BA`ݿ, mLn=vG@\@/'@<&>DCp;WۤㆆݮsszWA`=Pp%JM1vz P=7 ws 3r9BQ(?.SMQpmA([-ߡ٢C]P+oW eS%}?vz+vKU%Y-sNkdU\@!Q1  ߆  B}n o=O07S!)A~& $ n* ?PrE (9*  ?Vx+Eym/<[^ Э ?Vx+/CQ/  E@qWcW8P(rojq{ӷ_ ^ (j8;xE͛ CN7϶*7E2ɕviv X4 @:yOd.1ht*3&g9jN ]GJMX1I_U&coH$)_ 0EڭՑ) /PP")ލ/^5Ԩr@Scw8amMͺl6꧵PE/̾, gW{8Z3(S j8G`fDӃ946.6b\P`@یZIM^@CbOyy^P$fg}7CvLg65˔1GH ,<3:M̱;=uzʣˮlXwJ{w/F9 Σ]whDzp1U41pG=y_Yp &%-5if Ӫ:Uݝ~z&M&^;7TT[c Dlu!iM=aC)|D"QJ݆PSC8EA" !E|eP;b@CQ=+>~?b>Cr_18EæR^8Pjh̛BX'o U'7lAޅO*=hkӇfi*h"qo(] (mE1-_ߗjAδ^r P<ߺcӆupQɰA6%c&e; *ӫLL3ӄBsWC!co&CR3̄}WS+d.<6Æŏk㘻G'uJEv|_]X U:_wzh&u'Lkܾ^ SEe-krܙbɪlGeoPO>6\ct>ǘCQ⻥"FaXdz/d82}e)klk=8ԟhi0U 1'tZɽNj1 xBKC ڦ4).|8Wk|$> rahJS+@;m16TY #bk5pΞO/1Mgr)}^{Q8G7qA.iRqU%yJIdNV\xa~Ow7?ꍍʺ$y%1JM1P.Y(qQ6$sj:?V|I^c]ZV жf ԱL&Rn Hn r~=^Hl[M2)4;e眸)xѧF:y7Fc}CY&Dx 8 ;cJ{dȲK i$7)rF҆$ <ё_#!FPUl.;j\fLl*1QfɳE<1mF\GW-*n*R \ZczqgcKސ%eIbd7 '5 X2-kYKa1:;6sC`$FfIDA֗5d^Zk*{~{߇ʛ7[$l0}{RI,䁑a41Б+~:i'҆T b⣣*gYWIo1݉Ȑ]FNTi~ZS>,]\! gDO,aQmx,kd)ʤ}\- Fa-tjѲɍ o[:dp⭔B>c{DeB~5*M oCRԽb$kerdU*>Mb/6do54ɽ1MDNEu=nxEN2Ls{~9l ;vBcվL0kCoGqR] 7h}9mɣ*J.oC}+riA#[aHXĚ&,b؋ؓPz0cU]_ _K<2W+)ȚZșWo]OòrjY %'%Zx!ɷ ζ0}ck|W^}tsQjaZQvRCV+g:Әu5(PgsLLUpX>ޢZgKMyYUdxF@yf:or+@}FMdسƀ(-nҏN[:j0g-AS#oL9e!˲nH`?.R(b&<=`u;Dt* ?TK?iksMƺ Rv2g?u^C汆.356bQ/hQqwh'^cEΙJIܾuH {D䴸Yjiaь* E2uNƣWQt/hD"r8cbuNХXm] ~TjON\.Q'L;WDB1~~8'p&O_{YvJyt|tfs?RwXbgU*s`͘7C#?$k{eQsa&})6/m"崈 =f9~_,,PMfO.Vgd_T5.92X뺎ͨ> EfV ȤS}wy6X3Xx7s;Tlhj)V򩠽8XUØ@kT@@3¶~ڸPa#$*^Kd$w/lgܴ߸e/\y Cю`],ܰ W5aMp7vA򗪲bռ8}d!^ws%csEoY)x>a 9 $ XY*$?B&gK 6-2ȕ q:P$Z xC4) zOI@ yP|_◘%'ҦzMdȗY;ODӚL Ju7>!Lp*?+b G..)&Ыl ?^BIQaWYJ&ݽخwj1Iayte+|-rf⾕g#椨D5}>P4^ǬC?"*uF%uk[0K,w^n p *d2җW}k3ϣ5|6:}ǎ]}kE!i>B$I%1b=J.&«4>o qk[ǀjgr#o笿:K=a%ĕj98<8cyUPm6fpu~ \Aʹ}G'9ssZـK`=V'VcBcspe1֑/Ң[X7ϰK2q5:vW*~u_PDgy+'a.bǃUnҁS"^0 όl|w_S|]v>bmgrL(ڟO1uԻpLhHWZ6Й#Z1x:3TbÊUd^x|nS=gk ^';H=z]k&:ܜɞ/qL'!w҂zփlZek3W)5\PYX; ZOc3>F{k0 iɼ t?NC>L czu=- ɐ&|/زNt}XٚmL6ӆD dZy.$el=9=>eddZW/m~$ g9c|X gR pjbZd%z> XQc w=s%T^ҌneVJSW;_6)1TQdGLIà7%'ܱl[_"3w/gh DMhzaߎcPT| ~L˔](2Act-"/D:VM ά+_%gl֪u! m? 25NΧ0ޢ\E17R2yͰcGS%OgG+ӄOC ߋNb yҹ[Хs c>6üⰷ*:0?5,m.J^䜂BY7GS~f')&-2 U<,O5c1f}lDfzeڣcsCA"hIpZy,uɢTzesG> endobj 234 0 obj << /Filter[/FlateDecode] /Length1 818 /Length2 860 /Length3 533 /Length 1459 >> stream xR}4TyFGm$|Mk!m5"ꚹνܹ1eElVђ>a۴[N%򑶤ݥZ=ߞڠ)piSZonF klxV39 UFUG-F=UbnyW5Roy]\)\I;=H>#j/~Oޚ;ٶw'$TNidڬ4Ȝ0ksCCBOx-/sl"\w NHWCUW ~lRbϝT*(*=tj|mM-.F?Tc6 \Ԗx։'0`23KZ8T%b[ɢ=M+s7qXqPW0JL qbxyywOD'0$Zj~H6}^ǺZoq*n \àE"OD> endobj 237 0 obj << /Filter[/FlateDecode] /Length1 712 /Length2 1747 /Length3 533 /Length 2278 >> stream xy+}gBػݳtV;4>~K{׷r#W4.2?ʫ} Ŷ@L]Lb 4,M1Wso H}~jٴ2=9g=wooc* »Zxꐵ$OS'DbB>m~+27$YVf fs9<9~QVE7P rI8Hm}ГX1SΞ໊T?VزQQsRQF"31)_[1v* Y5dMߴ3Zʋ ؠ4$+c s>lء-zjɭ :(4>< __gdxk!1gSNvt~zQŀi پɯ GB?&POIrs\=~lZeN^lΌn ?._Ny —m/, }(7vQQYm9 Fd:U[(_UF߭&V4Iuj(σTvDJmj8iY4Ss1F ^9s175ytϬ;L$,X$'^fq2lu큲g&g9TúsP]V̷W }=QmQJجdy[3Jᗝ4y7'/?&&f/%Z*`\ܫd]N&a.ۧ<|T[wz0Ggզ}GG(lEn6US̵Ӓ어2SODr>WX`Z%vke9h)%BqӟХփea ~He(3 3VQiRn}Q%sKh:f夙~Z ĄEB>)|he?α)Q& Mjװ#xC?#k{/) ^ ?`=>m]71)byol۔rOJ_Rk݉8%tB;,{2MꔯU׀OiL׬c*IG\nO\ u#%zpjb݃ўBM3d`"F2hl-^Eٛ2bف^tt:}u.Ѵw~\6}T)Yu۶nOXrK;fŖBD H`T;33 endstream endobj 241 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-24 -250 1110 750] /FontName/HIZPYQ+CMMI8 /ItalicAngle -14.04 /StemV 78 /FontFile 240 0 R /Flags 68 >> endobj 240 0 obj << /Filter[/FlateDecode] /Length1 845 /Length2 1920 /Length3 533 /Length 2542 >> stream xk8ǝjeI^qffaL'19̋130fcCrR(RD -+HHNB5hֺvk>_E T?h͂8C pr- C"- CcAdH0ff&=hSΘ`HŦqm U)@dl NdNP Ģ@N! uH pja0F` V`ErCAv ^#TĈ`  icm5p&3WWg6IcDbp9 pbQA6Ri\];A@`hfM.4% 35ߒǷƁvڧm4]4k5Z8#6-AQ|sDaQiP `3l69 Fp@ AT0#(CXlbL 1XfdTL0*A S&_% سc` pBU)- Y84oRl6q~C᤿4`$HcAJp[ `\9\pp-t ~\Fd{ː&RM^wRU'oD8݀_[n5O#m jLr()%{#[=VD,%^.~̼/nMi1b:LQD,doWxǶodےxS2*MCZ6:5|CtAg7f3 "3a kI *q{^NŶwT+v\_̩i, RlArˣ)I)Mlxn'2MU7 {].MrK'Lٮ8uwߓn]vu>; Cp u(誤om|X_|uW/:zk!:խcM+t0X>6#{~d^c$YMvO%xs޿uQ:oi$k VbCˌvfDPAVwEX 0v8"܍LCt*c}:G̙-](:,{lҳ2qw(V`7ƍ]J,ӾUNXRU9M&]cdogԌlGa k =Q"%PHT,,;[p'>'~2谖*~wó3~IJOzLoz(_Acjx̌eE|ñ7&=UX 3x6eeG'Lk䫐\] ꢁ̷ٔ%>gqAzÍag%a79WO(ݰã`*X6%aciǤh~J82O6|}Tn ]"u! =˙geiAr\T#E}ިaͽxTouG{뾻tT1,\sbm/JNJ_3㴪cJ>ppKI /i ψ5m:UiUU3ZUڒčFݶ, 84-kLER޻ʶV>R'\-H%[|;RhZ0Yrg/>+zU?GOO4 -,bT:UiI;E?mƾ53iD]zuU-BV1Z?rr:_wݩ݈L>{g"lI/\v9e8jSmw)U$CFDywwX$6;S1=@>piHQUݟjl?{EHfsXL22I# endstream endobj 244 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[11 -250 1241 750] /FontName/EQKNMN+CMMI6 /ItalicAngle -14.04 /StemV 85 /FontFile 243 0 R /Flags 68 >> endobj 243 0 obj << /Filter[/FlateDecode] /Length1 771 /Length2 1258 /Length3 533 /Length 1835 >> stream xiXSW R# 9 PlكF6e.$7pI0!(Q\*V:,-EVE"TET`| /̷y/yt nB\d S`&pf=l|A$33gEDwAD( .lt+&F"ga#Dy8 P"8`#T 7"|'bHJKb/q(B"0a\؃c8H假 r'ЊCX9X" l-w܁`_7m.ψ$ @,\!n@$HnEӁ C%ȉ\(/d@ -^+ C/H?P" K]I-JW]ňQ@l<0L)JP.)Zz bjj,؈IQ7ͧr8&h.ѝڨ<19V&rEʶtVʒt쎘!y7.ܿ?+7ᄃcf==}|CWGfW*eWg5wMO;FCM?/%$JY8'UmkhU63RP?ot7 LfJrTt}㷴O&'Ɠ'ggW urLn:^ԟT*RbX2sJysa{ro}"wc)ݼ;Cϧ~iFJ y@HsIv9.~Z,Ud sd[/UWiߪ5>5vt(q(fL.MHa< a'іO D<󨅻NVSmKyѹ]$]G{BԄ'X[*n0ƒ)~C}D-!z7}Sv>~K C̟W*JIۋrBfq"&x>u>y<v/jKG ςBTK?ؼ.{_תX6J @Ƿw[ XG-n3Wuʾb~f(\*5 =)0~mX|NPW:ьiws]l`3YVƗX]rH[v.g^)6\t~g>$#`ܓ>3}E_f/T~Z=_4hlOm[ﭢ^[2MӍlp\ʝz9 w)|iAgJ?G7,);wQOI5 WʦVTɳd2O?ˏ .EPQ$үĝ endstream endobj 267 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-5 -232 545 699] /FontName/QRKVIM+CMTT8 /ItalicAngle 0 /StemV 76 /FontFile 266 0 R /Flags 4 >> endobj 266 0 obj << /Filter[/FlateDecode] /Length1 1268 /Length2 5869 /Length3 533 /Length 6679 >> stream xeXFDJJa$kfCZAA$EBPB$ I$/_;e~}u}]zP ($@Qpr*"a6('\JHB`$XpC:98j`3~?>IsvI>=RW|IǑ.Ϸea~-zbP>G{׾lX"~ 8JUg{geV×/`Orobs'eZn;UKnBG3/ꤊ>5W%D&yex(4|V||Q/{B0]Yw a]=ԒvxX!dv=Gv.)qGVft+⁗!`'6ÁRNԟ x5?4[Q# 9s* aR8rZS /_C]!K~o ^c 4r_doB%z* kE8ڥF>Jk6w ڛ>r.N+t@"a듿KVRO}c}{R2 `]<H[=Q!ޟM0ҨK[CY@ˋUWW)v331KtnB)K$9Z^n13²Psx>Ȥ-vC;XFj6VFr[ ۆoz$޲PCGA">Wnذ\MnRkx٠Z]} ͆"nd tz|VHǕ,V{C|%@TX`VRp ć^bq !4I\tS s-)\ v}Zٚk*'-4kP3&\kJ+Oyۺ!C ?D$۰6ՊC kEC,FTxX4:*bk/^hJ:9uѮsgjĎ F,`cy܈8#5=eq'i*=w_DOi1~1CLW^cH q! 'q! AO.h?6]2T^rA@9BT"*bY?jfijY)(t!쨲A;/9?5O8 l^,n]bf  G/phdNwa%:7I<\YeC׷0ց;9)X7r˹?DsUF=)@ǥq2ThmTPnK RZvJ4J;WgVCrʢ1iք#C?.Ȥ8EӷB:ɐ Y=e{h9j:a4AhT _[g[ٙͥn߈xZ! "q7FMd["%r_%=dg ~1C *(THNhUL/|(5JRj4-lPL476 ZKAؑ[HOG]%,nċrBףmFT1ƯI_U̾^YJNdBP@9'Ÿ), 'VqedY52'lHs\z}da }yC;IcU&Qt3_d3]2MV|O8, wArGgq#:oE&xՙry|ez{-]/ǭHM ɛ6d3YŲma^ߍ>m6UeK)?o{Int4{wڦRόAߟLeqEnYrI$rMNȂמ, 6 -/f&dʳe|&@g ^iY̎AF5>_="P2RPPYz:EP7+<*$V{ZNG^[{XCI>6 PT ͠v( >%5Nj/,1yU9Z?|7G/SB3vMӲ,v5[1{};_;zTlᔁ-@`Ms$Ƭ, 32+ͤ\H![ Zt}u^mͮb+,y.A=nDa켔iɼ1/_,f}H9pZ?5r l~(#F.N?hOՊhsN+@u|;]v!{䀤rq(Rf"Y/:[ Gx6EK.be(BY%Aj.Q]=\yuQWVЧ%3[ 8\,1ivZ{DAJ6fȏw&b4͛=$}pԡ:L|G P7imtCCY1i1.q7msi}:E9ܪ\ZaiC}-'Y/#8[Ykl:s@.H1Z W-✟.m.G4!uY7yP|lzj]6sN@[}H}Ϡ$n؛jMCO2)JH5ȚZU#J-Ae D#*VM*(Ѿ9E)ND-BhR6*\Jv.w̰bEXYiJZ/9eI.HelQ7F{fէȘlxI>4埢*;BHm G,<U͢`]-4zJ|DX@m2UzWa9cw'^vdB4nt+1wt5^oEZz ܽ ILQwT?] I?XƳHCp,]\*ؙ#x_F:wppr($GK(T'ε(4,+Hl|e)2a r/qc,?,>eZnH:P-q<%T&/2c{{/%>:]9ouSNX fXI>Β#FxBLOyaU 14LUtJu1Q!ǧ#V#zR#"| ֠Kfՙ(zڵy! PjM`wZ..&;́UπD[Td|>MElQޫ߄Ɍ$BhXc]Auw_#7J^xUNLzL􈣦w'w [I79W)j kڛc .}yqz(>Ũm-Ͷߍ̸VDZݲُ!:٬kf} VtȰt{Eb%kxi9dHoJ.x>-τ{ a~fؼtō5\`"b=7hm˻?FrOk}zVR$ԹrgϭkT8 =R_G0opǠmmvd*$mwO9+,(bI~{n󹰌>uZ_9WtVDS8vDbۋ{˷ Kj-iN5!6r 3]oi+s#ݲ&iQ{M~5IiȼL9;G+ď(w*Q+"ܖX_ߗ\'Nu<$wY9| pk]S y`iߐ}xjh*Ee>wrvH>Z5t‡F42>G ,]8CyL;ْMW$hՒǁX r*av~ r=&WjbMwLl5~%+_B ˮTv~&uѴQJҊ*j~RL3V۵ovlR7N>w'ukƗR4v8HZ IW6+$ʚBbw%E% %x.s-9T[ m;.IZ:XBINR2xhᬬ+ +G,WTm;2{a˽];C9N-^ /sWr]ӯ&tLV+˘X>SޑB}!KqMMJl;H {7gyQh ^<#)ZpűLܧoLy` q-W?fvO-yZD}_vMLr$?9*h 9 =[ Ae>DgR[6,=|x?ڴv+e{:4"*32Ȳ2Ẕx0PHMg!v0$ ft!!_rL< endstream endobj 278 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[0 -250 1171 750] /FontName/OSCGST+CMMI7 /ItalicAngle -14.04 /StemV 81 /FontFile 277 0 R /Flags 68 >> endobj 277 0 obj << /Filter[/FlateDecode] /Length1 874 /Length2 2933 /Length3 533 /Length 3565 >> stream xy<3ar2e.K,yK<7&c1$;d+d YEYEJd(ƒ:;}|?u}?}]3q'DY Cz4@_"PII=%},T(LPPh5eEJzD`Ӌ H|B:> %XC7qK$@,o,@àP$pǹWG¿SaD3w*$ӹ2ӝHnFi` x^[g7 S;H"^j d3q>g1d,Că)DY5ak}2i i AufVTp4ҩ^+JnPq8wsXDvfZ)I&/X>c_f3I|eUbm{PUqj~&܋zTE׉6Nj'M9[99++yKz kTo& ȜrNwb T^Zޠ^R. aVűzKG?4"t&qe ɝڡmSvo|cNVz{~.y|6De > e_7]{ M -!NCN2.i2w *\ apxN!EVZ8 O*HČnRLp4Ezs\|hA@Χ{o(B^d5[$'^dgnLb:\Smtyb ;;z*Ns@U/'ϭ x;R?5.MmdS93R߿ggn zR"G?s} ,M璓b)s-UKǾq߽Y+=[hXd+sgR23dqQ:.*gv&}c>.k0){eEi:~ERHsGT˃}O N[bm?x?|=]anWP%fČ%#Wm D7{Z-heEbON#fh[KT+`JT1nViJcQJ)LiCkk 8 'u.oUInYO\5ސD9NqÀWGZ_t(_T?ml<5Ib!ґ2ݗ!V}UxMl6˨bi'IB[03kն_[w\k긫dxEWsZ #ݒ9jNXZDV ViLJ٩{K=:E"u܊3ICJ^bS&[0XT* ӫ0xc̾_kZ̮+ۈj#u4ۮx?U_܌7X5%zjSE2q*ՖSmWC2vΧc*̥g]w{f˱LlP5_Z6?kgg^wK9 ?e1.PKפT34z^"ͤ (1ճw*[+'HJjeޢFV> ,𔛒;/X;m'E=2:FrY _GJ͏q[Qt%hfgzƫ>%/ 7h_ _J&y&ZFe+ƁW<ɁSh9L=qtx8i3h81{=mW[a 'WFe(E.ǰ[Puv/klGhƙђ_DgQs-Xﺄ3+{ۄ{nLXmHU>u9{t?X2֠w₰cMIP~ϟ.~z q[2v"$m*S\G_֯Mt֫9%|-^ڒz,>U|(`O-1DUWwytߏh.:.֨Ҁ;Z$#JϧY2EE}&a v _5D֎ v 붟e=f$_:A3'o# OA,L:# endstream endobj 285 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-30 -958 1146 777] /FontName/FCEIRJ+CMSY9 /ItalicAngle -14.035 /StemV 87 /FontFile 284 0 R /Flags 68 >> endobj 284 0 obj << /Filter[/FlateDecode] /Length1 824 /Length2 1064 /Length3 533 /Length 1664 >> stream xRmTgP zʂV$0>"چ LH@jD|X4+K*DZH+jl).FiuPt{:y}<ϸ!œb$U^0 ~ DD*׊T0fs Di*{L8@HT"AH(( ,x[2@E0#ryOgeR9T.rz*1-"2w4A݆PYX!\05Es -RWPU )Bsl(DsC%<\! eA>~jUIҀLe 38KF7;zv@x L !@,)!umsY(.!(.l? "IQVl??"h,PQ-M.$mz>>"fEq30 I ++Bo !49^> [`p8&hX Z̠5I"j柣.DHh|sꟶLwENS w );@Mmd2u?Tx.}~=ƝbN݌qmr0އ-Z,/UIԂ*pxFsgoi w%ڇ0dF:'qRu˻zۋ.o8^oCQxLks]#7nqe+of_/ZYtBS{#i}?1Ws*zvOȏmIWC Q\V".ExzEГ e0Z/=O6;Ro}듽nӞwGOsJyFlһ "3޴i*>ӕL8;˴ʎm]Hܢ2:qkQeIg SŮݠ?Yt\s&-{l'3LzI:},ex '\oVl[vw7"y|$2[ ^;y[FkKº?gڌ x>sóA9jǮ=Uq,2\voߕkvv*ĚoNwO+v؛`Nr>%9$M뱚ږx1 ng$gΟ?p0dR\Z]y^Vcjr}M`d8sDOUo? *%L?;*\ɸ\@!cO__["W ռ:hd\H7|G'ˮ MIΕ44xeޓK?ַ4E%YO#vHt\ޙf*l6&?n"WCs! gH%YBׇD;cڒֵ]=ptq֕JkH-=d\xnhGuR3;NI;bO_ꔤ_"4|ILJCDPm4/ endstream endobj 328 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-35 -250 1148 750] /FontName/SYTRPS+CMTI9 /ItalicAngle -14.04 /StemV 70 /FontFile 327 0 R /Flags 68 >> endobj 327 0 obj << /Filter[/FlateDecode] /Length1 720 /Length2 3360 /Length3 533 /Length 3913 >> stream xWTS뺆Kҋ ]tB Pj @"! ttEA@D(47AUĠґ*Ygcߝq3{?71EP0<( M@90@\\r%b8W"JQStAUDVA$/K"`8+}pY1PY/"rBbDdCA*HQS.)4y(4@ _2s=9K_M?"2#Q߭pԟl($]#sBd!Jr`?u/ Bc!Nr:?8-ͭd|6]18uMO5u 95!Gbp ++J@Pzz; Ye0QR(C`*@@| FcNgBL. %A 𚁥n55m*IL )c,*1B݋v}D說Wtcu:dP)!p|>(BT]-%ۚN(t_!a~ZgWvxA[fa{oe~ߚPP.4"9~\xRTNc_I|t/SP6$XE d_`vwPպB>+pS7&a/dƑ~4j ael3Ib*uR&c֛.pl,]۝K~S6!qN+ $v ]Z#u_7e.)"RL8_]M> +~ N#'?BՊTXgqb0L=dZP= }պ,u) 99dqϙ/ w)0qqF~k(BHD XÞsiŴYhш_y,YK놱ӐьO2RF&gϔ:>&msÅ ^ե1xI +ߋnk i:”TX`pMGLQ{J-r?3+~ ݺoY܃Ւ?Ö\WbN]"?ҙ#zBblTk {hty-h>#.D˒Ne:vQXS ,%xenV@= _#SϚ2}KlCCr7Vr/aи;.=([AEZ|G܉ꙧ/csb/VC6"Bl;p$E"} kx6OġA*-#MQzW,ZiaSSuiM݃py#rK+<#}efAXK\}]l" Sp |m_my0<:ϊ'.T+wZ0~. Wn׉ϵ^-{ X67cmD·uf<8Ujm*}zg*kDɘ.85LU) t4jnBQܽRCݶ`>ɱ lL&Q|y`<I6gW92^b5jr왿kfDӾH= Yj`N|G"a{؃h2Um bDPѼw8jV1WJ`Kvg֛0}2 20J]J|\R,Q|̆,٧ΧexJvz_2a}:(vMyeX3ē-l#^ L&ЬJ`!7(@fh4E0xƢMʥ|I 7/0f~T)3坃⬲LdM*vHC-KF`@i8l37yڄdIӃ"[(U.?,ӜKhL9rYjM5R0J_䜅ծ{*]u_U>異|z~yEa+YE=pwT]1*y^<{0؀ٌW/z iA eWi!͚Uԡڙ׺>܃z\ݳ_/ ?-4+RD7ZX˪ڵ hBF/vjnϠl]`Pfmz$K[e'y4r%dGRz)z<엠ckk*:TDD LhUtr#[S)Mt\t ,7N 1-9ڧcsՆHu%Lȏ=) @v"$:Ǝ5r#?Md*yk99dYDT(ă:&߽Ѻen?#"O5-T H,܍t>{Sn Ky[a]iߏ7H%`-u&{o,sAE^i{S (Ë E} oI+)ϴ{c=䷝ܛuOR\?5ME·BOjR%MX+zO^#[L Eҵ L:6tǹTUuzBxl-| ۿ8Mxh[׀"hLb!ܲNsabjJ> endobj 354 0 obj << /Filter[/FlateDecode] /Length1 720 /Length2 8134 /Length3 533 /Length 8689 >> stream xUT]ݶq'݃n`w  Npqn79u:^noj5o9i)Ձ掦`YG7VN6N!V rvt wKS_  bmi`b[X* 7+3hf vb$ ]PP89fnS L ݝ5vq`Ci`0[:Y 忍wsYw;;U_ {k;%pwrwT..MlnnS7Ͼ'\ `sv0w?]QGA[Yϡ:M O?jdb 0`#-&`hn` A._$%=}Xy\NNA?/)vvv+Hx9885swq;.j ?`3l}@ƽ3zFF\S+lb5zx-*f;'%IXNbj}ܕ+TYԴҭfSm?e,NvJXN^'ۑ^Жx}__8FxuO`Y)FWk:~ =7.n[$fQީBάuS9D!a#G$^3rϦg=#Itٛk_N ߍ2'0Үa0 FA D8zj/O*S ֍+݋gp052QK6)QnQs<U)1rӉXP82 Jx3ݮwJ}<ڼ(>ч^fD {S5%7R@ .Lhhj%$̮5?עPeL]r\26Gd~NZz YSX$jyfoL>[ `deQq~r_O'<Ǵy^_#+T 3Tsu Q_e/_Q#Z<9в2WtM51qAKn$H k OOZDgQ} ?^&uUhџ&TpR*a+YK~El8ޜ>ŋOmd:5wդ+Y$m&rZiSʹ+6$~ONS -{+硯?s7# d &(0_Ws*_;[Y{JwQ5!N}Zw-&Z{!l7N1%;:'Wq;14ο)Bhiޙoٌ킝IF?ska-?;%<\=%IV:4W7u׸hcTD_9 "|~\1ope&䙋z+nЯxta)ʗM@0G{:lD_u._, 2vW:!//L%iSgɰ\4ӺOX{׹A#R[`='Q]P4RYV8\aQ(V82srqA?o?J.)lO$%O2/0~v!MȐ׳*IMGQVzI׸"VU.h[ BO(g\$ ;CjLc]טfFz! . Aa!2Hͱ1ʖd O: \¤g`**2>)r2Yp5J݉pHNgC T U:.V@8+9'I9ApHn'0CクY##jƖspqXX%*^ǿ.C?Qt&zzSX$9]sMHO!==<m, 0k._\UmŸz0. u H=Oe\U$MI%R猒c@ʋb|f5YH&#lT HM(6RZEp<=~i͡*0-ĹjU3ޝOaA^>7.#]hNt.:8D./Ƒ5ūهG&ZQ84Hq z {Zϰ}ngSq!鹢D`ԙ~^n#0[O~Vߢgz˩jsBT%bm4eǽn ᮾ_Ƽ{֊H}_ǁo}\\ E+ػ?,A%p/ >6IM5lF$052#qn6ʲmf+~0#E2v(c[-~opF%t*bʘNQYC..+Ϊ & 6z#vNrT2Ei\$WE,}uwexg HXd!F?ȥB w%!nNrUi* 辜⥌'biE>ʊLh=(oPb )xu[|M<AZOd\*̴s1T$A$Im+ZT}C0=;>0&jEޘkUC? |D hwhWAmcz yknnk>wd J_mԂ%iM@I11o]WNS";d3lV|ƅr:l9ׁ;HU]En'Ķ 2lb4LjjE^j󉁨}E/R7񅞲YCS/gH>au8im04FKWԽ>>,z<c}VHflT̔got' $ ׏4M,]!BO"%_zc \ܓ liSjV{`- L^Dpv~iκ"wOÒֆO=. т&$h_[$H@04T267ldlސGf{1c.imH1!Xd Hޚx:]JgPהN}v֍\zyU' ;_5)k˔Gc haT%Bʂnj% 2NS1ߢ?K!U t]}#d)3{(G,Y"J2 "X 8Ͷ)ΙUc-.[Yִd<.]/=!Pp%&{6 [hs.V g,~gBΎ k:sI js,S"yʠr̻Y7BJ0+ ɐO .*OطY\{j76(k:Gn:D XRA:UbhL1tw79iRh9ET8b#Cǒص(:$%`Bpᩈ8t.ei8A>N=,`nd*nv݉4ƷT5w%ѝ8Ï)_fV d @]ͷCV[(vvXA''+`d癳U_,!/ o[Vsh0}GȣftO}W_9yBm)%Ͳ},gINJֳ0G%0 h7^u}o}}D*qjwJvPL$83%*މUTmKtS4hpDSr e&ll5;IVbj0NSaD.6#;`Q(Ԡeu7I8<߯o0M*$Ȳ!4N}-68cKAȦl04w)HDFl`+ ɼ9ULtH uT jǗGcUedDz&?RT"Lv dnj*Ҥ~@9QqY3^^E2n\՟ɯ[F<{Nɜb&);9bopi<1saz&~KۧO\fR 튧w EJ5Ϧό|vF.;G#F;QDrH-s4.9ivwu;6hX weYjJZ V?{SaTJkܮzMEK=ܒt7',sRpUpTx.UHvIDsLUy]¬ˋX_`ŰY`SQV#L00,qh6c`FW~"g5%WӐl)k&)y8pY;Gx"Q?zr^a)}r'mE M kTqʔq*~u!I㓲+NbeU6BlÌXxxLkc&kbb9.ZiR\0]26 5I]#&KZZŌƟoaZ]GV)ʂ*h] >W=쿤i x|vO!Q3kdߝ ?O7:mpDM7\o ݅e=M:Nwo5pzg9ygccK>Wb ܖe|IF؊5b$?CCBj[i )R!FoocIlD}#hހt{vL3P#Ab>S<2?'QUg/񩡀nӘc77 zT=n,bQj2y@cwVXIةnJczC7%ކO-. iw uw)hAoEE:mU铚2 O",,gNB0oZ4Zݓ_|'റ\-5Nhsы"v}aˑǐw~mܬ[λۤj9Qⱨ㍊Sx_Ļ@,] ;L5]i8?p7Ⱥ?!$aYWr{C)@z zk~lSBc'maabt;ݛIYM!Jg #iĹҚvaϲ\MI zcB HY %_!\O C۪1-r´nּWcR'xs}}2{Ahfƍ#qKY< iMhjz&Kϩ4W!Ƚg$r~Wd;k|T 7`+|~ֱ\ 1 m:FL M:bŬ>ιbs %κKX- ]i5l_ e jxThK @~z‘_lʰ=Gu%!3ZްEz)[S7J"]׆$j2Z >ԵX[6U)4R5Bt.RtmUGL0DQ622<6(u"<{qpEQgIdkl7Ō35:%6e $=mjƵXsay_.(aJdþKySa4xuTҔ@-E :/.3SHqP7gIն('>yZ˔A~?P!R{O^ꄟwM)'~ A'HLO nO|"LkD&x0GOvgG|uMZ%i5$4=ϕ^` I8&|%f~!6WSJPӧW=+U`J8=[ m[%p/1~|'wI}47 "w9чX˭~j sÛ5*>aK%LWӌ MɌt愖j4 ls'~ԺrxLxEMEqy~1m ,*WGk*+@6u0Jm3g|˅ B"<'$ɬz[t7sNuݫ:M҃N~o*H- [&z/vPurG}MU?MM:h?=EU6h щbhhw"e5r~#͙N>Nյ16E~8Ɲ:UdVW %?(GETk8btAw?^ޞ;EٗXkF/h>6!'0KTyuУk)^g o,fG3S%_uUeUo7T311PhȑofsJYѴWPQx#4ziȪvYd$ƌ3,1墓g"( w{C EΟ`*Z (>Hb#-8^jfE&VF}F]wRgWN6遒q^s{ΔD4k^Ik9^U6 9L 9;(\,=ѯ !ج6 軔Q# kbmX W戫~e"-"Pyvt5Ǩ K^B:~Ff/|^N$K-B&Z ec_ƤI ȈHPR'_T㐓< MJb<@fP88E,>5HDnH!fΕCrP'H uztMiAQRx x{ vA.y Tm$Ԁw0!~"TFݟHZ.(R9+ܞ!o;=\Ȼf`)(.{a'ՙ:gocHRR9=LPyL5\kp0JZifg /mlv*mB2~L1^~d s;Cl! ܗ?;ygM?gӈT)N槀.4Fed!I.vv[& 5'F*1YLUb X/F:SMa3vrDEK[SUTMi ];騟!ĄxU-cH FÇRnM 68(櫮U> endobj 357 0 obj << /Filter[/FlateDecode] /Length1 713 /Length2 1588 /Length3 533 /Length 2119 >> stream xy<Ǎ-gRb[͘1vt"Yc1S`iJiQB."b:A1Rȱ%[*K;ǥ3vu~|&%t F;[ DAuu E, @[Z,;0Fh3+ 8 Jp 鯹lȠ4 D!$"( , ^ax 2A2 E2` jH e2ЗV8`SQ4* AP#Wh(bXl*Օo0 /=2:dоzlo8T K DaDdE T&IiD[`dw#Rh}CƮ?ע0(BO_ sd -`id"--=2 aj MEc If) 273 Hc}:RQDHDvjAot>ss{zPl73ip]%1.։w9' Z< +73$fFK N3Yj7hnXiyD4 FO ŬH={ /拥iȾ0N1,:[Axv }A-B|m7MvjV]CY-y<'&KV#buX.Aïp)^+İHyM$*,el@f]mDn -R~ּK~[1wzNyPFS gf#Y妏_8ׯ0]ܮ-˦WIC5t^(,\Um~=*/}WxMkYOmaHӦm$u ?­zpB,s&3V#qb M2ob8mJ%5:e4,H [p5'9:}&J~KP|TwDaLjp䮼':F2 cA4ERT讲k*˸Zi[2.,6va1~[Z\b;!9C/\`#WDZ1v-X5WZРjRbb]/qk5a籹BJQٶ;T:n4*UVmDPp  tiG.H=–rɛSsqtZ0ZdlCJFGsb`Sǁ1 )RqO+8L?xN,8<4ŗ^>> endobj 384 0 obj << /Filter[/FlateDecode] /Length1 766 /Length2 1238 /Length3 533 /Length 1809 >> stream xyTSWKh6( *>PC6 *P|$/=|I "KaD<*PQD *KDT-"T@FX,z,VcIuXgg7g}rsCm8h<""[: 1:F&H>`E3@wrbP<>g&ә I$`^!/u9^ HA>aH$--a’!HF%q҇R2 q.j K%RCP|? Jq1 q~W0_[$ECVF@т!,@>Bx|OBXqBa;|!Jܸe+`g򥾯0"Bե7%@4 w7,f^~`07 R" b0@*$ L _. $ި$Ξ2p̔k#|&Fspt\β!凃;!¸$ČpVS4K,on &ZJ䟿c1FR3&k:f-+sSwvgk={򘽺B;d7ӲHԫ:HنrFK}*yQ谧|y6Ư{ڛ|FerC$Aj*Wjo cjXY^>5z~wyM!lC#8p FvP鬪ɠJ=nEKPKUٖ,T-QL&;ry zPubV@O~(E.n K[x#55:{. {A)DWiXMY{uبmtſjN~Kw RML·Sɏ OK66(F՟U@yiUA%3NFAWF"9RHvWDد1IvխtV&a.)F>n1ElFSlYwoͦHCO :q:,ԻnZy:mRe1Jlt,8ř5$PC_}T"S-r9Cq{K2ꨜBZf>>2Ue@~܎Ҫ:ur/?7Ӊc,]?}xCAMѧч7[)o8W^)OC@_~O_BaeʽJGm=7_^#&پT|#xpjn6雟+ ֯OXwl߱돟du[Pya/Frfw[֛fN78#@LV(6\詈 9heƴ't:XaC5d^ {ܦ(6]cr)t'Ga:GP_/1v6Otʚ)"se4ЯWo:aa1GݖAS놝%"ę ců䁂3t'hzVo &Jz,&FLi6V-Re&Hkj(ຒK ~[S\֙+;Xm G~qMp±ɲ⾈ʔV禓K,7L޺\OO5.@dWxeL#>"ޜ8Sf6oQaMr0n[^f#:KY#_B͇@L @,H;jP endstream endobj 388 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[37 -250 1349 750] /FontName/DBNJLE+CMMI5 /ItalicAngle -14.04 /StemV 90 /FontFile 387 0 R /Flags 68 >> endobj 387 0 obj << /Filter[/FlateDecode] /Length1 785 /Length2 1549 /Length3 533 /Length 2134 >> stream xR{8iW!C&uqcXds |f3v0bK")9ڄJl#KJ-vVmJk﻾#Y\3"N^^@rA A`.f9\ @$5ȋ@ ![@d`;:FDr*82JY̍D*Tƛ#^X1h( #( _U΢zƋXC8]nM Ic clI?D?ot}Jc0a*W)LO"MC8OȺ6/Vݹ0:"`YVy4#4_K0#Y#,ڧJ$;S=<]Lg^Q7 >?k1g,`! P?B>i¢i(+ X0c@ $Bʢ!|KYl q& 9ձJ&WSkI hI,V$Hwg-m֟Hq8$n}`#TLUzIQq/kgY-'36R_'lʻe,8XYrDϪ'С$:D F/E2ыfHq7 ]̳JF9oI[fym-\WHfUYfHZbNWʝM#qb5[z=ws yr>{wܞw?Mܨx/5stSbf_Ow_ 4M:6sj9kw~zLmҹzq3rO*d~+MU/9lmzsrr6 f+6nqk,(p,?+N.pڵeFŖoxjeoL] [7OܸLK̢]{grj}jNjꅽx ջ1?1z7=M&:O|-?a{&Z_eמ#qO}8j Tc{ˆoD_~~ulޮ{ە,d*]bN}~#蠥5^lVY&m( t-6'7/\DXak"E~1]|SwI7 bDFzoDzU?yA 6[? ;tV?Cd]gQClEKPxqOTmFH|t}v%M]$KD0.¼jg vJ>febŬ޽fF1FK]A:J0cG}ƣM?k۾4->"uoB~bBC=*(vH8~ }s-2d cL פ[tuP>xKz~ʑjGo 3 ,Kt4z<0ł̈Fdt>4:im;7͇oyX:H!\U/Tsl&̉`!FJ endstream endobj 577 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-24 -2960 1454 772] /FontName/VDEOUV+CMEX10 /ItalicAngle 0 /StemV 47 /FontFile 576 0 R /Flags 4 >> endobj 576 0 obj << /Filter[/FlateDecode] /Length1 1101 /Length2 2820 /Length3 534 /Length 3522 >> stream xSy<ۮDDɣ(03aG a"̃a(*o!EɖIѱ&)ݼrq>?}]}}L@u: PtMpCddti NRPUEF$y@"ԐH5%4"RݼiD'g: {{ &4"GLptgHO M"K<s] x::) )T&xE]il_wMB#DCefǾ[{HU#I޿2d7O:HLF= i$=ɿtצ8@"z@wq$R`oL~O G-#FD#zg#؉/ߺQT Uo;R.#"z۲JgT"@ G)$Б@tr/N (:px{ο=?F ?Rq܁75H@")mnF@l((X@xx?~U6S?o2 JJ w&Bt p8R=i!;ٗ=,)y꼆s Pe54jmwsw|Јr!dÍҗ0㗠 q2u c=KQF O{ 4kzY%6t(3Kߗ%7Na6IQ\j\H$;/E<0;gy&_̾+V*Gtae+EƊmR3+'}c :$1D=1"B VoCJ嗀${a]mREK[+o*ykNsX֚B>|͎QͣVK Sܟ { ?Ml"Qm㵙a ȧ\Ƶ;3>Df?˪KOw(41m!(&LAZu99Ccn~>.{iSn&ω@5 7pw(j(S:QNEmf!{? -rdQR1A@#y*+isI.{<\}6Do%iU<'sPڢR('4Zvs-aPKH-$Άklya5ژ3ȺGcOIi'TI=9}U-?묰 &bcB3\IۡtGΨx0 >(C%Y}})zL`6zWigLLoNx'bae!?U W!$I}5`ڢCg*Lu6}ɬ]u'ל%zv%/h ӊMC^q!g?jn7Qi9Z]A7^tnMwx@1/ =k쳽"dVh/r !`N$w#*dl_oe>WFgYRD^^ dU1v52TolLݣG5$93. )aه=[ 1̽wJz>qۋ3avND6M$)o@`|W&a̲VwTeY+4>{o%{}>]H E\QR˲7:?f~>hDeדXwd-JRN7)Q#WzG3:^<.>90gIdh'O˭`w ܌v5q֦}9eOe)fi"w~n_7#kfZ0¶QMX@CSͦ <1y o䗘_s~j|CI̕1\%3j)lԕ9Dxgf=Jh5Ϙ4>rI'*Ox6Ў@;<}0+"笀8|AV'J}0#t?Ujи9=s+-kP_K'}E>ɾjߙ+c ԩa4R%F8FK`0j25;4 *yܪxXޏwy ey4mRAU)5~Ӳlap_w?ݺ}!ӻ!ZKg=8ȳ]w]Wj5}T5mrcFPQ /)|]?m2/=.){Q>ٶ۷6Ԃ5;7 taäF`k`jJ$%8A 0 MofUA2O-99jǾ"[HS*cO,Tݾmu:M!r̕oUur=r!&o@6{g|ygr^y0cl|ӇoWp- FãRh&~Z9 MXs0ؑ{SKiw-'%67(g\9#G<ҿ3?ЀQ\䥪yCdz\M/?:Qg0UWi{sCͧՄ<\GKB/K*>ûuscW]#mkY?L̼'Q.-*[0E"EIJ[l{]zR{.Yu6~&Qܓ$<=\FM/$Э~H׋1fFq)2[հ"m1VeEoJܵal9}B:Q妖b& #ڼXH!xJ1tsЊۆ̜ ;\W3][8U s(Pt)z W;,?ҷU+ҿtbCZ@J dHO2}dY! Ŝ)2> endobj 675 0 obj << /Filter[/FlateDecode] /Length1 1168 /Length2 4720 /Length3 533 /Length 5483 >> stream xgXSk*Dz3W HIR n@A@P* IRޛ:9|93sk}H sAjD0H! <ҙa5 H D^TuJJ!2 Pi@ ģ @a ?dj$w H  QHB8FxhAq"Ppú′_/!>\@aRN )%EHWGrCc7\6vAug  ˗p$R_ጐ/柫zg4 uC#I(mTa"݁/OE3in&hikOZ5uFa ^Ob| LB\BB*$~rCS:΁  d@J W8< о> P =%ɐjH;C-BH_Hl7K>p }1tY`_$&Pv I!)?7$5Fο2@ ߐd IF^!|~C !~YpoH2 IFA!/zq $)% je\fEy"4P 9)?U/I"p$2 dd8^,C\sO| %\<&"cw-,FlE.&Qtc@^NCW61h6Aa4s =Dk;/Yl􇴐ƨJ4IP֑KN jxfUQ$aA8LlP?mۓdipku*cNֱg?B|iC| a.г%ި#3EHt%:GeV`3`v e.̲D|.sPL}uI0)|,ōJ?A~BqWR.њWT;q#)Kc)swaJ,qcmTm&;B֩yBdQ*K{ 9f=ƭf:>UIrJ*'#픒']YZs󴊤6>1e|s~&cWB zR|zg6= {CƉ<Ě>(K戴RN^ƆSW9PD1Yѷ'?  ڝ[ڢ_O+)'3k^t]?\YPP>~{{{lO vfV@ ;e;Igs7.]}d{;>Tˀ&8̩{Ƚ tW3Tʷ娖!g|iۦ욹Xo[)EK(nO: 07bzu=HPdUף?rÂBu(<7S`Zz}DQQL^NztO c[% J)|K_ۧ9Z36 yԈ}q@:hԒmg1aaU%S};j! RҼKgb,*]fstsҌ}m2CksQH¦=>y ;ӂ z^8Pes4UMculYל rTGJTt>ʊGbS k^jMAFGۻF5͐BfRmL~e4x̧ !VdT:a^[3eXo?=Q!J@;#$+TT*E]BYմn!,!RVE%l,_.o$Dp)qW]NrGA9d1|{a.+IH*]SYq{i`Brh7kAݶ相TZ9Alk ۥOqsϩA?A0·B+D)6Nς>}V>`(2ˌp4r_̗$ z0\ݫp<cWgHGt|}x? \X+y[+e[P#*R+Q1b~tX@ HK3Bv'ejd΢sVOR^Ա}W|2uiN{gKiMK *Y5iR F4CƎ+\,@ϵ"Ş#ߗF LJ;ܭ =˸rY4̛5O>3,pg+~;XnHRYos8Qo7n Mkv$1U:_i(,<#L+'h+pص8O ϗX|pJV,aR+yDurh:wtZ wS[H^X7»y\eSLn-Hf< UcT?䗬•{X$lFDEV5Y]xMAx+jڪU}Nύ7,2.50{;|pAI2f͡x̅A#@֦*KYWܫCPJ8h͎=e"VL_LAݶ<˒q@ "6Һl:DB4]ĴbF[+_*ӗZ5Scu%+DJ̥[}AqVv~^jROة%kJ =N2JJѨ0U i-ٺ#6Ӎ1:-uOYҟkqKBkX5!4jԄ1m4gR!(ؙQmaRUtv3F9> }cEx-VJ7g /cZ΍4^%Tb FNqsf%+wuO5dcjMhY>ο^ b`% 蚸?pޑ:Q|SXUΠw?ŭ@\!l*Ws2*6lIЏW#+"W뵕zUT+v+_N)Mm""HC:}&qBVzXĂo#wt-`tґ)nۭ /T#cY [tx9`9}C 7|[ Uq5&9Hny$zKTVvRBE[ϥ6oWO1e!% Rrv({hq9P]Ȁ'Jleu2>"B,yOdFp;JyN}e\gԹ "z*!oE_܅$^ho_%n8= ET&\&$ptYA/ipm+yK;Lzn4+xQqȉWt]D vf?ڽoczhÉEXgy36!Rko*BF=oEn4+h΀æҸm)4<hE|.͞}7 p]%DjU(5%l3aOI?|Mץں}o,Y=@h 4>6 ]]25Q]*^&cdm)b{ը/ϭ*U37xDHeՕMo^LPPjC\rݬ;}Ȕj?Y5 0KN&ݞuVj\}{ik}#>?:/\D`mv?k-"1M)KT$kk7d0{>t&So2xu5/vPA}:Ry7~E9г~pQfYj*r  ,a+:"7u{ua(׃QD_pZ^ KyKT;\s,YOto%yy: Α ]o>P?4DZ%k.|(r7P%x/mq[د%z i+EX2AAc7 y(VF}Hpz)o+8yTT8e Py}%/*@oIqq^ .vjJvS>'Hg<q{W endstream endobj 687 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-15 -951 1252 782] /FontName/PLHDEG+CMSY7 /ItalicAngle -14.035 /StemV 93 /FontFile 686 0 R /Flags 68 >> endobj 686 0 obj << /Filter[/FlateDecode] /Length1 881 /Length2 1445 /Length3 533 /Length 2089 >> stream xR{8TVJEP[.ɵ24fY`&I'ES4ErIQtqI.ŖK%{78"d{NsyZ}{?=-7O  9ā-ӏ 8FOϖQy0 s43(ٜ`ˎp`F(05\PāiTPy 5Q'C<lLca '00C 29!!lӣ"Q МF`l47~4wb2]9`Ń8M8ȏR_h1QY' l)`BZ$`̇n0 P\+!hgsbFx^ |W]80 ﷯Ghl:0"i @p "H@,M!l@G9V- gHw2T.:?6'cb;foV10#0OݗE2h1 _cE8֌z o%# ';D (-Áˊm%Ba2/on'ھM}3K<a7n69\8밅,r'75ӣ&DZʁɇ.ǖC*%(+}.[ȕ̎EÞS2^DM PL*Z{wgbQWieL}#lո;N 55Z'' Sƚ%C5-K-ٝM򵒏W佷W$$e~6+|-3y3yé/7H֧U0 hLu:Hv.5 вκ}cT,0.mnŢS/2*N_}Sǎ+qS{ϭE̻yiEwZ$9UڐUT KD0fܺ(%u~ݍ͵ͧ. E${<nYnuZdGfƌ!AS {] ̰) :kSVUsn뢄=C:i)n2~YR;2]<8V2g&;LGȬ"vI d_nxFm`-u_]X#4J=9Bg[fk"/E 'M= { ggەo20̹nmRSdI?WzQVeN[_o8̎s7HL5im {YゃC+ to Q9<6 ` K endstream endobj 1279 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-4 -948 1329 786] /FontName/DMVWWS+CMSY6 /ItalicAngle -14.035 /StemV 93 /FontFile 1278 0 R /Flags 68 >> endobj 1278 0 obj << /Filter[/FlateDecode] /Length1 773 /Length2 759 /Length3 533 /Length 1326 >> stream xkPW@AH@@( dJ &&":0Z)*Vܼ )VKhrhv:N:?}w{7=4/#XHS,`>_Ex<,IS8 }&`@LPDM g)1 Cq q685ᴚILa[AL0@jB I!LT CmBZ2@&.lQ45! ; r,oZ-בZӟ.QBi2H8& WY\KeF !L$%Yu<õp)b8 {|Idd8ID7Ӄ5BbH#F(qzla)5ME^g܄P$) )4rf-& 2ppiq'K<!W_Sdޘ`WgH?:䂅gyiOTn:EkU{VT%6Equݎ0u"wYcEWDžw585͵^8!pFGwJ=Kwpè_FQlZnQp#(k>\UT5 4/J#'o)c7G?P׭(;[D}Pvzz>pnOb;HR5+?bl|wN?ؓR^#~s z{o}g$U*_>!FZ?nݺqIrpgReFH<83ZDسe/}ywU~t}O|N0}a"碜Dq32|r~HPWz?s[Y?+w5'/wm޼y.rJ{CJ,? i:2sl_Rͯ3adϯu^X4XI:|O*0ک0cCM-<[[]gUl֮fY9mҍ/6ThͻAssHGm7AN*sg۽jǏZSV\W]%N?IN}#3Bƍ)tXVay$1yg<5).(7Lsزnm~_,yuQpK&VcWe6cu*3g淖̙ƐPXJw ϧ($!PԷ]ؠǎ~Yrmf(UN_INhJ \V \zxD_>  Kp&A` endstream endobj 2261 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-34 -233 569 710] /FontName/HBTWKG+TeX-cmbtt8 /ItalicAngle 0 /StemV 80 /FontFile 2260 0 R /Flags 4 >> endobj 2260 0 obj << /Filter[/FlateDecode] /Length1 913 /Length2 3071 /Length3 532 /Length 3742 >> stream xSys9<>bny#]HDqL@DZ+T:<08D0»SA T_Qp`J0U!O"@"Wpx,<+3nH\?b$_ م y=L۳ HN?,'bܷmV?'<X,r?;NswۖLsP~%P1x,$*˪Ē^ ⶫ0N,1 )K_$8'$O<O"0?bh<֍zz_O{GxO#3S.gM-~D@18,~5C_4;0T4HxP)x+˺ XOtHUC`|!0%*ITV AN$ dVhP̶'m A$YQo@=~*Tߠ:SN==\Y8PV.dhb ËQ(7 og79S$oT;gz]IO̢CO|}W+Zb[L2)-rSOB|1C7VGAnO~}`iKJݽqΙ,NWvfK6ZOfԢm%m;«0NJo#] 6򗞟<[X]ӊoOymgQַQrf&Q{*𛧥$$FE:bGF*+?trYR">qpNu6u^񕂆~FkO?ba.[jIZh+)^-_}C|ڪeYM@z3{'9`ʌOMN]Eœ^˘@M5,x b/o{e WDF=ʺދhh!V6U _ m ACb2|VY$f(XZ+ kWlW_llpmZ)j_)6'~L:t⣎$+sA&( >EsK0;Lsh60AemxijL튊ɂ9$›fՍ!" _]Ɯŕ~"Q'W#K=セ>#PWTDkKUݶ93b8؜LJ~ )w -)"tmF13J+n&.Qls/ &(oV-6@拠^b/92XId|š(/g]~~wJqvfQ$d #AV>6746X9bt*,:Qs~t_\%x+n˸ޠfIćnv8Q B&WVq[O%k o$`ݍ8wz͏Z{xl~;ga?#ُ}՟ e7 a#+nxW2JRX8ȭ@8ߚ4mK-RuSו_$>|D[7=t+a7E+6?EGZg(T B9؂iҢމm<uՔpG#ϵo4K4xF1]FX_eZBugWNjU0Jɹ{VzylZad`k茙CxºV G$,{>92D p!69C=ؑR\e?$˚Op_ӻT[Ӟv觶0*1F}sYPnUWnSm5KL)#cMU?jg&QuJxA|uaU--0y = Z㙀Q&{jtnjO@ ! {2wHxFXc+ovu?^bG'>ө>6]Ao?3GBw,Rӳ=GZ6vS F o@#$Ja+cW׻1ҋ5KȌGGy3RO?m`50;ヴ"gtn6uc.MYe6 o,ődV 8ϲp%VeDmek750WW_9KKʳo@̬+A[G3:% q'C/lHL](6ٍZ^wݖ.A&Oq ۅRo^-P6."uk6Q5}ePPV=tm^LevF w \ZSOG( .ilUU*m|ߙ֤Y %ĈH rS]+iw6;.|ҏ@p;jBDYdQ\uG-w 0+ ΍VA2^8qy]%kNN\VCrVEeQZx+r߆)HSoz.z>۫ `e{M2m'6绫M0L.oQ=;̷|F^ѓB\QCmFə:q{k /ж /Hg<-J`?;^▨2{({]V.Yd͚gbxqsOAFOKW -Y yHv+ Ob(TCq~ endstream endobj 2494 0 obj << /Type/FontDescriptor /CapHeight 850 /Ascent 850 /Descent -200 /FontBBox[-30 -250 1026 750] /FontName/CHJGWF+CMMI12 /ItalicAngle -14.04 /StemV 65 /FontFile 2493 0 R /Flags 68 >> endobj 2493 0 obj << /Filter[/FlateDecode] /Length1 797 /Length2 2486 /Length3 533 /Length 3088 >> stream xRy<F-Edj:ƚ%$2f~fk f DeB2,#Q~s?>}~prQ @0$ppAmI*aIxiblh  @vLM z_Y8dp q@@LL2``F._.`()0(T2 -F})aZ ;T T04@ pG`(p7~'h$WoaKDx2tVdL fHs)0]F%1i EawJrACHP2(?:܂hkA0vwHTпԂT.!HQa̤P Hl6AP  @2`rGA4Q@ zHh WOAw_ܜɍ!( #@aD7hhc `8#c7f ηW%:* r͋)31FF*#CDbQ _-m%2s9n.\t-Y:X鱗cXWGen:<(+|lwcض}ObMc EdͧjK]qƎKKi6F,I'W-~"YmrzF88#ScoܘcE[*==~Ævo^AUc\2^J9XZY!OD`vp "ʙJ#˥Sg % D) b2 B`󝶷չ_"L- ،;2{К^W>̌nștox[lKAzHO0m~M?'^v:WhO$O*O4q{#VӒp lJnwTYσbu ϝ[aFŭlrV\zMQ\MKi#S0 wa'Fo9nER>w3JJ O[oKBOo,Aɑ'vړ\!UZVg@bw"D1{a7$רgv{~=Dr‰;>cMbJpgGZr?02<=Uo꠲ 8[R-\ Ke[ DxeR#@> ϚJL4]!cϝx\oCTVNXa Ć޶IG?jxr΀^KaCL_3ttP,Ih-ӛ0IF {›1F_>:_O{t=^ ?淨_‘aMD0a[󐒛=^׎Q|}'bd(3ۥ28ڷe_>œaHWޗJV`-4źɶ0gjN[ *2-H̔>Ri )7Uw/Etx=%{/w7 ţ)I;q rI:8n|Naқ7; ȍ<6)u(UpůRBg8Įϭ~3g/qlUӯGO/VN)pMeso B_38^'YOebY*zj7m NM\%,@dOʅIu$*1VakӜy4-&޽" _.'40$vw endstream endobj 1 0 obj << /Creator( TeX output 2002.04.01:1527) /Producer(dvipdfm 0.13.2b, Copyright \251 1998, by Mark A. Wicks) /CreationDate(D:20020401152825+01'00') >> endobj 5 0 obj << /Type/Page /Resources 6 0 R /Contents[15 0 R 4 0 R 16 0 R 17 0 R] /Parent 3479 0 R >> endobj 19 0 obj << /Type/Page /Resources 20 0 R /Contents[15 0 R 4 0 R 21 0 R 17 0 R] /Parent 3479 0 R >> endobj 3479 0 obj << /Type/Pages /Count 2 /Kids[5 0 R 19 0 R] /Parent 3478 0 R >> endobj 22 0 obj << /Type/Page /Resources 23 0 R /Contents[15 0 R 4 0 R 46 0 R 17 0 R] /Parent 3480 0 R >> endobj 49 0 obj << /Type/Page /Resources 50 0 R /Contents[15 0 R 4 0 R 60 0 R 17 0 R] /Parent 3480 0 R >> endobj 62 0 obj << /Type/Page /Resources 63 0 R /Contents[15 0 R 4 0 R 67 0 R 17 0 R] /Parent 3480 0 R >> endobj 3480 0 obj << /Type/Pages /Count 3 /Kids[22 0 R 49 0 R 62 0 R] /Parent 3478 0 R >> endobj 69 0 obj << /Type/Page /Resources 70 0 R /Contents[15 0 R 4 0 R 71 0 R 17 0 R] /Parent 3481 0 R >> endobj 73 0 obj << /Type/Page /Resources 74 0 R /Contents[15 0 R 4 0 R 78 0 R 17 0 R] /Parent 3481 0 R >> endobj 80 0 obj << /Type/Page /Resources 81 0 R /Contents[15 0 R 4 0 R 82 0 R 17 0 R] /Parent 3481 0 R >> endobj 3481 0 obj << /Type/Pages /Count 3 /Kids[69 0 R 73 0 R 80 0 R] /Parent 3478 0 R >> endobj 84 0 obj << /Type/Page /Resources 85 0 R /Contents[15 0 R 4 0 R 86 0 R 17 0 R] /Parent 3482 0 R >> endobj 88 0 obj << /Type/Page /Resources 89 0 R /Contents[15 0 R 4 0 R 90 0 R 17 0 R] /Parent 3482 0 R >> endobj 92 0 obj << /Type/Page /Resources 93 0 R /Contents[15 0 R 4 0 R 94 0 R 17 0 R] /Parent 3482 0 R >> endobj 3482 0 obj << /Type/Pages /Count 3 /Kids[84 0 R 88 0 R 92 0 R] /Parent 3478 0 R >> endobj 3478 0 obj << /Type/Pages /Count 11 /Kids[3479 0 R 3480 0 R 3481 0 R 3482 0 R] /Parent 3477 0 R >> endobj 96 0 obj << /Type/Page /Resources 97 0 R /Contents[15 0 R 4 0 R 98 0 R 17 0 R] /Parent 3484 0 R >> endobj 100 0 obj << /Type/Page /Resources 101 0 R /Contents[15 0 R 4 0 R 102 0 R 17 0 R] /Parent 3484 0 R >> endobj 104 0 obj << /Type/Page /Resources 105 0 R /Contents[15 0 R 4 0 R 106 0 R 17 0 R] /Parent 3484 0 R >> endobj 3484 0 obj << /Type/Pages /Count 3 /Kids[96 0 R 100 0 R 104 0 R] /Parent 3483 0 R >> endobj 108 0 obj << /Type/Page /Resources 109 0 R /Contents[15 0 R 4 0 R 113 0 R 17 0 R] /Parent 3485 0 R >> endobj 115 0 obj << /Type/Page /Resources 116 0 R /Contents[15 0 R 4 0 R 117 0 R 17 0 R] /Parent 3485 0 R >> endobj 119 0 obj << /Type/Page /Resources 120 0 R /Contents[15 0 R 4 0 R 121 0 R 17 0 R] /Parent 3485 0 R >> endobj 3485 0 obj << /Type/Pages /Count 3 /Kids[108 0 R 115 0 R 119 0 R] /Parent 3483 0 R >> endobj 123 0 obj << /Type/Page /Resources 124 0 R /Contents[15 0 R 4 0 R 125 0 R 17 0 R] /Parent 3486 0 R >> endobj 127 0 obj << /Type/Page /Resources 128 0 R /Contents[15 0 R 4 0 R 129 0 R 17 0 R] /Parent 3486 0 R >> endobj 131 0 obj << /Type/Page /Resources 132 0 R /Contents[15 0 R 4 0 R 133 0 R 17 0 R] /Parent 3486 0 R >> endobj 3486 0 obj << /Type/Pages /Count 3 /Kids[123 0 R 127 0 R 131 0 R] /Parent 3483 0 R >> endobj 134 0 obj << /Type/Page /Resources 135 0 R /Contents[15 0 R 4 0 R 139 0 R 17 0 R] /Parent 3487 0 R >> endobj 141 0 obj << /Type/Page /Resources 142 0 R /Contents[15 0 R 4 0 R 146 0 R 17 0 R] /Parent 3487 0 R >> endobj 148 0 obj << /Type/Page /Resources 149 0 R /Contents[15 0 R 4 0 R 150 0 R 17 0 R] /Parent 3487 0 R >> endobj 3487 0 obj << /Type/Pages /Count 3 /Kids[134 0 R 141 0 R 148 0 R] /Parent 3483 0 R >> endobj 3483 0 obj << /Type/Pages /Count 12 /Kids[3484 0 R 3485 0 R 3486 0 R 3487 0 R] /Parent 3477 0 R >> endobj 152 0 obj << /Type/Page /Resources 153 0 R /Contents[15 0 R 4 0 R 157 0 R 17 0 R] /Parent 3489 0 R >> endobj 159 0 obj << /Type/Page /Resources 160 0 R /Contents[15 0 R 4 0 R 161 0 R 17 0 R] /Parent 3489 0 R >> endobj 163 0 obj << /Type/Page /Resources 164 0 R /Contents[15 0 R 4 0 R 165 0 R 17 0 R] /Parent 3489 0 R >> endobj 3489 0 obj << /Type/Pages /Count 3 /Kids[152 0 R 159 0 R 163 0 R] /Parent 3488 0 R >> endobj 167 0 obj << /Type/Page /Resources 168 0 R /Contents[15 0 R 4 0 R 172 0 R 17 0 R] /Parent 3490 0 R >> endobj 174 0 obj << /Type/Page /Resources 175 0 R /Contents[15 0 R 4 0 R 176 0 R 17 0 R] /Parent 3490 0 R >> endobj 178 0 obj << /Type/Page /Resources 179 0 R /Contents[15 0 R 4 0 R 185 0 R 17 0 R] /Parent 3490 0 R >> endobj 3490 0 obj << /Type/Pages /Count 3 /Kids[167 0 R 174 0 R 178 0 R] /Parent 3488 0 R >> endobj 188 0 obj << /Type/Page /Resources 189 0 R /Contents[15 0 R 4 0 R 190 0 R 17 0 R] /Parent 3491 0 R >> endobj 192 0 obj << /Type/Page /Resources 193 0 R /Contents[15 0 R 4 0 R 194 0 R 17 0 R] /Parent 3491 0 R >> endobj 196 0 obj << /Type/Page /Resources 197 0 R /Contents[15 0 R 4 0 R 198 0 R 17 0 R] /Parent 3491 0 R >> endobj 3491 0 obj << /Type/Pages /Count 3 /Kids[188 0 R 192 0 R 196 0 R] /Parent 3488 0 R >> endobj 200 0 obj << /Type/Page /Resources 201 0 R /Contents[15 0 R 4 0 R 202 0 R 17 0 R] /Parent 3492 0 R >> endobj 204 0 obj << /Type/Page /Resources 205 0 R /Contents[15 0 R 4 0 R 206 0 R 17 0 R] /Parent 3492 0 R >> endobj 207 0 obj << /Type/Page /Resources 208 0 R /Contents[15 0 R 4 0 R 209 0 R 17 0 R] /Parent 3492 0 R >> endobj 3492 0 obj << /Type/Pages /Count 3 /Kids[200 0 R 204 0 R 207 0 R] /Parent 3488 0 R >> endobj 3488 0 obj << /Type/Pages /Count 12 /Kids[3489 0 R 3490 0 R 3491 0 R 3492 0 R] /Parent 3477 0 R >> endobj 211 0 obj << /Type/Page /Resources 212 0 R /Contents[15 0 R 4 0 R 222 0 R 17 0 R] /Parent 3494 0 R >> endobj 224 0 obj << /Type/Page /Resources 225 0 R /Contents[15 0 R 4 0 R 226 0 R 17 0 R] /Parent 3494 0 R >> endobj 228 0 obj << /Type/Page /Resources 229 0 R /Contents[15 0 R 4 0 R 230 0 R 17 0 R] /Parent 3494 0 R >> endobj 3494 0 obj << /Type/Pages /Count 3 /Kids[211 0 R 224 0 R 228 0 R] /Parent 3493 0 R >> endobj 232 0 obj << /Type/Page /Resources 233 0 R /Contents[15 0 R 4 0 R 246 0 R 17 0 R] /Parent 3495 0 R >> endobj 248 0 obj << /Type/Page /Resources 249 0 R /Contents[15 0 R 4 0 R 250 0 R 17 0 R] /Parent 3495 0 R >> endobj 252 0 obj << /Type/Page /Resources 253 0 R /Contents[15 0 R 4 0 R 254 0 R 17 0 R] /Parent 3495 0 R >> endobj 3495 0 obj << /Type/Pages /Count 3 /Kids[232 0 R 248 0 R 252 0 R] /Parent 3493 0 R >> endobj 256 0 obj << /Type/Page /Resources 257 0 R /Contents[15 0 R 4 0 R 258 0 R 17 0 R] /Parent 3496 0 R >> endobj 260 0 obj << /Type/Page /Resources 261 0 R /Contents[15 0 R 4 0 R 262 0 R 17 0 R] /Parent 3496 0 R >> endobj 264 0 obj << /Type/Page /Resources 265 0 R /Contents[15 0 R 4 0 R 269 0 R 17 0 R] /Parent 3496 0 R >> endobj 3496 0 obj << /Type/Pages /Count 3 /Kids[256 0 R 260 0 R 264 0 R] /Parent 3493 0 R >> endobj 271 0 obj << /Type/Page /Resources 272 0 R /Contents[15 0 R 4 0 R 273 0 R 17 0 R] /Parent 3497 0 R >> endobj 275 0 obj << /Type/Page /Resources 276 0 R /Contents[15 0 R 4 0 R 280 0 R 17 0 R] /Parent 3497 0 R >> endobj 282 0 obj << /Type/Page /Resources 283 0 R /Contents[15 0 R 4 0 R 287 0 R 17 0 R] /Parent 3497 0 R >> endobj 3497 0 obj << /Type/Pages /Count 3 /Kids[271 0 R 275 0 R 282 0 R] /Parent 3493 0 R >> endobj 3493 0 obj << /Type/Pages /Count 12 /Kids[3494 0 R 3495 0 R 3496 0 R 3497 0 R] /Parent 3477 0 R >> endobj 3477 0 obj << /Type/Pages /Count 47 /Kids[3478 0 R 3483 0 R 3488 0 R 3493 0 R] /Parent 3476 0 R >> endobj 289 0 obj << /Type/Page /Resources 290 0 R /Contents[15 0 R 4 0 R 291 0 R 17 0 R] /Parent 3500 0 R >> endobj 293 0 obj << /Type/Page /Resources 294 0 R /Contents[15 0 R 4 0 R 295 0 R 17 0 R] /Parent 3500 0 R >> endobj 3500 0 obj << /Type/Pages /Count 2 /Kids[289 0 R 293 0 R] /Parent 3499 0 R >> endobj 297 0 obj << /Type/Page /Resources 298 0 R /Contents[15 0 R 4 0 R 299 0 R 17 0 R] /Parent 3501 0 R >> endobj 301 0 obj << /Type/Page /Resources 302 0 R /Contents[15 0 R 4 0 R 303 0 R 17 0 R] /Parent 3501 0 R >> endobj 305 0 obj << /Type/Page /Resources 306 0 R /Contents[15 0 R 4 0 R 307 0 R 17 0 R] /Parent 3501 0 R >> endobj 3501 0 obj << /Type/Pages /Count 3 /Kids[297 0 R 301 0 R 305 0 R] /Parent 3499 0 R >> endobj 309 0 obj << /Type/Page /Resources 310 0 R /Contents[15 0 R 4 0 R 311 0 R 17 0 R] /Parent 3502 0 R >> endobj 313 0 obj << /Type/Page /Resources 314 0 R /Contents[15 0 R 4 0 R 315 0 R 17 0 R] /Parent 3502 0 R >> endobj 317 0 obj << /Type/Page /Resources 318 0 R /Contents[15 0 R 4 0 R 319 0 R 17 0 R] /Parent 3502 0 R >> endobj 3502 0 obj << /Type/Pages /Count 3 /Kids[309 0 R 313 0 R 317 0 R] /Parent 3499 0 R >> endobj 321 0 obj << /Type/Page /Resources 322 0 R /Contents[15 0 R 4 0 R 323 0 R 17 0 R] /Parent 3503 0 R >> endobj 325 0 obj << /Type/Page /Resources 326 0 R /Contents[15 0 R 4 0 R 330 0 R 17 0 R] /Parent 3503 0 R >> endobj 332 0 obj << /Type/Page /Resources 333 0 R /Contents[15 0 R 4 0 R 334 0 R 17 0 R] /Parent 3503 0 R >> endobj 3503 0 obj << /Type/Pages /Count 3 /Kids[321 0 R 325 0 R 332 0 R] /Parent 3499 0 R >> endobj 3499 0 obj << /Type/Pages /Count 11 /Kids[3500 0 R 3501 0 R 3502 0 R 3503 0 R] /Parent 3498 0 R >> endobj 336 0 obj << /Type/Page /Resources 337 0 R /Contents[15 0 R 4 0 R 338 0 R 17 0 R] /Parent 3505 0 R >> endobj 340 0 obj << /Type/Page /Resources 341 0 R /Contents[15 0 R 4 0 R 342 0 R 17 0 R] /Parent 3505 0 R >> endobj 344 0 obj << /Type/Page /Resources 345 0 R /Contents[15 0 R 4 0 R 346 0 R 17 0 R] /Parent 3505 0 R >> endobj 3505 0 obj << /Type/Pages /Count 3 /Kids[336 0 R 340 0 R 344 0 R] /Parent 3504 0 R >> endobj 348 0 obj << /Type/Page /Resources 349 0 R /Contents[15 0 R 4 0 R 350 0 R 17 0 R] /Parent 3506 0 R >> endobj 352 0 obj << /Type/Page /Resources 353 0 R /Contents[15 0 R 4 0 R 360 0 R 17 0 R] /Parent 3506 0 R >> endobj 362 0 obj << /Type/Page /Resources 363 0 R /Contents[15 0 R 4 0 R 364 0 R 17 0 R] /Parent 3506 0 R >> endobj 3506 0 obj << /Type/Pages /Count 3 /Kids[348 0 R 352 0 R 362 0 R] /Parent 3504 0 R >> endobj 366 0 obj << /Type/Page /Resources 367 0 R /Contents[15 0 R 4 0 R 368 0 R 17 0 R] /Parent 3507 0 R >> endobj 370 0 obj << /Type/Page /Resources 371 0 R /Contents[15 0 R 4 0 R 372 0 R 17 0 R] /Parent 3507 0 R >> endobj 374 0 obj << /Type/Page /Resources 375 0 R /Contents[15 0 R 4 0 R 376 0 R 17 0 R] /Parent 3507 0 R >> endobj 3507 0 obj << /Type/Pages /Count 3 /Kids[366 0 R 370 0 R 374 0 R] /Parent 3504 0 R >> endobj 378 0 obj << /Type/Page /Resources 379 0 R /Contents[15 0 R 4 0 R 380 0 R 17 0 R] /Parent 3508 0 R >> endobj 382 0 obj << /Type/Page /Resources 383 0 R /Contents[15 0 R 4 0 R 390 0 R 17 0 R] /Parent 3508 0 R >> endobj 392 0 obj << /Type/Page /Resources 393 0 R /Contents[15 0 R 4 0 R 394 0 R 17 0 R] /Parent 3508 0 R >> endobj 3508 0 obj << /Type/Pages /Count 3 /Kids[378 0 R 382 0 R 392 0 R] /Parent 3504 0 R >> endobj 3504 0 obj << /Type/Pages /Count 12 /Kids[3505 0 R 3506 0 R 3507 0 R 3508 0 R] /Parent 3498 0 R >> endobj 396 0 obj << /Type/Page /Resources 397 0 R /Contents[15 0 R 4 0 R 398 0 R 17 0 R] /Parent 3510 0 R >> endobj 400 0 obj << /Type/Page /Resources 401 0 R /Contents[15 0 R 4 0 R 402 0 R 17 0 R] /Parent 3510 0 R >> endobj 404 0 obj << /Type/Page /Resources 405 0 R /Contents[15 0 R 4 0 R 406 0 R 17 0 R] /Parent 3510 0 R >> endobj 3510 0 obj << /Type/Pages /Count 3 /Kids[396 0 R 400 0 R 404 0 R] /Parent 3509 0 R >> endobj 408 0 obj << /Type/Page /Resources 409 0 R /Contents[15 0 R 4 0 R 410 0 R 17 0 R] /Parent 3511 0 R >> endobj 412 0 obj << /Type/Page /Resources 413 0 R /Contents[15 0 R 4 0 R 414 0 R 17 0 R] /Parent 3511 0 R >> endobj 416 0 obj << /Type/Page /Resources 417 0 R /Contents[15 0 R 4 0 R 418 0 R 17 0 R] /Parent 3511 0 R >> endobj 3511 0 obj << /Type/Pages /Count 3 /Kids[408 0 R 412 0 R 416 0 R] /Parent 3509 0 R >> endobj 420 0 obj << /Type/Page /Resources 421 0 R /Contents[15 0 R 4 0 R 424 0 R 17 0 R] /Parent 3512 0 R >> endobj 427 0 obj << /Type/Page /Resources 428 0 R /Contents[15 0 R 4 0 R 429 0 R 17 0 R] /Parent 3512 0 R >> endobj 431 0 obj << /Type/Page /Resources 432 0 R /Contents[15 0 R 4 0 R 433 0 R 17 0 R] /Parent 3512 0 R >> endobj 3512 0 obj << /Type/Pages /Count 3 /Kids[420 0 R 427 0 R 431 0 R] /Parent 3509 0 R >> endobj 435 0 obj << /Type/Page /Resources 436 0 R /Contents[15 0 R 4 0 R 437 0 R 17 0 R] /Parent 3513 0 R >> endobj 439 0 obj << /Type/Page /Resources 440 0 R /Contents[15 0 R 4 0 R 441 0 R 17 0 R] /Parent 3513 0 R >> endobj 443 0 obj << /Type/Page /Resources 444 0 R /Contents[15 0 R 4 0 R 445 0 R 17 0 R] /Parent 3513 0 R >> endobj 3513 0 obj << /Type/Pages /Count 3 /Kids[435 0 R 439 0 R 443 0 R] /Parent 3509 0 R >> endobj 3509 0 obj << /Type/Pages /Count 12 /Kids[3510 0 R 3511 0 R 3512 0 R 3513 0 R] /Parent 3498 0 R >> endobj 447 0 obj << /Type/Page /Resources 448 0 R /Contents[15 0 R 4 0 R 449 0 R 17 0 R] /Parent 3515 0 R >> endobj 451 0 obj << /Type/Page /Resources 452 0 R /Contents[15 0 R 4 0 R 453 0 R 17 0 R] /Parent 3515 0 R >> endobj 455 0 obj << /Type/Page /Resources 456 0 R /Contents[15 0 R 4 0 R 457 0 R 17 0 R] /Parent 3515 0 R >> endobj 3515 0 obj << /Type/Pages /Count 3 /Kids[447 0 R 451 0 R 455 0 R] /Parent 3514 0 R >> endobj 459 0 obj << /Type/Page /Resources 460 0 R /Contents[15 0 R 4 0 R 461 0 R 17 0 R] /Parent 3516 0 R >> endobj 463 0 obj << /Type/Page /Resources 464 0 R /Contents[15 0 R 4 0 R 465 0 R 17 0 R] /Parent 3516 0 R >> endobj 467 0 obj << /Type/Page /Resources 468 0 R /Contents[15 0 R 4 0 R 469 0 R 17 0 R] /Parent 3516 0 R >> endobj 3516 0 obj << /Type/Pages /Count 3 /Kids[459 0 R 463 0 R 467 0 R] /Parent 3514 0 R >> endobj 471 0 obj << /Type/Page /Resources 472 0 R /Contents[15 0 R 4 0 R 482 0 R 17 0 R] /Parent 3517 0 R >> endobj 485 0 obj << /Type/Page /Resources 486 0 R /Contents[15 0 R 4 0 R 487 0 R 17 0 R] /Parent 3517 0 R >> endobj 489 0 obj << /Type/Page /Resources 490 0 R /Contents[15 0 R 4 0 R 491 0 R 17 0 R] /Parent 3517 0 R >> endobj 3517 0 obj << /Type/Pages /Count 3 /Kids[471 0 R 485 0 R 489 0 R] /Parent 3514 0 R >> endobj 493 0 obj << /Type/Page /Resources 494 0 R /Contents[15 0 R 4 0 R 495 0 R 17 0 R] /Parent 3518 0 R >> endobj 497 0 obj << /Type/Page /Resources 498 0 R /Contents[15 0 R 4 0 R 499 0 R 17 0 R] /Parent 3518 0 R >> endobj 501 0 obj << /Type/Page /Resources 502 0 R /Contents[15 0 R 4 0 R 503 0 R 17 0 R] /Parent 3518 0 R >> endobj 3518 0 obj << /Type/Pages /Count 3 /Kids[493 0 R 497 0 R 501 0 R] /Parent 3514 0 R >> endobj 3514 0 obj << /Type/Pages /Count 12 /Kids[3515 0 R 3516 0 R 3517 0 R 3518 0 R] /Parent 3498 0 R >> endobj 3498 0 obj << /Type/Pages /Count 47 /Kids[3499 0 R 3504 0 R 3509 0 R 3514 0 R] /Parent 3476 0 R >> endobj 505 0 obj << /Type/Page /Resources 506 0 R /Contents[15 0 R 4 0 R 507 0 R 17 0 R] /Parent 3521 0 R >> endobj 509 0 obj << /Type/Page /Resources 510 0 R /Contents[15 0 R 4 0 R 515 0 R 17 0 R] /Parent 3521 0 R >> endobj 3521 0 obj << /Type/Pages /Count 2 /Kids[505 0 R 509 0 R] /Parent 3520 0 R >> endobj 518 0 obj << /Type/Page /Resources 519 0 R /Contents[15 0 R 4 0 R 520 0 R 17 0 R] /Parent 3522 0 R >> endobj 522 0 obj << /Type/Page /Resources 523 0 R /Contents[15 0 R 4 0 R 524 0 R 17 0 R] /Parent 3522 0 R >> endobj 526 0 obj << /Type/Page /Resources 527 0 R /Contents[15 0 R 4 0 R 528 0 R 17 0 R] /Parent 3522 0 R >> endobj 3522 0 obj << /Type/Pages /Count 3 /Kids[518 0 R 522 0 R 526 0 R] /Parent 3520 0 R >> endobj 530 0 obj << /Type/Page /Resources 531 0 R /Contents[15 0 R 4 0 R 532 0 R 17 0 R] /Parent 3523 0 R >> endobj 534 0 obj << /Type/Page /Resources 535 0 R /Contents[15 0 R 4 0 R 536 0 R 17 0 R] /Parent 3523 0 R >> endobj 538 0 obj << /Type/Page /Resources 539 0 R /Contents[15 0 R 4 0 R 540 0 R 17 0 R] /Parent 3523 0 R >> endobj 3523 0 obj << /Type/Pages /Count 3 /Kids[530 0 R 534 0 R 538 0 R] /Parent 3520 0 R >> endobj 542 0 obj << /Type/Page /Resources 543 0 R /Contents[15 0 R 4 0 R 544 0 R 17 0 R] /Parent 3524 0 R >> endobj 546 0 obj << /Type/Page /Resources 547 0 R /Contents[15 0 R 4 0 R 548 0 R 17 0 R] /Parent 3524 0 R >> endobj 550 0 obj << /Type/Page /Resources 551 0 R /Contents[15 0 R 4 0 R 552 0 R 17 0 R] /Parent 3524 0 R >> endobj 3524 0 obj << /Type/Pages /Count 3 /Kids[542 0 R 546 0 R 550 0 R] /Parent 3520 0 R >> endobj 3520 0 obj << /Type/Pages /Count 11 /Kids[3521 0 R 3522 0 R 3523 0 R 3524 0 R] /Parent 3519 0 R >> endobj 554 0 obj << /Type/Page /Resources 555 0 R /Contents[15 0 R 4 0 R 556 0 R 17 0 R] /Parent 3526 0 R >> endobj 558 0 obj << /Type/Page /Resources 559 0 R /Contents[15 0 R 4 0 R 560 0 R 17 0 R] /Parent 3526 0 R >> endobj 562 0 obj << /Type/Page /Resources 563 0 R /Contents[15 0 R 4 0 R 564 0 R 17 0 R] /Parent 3526 0 R >> endobj 3526 0 obj << /Type/Pages /Count 3 /Kids[554 0 R 558 0 R 562 0 R] /Parent 3525 0 R >> endobj 566 0 obj << /Type/Page /Resources 567 0 R /Contents[15 0 R 4 0 R 568 0 R 17 0 R] /Parent 3527 0 R >> endobj 570 0 obj << /Type/Page /Resources 571 0 R /Contents[15 0 R 4 0 R 572 0 R 17 0 R] /Parent 3527 0 R >> endobj 574 0 obj << /Type/Page /Resources 575 0 R /Contents[15 0 R 4 0 R 579 0 R 17 0 R] /Parent 3527 0 R >> endobj 3527 0 obj << /Type/Pages /Count 3 /Kids[566 0 R 570 0 R 574 0 R] /Parent 3525 0 R >> endobj 581 0 obj << /Type/Page /Resources 582 0 R /Contents[15 0 R 4 0 R 583 0 R 17 0 R] /Parent 3528 0 R >> endobj 585 0 obj << /Type/Page /Resources 586 0 R /Contents[15 0 R 4 0 R 587 0 R 17 0 R] /Parent 3528 0 R >> endobj 589 0 obj << /Type/Page /Resources 590 0 R /Contents[15 0 R 4 0 R 591 0 R 17 0 R] /Parent 3528 0 R >> endobj 3528 0 obj << /Type/Pages /Count 3 /Kids[581 0 R 585 0 R 589 0 R] /Parent 3525 0 R >> endobj 593 0 obj << /Type/Page /Resources 594 0 R /Contents[15 0 R 4 0 R 595 0 R 17 0 R] /Parent 3529 0 R >> endobj 597 0 obj << /Type/Page /Resources 598 0 R /Contents[15 0 R 4 0 R 599 0 R 17 0 R] /Parent 3529 0 R >> endobj 601 0 obj << /Type/Page /Resources 602 0 R /Contents[15 0 R 4 0 R 603 0 R 17 0 R] /Parent 3529 0 R >> endobj 3529 0 obj << /Type/Pages /Count 3 /Kids[593 0 R 597 0 R 601 0 R] /Parent 3525 0 R >> endobj 3525 0 obj << /Type/Pages /Count 12 /Kids[3526 0 R 3527 0 R 3528 0 R 3529 0 R] /Parent 3519 0 R >> endobj 605 0 obj << /Type/Page /Resources 606 0 R /Contents[15 0 R 4 0 R 607 0 R 17 0 R] /Parent 3531 0 R >> endobj 609 0 obj << /Type/Page /Resources 610 0 R /Contents[15 0 R 4 0 R 611 0 R 17 0 R] /Parent 3531 0 R >> endobj 613 0 obj << /Type/Page /Resources 614 0 R /Contents[15 0 R 4 0 R 615 0 R 17 0 R] /Parent 3531 0 R >> endobj 3531 0 obj << /Type/Pages /Count 3 /Kids[605 0 R 609 0 R 613 0 R] /Parent 3530 0 R >> endobj 617 0 obj << /Type/Page /Resources 618 0 R /Contents[15 0 R 4 0 R 619 0 R 17 0 R] /Parent 3532 0 R >> endobj 621 0 obj << /Type/Page /Resources 622 0 R /Contents[15 0 R 4 0 R 623 0 R 17 0 R] /Parent 3532 0 R >> endobj 625 0 obj << /Type/Page /Resources 626 0 R /Contents[15 0 R 4 0 R 627 0 R 17 0 R] /Parent 3532 0 R >> endobj 3532 0 obj << /Type/Pages /Count 3 /Kids[617 0 R 621 0 R 625 0 R] /Parent 3530 0 R >> endobj 629 0 obj << /Type/Page /Resources 630 0 R /Contents[15 0 R 4 0 R 631 0 R 17 0 R] /Parent 3533 0 R >> endobj 633 0 obj << /Type/Page /Resources 634 0 R /Contents[15 0 R 4 0 R 635 0 R 17 0 R] /Parent 3533 0 R >> endobj 637 0 obj << /Type/Page /Resources 638 0 R /Contents[15 0 R 4 0 R 639 0 R 17 0 R] /Parent 3533 0 R >> endobj 3533 0 obj << /Type/Pages /Count 3 /Kids[629 0 R 633 0 R 637 0 R] /Parent 3530 0 R >> endobj 641 0 obj << /Type/Page /Resources 642 0 R /Contents[15 0 R 4 0 R 643 0 R 17 0 R] /Parent 3534 0 R >> endobj 645 0 obj << /Type/Page /Resources 646 0 R /Contents[15 0 R 4 0 R 647 0 R 17 0 R] /Parent 3534 0 R >> endobj 649 0 obj << /Type/Page /Resources 650 0 R /Contents[15 0 R 4 0 R 651 0 R 17 0 R] /Parent 3534 0 R >> endobj 3534 0 obj << /Type/Pages /Count 3 /Kids[641 0 R 645 0 R 649 0 R] /Parent 3530 0 R >> endobj 3530 0 obj << /Type/Pages /Count 12 /Kids[3531 0 R 3532 0 R 3533 0 R 3534 0 R] /Parent 3519 0 R >> endobj 653 0 obj << /Type/Page /Resources 654 0 R /Contents[15 0 R 4 0 R 658 0 R 17 0 R] /Parent 3536 0 R >> endobj 661 0 obj << /Type/Page /Resources 662 0 R /Contents[15 0 R 4 0 R 663 0 R 17 0 R] /Parent 3536 0 R >> endobj 665 0 obj << /Type/Page /Resources 666 0 R /Contents[15 0 R 4 0 R 667 0 R 17 0 R] /Parent 3536 0 R >> endobj 3536 0 obj << /Type/Pages /Count 3 /Kids[653 0 R 661 0 R 665 0 R] /Parent 3535 0 R >> endobj 669 0 obj << /Type/Page /Resources 670 0 R /Contents[15 0 R 4 0 R 671 0 R 17 0 R] /Parent 3537 0 R >> endobj 673 0 obj << /Type/Page /Resources 674 0 R /Contents[15 0 R 4 0 R 678 0 R 17 0 R] /Parent 3537 0 R >> endobj 680 0 obj << /Type/Page /Resources 681 0 R /Contents[15 0 R 4 0 R 682 0 R 17 0 R] /Parent 3537 0 R >> endobj 3537 0 obj << /Type/Pages /Count 3 /Kids[669 0 R 673 0 R 680 0 R] /Parent 3535 0 R >> endobj 684 0 obj << /Type/Page /Resources 685 0 R /Contents[15 0 R 4 0 R 689 0 R 17 0 R] /Parent 3538 0 R >> endobj 691 0 obj << /Type/Page /Resources 692 0 R /Contents[15 0 R 4 0 R 693 0 R 17 0 R] /Parent 3538 0 R >> endobj 695 0 obj << /Type/Page /Resources 696 0 R /Contents[15 0 R 4 0 R 697 0 R 17 0 R] /Parent 3538 0 R >> endobj 3538 0 obj << /Type/Pages /Count 3 /Kids[684 0 R 691 0 R 695 0 R] /Parent 3535 0 R >> endobj 699 0 obj << /Type/Page /Resources 700 0 R /Contents[15 0 R 4 0 R 701 0 R 17 0 R] /Parent 3539 0 R >> endobj 703 0 obj << /Type/Page /Resources 704 0 R /Contents[15 0 R 4 0 R 705 0 R 17 0 R] /Parent 3539 0 R >> endobj 707 0 obj << /Type/Page /Resources 708 0 R /Contents[15 0 R 4 0 R 709 0 R 17 0 R] /Parent 3539 0 R >> endobj 3539 0 obj << /Type/Pages /Count 3 /Kids[699 0 R 703 0 R 707 0 R] /Parent 3535 0 R >> endobj 3535 0 obj << /Type/Pages /Count 12 /Kids[3536 0 R 3537 0 R 3538 0 R 3539 0 R] /Parent 3519 0 R >> endobj 3519 0 obj << /Type/Pages /Count 47 /Kids[3520 0 R 3525 0 R 3530 0 R 3535 0 R] /Parent 3476 0 R >> endobj 711 0 obj << /Type/Page /Resources 712 0 R /Contents[15 0 R 4 0 R 713 0 R 17 0 R] /Parent 3542 0 R >> endobj 715 0 obj << /Type/Page /Resources 716 0 R /Contents[15 0 R 4 0 R 717 0 R 17 0 R] /Parent 3542 0 R >> endobj 719 0 obj << /Type/Page /Resources 720 0 R /Contents[15 0 R 4 0 R 721 0 R 17 0 R] /Parent 3542 0 R >> endobj 3542 0 obj << /Type/Pages /Count 3 /Kids[711 0 R 715 0 R 719 0 R] /Parent 3541 0 R >> endobj 723 0 obj << /Type/Page /Resources 724 0 R /Contents[15 0 R 4 0 R 725 0 R 17 0 R] /Parent 3543 0 R >> endobj 727 0 obj << /Type/Page /Resources 728 0 R /Contents[15 0 R 4 0 R 729 0 R 17 0 R] /Parent 3543 0 R >> endobj 731 0 obj << /Type/Page /Resources 732 0 R /Contents[15 0 R 4 0 R 733 0 R 17 0 R] /Parent 3543 0 R >> endobj 3543 0 obj << /Type/Pages /Count 3 /Kids[723 0 R 727 0 R 731 0 R] /Parent 3541 0 R >> endobj 735 0 obj << /Type/Page /Resources 736 0 R /Contents[15 0 R 4 0 R 737 0 R 17 0 R] /Parent 3544 0 R >> endobj 739 0 obj << /Type/Page /Resources 740 0 R /Contents[15 0 R 4 0 R 745 0 R 17 0 R] /Parent 3544 0 R >> endobj 748 0 obj << /Type/Page /Resources 749 0 R /Contents[15 0 R 4 0 R 752 0 R 17 0 R] /Parent 3544 0 R >> endobj 3544 0 obj << /Type/Pages /Count 3 /Kids[735 0 R 739 0 R 748 0 R] /Parent 3541 0 R >> endobj 755 0 obj << /Type/Page /Resources 756 0 R /Contents[15 0 R 4 0 R 757 0 R 17 0 R] /Parent 3545 0 R >> endobj 759 0 obj << /Type/Page /Resources 760 0 R /Contents[15 0 R 4 0 R 765 0 R 17 0 R] /Parent 3545 0 R >> endobj 768 0 obj << /Type/Page /Resources 769 0 R /Contents[15 0 R 4 0 R 773 0 R 17 0 R] /Parent 3545 0 R >> endobj 3545 0 obj << /Type/Pages /Count 3 /Kids[755 0 R 759 0 R 768 0 R] /Parent 3541 0 R >> endobj 3541 0 obj << /Type/Pages /Count 12 /Kids[3542 0 R 3543 0 R 3544 0 R 3545 0 R] /Parent 3540 0 R >> endobj 776 0 obj << /Type/Page /Resources 777 0 R /Contents[15 0 R 4 0 R 778 0 R 17 0 R] /Parent 3547 0 R >> endobj 780 0 obj << /Type/Page /Resources 781 0 R /Contents[15 0 R 4 0 R 782 0 R 17 0 R] /Parent 3547 0 R >> endobj 784 0 obj << /Type/Page /Resources 785 0 R /Contents[15 0 R 4 0 R 788 0 R 17 0 R] /Parent 3547 0 R >> endobj 3547 0 obj << /Type/Pages /Count 3 /Kids[776 0 R 780 0 R 784 0 R] /Parent 3546 0 R >> endobj 791 0 obj << /Type/Page /Resources 792 0 R /Contents[15 0 R 4 0 R 793 0 R 17 0 R] /Parent 3548 0 R >> endobj 795 0 obj << /Type/Page /Resources 796 0 R /Contents[15 0 R 4 0 R 801 0 R 17 0 R] /Parent 3548 0 R >> endobj 804 0 obj << /Type/Page /Resources 805 0 R /Contents[15 0 R 4 0 R 806 0 R 17 0 R] /Parent 3548 0 R >> endobj 3548 0 obj << /Type/Pages /Count 3 /Kids[791 0 R 795 0 R 804 0 R] /Parent 3546 0 R >> endobj 808 0 obj << /Type/Page /Resources 809 0 R /Contents[15 0 R 4 0 R 810 0 R 17 0 R] /Parent 3549 0 R >> endobj 812 0 obj << /Type/Page /Resources 813 0 R /Contents[15 0 R 4 0 R 814 0 R 17 0 R] /Parent 3549 0 R >> endobj 816 0 obj << /Type/Page /Resources 817 0 R /Contents[15 0 R 4 0 R 818 0 R 17 0 R] /Parent 3549 0 R >> endobj 3549 0 obj << /Type/Pages /Count 3 /Kids[808 0 R 812 0 R 816 0 R] /Parent 3546 0 R >> endobj 820 0 obj << /Type/Page /Resources 821 0 R /Contents[15 0 R 4 0 R 826 0 R 17 0 R] /Parent 3550 0 R >> endobj 829 0 obj << /Type/Page /Resources 830 0 R /Contents[15 0 R 4 0 R 831 0 R 17 0 R] /Parent 3550 0 R >> endobj 833 0 obj << /Type/Page /Resources 834 0 R /Contents[15 0 R 4 0 R 835 0 R 17 0 R] /Parent 3550 0 R >> endobj 3550 0 obj << /Type/Pages /Count 3 /Kids[820 0 R 829 0 R 833 0 R] /Parent 3546 0 R >> endobj 3546 0 obj << /Type/Pages /Count 12 /Kids[3547 0 R 3548 0 R 3549 0 R 3550 0 R] /Parent 3540 0 R >> endobj 837 0 obj << /Type/Page /Resources 838 0 R /Contents[15 0 R 4 0 R 839 0 R 17 0 R] /Parent 3552 0 R >> endobj 841 0 obj << /Type/Page /Resources 842 0 R /Contents[15 0 R 4 0 R 843 0 R 17 0 R] /Parent 3552 0 R >> endobj 845 0 obj << /Type/Page /Resources 846 0 R /Contents[15 0 R 4 0 R 851 0 R 17 0 R] /Parent 3552 0 R >> endobj 3552 0 obj << /Type/Pages /Count 3 /Kids[837 0 R 841 0 R 845 0 R] /Parent 3551 0 R >> endobj 854 0 obj << /Type/Page /Resources 855 0 R /Contents[15 0 R 4 0 R 856 0 R 17 0 R] /Parent 3553 0 R >> endobj 858 0 obj << /Type/Page /Resources 859 0 R /Contents[15 0 R 4 0 R 860 0 R 17 0 R] /Parent 3553 0 R >> endobj 862 0 obj << /Type/Page /Resources 863 0 R /Contents[15 0 R 4 0 R 864 0 R 17 0 R] /Parent 3553 0 R >> endobj 3553 0 obj << /Type/Pages /Count 3 /Kids[854 0 R 858 0 R 862 0 R] /Parent 3551 0 R >> endobj 866 0 obj << /Type/Page /Resources 867 0 R /Contents[15 0 R 4 0 R 868 0 R 17 0 R] /Parent 3554 0 R >> endobj 870 0 obj << /Type/Page /Resources 871 0 R /Contents[15 0 R 4 0 R 872 0 R 17 0 R] /Parent 3554 0 R >> endobj 874 0 obj << /Type/Page /Resources 875 0 R /Contents[15 0 R 4 0 R 876 0 R 17 0 R] /Parent 3554 0 R >> endobj 3554 0 obj << /Type/Pages /Count 3 /Kids[866 0 R 870 0 R 874 0 R] /Parent 3551 0 R >> endobj 878 0 obj << /Type/Page /Resources 879 0 R /Contents[15 0 R 4 0 R 880 0 R 17 0 R] /Parent 3555 0 R >> endobj 882 0 obj << /Type/Page /Resources 883 0 R /Contents[15 0 R 4 0 R 884 0 R 17 0 R] /Parent 3555 0 R >> endobj 886 0 obj << /Type/Page /Resources 887 0 R /Contents[15 0 R 4 0 R 888 0 R 17 0 R] /Parent 3555 0 R >> endobj 3555 0 obj << /Type/Pages /Count 3 /Kids[878 0 R 882 0 R 886 0 R] /Parent 3551 0 R >> endobj 3551 0 obj << /Type/Pages /Count 12 /Kids[3552 0 R 3553 0 R 3554 0 R 3555 0 R] /Parent 3540 0 R >> endobj 890 0 obj << /Type/Page /Resources 891 0 R /Contents[15 0 R 4 0 R 894 0 R 17 0 R] /Parent 3557 0 R >> endobj 897 0 obj << /Type/Page /Resources 898 0 R /Contents[15 0 R 4 0 R 899 0 R 17 0 R] /Parent 3557 0 R >> endobj 901 0 obj << /Type/Page /Resources 902 0 R /Contents[15 0 R 4 0 R 903 0 R 17 0 R] /Parent 3557 0 R >> endobj 3557 0 obj << /Type/Pages /Count 3 /Kids[890 0 R 897 0 R 901 0 R] /Parent 3556 0 R >> endobj 905 0 obj << /Type/Page /Resources 906 0 R /Contents[15 0 R 4 0 R 907 0 R 17 0 R] /Parent 3558 0 R >> endobj 909 0 obj << /Type/Page /Resources 910 0 R /Contents[15 0 R 4 0 R 911 0 R 17 0 R] /Parent 3558 0 R >> endobj 913 0 obj << /Type/Page /Resources 914 0 R /Contents[15 0 R 4 0 R 915 0 R 17 0 R] /Parent 3558 0 R >> endobj 3558 0 obj << /Type/Pages /Count 3 /Kids[905 0 R 909 0 R 913 0 R] /Parent 3556 0 R >> endobj 917 0 obj << /Type/Page /Resources 918 0 R /Contents[15 0 R 4 0 R 919 0 R 17 0 R] /Parent 3559 0 R >> endobj 921 0 obj << /Type/Page /Resources 922 0 R /Contents[15 0 R 4 0 R 923 0 R 17 0 R] /Parent 3559 0 R >> endobj 925 0 obj << /Type/Page /Resources 926 0 R /Contents[15 0 R 4 0 R 927 0 R 17 0 R] /Parent 3559 0 R >> endobj 3559 0 obj << /Type/Pages /Count 3 /Kids[917 0 R 921 0 R 925 0 R] /Parent 3556 0 R >> endobj 929 0 obj << /Type/Page /Resources 930 0 R /Contents[15 0 R 4 0 R 931 0 R 17 0 R] /Parent 3560 0 R >> endobj 933 0 obj << /Type/Page /Resources 934 0 R /Contents[15 0 R 4 0 R 935 0 R 17 0 R] /Parent 3560 0 R >> endobj 937 0 obj << /Type/Page /Resources 938 0 R /Contents[15 0 R 4 0 R 939 0 R 17 0 R] /Parent 3560 0 R >> endobj 3560 0 obj << /Type/Pages /Count 3 /Kids[929 0 R 933 0 R 937 0 R] /Parent 3556 0 R >> endobj 3556 0 obj << /Type/Pages /Count 12 /Kids[3557 0 R 3558 0 R 3559 0 R 3560 0 R] /Parent 3540 0 R >> endobj 3540 0 obj << /Type/Pages /Count 48 /Kids[3541 0 R 3546 0 R 3551 0 R 3556 0 R] /Parent 3476 0 R >> endobj 3476 0 obj << /Type/Pages /Count 189 /Kids[3477 0 R 3498 0 R 3519 0 R 3540 0 R] /Parent 3 0 R >> endobj 941 0 obj << /Type/Page /Resources 942 0 R /Contents[15 0 R 4 0 R 943 0 R 17 0 R] /Parent 3564 0 R >> endobj 945 0 obj << /Type/Page /Resources 946 0 R /Contents[15 0 R 4 0 R 947 0 R 17 0 R] /Parent 3564 0 R >> endobj 3564 0 obj << /Type/Pages /Count 2 /Kids[941 0 R 945 0 R] /Parent 3563 0 R >> endobj 949 0 obj << /Type/Page /Resources 950 0 R /Contents[15 0 R 4 0 R 953 0 R 17 0 R] /Parent 3565 0 R >> endobj 956 0 obj << /Type/Page /Resources 957 0 R /Contents[15 0 R 4 0 R 960 0 R 17 0 R] /Parent 3565 0 R >> endobj 963 0 obj << /Type/Page /Resources 964 0 R /Contents[15 0 R 4 0 R 965 0 R 17 0 R] /Parent 3565 0 R >> endobj 3565 0 obj << /Type/Pages /Count 3 /Kids[949 0 R 956 0 R 963 0 R] /Parent 3563 0 R >> endobj 967 0 obj << /Type/Page /Resources 968 0 R /Contents[15 0 R 4 0 R 969 0 R 17 0 R] /Parent 3566 0 R >> endobj 971 0 obj << /Type/Page /Resources 972 0 R /Contents[15 0 R 4 0 R 973 0 R 17 0 R] /Parent 3566 0 R >> endobj 975 0 obj << /Type/Page /Resources 976 0 R /Contents[15 0 R 4 0 R 977 0 R 17 0 R] /Parent 3566 0 R >> endobj 3566 0 obj << /Type/Pages /Count 3 /Kids[967 0 R 971 0 R 975 0 R] /Parent 3563 0 R >> endobj 979 0 obj << /Type/Page /Resources 980 0 R /Contents[15 0 R 4 0 R 981 0 R 17 0 R] /Parent 3567 0 R >> endobj 983 0 obj << /Type/Page /Resources 984 0 R /Contents[15 0 R 4 0 R 985 0 R 17 0 R] /Parent 3567 0 R >> endobj 987 0 obj << /Type/Page /Resources 988 0 R /Contents[15 0 R 4 0 R 989 0 R 17 0 R] /Parent 3567 0 R >> endobj 3567 0 obj << /Type/Pages /Count 3 /Kids[979 0 R 983 0 R 987 0 R] /Parent 3563 0 R >> endobj 3563 0 obj << /Type/Pages /Count 11 /Kids[3564 0 R 3565 0 R 3566 0 R 3567 0 R] /Parent 3562 0 R >> endobj 991 0 obj << /Type/Page /Resources 992 0 R /Contents[15 0 R 4 0 R 993 0 R 17 0 R] /Parent 3569 0 R >> endobj 995 0 obj << /Type/Page /Resources 996 0 R /Contents[15 0 R 4 0 R 997 0 R 17 0 R] /Parent 3569 0 R >> endobj 999 0 obj << /Type/Page /Resources 1000 0 R /Contents[15 0 R 4 0 R 1003 0 R 17 0 R] /Parent 3569 0 R >> endobj 3569 0 obj << /Type/Pages /Count 3 /Kids[991 0 R 995 0 R 999 0 R] /Parent 3568 0 R >> endobj 1006 0 obj << /Type/Page /Resources 1007 0 R /Contents[15 0 R 4 0 R 1008 0 R 17 0 R] /Parent 3570 0 R >> endobj 1010 0 obj << /Type/Page /Resources 1011 0 R /Contents[15 0 R 4 0 R 1012 0 R 17 0 R] /Parent 3570 0 R >> endobj 1014 0 obj << /Type/Page /Resources 1015 0 R /Contents[15 0 R 4 0 R 1016 0 R 17 0 R] /Parent 3570 0 R >> endobj 3570 0 obj << /Type/Pages /Count 3 /Kids[1006 0 R 1010 0 R 1014 0 R] /Parent 3568 0 R >> endobj 1018 0 obj << /Type/Page /Resources 1019 0 R /Contents[15 0 R 4 0 R 1020 0 R 17 0 R] /Parent 3571 0 R >> endobj 1022 0 obj << /Type/Page /Resources 1023 0 R /Contents[15 0 R 4 0 R 1024 0 R 17 0 R] /Parent 3571 0 R >> endobj 1026 0 obj << /Type/Page /Resources 1027 0 R /Contents[15 0 R 4 0 R 1030 0 R 17 0 R] /Parent 3571 0 R >> endobj 3571 0 obj << /Type/Pages /Count 3 /Kids[1018 0 R 1022 0 R 1026 0 R] /Parent 3568 0 R >> endobj 1033 0 obj << /Type/Page /Resources 1034 0 R /Contents[15 0 R 4 0 R 1035 0 R 17 0 R] /Parent 3572 0 R >> endobj 1037 0 obj << /Type/Page /Resources 1038 0 R /Contents[15 0 R 4 0 R 1039 0 R 17 0 R] /Parent 3572 0 R >> endobj 1041 0 obj << /Type/Page /Resources 1042 0 R /Contents[15 0 R 4 0 R 1043 0 R 17 0 R] /Parent 3572 0 R >> endobj 3572 0 obj << /Type/Pages /Count 3 /Kids[1033 0 R 1037 0 R 1041 0 R] /Parent 3568 0 R >> endobj 3568 0 obj << /Type/Pages /Count 12 /Kids[3569 0 R 3570 0 R 3571 0 R 3572 0 R] /Parent 3562 0 R >> endobj 1045 0 obj << /Type/Page /Resources 1046 0 R /Contents[15 0 R 4 0 R 1047 0 R 17 0 R] /Parent 3574 0 R >> endobj 1049 0 obj << /Type/Page /Resources 1050 0 R /Contents[15 0 R 4 0 R 1051 0 R 17 0 R] /Parent 3574 0 R >> endobj 1053 0 obj << /Type/Page /Resources 1054 0 R /Contents[15 0 R 4 0 R 1055 0 R 17 0 R] /Parent 3574 0 R >> endobj 3574 0 obj << /Type/Pages /Count 3 /Kids[1045 0 R 1049 0 R 1053 0 R] /Parent 3573 0 R >> endobj 1057 0 obj << /Type/Page /Resources 1058 0 R /Contents[15 0 R 4 0 R 1059 0 R 17 0 R] /Parent 3575 0 R >> endobj 1061 0 obj << /Type/Page /Resources 1062 0 R /Contents[15 0 R 4 0 R 1063 0 R 17 0 R] /Parent 3575 0 R >> endobj 1065 0 obj << /Type/Page /Resources 1066 0 R /Contents[15 0 R 4 0 R 1067 0 R 17 0 R] /Parent 3575 0 R >> endobj 3575 0 obj << /Type/Pages /Count 3 /Kids[1057 0 R 1061 0 R 1065 0 R] /Parent 3573 0 R >> endobj 1069 0 obj << /Type/Page /Resources 1070 0 R /Contents[15 0 R 4 0 R 1071 0 R 17 0 R] /Parent 3576 0 R >> endobj 1073 0 obj << /Type/Page /Resources 1074 0 R /Contents[15 0 R 4 0 R 1075 0 R 17 0 R] /Parent 3576 0 R >> endobj 1076 0 obj << /Type/Page /Resources 1077 0 R /Contents[15 0 R 4 0 R 1078 0 R 17 0 R] /Parent 3576 0 R >> endobj 3576 0 obj << /Type/Pages /Count 3 /Kids[1069 0 R 1073 0 R 1076 0 R] /Parent 3573 0 R >> endobj 1080 0 obj << /Type/Page /Resources 1081 0 R /Contents[15 0 R 4 0 R 1082 0 R 17 0 R] /Parent 3577 0 R >> endobj 1084 0 obj << /Type/Page /Resources 1085 0 R /Contents[15 0 R 4 0 R 1086 0 R 17 0 R] /Parent 3577 0 R >> endobj 1088 0 obj << /Type/Page /Resources 1089 0 R /Contents[15 0 R 4 0 R 1090 0 R 17 0 R] /Parent 3577 0 R >> endobj 3577 0 obj << /Type/Pages /Count 3 /Kids[1080 0 R 1084 0 R 1088 0 R] /Parent 3573 0 R >> endobj 3573 0 obj << /Type/Pages /Count 12 /Kids[3574 0 R 3575 0 R 3576 0 R 3577 0 R] /Parent 3562 0 R >> endobj 1092 0 obj << /Type/Page /Resources 1093 0 R /Contents[15 0 R 4 0 R 1094 0 R 17 0 R] /Parent 3579 0 R >> endobj 1096 0 obj << /Type/Page /Resources 1097 0 R /Contents[15 0 R 4 0 R 1103 0 R 17 0 R] /Parent 3579 0 R >> endobj 1106 0 obj << /Type/Page /Resources 1107 0 R /Contents[15 0 R 4 0 R 1108 0 R 17 0 R] /Parent 3579 0 R >> endobj 3579 0 obj << /Type/Pages /Count 3 /Kids[1092 0 R 1096 0 R 1106 0 R] /Parent 3578 0 R >> endobj 1110 0 obj << /Type/Page /Resources 1111 0 R /Contents[15 0 R 4 0 R 1112 0 R 17 0 R] /Parent 3580 0 R >> endobj 1114 0 obj << /Type/Page /Resources 1115 0 R /Contents[15 0 R 4 0 R 1116 0 R 17 0 R] /Parent 3580 0 R >> endobj 1118 0 obj << /Type/Page /Resources 1119 0 R /Contents[15 0 R 4 0 R 1122 0 R 17 0 R] /Parent 3580 0 R >> endobj 3580 0 obj << /Type/Pages /Count 3 /Kids[1110 0 R 1114 0 R 1118 0 R] /Parent 3578 0 R >> endobj 1125 0 obj << /Type/Page /Resources 1126 0 R /Contents[15 0 R 4 0 R 1127 0 R 17 0 R] /Parent 3581 0 R >> endobj 1129 0 obj << /Type/Page /Resources 1130 0 R /Contents[15 0 R 4 0 R 1133 0 R 17 0 R] /Parent 3581 0 R >> endobj 1136 0 obj << /Type/Page /Resources 1137 0 R /Contents[15 0 R 4 0 R 1138 0 R 17 0 R] /Parent 3581 0 R >> endobj 3581 0 obj << /Type/Pages /Count 3 /Kids[1125 0 R 1129 0 R 1136 0 R] /Parent 3578 0 R >> endobj 1140 0 obj << /Type/Page /Resources 1141 0 R /Contents[15 0 R 4 0 R 1142 0 R 17 0 R] /Parent 3582 0 R >> endobj 1144 0 obj << /Type/Page /Resources 1145 0 R /Contents[15 0 R 4 0 R 1146 0 R 17 0 R] /Parent 3582 0 R >> endobj 1148 0 obj << /Type/Page /Resources 1149 0 R /Contents[15 0 R 4 0 R 1150 0 R 17 0 R] /Parent 3582 0 R >> endobj 3582 0 obj << /Type/Pages /Count 3 /Kids[1140 0 R 1144 0 R 1148 0 R] /Parent 3578 0 R >> endobj 3578 0 obj << /Type/Pages /Count 12 /Kids[3579 0 R 3580 0 R 3581 0 R 3582 0 R] /Parent 3562 0 R >> endobj 3562 0 obj << /Type/Pages /Count 47 /Kids[3563 0 R 3568 0 R 3573 0 R 3578 0 R] /Parent 3561 0 R >> endobj 1152 0 obj << /Type/Page /Resources 1153 0 R /Contents[15 0 R 4 0 R 1154 0 R 17 0 R] /Parent 3585 0 R >> endobj 1156 0 obj << /Type/Page /Resources 1157 0 R /Contents[15 0 R 4 0 R 1158 0 R 17 0 R] /Parent 3585 0 R >> endobj 3585 0 obj << /Type/Pages /Count 2 /Kids[1152 0 R 1156 0 R] /Parent 3584 0 R >> endobj 1160 0 obj << /Type/Page /Resources 1161 0 R /Contents[15 0 R 4 0 R 1162 0 R 17 0 R] /Parent 3586 0 R >> endobj 1164 0 obj << /Type/Page /Resources 1165 0 R /Contents[15 0 R 4 0 R 1166 0 R 17 0 R] /Parent 3586 0 R >> endobj 1168 0 obj << /Type/Page /Resources 1169 0 R /Contents[15 0 R 4 0 R 1170 0 R 17 0 R] /Parent 3586 0 R >> endobj 3586 0 obj << /Type/Pages /Count 3 /Kids[1160 0 R 1164 0 R 1168 0 R] /Parent 3584 0 R >> endobj 1172 0 obj << /Type/Page /Resources 1173 0 R /Contents[15 0 R 4 0 R 1174 0 R 17 0 R] /Parent 3587 0 R >> endobj 1176 0 obj << /Type/Page /Resources 1177 0 R /Contents[15 0 R 4 0 R 1178 0 R 17 0 R] /Parent 3587 0 R >> endobj 1180 0 obj << /Type/Page /Resources 1181 0 R /Contents[15 0 R 4 0 R 1182 0 R 17 0 R] /Parent 3587 0 R >> endobj 3587 0 obj << /Type/Pages /Count 3 /Kids[1172 0 R 1176 0 R 1180 0 R] /Parent 3584 0 R >> endobj 1184 0 obj << /Type/Page /Resources 1185 0 R /Contents[15 0 R 4 0 R 1186 0 R 17 0 R] /Parent 3588 0 R >> endobj 1188 0 obj << /Type/Page /Resources 1189 0 R /Contents[15 0 R 4 0 R 1190 0 R 17 0 R] /Parent 3588 0 R >> endobj 1192 0 obj << /Type/Page /Resources 1193 0 R /Contents[15 0 R 4 0 R 1194 0 R 17 0 R] /Parent 3588 0 R >> endobj 3588 0 obj << /Type/Pages /Count 3 /Kids[1184 0 R 1188 0 R 1192 0 R] /Parent 3584 0 R >> endobj 3584 0 obj << /Type/Pages /Count 11 /Kids[3585 0 R 3586 0 R 3587 0 R 3588 0 R] /Parent 3583 0 R >> endobj 1196 0 obj << /Type/Page /Resources 1197 0 R /Contents[15 0 R 4 0 R 1198 0 R 17 0 R] /Parent 3590 0 R >> endobj 1200 0 obj << /Type/Page /Resources 1201 0 R /Contents[15 0 R 4 0 R 1202 0 R 17 0 R] /Parent 3590 0 R >> endobj 1204 0 obj << /Type/Page /Resources 1205 0 R /Contents[15 0 R 4 0 R 1206 0 R 17 0 R] /Parent 3590 0 R >> endobj 3590 0 obj << /Type/Pages /Count 3 /Kids[1196 0 R 1200 0 R 1204 0 R] /Parent 3589 0 R >> endobj 1208 0 obj << /Type/Page /Resources 1209 0 R /Contents[15 0 R 4 0 R 1210 0 R 17 0 R] /Parent 3591 0 R >> endobj 1212 0 obj << /Type/Page /Resources 1213 0 R /Contents[15 0 R 4 0 R 1214 0 R 17 0 R] /Parent 3591 0 R >> endobj 1216 0 obj << /Type/Page /Resources 1217 0 R /Contents[15 0 R 4 0 R 1218 0 R 17 0 R] /Parent 3591 0 R >> endobj 3591 0 obj << /Type/Pages /Count 3 /Kids[1208 0 R 1212 0 R 1216 0 R] /Parent 3589 0 R >> endobj 1220 0 obj << /Type/Page /Resources 1221 0 R /Contents[15 0 R 4 0 R 1222 0 R 17 0 R] /Parent 3592 0 R >> endobj 1224 0 obj << /Type/Page /Resources 1225 0 R /Contents[15 0 R 4 0 R 1226 0 R 17 0 R] /Parent 3592 0 R >> endobj 1228 0 obj << /Type/Page /Resources 1229 0 R /Contents[15 0 R 4 0 R 1230 0 R 17 0 R] /Parent 3592 0 R >> endobj 3592 0 obj << /Type/Pages /Count 3 /Kids[1220 0 R 1224 0 R 1228 0 R] /Parent 3589 0 R >> endobj 1232 0 obj << /Type/Page /Resources 1233 0 R /Contents[15 0 R 4 0 R 1234 0 R 17 0 R] /Parent 3593 0 R >> endobj 1236 0 obj << /Type/Page /Resources 1237 0 R /Contents[15 0 R 4 0 R 1238 0 R 17 0 R] /Parent 3593 0 R >> endobj 1240 0 obj << /Type/Page /Resources 1241 0 R /Contents[15 0 R 4 0 R 1242 0 R 17 0 R] /Parent 3593 0 R >> endobj 3593 0 obj << /Type/Pages /Count 3 /Kids[1232 0 R 1236 0 R 1240 0 R] /Parent 3589 0 R >> endobj 3589 0 obj << /Type/Pages /Count 12 /Kids[3590 0 R 3591 0 R 3592 0 R 3593 0 R] /Parent 3583 0 R >> endobj 1244 0 obj << /Type/Page /Resources 1245 0 R /Contents[15 0 R 4 0 R 1246 0 R 17 0 R] /Parent 3595 0 R >> endobj 1248 0 obj << /Type/Page /Resources 1249 0 R /Contents[15 0 R 4 0 R 1250 0 R 17 0 R] /Parent 3595 0 R >> endobj 1252 0 obj << /Type/Page /Resources 1253 0 R /Contents[15 0 R 4 0 R 1254 0 R 17 0 R] /Parent 3595 0 R >> endobj 3595 0 obj << /Type/Pages /Count 3 /Kids[1244 0 R 1248 0 R 1252 0 R] /Parent 3594 0 R >> endobj 1256 0 obj << /Type/Page /Resources 1257 0 R /Contents[15 0 R 4 0 R 1258 0 R 17 0 R] /Parent 3596 0 R >> endobj 1260 0 obj << /Type/Page /Resources 1261 0 R /Contents[15 0 R 4 0 R 1262 0 R 17 0 R] /Parent 3596 0 R >> endobj 1264 0 obj << /Type/Page /Resources 1265 0 R /Contents[15 0 R 4 0 R 1266 0 R 17 0 R] /Parent 3596 0 R >> endobj 3596 0 obj << /Type/Pages /Count 3 /Kids[1256 0 R 1260 0 R 1264 0 R] /Parent 3594 0 R >> endobj 1268 0 obj << /Type/Page /Resources 1269 0 R /Contents[15 0 R 4 0 R 1270 0 R 17 0 R] /Parent 3597 0 R >> endobj 1272 0 obj << /Type/Page /Resources 1273 0 R /Contents[15 0 R 4 0 R 1274 0 R 17 0 R] /Parent 3597 0 R >> endobj 1276 0 obj << /Type/Page /Resources 1277 0 R /Contents[15 0 R 4 0 R 1281 0 R 17 0 R] /Parent 3597 0 R >> endobj 3597 0 obj << /Type/Pages /Count 3 /Kids[1268 0 R 1272 0 R 1276 0 R] /Parent 3594 0 R >> endobj 1283 0 obj << /Type/Page /Resources 1284 0 R /Contents[15 0 R 4 0 R 1285 0 R 17 0 R] /Parent 3598 0 R >> endobj 1287 0 obj << /Type/Page /Resources 1288 0 R /Contents[15 0 R 4 0 R 1289 0 R 17 0 R] /Parent 3598 0 R >> endobj 1291 0 obj << /Type/Page /Resources 1292 0 R /Contents[15 0 R 4 0 R 1293 0 R 17 0 R] /Parent 3598 0 R >> endobj 3598 0 obj << /Type/Pages /Count 3 /Kids[1283 0 R 1287 0 R 1291 0 R] /Parent 3594 0 R >> endobj 3594 0 obj << /Type/Pages /Count 12 /Kids[3595 0 R 3596 0 R 3597 0 R 3598 0 R] /Parent 3583 0 R >> endobj 1295 0 obj << /Type/Page /Resources 1296 0 R /Contents[15 0 R 4 0 R 1297 0 R 17 0 R] /Parent 3600 0 R >> endobj 1299 0 obj << /Type/Page /Resources 1300 0 R /Contents[15 0 R 4 0 R 1301 0 R 17 0 R] /Parent 3600 0 R >> endobj 1303 0 obj << /Type/Page /Resources 1304 0 R /Contents[15 0 R 4 0 R 1305 0 R 17 0 R] /Parent 3600 0 R >> endobj 3600 0 obj << /Type/Pages /Count 3 /Kids[1295 0 R 1299 0 R 1303 0 R] /Parent 3599 0 R >> endobj 1307 0 obj << /Type/Page /Resources 1308 0 R /Contents[15 0 R 4 0 R 1311 0 R 17 0 R] /Parent 3601 0 R >> endobj 1314 0 obj << /Type/Page /Resources 1315 0 R /Contents[15 0 R 4 0 R 1316 0 R 17 0 R] /Parent 3601 0 R >> endobj 1318 0 obj << /Type/Page /Resources 1319 0 R /Contents[15 0 R 4 0 R 1320 0 R 17 0 R] /Parent 3601 0 R >> endobj 3601 0 obj << /Type/Pages /Count 3 /Kids[1307 0 R 1314 0 R 1318 0 R] /Parent 3599 0 R >> endobj 1322 0 obj << /Type/Page /Resources 1323 0 R /Contents[15 0 R 4 0 R 1326 0 R 17 0 R] /Parent 3602 0 R >> endobj 1329 0 obj << /Type/Page /Resources 1330 0 R /Contents[15 0 R 4 0 R 1333 0 R 17 0 R] /Parent 3602 0 R >> endobj 1336 0 obj << /Type/Page /Resources 1337 0 R /Contents[15 0 R 4 0 R 1340 0 R 17 0 R] /Parent 3602 0 R >> endobj 3602 0 obj << /Type/Pages /Count 3 /Kids[1322 0 R 1329 0 R 1336 0 R] /Parent 3599 0 R >> endobj 1343 0 obj << /Type/Page /Resources 1344 0 R /Contents[15 0 R 4 0 R 1358 0 R 17 0 R] /Parent 3603 0 R >> endobj 1361 0 obj << /Type/Page /Resources 1362 0 R /Contents[15 0 R 4 0 R 1365 0 R 17 0 R] /Parent 3603 0 R >> endobj 1368 0 obj << /Type/Page /Resources 1369 0 R /Contents[15 0 R 4 0 R 1386 0 R 17 0 R] /Parent 3603 0 R >> endobj 3603 0 obj << /Type/Pages /Count 3 /Kids[1343 0 R 1361 0 R 1368 0 R] /Parent 3599 0 R >> endobj 3599 0 obj << /Type/Pages /Count 12 /Kids[3600 0 R 3601 0 R 3602 0 R 3603 0 R] /Parent 3583 0 R >> endobj 3583 0 obj << /Type/Pages /Count 47 /Kids[3584 0 R 3589 0 R 3594 0 R 3599 0 R] /Parent 3561 0 R >> endobj 1389 0 obj << /Type/Page /Resources 1390 0 R /Contents[15 0 R 4 0 R 1413 0 R 17 0 R] /Parent 3606 0 R >> endobj 1416 0 obj << /Type/Page /Resources 1417 0 R /Contents[15 0 R 4 0 R 1418 0 R 17 0 R] /Parent 3606 0 R >> endobj 3606 0 obj << /Type/Pages /Count 2 /Kids[1389 0 R 1416 0 R] /Parent 3605 0 R >> endobj 1420 0 obj << /Type/Page /Resources 1421 0 R /Contents[15 0 R 4 0 R 1422 0 R 17 0 R] /Parent 3607 0 R >> endobj 1424 0 obj << /Type/Page /Resources 1425 0 R /Contents[15 0 R 4 0 R 1426 0 R 17 0 R] /Parent 3607 0 R >> endobj 1428 0 obj << /Type/Page /Resources 1429 0 R /Contents[15 0 R 4 0 R 1439 0 R 17 0 R] /Parent 3607 0 R >> endobj 3607 0 obj << /Type/Pages /Count 3 /Kids[1420 0 R 1424 0 R 1428 0 R] /Parent 3605 0 R >> endobj 1442 0 obj << /Type/Page /Resources 1443 0 R /Contents[15 0 R 4 0 R 1444 0 R 17 0 R] /Parent 3608 0 R >> endobj 1446 0 obj << /Type/Page /Resources 1447 0 R /Contents[15 0 R 4 0 R 1448 0 R 17 0 R] /Parent 3608 0 R >> endobj 1450 0 obj << /Type/Page /Resources 1451 0 R /Contents[15 0 R 4 0 R 1452 0 R 17 0 R] /Parent 3608 0 R >> endobj 3608 0 obj << /Type/Pages /Count 3 /Kids[1442 0 R 1446 0 R 1450 0 R] /Parent 3605 0 R >> endobj 1454 0 obj << /Type/Page /Resources 1455 0 R /Contents[15 0 R 4 0 R 1456 0 R 17 0 R] /Parent 3609 0 R >> endobj 1458 0 obj << /Type/Page /Resources 1459 0 R /Contents[15 0 R 4 0 R 1460 0 R 17 0 R] /Parent 3609 0 R >> endobj 1462 0 obj << /Type/Page /Resources 1463 0 R /Contents[15 0 R 4 0 R 1464 0 R 17 0 R] /Parent 3609 0 R >> endobj 3609 0 obj << /Type/Pages /Count 3 /Kids[1454 0 R 1458 0 R 1462 0 R] /Parent 3605 0 R >> endobj 3605 0 obj << /Type/Pages /Count 11 /Kids[3606 0 R 3607 0 R 3608 0 R 3609 0 R] /Parent 3604 0 R >> endobj 1466 0 obj << /Type/Page /Resources 1467 0 R /Contents[15 0 R 4 0 R 1468 0 R 17 0 R] /Parent 3611 0 R >> endobj 1470 0 obj << /Type/Page /Resources 1471 0 R /Contents[15 0 R 4 0 R 1472 0 R 17 0 R] /Parent 3611 0 R >> endobj 1474 0 obj << /Type/Page /Resources 1475 0 R /Contents[15 0 R 4 0 R 1476 0 R 17 0 R] /Parent 3611 0 R >> endobj 3611 0 obj << /Type/Pages /Count 3 /Kids[1466 0 R 1470 0 R 1474 0 R] /Parent 3610 0 R >> endobj 1478 0 obj << /Type/Page /Resources 1479 0 R /Contents[15 0 R 4 0 R 1480 0 R 17 0 R] /Parent 3612 0 R >> endobj 1482 0 obj << /Type/Page /Resources 1483 0 R /Contents[15 0 R 4 0 R 1484 0 R 17 0 R] /Parent 3612 0 R >> endobj 1486 0 obj << /Type/Page /Resources 1487 0 R /Contents[15 0 R 4 0 R 1488 0 R 17 0 R] /Parent 3612 0 R >> endobj 3612 0 obj << /Type/Pages /Count 3 /Kids[1478 0 R 1482 0 R 1486 0 R] /Parent 3610 0 R >> endobj 1490 0 obj << /Type/Page /Resources 1491 0 R /Contents[15 0 R 4 0 R 1492 0 R 17 0 R] /Parent 3613 0 R >> endobj 1494 0 obj << /Type/Page /Resources 1495 0 R /Contents[15 0 R 4 0 R 1496 0 R 17 0 R] /Parent 3613 0 R >> endobj 1498 0 obj << /Type/Page /Resources 1499 0 R /Contents[15 0 R 4 0 R 1500 0 R 17 0 R] /Parent 3613 0 R >> endobj 3613 0 obj << /Type/Pages /Count 3 /Kids[1490 0 R 1494 0 R 1498 0 R] /Parent 3610 0 R >> endobj 1502 0 obj << /Type/Page /Resources 1503 0 R /Contents[15 0 R 4 0 R 1504 0 R 17 0 R] /Parent 3614 0 R >> endobj 1506 0 obj << /Type/Page /Resources 1507 0 R /Contents[15 0 R 4 0 R 1508 0 R 17 0 R] /Parent 3614 0 R >> endobj 1510 0 obj << /Type/Page /Resources 1511 0 R /Contents[15 0 R 4 0 R 1512 0 R 17 0 R] /Parent 3614 0 R >> endobj 3614 0 obj << /Type/Pages /Count 3 /Kids[1502 0 R 1506 0 R 1510 0 R] /Parent 3610 0 R >> endobj 3610 0 obj << /Type/Pages /Count 12 /Kids[3611 0 R 3612 0 R 3613 0 R 3614 0 R] /Parent 3604 0 R >> endobj 1514 0 obj << /Type/Page /Resources 1515 0 R /Contents[15 0 R 4 0 R 1516 0 R 17 0 R] /Parent 3616 0 R >> endobj 1518 0 obj << /Type/Page /Resources 1519 0 R /Contents[15 0 R 4 0 R 1520 0 R 17 0 R] /Parent 3616 0 R >> endobj 1522 0 obj << /Type/Page /Resources 1523 0 R /Contents[15 0 R 4 0 R 1524 0 R 17 0 R] /Parent 3616 0 R >> endobj 3616 0 obj << /Type/Pages /Count 3 /Kids[1514 0 R 1518 0 R 1522 0 R] /Parent 3615 0 R >> endobj 1526 0 obj << /Type/Page /Resources 1527 0 R /Contents[15 0 R 4 0 R 1528 0 R 17 0 R] /Parent 3617 0 R >> endobj 1530 0 obj << /Type/Page /Resources 1531 0 R /Contents[15 0 R 4 0 R 1532 0 R 17 0 R] /Parent 3617 0 R >> endobj 1534 0 obj << /Type/Page /Resources 1535 0 R /Contents[15 0 R 4 0 R 1536 0 R 17 0 R] /Parent 3617 0 R >> endobj 3617 0 obj << /Type/Pages /Count 3 /Kids[1526 0 R 1530 0 R 1534 0 R] /Parent 3615 0 R >> endobj 1538 0 obj << /Type/Page /Resources 1539 0 R /Contents[15 0 R 4 0 R 1540 0 R 17 0 R] /Parent 3618 0 R >> endobj 1542 0 obj << /Type/Page /Resources 1543 0 R /Contents[15 0 R 4 0 R 1544 0 R 17 0 R] /Parent 3618 0 R >> endobj 1546 0 obj << /Type/Page /Resources 1547 0 R /Contents[15 0 R 4 0 R 1548 0 R 17 0 R] /Parent 3618 0 R >> endobj 3618 0 obj << /Type/Pages /Count 3 /Kids[1538 0 R 1542 0 R 1546 0 R] /Parent 3615 0 R >> endobj 1550 0 obj << /Type/Page /Resources 1551 0 R /Contents[15 0 R 4 0 R 1552 0 R 17 0 R] /Parent 3619 0 R >> endobj 1554 0 obj << /Type/Page /Resources 1555 0 R /Contents[15 0 R 4 0 R 1556 0 R 17 0 R] /Parent 3619 0 R >> endobj 1558 0 obj << /Type/Page /Resources 1559 0 R /Contents[15 0 R 4 0 R 1560 0 R 17 0 R] /Parent 3619 0 R >> endobj 3619 0 obj << /Type/Pages /Count 3 /Kids[1550 0 R 1554 0 R 1558 0 R] /Parent 3615 0 R >> endobj 3615 0 obj << /Type/Pages /Count 12 /Kids[3616 0 R 3617 0 R 3618 0 R 3619 0 R] /Parent 3604 0 R >> endobj 1562 0 obj << /Type/Page /Resources 1563 0 R /Contents[15 0 R 4 0 R 1564 0 R 17 0 R] /Parent 3621 0 R >> endobj 1566 0 obj << /Type/Page /Resources 1567 0 R /Contents[15 0 R 4 0 R 1568 0 R 17 0 R] /Parent 3621 0 R >> endobj 1570 0 obj << /Type/Page /Resources 1571 0 R /Contents[15 0 R 4 0 R 1572 0 R 17 0 R] /Parent 3621 0 R >> endobj 3621 0 obj << /Type/Pages /Count 3 /Kids[1562 0 R 1566 0 R 1570 0 R] /Parent 3620 0 R >> endobj 1574 0 obj << /Type/Page /Resources 1575 0 R /Contents[15 0 R 4 0 R 1576 0 R 17 0 R] /Parent 3622 0 R >> endobj 1578 0 obj << /Type/Page /Resources 1579 0 R /Contents[15 0 R 4 0 R 1580 0 R 17 0 R] /Parent 3622 0 R >> endobj 1582 0 obj << /Type/Page /Resources 1583 0 R /Contents[15 0 R 4 0 R 1584 0 R 17 0 R] /Parent 3622 0 R >> endobj 3622 0 obj << /Type/Pages /Count 3 /Kids[1574 0 R 1578 0 R 1582 0 R] /Parent 3620 0 R >> endobj 1586 0 obj << /Type/Page /Resources 1587 0 R /Contents[15 0 R 4 0 R 1588 0 R 17 0 R] /Parent 3623 0 R >> endobj 1590 0 obj << /Type/Page /Resources 1591 0 R /Contents[15 0 R 4 0 R 1592 0 R 17 0 R] /Parent 3623 0 R >> endobj 1594 0 obj << /Type/Page /Resources 1595 0 R /Contents[15 0 R 4 0 R 1596 0 R 17 0 R] /Parent 3623 0 R >> endobj 3623 0 obj << /Type/Pages /Count 3 /Kids[1586 0 R 1590 0 R 1594 0 R] /Parent 3620 0 R >> endobj 1598 0 obj << /Type/Page /Resources 1599 0 R /Contents[15 0 R 4 0 R 1600 0 R 17 0 R] /Parent 3624 0 R >> endobj 1602 0 obj << /Type/Page /Resources 1603 0 R /Contents[15 0 R 4 0 R 1604 0 R 17 0 R] /Parent 3624 0 R >> endobj 1606 0 obj << /Type/Page /Resources 1607 0 R /Contents[15 0 R 4 0 R 1608 0 R 17 0 R] /Parent 3624 0 R >> endobj 3624 0 obj << /Type/Pages /Count 3 /Kids[1598 0 R 1602 0 R 1606 0 R] /Parent 3620 0 R >> endobj 3620 0 obj << /Type/Pages /Count 12 /Kids[3621 0 R 3622 0 R 3623 0 R 3624 0 R] /Parent 3604 0 R >> endobj 3604 0 obj << /Type/Pages /Count 47 /Kids[3605 0 R 3610 0 R 3615 0 R 3620 0 R] /Parent 3561 0 R >> endobj 1610 0 obj << /Type/Page /Resources 1611 0 R /Contents[15 0 R 4 0 R 1612 0 R 17 0 R] /Parent 3627 0 R >> endobj 1614 0 obj << /Type/Page /Resources 1615 0 R /Contents[15 0 R 4 0 R 1616 0 R 17 0 R] /Parent 3627 0 R >> endobj 1618 0 obj << /Type/Page /Resources 1619 0 R /Contents[15 0 R 4 0 R 1620 0 R 17 0 R] /Parent 3627 0 R >> endobj 3627 0 obj << /Type/Pages /Count 3 /Kids[1610 0 R 1614 0 R 1618 0 R] /Parent 3626 0 R >> endobj 1622 0 obj << /Type/Page /Resources 1623 0 R /Contents[15 0 R 4 0 R 1624 0 R 17 0 R] /Parent 3628 0 R >> endobj 1626 0 obj << /Type/Page /Resources 1627 0 R /Contents[15 0 R 4 0 R 1628 0 R 17 0 R] /Parent 3628 0 R >> endobj 1630 0 obj << /Type/Page /Resources 1631 0 R /Contents[15 0 R 4 0 R 1632 0 R 17 0 R] /Parent 3628 0 R >> endobj 3628 0 obj << /Type/Pages /Count 3 /Kids[1622 0 R 1626 0 R 1630 0 R] /Parent 3626 0 R >> endobj 1634 0 obj << /Type/Page /Resources 1635 0 R /Contents[15 0 R 4 0 R 1636 0 R 17 0 R] /Parent 3629 0 R >> endobj 1638 0 obj << /Type/Page /Resources 1639 0 R /Contents[15 0 R 4 0 R 1640 0 R 17 0 R] /Parent 3629 0 R >> endobj 1642 0 obj << /Type/Page /Resources 1643 0 R /Contents[15 0 R 4 0 R 1644 0 R 17 0 R] /Parent 3629 0 R >> endobj 3629 0 obj << /Type/Pages /Count 3 /Kids[1634 0 R 1638 0 R 1642 0 R] /Parent 3626 0 R >> endobj 1646 0 obj << /Type/Page /Resources 1647 0 R /Contents[15 0 R 4 0 R 1648 0 R 17 0 R] /Parent 3630 0 R >> endobj 1650 0 obj << /Type/Page /Resources 1651 0 R /Contents[15 0 R 4 0 R 1652 0 R 17 0 R] /Parent 3630 0 R >> endobj 1654 0 obj << /Type/Page /Resources 1655 0 R /Contents[15 0 R 4 0 R 1656 0 R 17 0 R] /Parent 3630 0 R >> endobj 3630 0 obj << /Type/Pages /Count 3 /Kids[1646 0 R 1650 0 R 1654 0 R] /Parent 3626 0 R >> endobj 3626 0 obj << /Type/Pages /Count 12 /Kids[3627 0 R 3628 0 R 3629 0 R 3630 0 R] /Parent 3625 0 R >> endobj 1658 0 obj << /Type/Page /Resources 1659 0 R /Contents[15 0 R 4 0 R 1670 0 R 17 0 R] /Parent 3632 0 R >> endobj 1673 0 obj << /Type/Page /Resources 1674 0 R /Contents[15 0 R 4 0 R 1675 0 R 17 0 R] /Parent 3632 0 R >> endobj 1677 0 obj << /Type/Page /Resources 1678 0 R /Contents[15 0 R 4 0 R 1679 0 R 17 0 R] /Parent 3632 0 R >> endobj 3632 0 obj << /Type/Pages /Count 3 /Kids[1658 0 R 1673 0 R 1677 0 R] /Parent 3631 0 R >> endobj 1681 0 obj << /Type/Page /Resources 1682 0 R /Contents[15 0 R 4 0 R 1683 0 R 17 0 R] /Parent 3633 0 R >> endobj 1685 0 obj << /Type/Page /Resources 1686 0 R /Contents[15 0 R 4 0 R 1698 0 R 17 0 R] /Parent 3633 0 R >> endobj 1701 0 obj << /Type/Page /Resources 1702 0 R /Contents[15 0 R 4 0 R 1703 0 R 17 0 R] /Parent 3633 0 R >> endobj 3633 0 obj << /Type/Pages /Count 3 /Kids[1681 0 R 1685 0 R 1701 0 R] /Parent 3631 0 R >> endobj 1705 0 obj << /Type/Page /Resources 1706 0 R /Contents[15 0 R 4 0 R 1707 0 R 17 0 R] /Parent 3634 0 R >> endobj 1709 0 obj << /Type/Page /Resources 1710 0 R /Contents[15 0 R 4 0 R 1711 0 R 17 0 R] /Parent 3634 0 R >> endobj 1713 0 obj << /Type/Page /Resources 1714 0 R /Contents[15 0 R 4 0 R 1715 0 R 17 0 R] /Parent 3634 0 R >> endobj 3634 0 obj << /Type/Pages /Count 3 /Kids[1705 0 R 1709 0 R 1713 0 R] /Parent 3631 0 R >> endobj 1717 0 obj << /Type/Page /Resources 1718 0 R /Contents[15 0 R 4 0 R 1719 0 R 17 0 R] /Parent 3635 0 R >> endobj 1721 0 obj << /Type/Page /Resources 1722 0 R /Contents[15 0 R 4 0 R 1725 0 R 17 0 R] /Parent 3635 0 R >> endobj 1728 0 obj << /Type/Page /Resources 1729 0 R /Contents[15 0 R 4 0 R 1730 0 R 17 0 R] /Parent 3635 0 R >> endobj 3635 0 obj << /Type/Pages /Count 3 /Kids[1717 0 R 1721 0 R 1728 0 R] /Parent 3631 0 R >> endobj 3631 0 obj << /Type/Pages /Count 12 /Kids[3632 0 R 3633 0 R 3634 0 R 3635 0 R] /Parent 3625 0 R >> endobj 1732 0 obj << /Type/Page /Resources 1733 0 R /Contents[15 0 R 4 0 R 1734 0 R 17 0 R] /Parent 3637 0 R >> endobj 1736 0 obj << /Type/Page /Resources 1737 0 R /Contents[15 0 R 4 0 R 1738 0 R 17 0 R] /Parent 3637 0 R >> endobj 1740 0 obj << /Type/Page /Resources 1741 0 R /Contents[15 0 R 4 0 R 1742 0 R 17 0 R] /Parent 3637 0 R >> endobj 3637 0 obj << /Type/Pages /Count 3 /Kids[1732 0 R 1736 0 R 1740 0 R] /Parent 3636 0 R >> endobj 1744 0 obj << /Type/Page /Resources 1745 0 R /Contents[15 0 R 4 0 R 1746 0 R 17 0 R] /Parent 3638 0 R >> endobj 1748 0 obj << /Type/Page /Resources 1749 0 R /Contents[15 0 R 4 0 R 1750 0 R 17 0 R] /Parent 3638 0 R >> endobj 1752 0 obj << /Type/Page /Resources 1753 0 R /Contents[15 0 R 4 0 R 1757 0 R 17 0 R] /Parent 3638 0 R >> endobj 3638 0 obj << /Type/Pages /Count 3 /Kids[1744 0 R 1748 0 R 1752 0 R] /Parent 3636 0 R >> endobj 1760 0 obj << /Type/Page /Resources 1761 0 R /Contents[15 0 R 4 0 R 1762 0 R 17 0 R] /Parent 3639 0 R >> endobj 1764 0 obj << /Type/Page /Resources 1765 0 R /Contents[15 0 R 4 0 R 1766 0 R 17 0 R] /Parent 3639 0 R >> endobj 1768 0 obj << /Type/Page /Resources 1769 0 R /Contents[15 0 R 4 0 R 1770 0 R 17 0 R] /Parent 3639 0 R >> endobj 3639 0 obj << /Type/Pages /Count 3 /Kids[1760 0 R 1764 0 R 1768 0 R] /Parent 3636 0 R >> endobj 1772 0 obj << /Type/Page /Resources 1773 0 R /Contents[15 0 R 4 0 R 1774 0 R 17 0 R] /Parent 3640 0 R >> endobj 1776 0 obj << /Type/Page /Resources 1777 0 R /Contents[15 0 R 4 0 R 1778 0 R 17 0 R] /Parent 3640 0 R >> endobj 1780 0 obj << /Type/Page /Resources 1781 0 R /Contents[15 0 R 4 0 R 1782 0 R 17 0 R] /Parent 3640 0 R >> endobj 3640 0 obj << /Type/Pages /Count 3 /Kids[1772 0 R 1776 0 R 1780 0 R] /Parent 3636 0 R >> endobj 3636 0 obj << /Type/Pages /Count 12 /Kids[3637 0 R 3638 0 R 3639 0 R 3640 0 R] /Parent 3625 0 R >> endobj 1784 0 obj << /Type/Page /Resources 1785 0 R /Contents[15 0 R 4 0 R 1786 0 R 17 0 R] /Parent 3642 0 R >> endobj 1788 0 obj << /Type/Page /Resources 1789 0 R /Contents[15 0 R 4 0 R 1790 0 R 17 0 R] /Parent 3642 0 R >> endobj 1792 0 obj << /Type/Page /Resources 1793 0 R /Contents[15 0 R 4 0 R 1794 0 R 17 0 R] /Parent 3642 0 R >> endobj 3642 0 obj << /Type/Pages /Count 3 /Kids[1784 0 R 1788 0 R 1792 0 R] /Parent 3641 0 R >> endobj 1796 0 obj << /Type/Page /Resources 1797 0 R /Contents[15 0 R 4 0 R 1798 0 R 17 0 R] /Parent 3643 0 R >> endobj 1800 0 obj << /Type/Page /Resources 1801 0 R /Contents[15 0 R 4 0 R 1802 0 R 17 0 R] /Parent 3643 0 R >> endobj 1804 0 obj << /Type/Page /Resources 1805 0 R /Contents[15 0 R 4 0 R 1806 0 R 17 0 R] /Parent 3643 0 R >> endobj 3643 0 obj << /Type/Pages /Count 3 /Kids[1796 0 R 1800 0 R 1804 0 R] /Parent 3641 0 R >> endobj 1808 0 obj << /Type/Page /Resources 1809 0 R /Contents[15 0 R 4 0 R 1810 0 R 17 0 R] /Parent 3644 0 R >> endobj 1812 0 obj << /Type/Page /Resources 1813 0 R /Contents[15 0 R 4 0 R 1814 0 R 17 0 R] /Parent 3644 0 R >> endobj 1816 0 obj << /Type/Page /Resources 1817 0 R /Contents[15 0 R 4 0 R 1818 0 R 17 0 R] /Parent 3644 0 R >> endobj 3644 0 obj << /Type/Pages /Count 3 /Kids[1808 0 R 1812 0 R 1816 0 R] /Parent 3641 0 R >> endobj 1820 0 obj << /Type/Page /Resources 1821 0 R /Contents[15 0 R 4 0 R 1822 0 R 17 0 R] /Parent 3645 0 R >> endobj 1824 0 obj << /Type/Page /Resources 1825 0 R /Contents[15 0 R 4 0 R 1826 0 R 17 0 R] /Parent 3645 0 R >> endobj 1828 0 obj << /Type/Page /Resources 1829 0 R /Contents[15 0 R 4 0 R 1830 0 R 17 0 R] /Parent 3645 0 R >> endobj 3645 0 obj << /Type/Pages /Count 3 /Kids[1820 0 R 1824 0 R 1828 0 R] /Parent 3641 0 R >> endobj 3641 0 obj << /Type/Pages /Count 12 /Kids[3642 0 R 3643 0 R 3644 0 R 3645 0 R] /Parent 3625 0 R >> endobj 3625 0 obj << /Type/Pages /Count 48 /Kids[3626 0 R 3631 0 R 3636 0 R 3641 0 R] /Parent 3561 0 R >> endobj 3561 0 obj << /Type/Pages /Count 189 /Kids[3562 0 R 3583 0 R 3604 0 R 3625 0 R] /Parent 3 0 R >> endobj 1832 0 obj << /Type/Page /Resources 1833 0 R /Contents[15 0 R 4 0 R 1834 0 R 17 0 R] /Parent 3649 0 R >> endobj 1836 0 obj << /Type/Page /Resources 1837 0 R /Contents[15 0 R 4 0 R 1838 0 R 17 0 R] /Parent 3649 0 R >> endobj 3649 0 obj << /Type/Pages /Count 2 /Kids[1832 0 R 1836 0 R] /Parent 3648 0 R >> endobj 1840 0 obj << /Type/Page /Resources 1841 0 R /Contents[15 0 R 4 0 R 1842 0 R 17 0 R] /Parent 3650 0 R >> endobj 1844 0 obj << /Type/Page /Resources 1845 0 R /Contents[15 0 R 4 0 R 1846 0 R 17 0 R] /Parent 3650 0 R >> endobj 1848 0 obj << /Type/Page /Resources 1849 0 R /Contents[15 0 R 4 0 R 1850 0 R 17 0 R] /Parent 3650 0 R >> endobj 3650 0 obj << /Type/Pages /Count 3 /Kids[1840 0 R 1844 0 R 1848 0 R] /Parent 3648 0 R >> endobj 1852 0 obj << /Type/Page /Resources 1853 0 R /Contents[15 0 R 4 0 R 1854 0 R 17 0 R] /Parent 3651 0 R >> endobj 1856 0 obj << /Type/Page /Resources 1857 0 R /Contents[15 0 R 4 0 R 1858 0 R 17 0 R] /Parent 3651 0 R >> endobj 1860 0 obj << /Type/Page /Resources 1861 0 R /Contents[15 0 R 4 0 R 1862 0 R 17 0 R] /Parent 3651 0 R >> endobj 3651 0 obj << /Type/Pages /Count 3 /Kids[1852 0 R 1856 0 R 1860 0 R] /Parent 3648 0 R >> endobj 1864 0 obj << /Type/Page /Resources 1865 0 R /Contents[15 0 R 4 0 R 1866 0 R 17 0 R] /Parent 3652 0 R >> endobj 1868 0 obj << /Type/Page /Resources 1869 0 R /Contents[15 0 R 4 0 R 1870 0 R 17 0 R] /Parent 3652 0 R >> endobj 1872 0 obj << /Type/Page /Resources 1873 0 R /Contents[15 0 R 4 0 R 1874 0 R 17 0 R] /Parent 3652 0 R >> endobj 3652 0 obj << /Type/Pages /Count 3 /Kids[1864 0 R 1868 0 R 1872 0 R] /Parent 3648 0 R >> endobj 3648 0 obj << /Type/Pages /Count 11 /Kids[3649 0 R 3650 0 R 3651 0 R 3652 0 R] /Parent 3647 0 R >> endobj 1876 0 obj << /Type/Page /Resources 1877 0 R /Contents[15 0 R 4 0 R 1878 0 R 17 0 R] /Parent 3654 0 R >> endobj 1880 0 obj << /Type/Page /Resources 1881 0 R /Contents[15 0 R 4 0 R 1882 0 R 17 0 R] /Parent 3654 0 R >> endobj 1884 0 obj << /Type/Page /Resources 1885 0 R /Contents[15 0 R 4 0 R 1888 0 R 17 0 R] /Parent 3654 0 R >> endobj 3654 0 obj << /Type/Pages /Count 3 /Kids[1876 0 R 1880 0 R 1884 0 R] /Parent 3653 0 R >> endobj 1891 0 obj << /Type/Page /Resources 1892 0 R /Contents[15 0 R 4 0 R 1895 0 R 17 0 R] /Parent 3655 0 R >> endobj 1898 0 obj << /Type/Page /Resources 1899 0 R /Contents[15 0 R 4 0 R 1900 0 R 17 0 R] /Parent 3655 0 R >> endobj 1902 0 obj << /Type/Page /Resources 1903 0 R /Contents[15 0 R 4 0 R 1906 0 R 17 0 R] /Parent 3655 0 R >> endobj 3655 0 obj << /Type/Pages /Count 3 /Kids[1891 0 R 1898 0 R 1902 0 R] /Parent 3653 0 R >> endobj 1909 0 obj << /Type/Page /Resources 1910 0 R /Contents[15 0 R 4 0 R 1911 0 R 17 0 R] /Parent 3656 0 R >> endobj 1913 0 obj << /Type/Page /Resources 1914 0 R /Contents[15 0 R 4 0 R 1915 0 R 17 0 R] /Parent 3656 0 R >> endobj 1917 0 obj << /Type/Page /Resources 1918 0 R /Contents[15 0 R 4 0 R 1925 0 R 17 0 R] /Parent 3656 0 R >> endobj 3656 0 obj << /Type/Pages /Count 3 /Kids[1909 0 R 1913 0 R 1917 0 R] /Parent 3653 0 R >> endobj 1928 0 obj << /Type/Page /Resources 1929 0 R /Contents[15 0 R 4 0 R 1930 0 R 17 0 R] /Parent 3657 0 R >> endobj 1932 0 obj << /Type/Page /Resources 1933 0 R /Contents[15 0 R 4 0 R 1934 0 R 17 0 R] /Parent 3657 0 R >> endobj 1936 0 obj << /Type/Page /Resources 1937 0 R /Contents[15 0 R 4 0 R 1940 0 R 17 0 R] /Parent 3657 0 R >> endobj 3657 0 obj << /Type/Pages /Count 3 /Kids[1928 0 R 1932 0 R 1936 0 R] /Parent 3653 0 R >> endobj 3653 0 obj << /Type/Pages /Count 12 /Kids[3654 0 R 3655 0 R 3656 0 R 3657 0 R] /Parent 3647 0 R >> endobj 1943 0 obj << /Type/Page /Resources 1944 0 R /Contents[15 0 R 4 0 R 1945 0 R 17 0 R] /Parent 3659 0 R >> endobj 1947 0 obj << /Type/Page /Resources 1948 0 R /Contents[15 0 R 4 0 R 1951 0 R 17 0 R] /Parent 3659 0 R >> endobj 1954 0 obj << /Type/Page /Resources 1955 0 R /Contents[15 0 R 4 0 R 1956 0 R 17 0 R] /Parent 3659 0 R >> endobj 3659 0 obj << /Type/Pages /Count 3 /Kids[1943 0 R 1947 0 R 1954 0 R] /Parent 3658 0 R >> endobj 1958 0 obj << /Type/Page /Resources 1959 0 R /Contents[15 0 R 4 0 R 1960 0 R 17 0 R] /Parent 3660 0 R >> endobj 1962 0 obj << /Type/Page /Resources 1963 0 R /Contents[15 0 R 4 0 R 1964 0 R 17 0 R] /Parent 3660 0 R >> endobj 1966 0 obj << /Type/Page /Resources 1967 0 R /Contents[15 0 R 4 0 R 1970 0 R 17 0 R] /Parent 3660 0 R >> endobj 3660 0 obj << /Type/Pages /Count 3 /Kids[1958 0 R 1962 0 R 1966 0 R] /Parent 3658 0 R >> endobj 1973 0 obj << /Type/Page /Resources 1974 0 R /Contents[15 0 R 4 0 R 1975 0 R 17 0 R] /Parent 3661 0 R >> endobj 1977 0 obj << /Type/Page /Resources 1978 0 R /Contents[15 0 R 4 0 R 1979 0 R 17 0 R] /Parent 3661 0 R >> endobj 1981 0 obj << /Type/Page /Resources 1982 0 R /Contents[15 0 R 4 0 R 1985 0 R 17 0 R] /Parent 3661 0 R >> endobj 3661 0 obj << /Type/Pages /Count 3 /Kids[1973 0 R 1977 0 R 1981 0 R] /Parent 3658 0 R >> endobj 1988 0 obj << /Type/Page /Resources 1989 0 R /Contents[15 0 R 4 0 R 1990 0 R 17 0 R] /Parent 3662 0 R >> endobj 1992 0 obj << /Type/Page /Resources 1993 0 R /Contents[15 0 R 4 0 R 1994 0 R 17 0 R] /Parent 3662 0 R >> endobj 1996 0 obj << /Type/Page /Resources 1997 0 R /Contents[15 0 R 4 0 R 1998 0 R 17 0 R] /Parent 3662 0 R >> endobj 3662 0 obj << /Type/Pages /Count 3 /Kids[1988 0 R 1992 0 R 1996 0 R] /Parent 3658 0 R >> endobj 3658 0 obj << /Type/Pages /Count 12 /Kids[3659 0 R 3660 0 R 3661 0 R 3662 0 R] /Parent 3647 0 R >> endobj 2000 0 obj << /Type/Page /Resources 2001 0 R /Contents[15 0 R 4 0 R 2002 0 R 17 0 R] /Parent 3664 0 R >> endobj 2004 0 obj << /Type/Page /Resources 2005 0 R /Contents[15 0 R 4 0 R 2006 0 R 17 0 R] /Parent 3664 0 R >> endobj 2008 0 obj << /Type/Page /Resources 2009 0 R /Contents[15 0 R 4 0 R 2010 0 R 17 0 R] /Parent 3664 0 R >> endobj 3664 0 obj << /Type/Pages /Count 3 /Kids[2000 0 R 2004 0 R 2008 0 R] /Parent 3663 0 R >> endobj 2012 0 obj << /Type/Page /Resources 2013 0 R /Contents[15 0 R 4 0 R 2014 0 R 17 0 R] /Parent 3665 0 R >> endobj 2016 0 obj << /Type/Page /Resources 2017 0 R /Contents[15 0 R 4 0 R 2018 0 R 17 0 R] /Parent 3665 0 R >> endobj 2020 0 obj << /Type/Page /Resources 2021 0 R /Contents[15 0 R 4 0 R 2022 0 R 17 0 R] /Parent 3665 0 R >> endobj 3665 0 obj << /Type/Pages /Count 3 /Kids[2012 0 R 2016 0 R 2020 0 R] /Parent 3663 0 R >> endobj 2024 0 obj << /Type/Page /Resources 2025 0 R /Contents[15 0 R 4 0 R 2026 0 R 17 0 R] /Parent 3666 0 R >> endobj 2028 0 obj << /Type/Page /Resources 2029 0 R /Contents[15 0 R 4 0 R 2030 0 R 17 0 R] /Parent 3666 0 R >> endobj 2032 0 obj << /Type/Page /Resources 2033 0 R /Contents[15 0 R 4 0 R 2034 0 R 17 0 R] /Parent 3666 0 R >> endobj 3666 0 obj << /Type/Pages /Count 3 /Kids[2024 0 R 2028 0 R 2032 0 R] /Parent 3663 0 R >> endobj 2036 0 obj << /Type/Page /Resources 2037 0 R /Contents[15 0 R 4 0 R 2040 0 R 17 0 R] /Parent 3667 0 R >> endobj 2043 0 obj << /Type/Page /Resources 2044 0 R /Contents[15 0 R 4 0 R 2045 0 R 17 0 R] /Parent 3667 0 R >> endobj 2047 0 obj << /Type/Page /Resources 2048 0 R /Contents[15 0 R 4 0 R 2049 0 R 17 0 R] /Parent 3667 0 R >> endobj 3667 0 obj << /Type/Pages /Count 3 /Kids[2036 0 R 2043 0 R 2047 0 R] /Parent 3663 0 R >> endobj 3663 0 obj << /Type/Pages /Count 12 /Kids[3664 0 R 3665 0 R 3666 0 R 3667 0 R] /Parent 3647 0 R >> endobj 3647 0 obj << /Type/Pages /Count 47 /Kids[3648 0 R 3653 0 R 3658 0 R 3663 0 R] /Parent 3646 0 R >> endobj 2051 0 obj << /Type/Page /Resources 2052 0 R /Contents[15 0 R 4 0 R 2053 0 R 17 0 R] /Parent 3670 0 R >> endobj 2055 0 obj << /Type/Page /Resources 2056 0 R /Contents[15 0 R 4 0 R 2057 0 R 17 0 R] /Parent 3670 0 R >> endobj 3670 0 obj << /Type/Pages /Count 2 /Kids[2051 0 R 2055 0 R] /Parent 3669 0 R >> endobj 2059 0 obj << /Type/Page /Resources 2060 0 R /Contents[15 0 R 4 0 R 2061 0 R 17 0 R] /Parent 3671 0 R >> endobj 2062 0 obj << /Type/Page /Resources 2063 0 R /Contents[15 0 R 4 0 R 2064 0 R 17 0 R] /Parent 3671 0 R >> endobj 2066 0 obj << /Type/Page /Resources 2067 0 R /Contents[15 0 R 4 0 R 2068 0 R 17 0 R] /Parent 3671 0 R >> endobj 3671 0 obj << /Type/Pages /Count 3 /Kids[2059 0 R 2062 0 R 2066 0 R] /Parent 3669 0 R >> endobj 2070 0 obj << /Type/Page /Resources 2071 0 R /Contents[15 0 R 4 0 R 2072 0 R 17 0 R] /Parent 3672 0 R >> endobj 2074 0 obj << /Type/Page /Resources 2075 0 R /Contents[15 0 R 4 0 R 2076 0 R 17 0 R] /Parent 3672 0 R >> endobj 2078 0 obj << /Type/Page /Resources 2079 0 R /Contents[15 0 R 4 0 R 2080 0 R 17 0 R] /Parent 3672 0 R >> endobj 3672 0 obj << /Type/Pages /Count 3 /Kids[2070 0 R 2074 0 R 2078 0 R] /Parent 3669 0 R >> endobj 2082 0 obj << /Type/Page /Resources 2083 0 R /Contents[15 0 R 4 0 R 2084 0 R 17 0 R] /Parent 3673 0 R >> endobj 2086 0 obj << /Type/Page /Resources 2087 0 R /Contents[15 0 R 4 0 R 2088 0 R 17 0 R] /Parent 3673 0 R >> endobj 2090 0 obj << /Type/Page /Resources 2091 0 R /Contents[15 0 R 4 0 R 2092 0 R 17 0 R] /Parent 3673 0 R >> endobj 3673 0 obj << /Type/Pages /Count 3 /Kids[2082 0 R 2086 0 R 2090 0 R] /Parent 3669 0 R >> endobj 3669 0 obj << /Type/Pages /Count 11 /Kids[3670 0 R 3671 0 R 3672 0 R 3673 0 R] /Parent 3668 0 R >> endobj 2094 0 obj << /Type/Page /Resources 2095 0 R /Contents[15 0 R 4 0 R 2096 0 R 17 0 R] /Parent 3675 0 R >> endobj 2098 0 obj << /Type/Page /Resources 2099 0 R /Contents[15 0 R 4 0 R 2100 0 R 17 0 R] /Parent 3675 0 R >> endobj 2102 0 obj << /Type/Page /Resources 2103 0 R /Contents[15 0 R 4 0 R 2104 0 R 17 0 R] /Parent 3675 0 R >> endobj 3675 0 obj << /Type/Pages /Count 3 /Kids[2094 0 R 2098 0 R 2102 0 R] /Parent 3674 0 R >> endobj 2106 0 obj << /Type/Page /Resources 2107 0 R /Contents[15 0 R 4 0 R 2108 0 R 17 0 R] /Parent 3676 0 R >> endobj 2110 0 obj << /Type/Page /Resources 2111 0 R /Contents[15 0 R 4 0 R 2112 0 R 17 0 R] /Parent 3676 0 R >> endobj 2114 0 obj << /Type/Page /Resources 2115 0 R /Contents[15 0 R 4 0 R 2116 0 R 17 0 R] /Parent 3676 0 R >> endobj 3676 0 obj << /Type/Pages /Count 3 /Kids[2106 0 R 2110 0 R 2114 0 R] /Parent 3674 0 R >> endobj 2118 0 obj << /Type/Page /Resources 2119 0 R /Contents[15 0 R 4 0 R 2120 0 R 17 0 R] /Parent 3677 0 R >> endobj 2122 0 obj << /Type/Page /Resources 2123 0 R /Contents[15 0 R 4 0 R 2124 0 R 17 0 R] /Parent 3677 0 R >> endobj 2126 0 obj << /Type/Page /Resources 2127 0 R /Contents[15 0 R 4 0 R 2128 0 R 17 0 R] /Parent 3677 0 R >> endobj 3677 0 obj << /Type/Pages /Count 3 /Kids[2118 0 R 2122 0 R 2126 0 R] /Parent 3674 0 R >> endobj 2130 0 obj << /Type/Page /Resources 2131 0 R /Contents[15 0 R 4 0 R 2132 0 R 17 0 R] /Parent 3678 0 R >> endobj 2134 0 obj << /Type/Page /Resources 2135 0 R /Contents[15 0 R 4 0 R 2136 0 R 17 0 R] /Parent 3678 0 R >> endobj 2138 0 obj << /Type/Page /Resources 2139 0 R /Contents[15 0 R 4 0 R 2140 0 R 17 0 R] /Parent 3678 0 R >> endobj 3678 0 obj << /Type/Pages /Count 3 /Kids[2130 0 R 2134 0 R 2138 0 R] /Parent 3674 0 R >> endobj 3674 0 obj << /Type/Pages /Count 12 /Kids[3675 0 R 3676 0 R 3677 0 R 3678 0 R] /Parent 3668 0 R >> endobj 2142 0 obj << /Type/Page /Resources 2143 0 R /Contents[15 0 R 4 0 R 2144 0 R 17 0 R] /Parent 3680 0 R >> endobj 2146 0 obj << /Type/Page /Resources 2147 0 R /Contents[15 0 R 4 0 R 2150 0 R 17 0 R] /Parent 3680 0 R >> endobj 2153 0 obj << /Type/Page /Resources 2154 0 R /Contents[15 0 R 4 0 R 2155 0 R 17 0 R] /Parent 3680 0 R >> endobj 3680 0 obj << /Type/Pages /Count 3 /Kids[2142 0 R 2146 0 R 2153 0 R] /Parent 3679 0 R >> endobj 2157 0 obj << /Type/Page /Resources 2158 0 R /Contents[15 0 R 4 0 R 2159 0 R 17 0 R] /Parent 3681 0 R >> endobj 2161 0 obj << /Type/Page /Resources 2162 0 R /Contents[15 0 R 4 0 R 2163 0 R 17 0 R] /Parent 3681 0 R >> endobj 2165 0 obj << /Type/Page /Resources 2166 0 R /Contents[15 0 R 4 0 R 2167 0 R 17 0 R] /Parent 3681 0 R >> endobj 3681 0 obj << /Type/Pages /Count 3 /Kids[2157 0 R 2161 0 R 2165 0 R] /Parent 3679 0 R >> endobj 2169 0 obj << /Type/Page /Resources 2170 0 R /Contents[15 0 R 4 0 R 2171 0 R 17 0 R] /Parent 3682 0 R >> endobj 2173 0 obj << /Type/Page /Resources 2174 0 R /Contents[15 0 R 4 0 R 2175 0 R 17 0 R] /Parent 3682 0 R >> endobj 2177 0 obj << /Type/Page /Resources 2178 0 R /Contents[15 0 R 4 0 R 2179 0 R 17 0 R] /Parent 3682 0 R >> endobj 3682 0 obj << /Type/Pages /Count 3 /Kids[2169 0 R 2173 0 R 2177 0 R] /Parent 3679 0 R >> endobj 2181 0 obj << /Type/Page /Resources 2182 0 R /Contents[15 0 R 4 0 R 2183 0 R 17 0 R] /Parent 3683 0 R >> endobj 2185 0 obj << /Type/Page /Resources 2186 0 R /Contents[15 0 R 4 0 R 2187 0 R 17 0 R] /Parent 3683 0 R >> endobj 2189 0 obj << /Type/Page /Resources 2190 0 R /Contents[15 0 R 4 0 R 2191 0 R 17 0 R] /Parent 3683 0 R >> endobj 3683 0 obj << /Type/Pages /Count 3 /Kids[2181 0 R 2185 0 R 2189 0 R] /Parent 3679 0 R >> endobj 3679 0 obj << /Type/Pages /Count 12 /Kids[3680 0 R 3681 0 R 3682 0 R 3683 0 R] /Parent 3668 0 R >> endobj 2193 0 obj << /Type/Page /Resources 2194 0 R /Contents[15 0 R 4 0 R 2195 0 R 17 0 R] /Parent 3685 0 R >> endobj 2197 0 obj << /Type/Page /Resources 2198 0 R /Contents[15 0 R 4 0 R 2199 0 R 17 0 R] /Parent 3685 0 R >> endobj 2201 0 obj << /Type/Page /Resources 2202 0 R /Contents[15 0 R 4 0 R 2203 0 R 17 0 R] /Parent 3685 0 R >> endobj 3685 0 obj << /Type/Pages /Count 3 /Kids[2193 0 R 2197 0 R 2201 0 R] /Parent 3684 0 R >> endobj 2205 0 obj << /Type/Page /Resources 2206 0 R /Contents[15 0 R 4 0 R 2207 0 R 17 0 R] /Parent 3686 0 R >> endobj 2209 0 obj << /Type/Page /Resources 2210 0 R /Contents[15 0 R 4 0 R 2213 0 R 17 0 R] /Parent 3686 0 R >> endobj 2216 0 obj << /Type/Page /Resources 2217 0 R /Contents[15 0 R 4 0 R 2218 0 R 17 0 R] /Parent 3686 0 R >> endobj 3686 0 obj << /Type/Pages /Count 3 /Kids[2205 0 R 2209 0 R 2216 0 R] /Parent 3684 0 R >> endobj 2220 0 obj << /Type/Page /Resources 2221 0 R /Contents[15 0 R 4 0 R 2222 0 R 17 0 R] /Parent 3687 0 R >> endobj 2224 0 obj << /Type/Page /Resources 2225 0 R /Contents[15 0 R 4 0 R 2226 0 R 17 0 R] /Parent 3687 0 R >> endobj 2228 0 obj << /Type/Page /Resources 2229 0 R /Contents[15 0 R 4 0 R 2232 0 R 17 0 R] /Parent 3687 0 R >> endobj 3687 0 obj << /Type/Pages /Count 3 /Kids[2220 0 R 2224 0 R 2228 0 R] /Parent 3684 0 R >> endobj 2235 0 obj << /Type/Page /Resources 2236 0 R /Contents[15 0 R 4 0 R 2237 0 R 17 0 R] /Parent 3688 0 R >> endobj 2239 0 obj << /Type/Page /Resources 2240 0 R /Contents[15 0 R 4 0 R 2243 0 R 17 0 R] /Parent 3688 0 R >> endobj 2246 0 obj << /Type/Page /Resources 2247 0 R /Contents[15 0 R 4 0 R 2248 0 R 17 0 R] /Parent 3688 0 R >> endobj 3688 0 obj << /Type/Pages /Count 3 /Kids[2235 0 R 2239 0 R 2246 0 R] /Parent 3684 0 R >> endobj 3684 0 obj << /Type/Pages /Count 12 /Kids[3685 0 R 3686 0 R 3687 0 R 3688 0 R] /Parent 3668 0 R >> endobj 3668 0 obj << /Type/Pages /Count 47 /Kids[3669 0 R 3674 0 R 3679 0 R 3684 0 R] /Parent 3646 0 R >> endobj 2250 0 obj << /Type/Page /Resources 2251 0 R /Contents[15 0 R 4 0 R 2252 0 R 17 0 R] /Parent 3691 0 R >> endobj 2254 0 obj << /Type/Page /Resources 2255 0 R /Contents[15 0 R 4 0 R 2256 0 R 17 0 R] /Parent 3691 0 R >> endobj 3691 0 obj << /Type/Pages /Count 2 /Kids[2250 0 R 2254 0 R] /Parent 3690 0 R >> endobj 2258 0 obj << /Type/Page /Resources 2259 0 R /Contents[15 0 R 4 0 R 2263 0 R 17 0 R] /Parent 3692 0 R >> endobj 2265 0 obj << /Type/Page /Resources 2266 0 R /Contents[15 0 R 4 0 R 2267 0 R 17 0 R] /Parent 3692 0 R >> endobj 2269 0 obj << /Type/Page /Resources 2270 0 R /Contents[15 0 R 4 0 R 2273 0 R 17 0 R] /Parent 3692 0 R >> endobj 3692 0 obj << /Type/Pages /Count 3 /Kids[2258 0 R 2265 0 R 2269 0 R] /Parent 3690 0 R >> endobj 2276 0 obj << /Type/Page /Resources 2277 0 R /Contents[15 0 R 4 0 R 2278 0 R 17 0 R] /Parent 3693 0 R >> endobj 2280 0 obj << /Type/Page /Resources 2281 0 R /Contents[15 0 R 4 0 R 2282 0 R 17 0 R] /Parent 3693 0 R >> endobj 2284 0 obj << /Type/Page /Resources 2285 0 R /Contents[15 0 R 4 0 R 2286 0 R 17 0 R] /Parent 3693 0 R >> endobj 3693 0 obj << /Type/Pages /Count 3 /Kids[2276 0 R 2280 0 R 2284 0 R] /Parent 3690 0 R >> endobj 2288 0 obj << /Type/Page /Resources 2289 0 R /Contents[15 0 R 4 0 R 2290 0 R 17 0 R] /Parent 3694 0 R >> endobj 2292 0 obj << /Type/Page /Resources 2293 0 R /Contents[15 0 R 4 0 R 2294 0 R 17 0 R] /Parent 3694 0 R >> endobj 2296 0 obj << /Type/Page /Resources 2297 0 R /Contents[15 0 R 4 0 R 2298 0 R 17 0 R] /Parent 3694 0 R >> endobj 3694 0 obj << /Type/Pages /Count 3 /Kids[2288 0 R 2292 0 R 2296 0 R] /Parent 3690 0 R >> endobj 3690 0 obj << /Type/Pages /Count 11 /Kids[3691 0 R 3692 0 R 3693 0 R 3694 0 R] /Parent 3689 0 R >> endobj 2300 0 obj << /Type/Page /Resources 2301 0 R /Contents[15 0 R 4 0 R 2306 0 R 17 0 R] /Parent 3696 0 R >> endobj 2309 0 obj << /Type/Page /Resources 2310 0 R /Contents[15 0 R 4 0 R 2313 0 R 17 0 R] /Parent 3696 0 R >> endobj 2316 0 obj << /Type/Page /Resources 2317 0 R /Contents[15 0 R 4 0 R 2318 0 R 17 0 R] /Parent 3696 0 R >> endobj 3696 0 obj << /Type/Pages /Count 3 /Kids[2300 0 R 2309 0 R 2316 0 R] /Parent 3695 0 R >> endobj 2320 0 obj << /Type/Page /Resources 2321 0 R /Contents[15 0 R 4 0 R 2322 0 R 17 0 R] /Parent 3697 0 R >> endobj 2324 0 obj << /Type/Page /Resources 2325 0 R /Contents[15 0 R 4 0 R 2326 0 R 17 0 R] /Parent 3697 0 R >> endobj 2328 0 obj << /Type/Page /Resources 2329 0 R /Contents[15 0 R 4 0 R 2330 0 R 17 0 R] /Parent 3697 0 R >> endobj 3697 0 obj << /Type/Pages /Count 3 /Kids[2320 0 R 2324 0 R 2328 0 R] /Parent 3695 0 R >> endobj 2332 0 obj << /Type/Page /Resources 2333 0 R /Contents[15 0 R 4 0 R 2334 0 R 17 0 R] /Parent 3698 0 R >> endobj 2336 0 obj << /Type/Page /Resources 2337 0 R /Contents[15 0 R 4 0 R 2338 0 R 17 0 R] /Parent 3698 0 R >> endobj 2340 0 obj << /Type/Page /Resources 2341 0 R /Contents[15 0 R 4 0 R 2342 0 R 17 0 R] /Parent 3698 0 R >> endobj 3698 0 obj << /Type/Pages /Count 3 /Kids[2332 0 R 2336 0 R 2340 0 R] /Parent 3695 0 R >> endobj 2344 0 obj << /Type/Page /Resources 2345 0 R /Contents[15 0 R 4 0 R 2346 0 R 17 0 R] /Parent 3699 0 R >> endobj 2348 0 obj << /Type/Page /Resources 2349 0 R /Contents[15 0 R 4 0 R 2350 0 R 17 0 R] /Parent 3699 0 R >> endobj 2352 0 obj << /Type/Page /Resources 2353 0 R /Contents[15 0 R 4 0 R 2354 0 R 17 0 R] /Parent 3699 0 R >> endobj 3699 0 obj << /Type/Pages /Count 3 /Kids[2344 0 R 2348 0 R 2352 0 R] /Parent 3695 0 R >> endobj 3695 0 obj << /Type/Pages /Count 12 /Kids[3696 0 R 3697 0 R 3698 0 R 3699 0 R] /Parent 3689 0 R >> endobj 2356 0 obj << /Type/Page /Resources 2357 0 R /Contents[15 0 R 4 0 R 2358 0 R 17 0 R] /Parent 3701 0 R >> endobj 2360 0 obj << /Type/Page /Resources 2361 0 R /Contents[15 0 R 4 0 R 2362 0 R 17 0 R] /Parent 3701 0 R >> endobj 2364 0 obj << /Type/Page /Resources 2365 0 R /Contents[15 0 R 4 0 R 2366 0 R 17 0 R] /Parent 3701 0 R >> endobj 3701 0 obj << /Type/Pages /Count 3 /Kids[2356 0 R 2360 0 R 2364 0 R] /Parent 3700 0 R >> endobj 2368 0 obj << /Type/Page /Resources 2369 0 R /Contents[15 0 R 4 0 R 2370 0 R 17 0 R] /Parent 3702 0 R >> endobj 2372 0 obj << /Type/Page /Resources 2373 0 R /Contents[15 0 R 4 0 R 2374 0 R 17 0 R] /Parent 3702 0 R >> endobj 2376 0 obj << /Type/Page /Resources 2377 0 R /Contents[15 0 R 4 0 R 2378 0 R 17 0 R] /Parent 3702 0 R >> endobj 3702 0 obj << /Type/Pages /Count 3 /Kids[2368 0 R 2372 0 R 2376 0 R] /Parent 3700 0 R >> endobj 2380 0 obj << /Type/Page /Resources 2381 0 R /Contents[15 0 R 4 0 R 2382 0 R 17 0 R] /Parent 3703 0 R >> endobj 2384 0 obj << /Type/Page /Resources 2385 0 R /Contents[15 0 R 4 0 R 2386 0 R 17 0 R] /Parent 3703 0 R >> endobj 2388 0 obj << /Type/Page /Resources 2389 0 R /Contents[15 0 R 4 0 R 2390 0 R 17 0 R] /Parent 3703 0 R >> endobj 3703 0 obj << /Type/Pages /Count 3 /Kids[2380 0 R 2384 0 R 2388 0 R] /Parent 3700 0 R >> endobj 2392 0 obj << /Type/Page /Resources 2393 0 R /Contents[15 0 R 4 0 R 2394 0 R 17 0 R] /Parent 3704 0 R >> endobj 2396 0 obj << /Type/Page /Resources 2397 0 R /Contents[15 0 R 4 0 R 2398 0 R 17 0 R] /Parent 3704 0 R >> endobj 2400 0 obj << /Type/Page /Resources 2401 0 R /Contents[15 0 R 4 0 R 2402 0 R 17 0 R] /Parent 3704 0 R >> endobj 3704 0 obj << /Type/Pages /Count 3 /Kids[2392 0 R 2396 0 R 2400 0 R] /Parent 3700 0 R >> endobj 3700 0 obj << /Type/Pages /Count 12 /Kids[3701 0 R 3702 0 R 3703 0 R 3704 0 R] /Parent 3689 0 R >> endobj 2404 0 obj << /Type/Page /Resources 2405 0 R /Contents[15 0 R 4 0 R 2406 0 R 17 0 R] /Parent 3706 0 R >> endobj 2408 0 obj << /Type/Page /Resources 2409 0 R /Contents[15 0 R 4 0 R 2410 0 R 17 0 R] /Parent 3706 0 R >> endobj 2412 0 obj << /Type/Page /Resources 2413 0 R /Contents[15 0 R 4 0 R 2414 0 R 17 0 R] /Parent 3706 0 R >> endobj 3706 0 obj << /Type/Pages /Count 3 /Kids[2404 0 R 2408 0 R 2412 0 R] /Parent 3705 0 R >> endobj 2416 0 obj << /Type/Page /Resources 2417 0 R /Contents[15 0 R 4 0 R 2418 0 R 17 0 R] /Parent 3707 0 R >> endobj 2420 0 obj << /Type/Page /Resources 2421 0 R /Contents[15 0 R 4 0 R 2424 0 R 17 0 R] /Parent 3707 0 R >> endobj 2427 0 obj << /Type/Page /Resources 2428 0 R /Contents[15 0 R 4 0 R 2429 0 R 17 0 R] /Parent 3707 0 R >> endobj 3707 0 obj << /Type/Pages /Count 3 /Kids[2416 0 R 2420 0 R 2427 0 R] /Parent 3705 0 R >> endobj 2431 0 obj << /Type/Page /Resources 2432 0 R /Contents[15 0 R 4 0 R 2433 0 R 17 0 R] /Parent 3708 0 R >> endobj 2435 0 obj << /Type/Page /Resources 2436 0 R /Contents[15 0 R 4 0 R 2437 0 R 17 0 R] /Parent 3708 0 R >> endobj 2439 0 obj << /Type/Page /Resources 2440 0 R /Contents[15 0 R 4 0 R 2441 0 R 17 0 R] /Parent 3708 0 R >> endobj 3708 0 obj << /Type/Pages /Count 3 /Kids[2431 0 R 2435 0 R 2439 0 R] /Parent 3705 0 R >> endobj 2443 0 obj << /Type/Page /Resources 2444 0 R /Contents[15 0 R 4 0 R 2445 0 R 17 0 R] /Parent 3709 0 R >> endobj 2447 0 obj << /Type/Page /Resources 2448 0 R /Contents[15 0 R 4 0 R 2449 0 R 17 0 R] /Parent 3709 0 R >> endobj 2451 0 obj << /Type/Page /Resources 2452 0 R /Contents[15 0 R 4 0 R 2453 0 R 17 0 R] /Parent 3709 0 R >> endobj 3709 0 obj << /Type/Pages /Count 3 /Kids[2443 0 R 2447 0 R 2451 0 R] /Parent 3705 0 R >> endobj 3705 0 obj << /Type/Pages /Count 12 /Kids[3706 0 R 3707 0 R 3708 0 R 3709 0 R] /Parent 3689 0 R >> endobj 3689 0 obj << /Type/Pages /Count 47 /Kids[3690 0 R 3695 0 R 3700 0 R 3705 0 R] /Parent 3646 0 R >> endobj 2455 0 obj << /Type/Page /Resources 2456 0 R /Contents[15 0 R 4 0 R 2457 0 R 17 0 R] /Parent 3712 0 R >> endobj 2459 0 obj << /Type/Page /Resources 2460 0 R /Contents[15 0 R 4 0 R 2461 0 R 17 0 R] /Parent 3712 0 R >> endobj 2463 0 obj << /Type/Page /Resources 2464 0 R /Contents[15 0 R 4 0 R 2465 0 R 17 0 R] /Parent 3712 0 R >> endobj 3712 0 obj << /Type/Pages /Count 3 /Kids[2455 0 R 2459 0 R 2463 0 R] /Parent 3711 0 R >> endobj 2467 0 obj << /Type/Page /Resources 2468 0 R /Contents[15 0 R 4 0 R 2469 0 R 17 0 R] /Parent 3713 0 R >> endobj 2471 0 obj << /Type/Page /Resources 2472 0 R /Contents[15 0 R 4 0 R 2473 0 R 17 0 R] /Parent 3713 0 R >> endobj 2475 0 obj << /Type/Page /Resources 2476 0 R /Contents[15 0 R 4 0 R 2477 0 R 17 0 R] /Parent 3713 0 R >> endobj 3713 0 obj << /Type/Pages /Count 3 /Kids[2467 0 R 2471 0 R 2475 0 R] /Parent 3711 0 R >> endobj 2479 0 obj << /Type/Page /Resources 2480 0 R /Contents[15 0 R 4 0 R 2481 0 R 17 0 R] /Parent 3714 0 R >> endobj 2483 0 obj << /Type/Page /Resources 2484 0 R /Contents[15 0 R 4 0 R 2485 0 R 17 0 R] /Parent 3714 0 R >> endobj 2487 0 obj << /Type/Page /Resources 2488 0 R /Contents[15 0 R 4 0 R 2489 0 R 17 0 R] /Parent 3714 0 R >> endobj 3714 0 obj << /Type/Pages /Count 3 /Kids[2479 0 R 2483 0 R 2487 0 R] /Parent 3711 0 R >> endobj 2491 0 obj << /Type/Page /Resources 2492 0 R /Contents[15 0 R 4 0 R 2498 0 R 17 0 R] /Parent 3715 0 R >> endobj 2501 0 obj << /Type/Page /Resources 2502 0 R /Contents[15 0 R 4 0 R 2505 0 R 17 0 R] /Parent 3715 0 R >> endobj 2508 0 obj << /Type/Page /Resources 2509 0 R /Contents[15 0 R 4 0 R 2512 0 R 17 0 R] /Parent 3715 0 R >> endobj 3715 0 obj << /Type/Pages /Count 3 /Kids[2491 0 R 2501 0 R 2508 0 R] /Parent 3711 0 R >> endobj 3711 0 obj << /Type/Pages /Count 12 /Kids[3712 0 R 3713 0 R 3714 0 R 3715 0 R] /Parent 3710 0 R >> endobj 2515 0 obj << /Type/Page /Resources 2516 0 R /Contents[15 0 R 4 0 R 2517 0 R 17 0 R] /Parent 3717 0 R >> endobj 2519 0 obj << /Type/Page /Resources 2520 0 R /Contents[15 0 R 4 0 R 2521 0 R 17 0 R] /Parent 3717 0 R >> endobj 2523 0 obj << /Type/Page /Resources 2524 0 R /Contents[15 0 R 4 0 R 2525 0 R 17 0 R] /Parent 3717 0 R >> endobj 3717 0 obj << /Type/Pages /Count 3 /Kids[2515 0 R 2519 0 R 2523 0 R] /Parent 3716 0 R >> endobj 2527 0 obj << /Type/Page /Resources 2528 0 R /Contents[15 0 R 4 0 R 2529 0 R 17 0 R] /Parent 3718 0 R >> endobj 2531 0 obj << /Type/Page /Resources 2532 0 R /Contents[15 0 R 4 0 R 2535 0 R 17 0 R] /Parent 3718 0 R >> endobj 2538 0 obj << /Type/Page /Resources 2539 0 R /Contents[15 0 R 4 0 R 2540 0 R 17 0 R] /Parent 3718 0 R >> endobj 3718 0 obj << /Type/Pages /Count 3 /Kids[2527 0 R 2531 0 R 2538 0 R] /Parent 3716 0 R >> endobj 2542 0 obj << /Type/Page /Resources 2543 0 R /Contents[15 0 R 4 0 R 2544 0 R 17 0 R] /Parent 3719 0 R >> endobj 2546 0 obj << /Type/Page /Resources 2547 0 R /Contents[15 0 R 4 0 R 2548 0 R 17 0 R] /Parent 3719 0 R >> endobj 2550 0 obj << /Type/Page /Resources 2551 0 R /Contents[15 0 R 4 0 R 2552 0 R 17 0 R] /Parent 3719 0 R >> endobj 3719 0 obj << /Type/Pages /Count 3 /Kids[2542 0 R 2546 0 R 2550 0 R] /Parent 3716 0 R >> endobj 2554 0 obj << /Type/Page /Resources 2555 0 R /Contents[15 0 R 4 0 R 2558 0 R 17 0 R] /Parent 3720 0 R >> endobj 2561 0 obj << /Type/Page /Resources 2562 0 R /Contents[15 0 R 4 0 R 2563 0 R 17 0 R] /Parent 3720 0 R >> endobj 2565 0 obj << /Type/Page /Resources 2566 0 R /Contents[15 0 R 4 0 R 2567 0 R 17 0 R] /Parent 3720 0 R >> endobj 3720 0 obj << /Type/Pages /Count 3 /Kids[2554 0 R 2561 0 R 2565 0 R] /Parent 3716 0 R >> endobj 3716 0 obj << /Type/Pages /Count 12 /Kids[3717 0 R 3718 0 R 3719 0 R 3720 0 R] /Parent 3710 0 R >> endobj 2569 0 obj << /Type/Page /Resources 2570 0 R /Contents[15 0 R 4 0 R 2571 0 R 17 0 R] /Parent 3722 0 R >> endobj 2573 0 obj << /Type/Page /Resources 2574 0 R /Contents[15 0 R 4 0 R 2575 0 R 17 0 R] /Parent 3722 0 R >> endobj 2577 0 obj << /Type/Page /Resources 2578 0 R /Contents[15 0 R 4 0 R 2579 0 R 17 0 R] /Parent 3722 0 R >> endobj 3722 0 obj << /Type/Pages /Count 3 /Kids[2569 0 R 2573 0 R 2577 0 R] /Parent 3721 0 R >> endobj 2581 0 obj << /Type/Page /Resources 2582 0 R /Contents[15 0 R 4 0 R 2583 0 R 17 0 R] /Parent 3723 0 R >> endobj 2585 0 obj << /Type/Page /Resources 2586 0 R /Contents[15 0 R 4 0 R 2587 0 R 17 0 R] /Parent 3723 0 R >> endobj 2589 0 obj << /Type/Page /Resources 2590 0 R /Contents[15 0 R 4 0 R 2591 0 R 17 0 R] /Parent 3723 0 R >> endobj 3723 0 obj << /Type/Pages /Count 3 /Kids[2581 0 R 2585 0 R 2589 0 R] /Parent 3721 0 R >> endobj 2593 0 obj << /Type/Page /Resources 2594 0 R /Contents[15 0 R 4 0 R 2595 0 R 17 0 R] /Parent 3724 0 R >> endobj 2597 0 obj << /Type/Page /Resources 2598 0 R /Contents[15 0 R 4 0 R 2599 0 R 17 0 R] /Parent 3724 0 R >> endobj 2601 0 obj << /Type/Page /Resources 2602 0 R /Contents[15 0 R 4 0 R 2603 0 R 17 0 R] /Parent 3724 0 R >> endobj 3724 0 obj << /Type/Pages /Count 3 /Kids[2593 0 R 2597 0 R 2601 0 R] /Parent 3721 0 R >> endobj 2605 0 obj << /Type/Page /Resources 2606 0 R /Contents[15 0 R 4 0 R 2607 0 R 17 0 R] /Parent 3725 0 R >> endobj 2609 0 obj << /Type/Page /Resources 2610 0 R /Contents[15 0 R 4 0 R 2613 0 R 17 0 R] /Parent 3725 0 R >> endobj 2616 0 obj << /Type/Page /Resources 2617 0 R /Contents[15 0 R 4 0 R 2620 0 R 17 0 R] /Parent 3725 0 R >> endobj 3725 0 obj << /Type/Pages /Count 3 /Kids[2605 0 R 2609 0 R 2616 0 R] /Parent 3721 0 R >> endobj 3721 0 obj << /Type/Pages /Count 12 /Kids[3722 0 R 3723 0 R 3724 0 R 3725 0 R] /Parent 3710 0 R >> endobj 2623 0 obj << /Type/Page /Resources 2624 0 R /Contents[15 0 R 4 0 R 2627 0 R 17 0 R] /Parent 3727 0 R >> endobj 2630 0 obj << /Type/Page /Resources 2631 0 R /Contents[15 0 R 4 0 R 2632 0 R 17 0 R] /Parent 3727 0 R >> endobj 2634 0 obj << /Type/Page /Resources 2635 0 R /Contents[15 0 R 4 0 R 2636 0 R 17 0 R] /Parent 3727 0 R >> endobj 3727 0 obj << /Type/Pages /Count 3 /Kids[2623 0 R 2630 0 R 2634 0 R] /Parent 3726 0 R >> endobj 2638 0 obj << /Type/Page /Resources 2639 0 R /Contents[15 0 R 4 0 R 2640 0 R 17 0 R] /Parent 3728 0 R >> endobj 2642 0 obj << /Type/Page /Resources 2643 0 R /Contents[15 0 R 4 0 R 2644 0 R 17 0 R] /Parent 3728 0 R >> endobj 2646 0 obj << /Type/Page /Resources 2647 0 R /Contents[15 0 R 4 0 R 2648 0 R 17 0 R] /Parent 3728 0 R >> endobj 3728 0 obj << /Type/Pages /Count 3 /Kids[2638 0 R 2642 0 R 2646 0 R] /Parent 3726 0 R >> endobj 2650 0 obj << /Type/Page /Resources 2651 0 R /Contents[15 0 R 4 0 R 2652 0 R 17 0 R] /Parent 3729 0 R >> endobj 2654 0 obj << /Type/Page /Resources 2655 0 R /Contents[15 0 R 4 0 R 2656 0 R 17 0 R] /Parent 3729 0 R >> endobj 2658 0 obj << /Type/Page /Resources 2659 0 R /Contents[15 0 R 4 0 R 2660 0 R 17 0 R] /Parent 3729 0 R >> endobj 3729 0 obj << /Type/Pages /Count 3 /Kids[2650 0 R 2654 0 R 2658 0 R] /Parent 3726 0 R >> endobj 2662 0 obj << /Type/Page /Resources 2663 0 R /Contents[15 0 R 4 0 R 2664 0 R 17 0 R] /Parent 3730 0 R >> endobj 2666 0 obj << /Type/Page /Resources 2667 0 R /Contents[15 0 R 4 0 R 2668 0 R 17 0 R] /Parent 3730 0 R >> endobj 2670 0 obj << /Type/Page /Resources 2671 0 R /Contents[15 0 R 4 0 R 2672 0 R 17 0 R] /Parent 3730 0 R >> endobj 3730 0 obj << /Type/Pages /Count 3 /Kids[2662 0 R 2666 0 R 2670 0 R] /Parent 3726 0 R >> endobj 3726 0 obj << /Type/Pages /Count 12 /Kids[3727 0 R 3728 0 R 3729 0 R 3730 0 R] /Parent 3710 0 R >> endobj 3710 0 obj << /Type/Pages /Count 48 /Kids[3711 0 R 3716 0 R 3721 0 R 3726 0 R] /Parent 3646 0 R >> endobj 3646 0 obj << /Type/Pages /Count 189 /Kids[3647 0 R 3668 0 R 3689 0 R 3710 0 R] /Parent 3 0 R >> endobj 2674 0 obj << /Type/Page /Resources 2675 0 R /Contents[15 0 R 4 0 R 2676 0 R 17 0 R] /Parent 3734 0 R >> endobj 2678 0 obj << /Type/Page /Resources 2679 0 R /Contents[15 0 R 4 0 R 2680 0 R 17 0 R] /Parent 3734 0 R >> endobj 3734 0 obj << /Type/Pages /Count 2 /Kids[2674 0 R 2678 0 R] /Parent 3733 0 R >> endobj 2682 0 obj << /Type/Page /Resources 2683 0 R /Contents[15 0 R 4 0 R 2684 0 R 17 0 R] /Parent 3735 0 R >> endobj 2686 0 obj << /Type/Page /Resources 2687 0 R /Contents[15 0 R 4 0 R 2688 0 R 17 0 R] /Parent 3735 0 R >> endobj 2690 0 obj << /Type/Page /Resources 2691 0 R /Contents[15 0 R 4 0 R 2692 0 R 17 0 R] /Parent 3735 0 R >> endobj 3735 0 obj << /Type/Pages /Count 3 /Kids[2682 0 R 2686 0 R 2690 0 R] /Parent 3733 0 R >> endobj 2694 0 obj << /Type/Page /Resources 2695 0 R /Contents[15 0 R 4 0 R 2696 0 R 17 0 R] /Parent 3736 0 R >> endobj 2698 0 obj << /Type/Page /Resources 2699 0 R /Contents[15 0 R 4 0 R 2700 0 R 17 0 R] /Parent 3736 0 R >> endobj 2702 0 obj << /Type/Page /Resources 2703 0 R /Contents[15 0 R 4 0 R 2704 0 R 17 0 R] /Parent 3736 0 R >> endobj 3736 0 obj << /Type/Pages /Count 3 /Kids[2694 0 R 2698 0 R 2702 0 R] /Parent 3733 0 R >> endobj 2706 0 obj << /Type/Page /Resources 2707 0 R /Contents[15 0 R 4 0 R 2708 0 R 17 0 R] /Parent 3737 0 R >> endobj 2710 0 obj << /Type/Page /Resources 2711 0 R /Contents[15 0 R 4 0 R 2712 0 R 17 0 R] /Parent 3737 0 R >> endobj 2714 0 obj << /Type/Page /Resources 2715 0 R /Contents[15 0 R 4 0 R 2718 0 R 17 0 R] /Parent 3737 0 R >> endobj 3737 0 obj << /Type/Pages /Count 3 /Kids[2706 0 R 2710 0 R 2714 0 R] /Parent 3733 0 R >> endobj 3733 0 obj << /Type/Pages /Count 11 /Kids[3734 0 R 3735 0 R 3736 0 R 3737 0 R] /Parent 3732 0 R >> endobj 2721 0 obj << /Type/Page /Resources 2722 0 R /Contents[15 0 R 4 0 R 2723 0 R 17 0 R] /Parent 3739 0 R >> endobj 2725 0 obj << /Type/Page /Resources 2726 0 R /Contents[15 0 R 4 0 R 2727 0 R 17 0 R] /Parent 3739 0 R >> endobj 2729 0 obj << /Type/Page /Resources 2730 0 R /Contents[15 0 R 4 0 R 2731 0 R 17 0 R] /Parent 3739 0 R >> endobj 3739 0 obj << /Type/Pages /Count 3 /Kids[2721 0 R 2725 0 R 2729 0 R] /Parent 3738 0 R >> endobj 2733 0 obj << /Type/Page /Resources 2734 0 R /Contents[15 0 R 4 0 R 2737 0 R 17 0 R] /Parent 3740 0 R >> endobj 2740 0 obj << /Type/Page /Resources 2741 0 R /Contents[15 0 R 4 0 R 2742 0 R 17 0 R] /Parent 3740 0 R >> endobj 2744 0 obj << /Type/Page /Resources 2745 0 R /Contents[15 0 R 4 0 R 2748 0 R 17 0 R] /Parent 3740 0 R >> endobj 3740 0 obj << /Type/Pages /Count 3 /Kids[2733 0 R 2740 0 R 2744 0 R] /Parent 3738 0 R >> endobj 2751 0 obj << /Type/Page /Resources 2752 0 R /Contents[15 0 R 4 0 R 2755 0 R 17 0 R] /Parent 3741 0 R >> endobj 2758 0 obj << /Type/Page /Resources 2759 0 R /Contents[15 0 R 4 0 R 2760 0 R 17 0 R] /Parent 3741 0 R >> endobj 2762 0 obj << /Type/Page /Resources 2763 0 R /Contents[15 0 R 4 0 R 2764 0 R 17 0 R] /Parent 3741 0 R >> endobj 3741 0 obj << /Type/Pages /Count 3 /Kids[2751 0 R 2758 0 R 2762 0 R] /Parent 3738 0 R >> endobj 2766 0 obj << /Type/Page /Resources 2767 0 R /Contents[15 0 R 4 0 R 2768 0 R 17 0 R] /Parent 3742 0 R >> endobj 2770 0 obj << /Type/Page /Resources 2771 0 R /Contents[15 0 R 4 0 R 2772 0 R 17 0 R] /Parent 3742 0 R >> endobj 2774 0 obj << /Type/Page /Resources 2775 0 R /Contents[15 0 R 4 0 R 2776 0 R 17 0 R] /Parent 3742 0 R >> endobj 3742 0 obj << /Type/Pages /Count 3 /Kids[2766 0 R 2770 0 R 2774 0 R] /Parent 3738 0 R >> endobj 3738 0 obj << /Type/Pages /Count 12 /Kids[3739 0 R 3740 0 R 3741 0 R 3742 0 R] /Parent 3732 0 R >> endobj 2778 0 obj << /Type/Page /Resources 2779 0 R /Contents[15 0 R 4 0 R 2780 0 R 17 0 R] /Parent 3744 0 R >> endobj 2782 0 obj << /Type/Page /Resources 2783 0 R /Contents[15 0 R 4 0 R 2784 0 R 17 0 R] /Parent 3744 0 R >> endobj 2785 0 obj << /Type/Page /Resources 2786 0 R /Contents[15 0 R 4 0 R 2787 0 R 17 0 R] /Parent 3744 0 R >> endobj 3744 0 obj << /Type/Pages /Count 3 /Kids[2778 0 R 2782 0 R 2785 0 R] /Parent 3743 0 R >> endobj 2789 0 obj << /Type/Page /Resources 2790 0 R /Contents[15 0 R 4 0 R 2794 0 R 17 0 R] /Parent 3745 0 R >> endobj 2797 0 obj << /Type/Page /Resources 2798 0 R /Contents[15 0 R 4 0 R 2799 0 R 17 0 R] /Parent 3745 0 R >> endobj 2801 0 obj << /Type/Page /Resources 2802 0 R /Contents[15 0 R 4 0 R 2803 0 R 17 0 R] /Parent 3745 0 R >> endobj 3745 0 obj << /Type/Pages /Count 3 /Kids[2789 0 R 2797 0 R 2801 0 R] /Parent 3743 0 R >> endobj 2805 0 obj << /Type/Page /Resources 2806 0 R /Contents[15 0 R 4 0 R 2807 0 R 17 0 R] /Parent 3746 0 R >> endobj 2809 0 obj << /Type/Page /Resources 2810 0 R /Contents[15 0 R 4 0 R 2811 0 R 17 0 R] /Parent 3746 0 R >> endobj 2813 0 obj << /Type/Page /Resources 2814 0 R /Contents[15 0 R 4 0 R 2815 0 R 17 0 R] /Parent 3746 0 R >> endobj 3746 0 obj << /Type/Pages /Count 3 /Kids[2805 0 R 2809 0 R 2813 0 R] /Parent 3743 0 R >> endobj 2817 0 obj << /Type/Page /Resources 2818 0 R /Contents[15 0 R 4 0 R 2819 0 R 17 0 R] /Parent 3747 0 R >> endobj 2821 0 obj << /Type/Page /Resources 2822 0 R /Contents[15 0 R 4 0 R 2823 0 R 17 0 R] /Parent 3747 0 R >> endobj 2825 0 obj << /Type/Page /Resources 2826 0 R /Contents[15 0 R 4 0 R 2827 0 R 17 0 R] /Parent 3747 0 R >> endobj 3747 0 obj << /Type/Pages /Count 3 /Kids[2817 0 R 2821 0 R 2825 0 R] /Parent 3743 0 R >> endobj 3743 0 obj << /Type/Pages /Count 12 /Kids[3744 0 R 3745 0 R 3746 0 R 3747 0 R] /Parent 3732 0 R >> endobj 2829 0 obj << /Type/Page /Resources 2830 0 R /Contents[15 0 R 4 0 R 2831 0 R 17 0 R] /Parent 3749 0 R >> endobj 2833 0 obj << /Type/Page /Resources 2834 0 R /Contents[15 0 R 4 0 R 2835 0 R 17 0 R] /Parent 3749 0 R >> endobj 2837 0 obj << /Type/Page /Resources 2838 0 R /Contents[15 0 R 4 0 R 2839 0 R 17 0 R] /Parent 3749 0 R >> endobj 3749 0 obj << /Type/Pages /Count 3 /Kids[2829 0 R 2833 0 R 2837 0 R] /Parent 3748 0 R >> endobj 2841 0 obj << /Type/Page /Resources 2842 0 R /Contents[15 0 R 4 0 R 2843 0 R 17 0 R] /Parent 3750 0 R >> endobj 2845 0 obj << /Type/Page /Resources 2846 0 R /Contents[15 0 R 4 0 R 2847 0 R 17 0 R] /Parent 3750 0 R >> endobj 2849 0 obj << /Type/Page /Resources 2850 0 R /Contents[15 0 R 4 0 R 2851 0 R 17 0 R] /Parent 3750 0 R >> endobj 3750 0 obj << /Type/Pages /Count 3 /Kids[2841 0 R 2845 0 R 2849 0 R] /Parent 3748 0 R >> endobj 2853 0 obj << /Type/Page /Resources 2854 0 R /Contents[15 0 R 4 0 R 2855 0 R 17 0 R] /Parent 3751 0 R >> endobj 2857 0 obj << /Type/Page /Resources 2858 0 R /Contents[15 0 R 4 0 R 2859 0 R 17 0 R] /Parent 3751 0 R >> endobj 2861 0 obj << /Type/Page /Resources 2862 0 R /Contents[15 0 R 4 0 R 2863 0 R 17 0 R] /Parent 3751 0 R >> endobj 3751 0 obj << /Type/Pages /Count 3 /Kids[2853 0 R 2857 0 R 2861 0 R] /Parent 3748 0 R >> endobj 2865 0 obj << /Type/Page /Resources 2866 0 R /Contents[15 0 R 4 0 R 2867 0 R 17 0 R] /Parent 3752 0 R >> endobj 2869 0 obj << /Type/Page /Resources 2870 0 R /Contents[15 0 R 4 0 R 2871 0 R 17 0 R] /Parent 3752 0 R >> endobj 2873 0 obj << /Type/Page /Resources 2874 0 R /Contents[15 0 R 4 0 R 2875 0 R 17 0 R] /Parent 3752 0 R >> endobj 3752 0 obj << /Type/Pages /Count 3 /Kids[2865 0 R 2869 0 R 2873 0 R] /Parent 3748 0 R >> endobj 3748 0 obj << /Type/Pages /Count 12 /Kids[3749 0 R 3750 0 R 3751 0 R 3752 0 R] /Parent 3732 0 R >> endobj 3732 0 obj << /Type/Pages /Count 47 /Kids[3733 0 R 3738 0 R 3743 0 R 3748 0 R] /Parent 3731 0 R >> endobj 2877 0 obj << /Type/Page /Resources 2878 0 R /Contents[15 0 R 4 0 R 2879 0 R 17 0 R] /Parent 3755 0 R >> endobj 2881 0 obj << /Type/Page /Resources 2882 0 R /Contents[15 0 R 4 0 R 2883 0 R 17 0 R] /Parent 3755 0 R >> endobj 2885 0 obj << /Type/Page /Resources 2886 0 R /Contents[15 0 R 4 0 R 2887 0 R 17 0 R] /Parent 3755 0 R >> endobj 3755 0 obj << /Type/Pages /Count 3 /Kids[2877 0 R 2881 0 R 2885 0 R] /Parent 3754 0 R >> endobj 2889 0 obj << /Type/Page /Resources 2890 0 R /Contents[15 0 R 4 0 R 2891 0 R 17 0 R] /Parent 3756 0 R >> endobj 2893 0 obj << /Type/Page /Resources 2894 0 R /Contents[15 0 R 4 0 R 2895 0 R 17 0 R] /Parent 3756 0 R >> endobj 2897 0 obj << /Type/Page /Resources 2898 0 R /Contents[15 0 R 4 0 R 2899 0 R 17 0 R] /Parent 3756 0 R >> endobj 3756 0 obj << /Type/Pages /Count 3 /Kids[2889 0 R 2893 0 R 2897 0 R] /Parent 3754 0 R >> endobj 2901 0 obj << /Type/Page /Resources 2902 0 R /Contents[15 0 R 4 0 R 2903 0 R 17 0 R] /Parent 3757 0 R >> endobj 2905 0 obj << /Type/Page /Resources 2906 0 R /Contents[15 0 R 4 0 R 2907 0 R 17 0 R] /Parent 3757 0 R >> endobj 2909 0 obj << /Type/Page /Resources 2910 0 R /Contents[15 0 R 4 0 R 2911 0 R 17 0 R] /Parent 3757 0 R >> endobj 3757 0 obj << /Type/Pages /Count 3 /Kids[2901 0 R 2905 0 R 2909 0 R] /Parent 3754 0 R >> endobj 2913 0 obj << /Type/Page /Resources 2914 0 R /Contents[15 0 R 4 0 R 2915 0 R 17 0 R] /Parent 3758 0 R >> endobj 2917 0 obj << /Type/Page /Resources 2918 0 R /Contents[15 0 R 4 0 R 2919 0 R 17 0 R] /Parent 3758 0 R >> endobj 2921 0 obj << /Type/Page /Resources 2922 0 R /Contents[15 0 R 4 0 R 2923 0 R 17 0 R] /Parent 3758 0 R >> endobj 3758 0 obj << /Type/Pages /Count 3 /Kids[2913 0 R 2917 0 R 2921 0 R] /Parent 3754 0 R >> endobj 3754 0 obj << /Type/Pages /Count 12 /Kids[3755 0 R 3756 0 R 3757 0 R 3758 0 R] /Parent 3753 0 R >> endobj 2925 0 obj << /Type/Page /Resources 2926 0 R /Contents[15 0 R 4 0 R 2927 0 R 17 0 R] /Parent 3760 0 R >> endobj 2929 0 obj << /Type/Page /Resources 2930 0 R /Contents[15 0 R 4 0 R 2931 0 R 17 0 R] /Parent 3760 0 R >> endobj 2933 0 obj << /Type/Page /Resources 2934 0 R /Contents[15 0 R 4 0 R 2935 0 R 17 0 R] /Parent 3760 0 R >> endobj 3760 0 obj << /Type/Pages /Count 3 /Kids[2925 0 R 2929 0 R 2933 0 R] /Parent 3759 0 R >> endobj 2937 0 obj << /Type/Page /Resources 2938 0 R /Contents[15 0 R 4 0 R 2939 0 R 17 0 R] /Parent 3761 0 R >> endobj 2941 0 obj << /Type/Page /Resources 2942 0 R /Contents[15 0 R 4 0 R 2944 0 R 17 0 R] /Parent 3761 0 R >> endobj 2947 0 obj << /Type/Page /Resources 2948 0 R /Contents[15 0 R 4 0 R 2949 0 R 17 0 R] /Parent 3761 0 R >> endobj 3761 0 obj << /Type/Pages /Count 3 /Kids[2937 0 R 2941 0 R 2947 0 R] /Parent 3759 0 R >> endobj 2951 0 obj << /Type/Page /Resources 2952 0 R /Contents[15 0 R 4 0 R 2953 0 R 17 0 R] /Parent 3762 0 R >> endobj 2955 0 obj << /Type/Page /Resources 2956 0 R /Contents[15 0 R 4 0 R 2957 0 R 17 0 R] /Parent 3762 0 R >> endobj 2959 0 obj << /Type/Page /Resources 2960 0 R /Contents[15 0 R 4 0 R 2961 0 R 17 0 R] /Parent 3762 0 R >> endobj 3762 0 obj << /Type/Pages /Count 3 /Kids[2951 0 R 2955 0 R 2959 0 R] /Parent 3759 0 R >> endobj 2963 0 obj << /Type/Page /Resources 2964 0 R /Contents[15 0 R 4 0 R 2965 0 R 17 0 R] /Parent 3763 0 R >> endobj 2967 0 obj << /Type/Page /Resources 2968 0 R /Contents[15 0 R 4 0 R 2969 0 R 17 0 R] /Parent 3763 0 R >> endobj 2971 0 obj << /Type/Page /Resources 2972 0 R /Contents[15 0 R 4 0 R 2973 0 R 17 0 R] /Parent 3763 0 R >> endobj 3763 0 obj << /Type/Pages /Count 3 /Kids[2963 0 R 2967 0 R 2971 0 R] /Parent 3759 0 R >> endobj 3759 0 obj << /Type/Pages /Count 12 /Kids[3760 0 R 3761 0 R 3762 0 R 3763 0 R] /Parent 3753 0 R >> endobj 2975 0 obj << /Type/Page /Resources 2976 0 R /Contents[15 0 R 4 0 R 2977 0 R 17 0 R] /Parent 3765 0 R >> endobj 2979 0 obj << /Type/Page /Resources 2980 0 R /Contents[15 0 R 4 0 R 2981 0 R 17 0 R] /Parent 3765 0 R >> endobj 2983 0 obj << /Type/Page /Resources 2984 0 R /Contents[15 0 R 4 0 R 2985 0 R 17 0 R] /Parent 3765 0 R >> endobj 3765 0 obj << /Type/Pages /Count 3 /Kids[2975 0 R 2979 0 R 2983 0 R] /Parent 3764 0 R >> endobj 2987 0 obj << /Type/Page /Resources 2988 0 R /Contents[15 0 R 4 0 R 2989 0 R 17 0 R] /Parent 3766 0 R >> endobj 2991 0 obj << /Type/Page /Resources 2992 0 R /Contents[15 0 R 4 0 R 2993 0 R 17 0 R] /Parent 3766 0 R >> endobj 2995 0 obj << /Type/Page /Resources 2996 0 R /Contents[15 0 R 4 0 R 2997 0 R 17 0 R] /Parent 3766 0 R >> endobj 3766 0 obj << /Type/Pages /Count 3 /Kids[2987 0 R 2991 0 R 2995 0 R] /Parent 3764 0 R >> endobj 2999 0 obj << /Type/Page /Resources 3000 0 R /Contents[15 0 R 4 0 R 3001 0 R 17 0 R] /Parent 3767 0 R >> endobj 3003 0 obj << /Type/Page /Resources 3004 0 R /Contents[15 0 R 4 0 R 3005 0 R 17 0 R] /Parent 3767 0 R >> endobj 3007 0 obj << /Type/Page /Resources 3008 0 R /Contents[15 0 R 4 0 R 3009 0 R 17 0 R] /Parent 3767 0 R >> endobj 3767 0 obj << /Type/Pages /Count 3 /Kids[2999 0 R 3003 0 R 3007 0 R] /Parent 3764 0 R >> endobj 3011 0 obj << /Type/Page /Resources 3012 0 R /Contents[15 0 R 4 0 R 3013 0 R 17 0 R] /Parent 3768 0 R >> endobj 3015 0 obj << /Type/Page /Resources 3016 0 R /Contents[15 0 R 4 0 R 3017 0 R 17 0 R] /Parent 3768 0 R >> endobj 3019 0 obj << /Type/Page /Resources 3020 0 R /Contents[15 0 R 4 0 R 3021 0 R 17 0 R] /Parent 3768 0 R >> endobj 3768 0 obj << /Type/Pages /Count 3 /Kids[3011 0 R 3015 0 R 3019 0 R] /Parent 3764 0 R >> endobj 3764 0 obj << /Type/Pages /Count 12 /Kids[3765 0 R 3766 0 R 3767 0 R 3768 0 R] /Parent 3753 0 R >> endobj 3023 0 obj << /Type/Page /Resources 3024 0 R /Contents[15 0 R 4 0 R 3025 0 R 17 0 R] /Parent 3770 0 R >> endobj 3027 0 obj << /Type/Page /Resources 3028 0 R /Contents[15 0 R 4 0 R 3029 0 R 17 0 R] /Parent 3770 0 R >> endobj 3031 0 obj << /Type/Page /Resources 3032 0 R /Contents[15 0 R 4 0 R 3033 0 R 17 0 R] /Parent 3770 0 R >> endobj 3770 0 obj << /Type/Pages /Count 3 /Kids[3023 0 R 3027 0 R 3031 0 R] /Parent 3769 0 R >> endobj 3035 0 obj << /Type/Page /Resources 3036 0 R /Contents[15 0 R 4 0 R 3037 0 R 17 0 R] /Parent 3771 0 R >> endobj 3039 0 obj << /Type/Page /Resources 3040 0 R /Contents[15 0 R 4 0 R 3041 0 R 17 0 R] /Parent 3771 0 R >> endobj 3043 0 obj << /Type/Page /Resources 3044 0 R /Contents[15 0 R 4 0 R 3045 0 R 17 0 R] /Parent 3771 0 R >> endobj 3771 0 obj << /Type/Pages /Count 3 /Kids[3035 0 R 3039 0 R 3043 0 R] /Parent 3769 0 R >> endobj 3047 0 obj << /Type/Page /Resources 3048 0 R /Contents[15 0 R 4 0 R 3052 0 R 17 0 R] /Parent 3772 0 R >> endobj 3055 0 obj << /Type/Page /Resources 3056 0 R /Contents[15 0 R 4 0 R 3059 0 R 17 0 R] /Parent 3772 0 R >> endobj 3062 0 obj << /Type/Page /Resources 3063 0 R /Contents[15 0 R 4 0 R 3064 0 R 17 0 R] /Parent 3772 0 R >> endobj 3772 0 obj << /Type/Pages /Count 3 /Kids[3047 0 R 3055 0 R 3062 0 R] /Parent 3769 0 R >> endobj 3066 0 obj << /Type/Page /Resources 3067 0 R /Contents[15 0 R 4 0 R 3068 0 R 17 0 R] /Parent 3773 0 R >> endobj 3070 0 obj << /Type/Page /Resources 3071 0 R /Contents[15 0 R 4 0 R 3072 0 R 17 0 R] /Parent 3773 0 R >> endobj 3074 0 obj << /Type/Page /Resources 3075 0 R /Contents[15 0 R 4 0 R 3076 0 R 17 0 R] /Parent 3773 0 R >> endobj 3773 0 obj << /Type/Pages /Count 3 /Kids[3066 0 R 3070 0 R 3074 0 R] /Parent 3769 0 R >> endobj 3769 0 obj << /Type/Pages /Count 12 /Kids[3770 0 R 3771 0 R 3772 0 R 3773 0 R] /Parent 3753 0 R >> endobj 3753 0 obj << /Type/Pages /Count 48 /Kids[3754 0 R 3759 0 R 3764 0 R 3769 0 R] /Parent 3731 0 R >> endobj 3078 0 obj << /Type/Page /Resources 3079 0 R /Contents[15 0 R 4 0 R 3080 0 R 17 0 R] /Parent 3776 0 R >> endobj 3082 0 obj << /Type/Page /Resources 3083 0 R /Contents[15 0 R 4 0 R 3084 0 R 17 0 R] /Parent 3776 0 R >> endobj 3776 0 obj << /Type/Pages /Count 2 /Kids[3078 0 R 3082 0 R] /Parent 3775 0 R >> endobj 3086 0 obj << /Type/Page /Resources 3087 0 R /Contents[15 0 R 4 0 R 3088 0 R 17 0 R] /Parent 3777 0 R >> endobj 3090 0 obj << /Type/Page /Resources 3091 0 R /Contents[15 0 R 4 0 R 3092 0 R 17 0 R] /Parent 3777 0 R >> endobj 3094 0 obj << /Type/Page /Resources 3095 0 R /Contents[15 0 R 4 0 R 3096 0 R 17 0 R] /Parent 3777 0 R >> endobj 3777 0 obj << /Type/Pages /Count 3 /Kids[3086 0 R 3090 0 R 3094 0 R] /Parent 3775 0 R >> endobj 3098 0 obj << /Type/Page /Resources 3099 0 R /Contents[15 0 R 4 0 R 3100 0 R 17 0 R] /Parent 3778 0 R >> endobj 3102 0 obj << /Type/Page /Resources 3103 0 R /Contents[15 0 R 4 0 R 3106 0 R 17 0 R] /Parent 3778 0 R >> endobj 3109 0 obj << /Type/Page /Resources 3110 0 R /Contents[15 0 R 4 0 R 3111 0 R 17 0 R] /Parent 3778 0 R >> endobj 3778 0 obj << /Type/Pages /Count 3 /Kids[3098 0 R 3102 0 R 3109 0 R] /Parent 3775 0 R >> endobj 3113 0 obj << /Type/Page /Resources 3114 0 R /Contents[15 0 R 4 0 R 3115 0 R 17 0 R] /Parent 3779 0 R >> endobj 3117 0 obj << /Type/Page /Resources 3118 0 R /Contents[15 0 R 4 0 R 3119 0 R 17 0 R] /Parent 3779 0 R >> endobj 3121 0 obj << /Type/Page /Resources 3122 0 R /Contents[15 0 R 4 0 R 3123 0 R 17 0 R] /Parent 3779 0 R >> endobj 3779 0 obj << /Type/Pages /Count 3 /Kids[3113 0 R 3117 0 R 3121 0 R] /Parent 3775 0 R >> endobj 3775 0 obj << /Type/Pages /Count 11 /Kids[3776 0 R 3777 0 R 3778 0 R 3779 0 R] /Parent 3774 0 R >> endobj 3125 0 obj << /Type/Page /Resources 3126 0 R /Contents[15 0 R 4 0 R 3127 0 R 17 0 R] /Parent 3781 0 R >> endobj 3129 0 obj << /Type/Page /Resources 3130 0 R /Contents[15 0 R 4 0 R 3131 0 R 17 0 R] /Parent 3781 0 R >> endobj 3133 0 obj << /Type/Page /Resources 3134 0 R /Contents[15 0 R 4 0 R 3135 0 R 17 0 R] /Parent 3781 0 R >> endobj 3781 0 obj << /Type/Pages /Count 3 /Kids[3125 0 R 3129 0 R 3133 0 R] /Parent 3780 0 R >> endobj 3137 0 obj << /Type/Page /Resources 3138 0 R /Contents[15 0 R 4 0 R 3139 0 R 17 0 R] /Parent 3782 0 R >> endobj 3141 0 obj << /Type/Page /Resources 3142 0 R /Contents[15 0 R 4 0 R 3143 0 R 17 0 R] /Parent 3782 0 R >> endobj 3145 0 obj << /Type/Page /Resources 3146 0 R /Contents[15 0 R 4 0 R 3147 0 R 17 0 R] /Parent 3782 0 R >> endobj 3782 0 obj << /Type/Pages /Count 3 /Kids[3137 0 R 3141 0 R 3145 0 R] /Parent 3780 0 R >> endobj 3149 0 obj << /Type/Page /Resources 3150 0 R /Contents[15 0 R 4 0 R 3151 0 R 17 0 R] /Parent 3783 0 R >> endobj 3153 0 obj << /Type/Page /Resources 3154 0 R /Contents[15 0 R 4 0 R 3155 0 R 17 0 R] /Parent 3783 0 R >> endobj 3157 0 obj << /Type/Page /Resources 3158 0 R /Contents[15 0 R 4 0 R 3159 0 R 17 0 R] /Parent 3783 0 R >> endobj 3783 0 obj << /Type/Pages /Count 3 /Kids[3149 0 R 3153 0 R 3157 0 R] /Parent 3780 0 R >> endobj 3161 0 obj << /Type/Page /Resources 3162 0 R /Contents[15 0 R 4 0 R 3163 0 R 17 0 R] /Parent 3784 0 R >> endobj 3165 0 obj << /Type/Page /Resources 3166 0 R /Contents[15 0 R 4 0 R 3167 0 R 17 0 R] /Parent 3784 0 R >> endobj 3169 0 obj << /Type/Page /Resources 3170 0 R /Contents[15 0 R 4 0 R 3171 0 R 17 0 R] /Parent 3784 0 R >> endobj 3784 0 obj << /Type/Pages /Count 3 /Kids[3161 0 R 3165 0 R 3169 0 R] /Parent 3780 0 R >> endobj 3780 0 obj << /Type/Pages /Count 12 /Kids[3781 0 R 3782 0 R 3783 0 R 3784 0 R] /Parent 3774 0 R >> endobj 3173 0 obj << /Type/Page /Resources 3174 0 R /Contents[15 0 R 4 0 R 3175 0 R 17 0 R] /Parent 3786 0 R >> endobj 3177 0 obj << /Type/Page /Resources 3178 0 R /Contents[15 0 R 4 0 R 3179 0 R 17 0 R] /Parent 3786 0 R >> endobj 3181 0 obj << /Type/Page /Resources 3182 0 R /Contents[15 0 R 4 0 R 3183 0 R 17 0 R] /Parent 3786 0 R >> endobj 3786 0 obj << /Type/Pages /Count 3 /Kids[3173 0 R 3177 0 R 3181 0 R] /Parent 3785 0 R >> endobj 3185 0 obj << /Type/Page /Resources 3186 0 R /Contents[15 0 R 4 0 R 3187 0 R 17 0 R] /Parent 3787 0 R >> endobj 3189 0 obj << /Type/Page /Resources 3190 0 R /Contents[15 0 R 4 0 R 3191 0 R 17 0 R] /Parent 3787 0 R >> endobj 3193 0 obj << /Type/Page /Resources 3194 0 R /Contents[15 0 R 4 0 R 3195 0 R 17 0 R] /Parent 3787 0 R >> endobj 3787 0 obj << /Type/Pages /Count 3 /Kids[3185 0 R 3189 0 R 3193 0 R] /Parent 3785 0 R >> endobj 3197 0 obj << /Type/Page /Resources 3198 0 R /Contents[15 0 R 4 0 R 3201 0 R 17 0 R] /Parent 3788 0 R >> endobj 3204 0 obj << /Type/Page /Resources 3205 0 R /Contents[15 0 R 4 0 R 3206 0 R 17 0 R] /Parent 3788 0 R >> endobj 3208 0 obj << /Type/Page /Resources 3209 0 R /Contents[15 0 R 4 0 R 3210 0 R 17 0 R] /Parent 3788 0 R >> endobj 3788 0 obj << /Type/Pages /Count 3 /Kids[3197 0 R 3204 0 R 3208 0 R] /Parent 3785 0 R >> endobj 3212 0 obj << /Type/Page /Resources 3213 0 R /Contents[15 0 R 4 0 R 3214 0 R 17 0 R] /Parent 3789 0 R >> endobj 3216 0 obj << /Type/Page /Resources 3217 0 R /Contents[15 0 R 4 0 R 3218 0 R 17 0 R] /Parent 3789 0 R >> endobj 3220 0 obj << /Type/Page /Resources 3221 0 R /Contents[15 0 R 4 0 R 3224 0 R 17 0 R] /Parent 3789 0 R >> endobj 3789 0 obj << /Type/Pages /Count 3 /Kids[3212 0 R 3216 0 R 3220 0 R] /Parent 3785 0 R >> endobj 3785 0 obj << /Type/Pages /Count 12 /Kids[3786 0 R 3787 0 R 3788 0 R 3789 0 R] /Parent 3774 0 R >> endobj 3227 0 obj << /Type/Page /Resources 3228 0 R /Contents[15 0 R 4 0 R 3229 0 R 17 0 R] /Parent 3791 0 R >> endobj 3231 0 obj << /Type/Page /Resources 3232 0 R /Contents[15 0 R 4 0 R 3233 0 R 17 0 R] /Parent 3791 0 R >> endobj 3235 0 obj << /Type/Page /Resources 3236 0 R /Contents[15 0 R 4 0 R 3237 0 R 17 0 R] /Parent 3791 0 R >> endobj 3791 0 obj << /Type/Pages /Count 3 /Kids[3227 0 R 3231 0 R 3235 0 R] /Parent 3790 0 R >> endobj 3239 0 obj << /Type/Page /Resources 3240 0 R /Contents[15 0 R 4 0 R 3241 0 R 17 0 R] /Parent 3792 0 R >> endobj 3243 0 obj << /Type/Page /Resources 3244 0 R /Contents[15 0 R 4 0 R 3245 0 R 17 0 R] /Parent 3792 0 R >> endobj 3247 0 obj << /Type/Page /Resources 3248 0 R /Contents[15 0 R 4 0 R 3249 0 R 17 0 R] /Parent 3792 0 R >> endobj 3792 0 obj << /Type/Pages /Count 3 /Kids[3239 0 R 3243 0 R 3247 0 R] /Parent 3790 0 R >> endobj 3251 0 obj << /Type/Page /Resources 3252 0 R /Contents[15 0 R 4 0 R 3255 0 R 17 0 R] /Parent 3793 0 R >> endobj 3258 0 obj << /Type/Page /Resources 3259 0 R /Contents[15 0 R 4 0 R 3260 0 R 17 0 R] /Parent 3793 0 R >> endobj 3262 0 obj << /Type/Page /Resources 3263 0 R /Contents[15 0 R 4 0 R 3264 0 R 17 0 R] /Parent 3793 0 R >> endobj 3793 0 obj << /Type/Pages /Count 3 /Kids[3251 0 R 3258 0 R 3262 0 R] /Parent 3790 0 R >> endobj 3266 0 obj << /Type/Page /Resources 3267 0 R /Contents[15 0 R 4 0 R 3268 0 R 17 0 R] /Parent 3794 0 R >> endobj 3270 0 obj << /Type/Page /Resources 3271 0 R /Contents[15 0 R 4 0 R 3272 0 R 17 0 R] /Parent 3794 0 R >> endobj 3274 0 obj << /Type/Page /Resources 3275 0 R /Contents[15 0 R 4 0 R 3276 0 R 17 0 R] /Parent 3794 0 R >> endobj 3794 0 obj << /Type/Pages /Count 3 /Kids[3266 0 R 3270 0 R 3274 0 R] /Parent 3790 0 R >> endobj 3790 0 obj << /Type/Pages /Count 12 /Kids[3791 0 R 3792 0 R 3793 0 R 3794 0 R] /Parent 3774 0 R >> endobj 3774 0 obj << /Type/Pages /Count 47 /Kids[3775 0 R 3780 0 R 3785 0 R 3790 0 R] /Parent 3731 0 R >> endobj 3278 0 obj << /Type/Page /Resources 3279 0 R /Contents[15 0 R 4 0 R 3280 0 R 17 0 R] /Parent 3797 0 R >> endobj 3282 0 obj << /Type/Page /Resources 3283 0 R /Contents[15 0 R 4 0 R 3284 0 R 17 0 R] /Parent 3797 0 R >> endobj 3286 0 obj << /Type/Page /Resources 3287 0 R /Contents[15 0 R 4 0 R 3288 0 R 17 0 R] /Parent 3797 0 R >> endobj 3797 0 obj << /Type/Pages /Count 3 /Kids[3278 0 R 3282 0 R 3286 0 R] /Parent 3796 0 R >> endobj 3290 0 obj << /Type/Page /Resources 3291 0 R /Contents[15 0 R 4 0 R 3292 0 R 17 0 R] /Parent 3798 0 R >> endobj 3294 0 obj << /Type/Page /Resources 3295 0 R /Contents[15 0 R 4 0 R 3296 0 R 17 0 R] /Parent 3798 0 R >> endobj 3298 0 obj << /Type/Page /Resources 3299 0 R /Contents[15 0 R 4 0 R 3300 0 R 17 0 R] /Parent 3798 0 R >> endobj 3798 0 obj << /Type/Pages /Count 3 /Kids[3290 0 R 3294 0 R 3298 0 R] /Parent 3796 0 R >> endobj 3302 0 obj << /Type/Page /Resources 3303 0 R /Contents[15 0 R 4 0 R 3304 0 R 17 0 R] /Parent 3799 0 R >> endobj 3306 0 obj << /Type/Page /Resources 3307 0 R /Contents[15 0 R 4 0 R 3308 0 R 17 0 R] /Parent 3799 0 R >> endobj 3310 0 obj << /Type/Page /Resources 3311 0 R /Contents[15 0 R 4 0 R 3312 0 R 17 0 R] /Parent 3799 0 R >> endobj 3799 0 obj << /Type/Pages /Count 3 /Kids[3302 0 R 3306 0 R 3310 0 R] /Parent 3796 0 R >> endobj 3314 0 obj << /Type/Page /Resources 3315 0 R /Contents[15 0 R 4 0 R 3316 0 R 17 0 R] /Parent 3800 0 R >> endobj 3318 0 obj << /Type/Page /Resources 3319 0 R /Contents[15 0 R 4 0 R 3320 0 R 17 0 R] /Parent 3800 0 R >> endobj 3322 0 obj << /Type/Page /Resources 3323 0 R /Contents[15 0 R 4 0 R 3324 0 R 17 0 R] /Parent 3800 0 R >> endobj 3800 0 obj << /Type/Pages /Count 3 /Kids[3314 0 R 3318 0 R 3322 0 R] /Parent 3796 0 R >> endobj 3796 0 obj << /Type/Pages /Count 12 /Kids[3797 0 R 3798 0 R 3799 0 R 3800 0 R] /Parent 3795 0 R >> endobj 3326 0 obj << /Type/Page /Resources 3327 0 R /Contents[15 0 R 4 0 R 3328 0 R 17 0 R] /Parent 3802 0 R >> endobj 3330 0 obj << /Type/Page /Resources 3331 0 R /Contents[15 0 R 4 0 R 3332 0 R 17 0 R] /Parent 3802 0 R >> endobj 3334 0 obj << /Type/Page /Resources 3335 0 R /Contents[15 0 R 4 0 R 3336 0 R 17 0 R] /Parent 3802 0 R >> endobj 3802 0 obj << /Type/Pages /Count 3 /Kids[3326 0 R 3330 0 R 3334 0 R] /Parent 3801 0 R >> endobj 3337 0 obj << /Type/Page /Resources 3338 0 R /Contents[15 0 R 4 0 R 3339 0 R 17 0 R] /Parent 3803 0 R >> endobj 3341 0 obj << /Type/Page /Resources 3342 0 R /Contents[15 0 R 4 0 R 3343 0 R 17 0 R] /Parent 3803 0 R >> endobj 3345 0 obj << /Type/Page /Resources 3346 0 R /Contents[15 0 R 4 0 R 3347 0 R 17 0 R] /Parent 3803 0 R >> endobj 3803 0 obj << /Type/Pages /Count 3 /Kids[3337 0 R 3341 0 R 3345 0 R] /Parent 3801 0 R >> endobj 3349 0 obj << /Type/Page /Resources 3350 0 R /Contents[15 0 R 4 0 R 3351 0 R 17 0 R] /Parent 3804 0 R >> endobj 3353 0 obj << /Type/Page /Resources 3354 0 R /Contents[15 0 R 4 0 R 3355 0 R 17 0 R] /Parent 3804 0 R >> endobj 3357 0 obj << /Type/Page /Resources 3358 0 R /Contents[15 0 R 4 0 R 3359 0 R 17 0 R] /Parent 3804 0 R >> endobj 3804 0 obj << /Type/Pages /Count 3 /Kids[3349 0 R 3353 0 R 3357 0 R] /Parent 3801 0 R >> endobj 3361 0 obj << /Type/Page /Resources 3362 0 R /Contents[15 0 R 4 0 R 3363 0 R 17 0 R] /Parent 3805 0 R >> endobj 3365 0 obj << /Type/Page /Resources 3366 0 R /Contents[15 0 R 4 0 R 3367 0 R 17 0 R] /Parent 3805 0 R >> endobj 3369 0 obj << /Type/Page /Resources 3370 0 R /Contents[15 0 R 4 0 R 3371 0 R 17 0 R] /Parent 3805 0 R >> endobj 3805 0 obj << /Type/Pages /Count 3 /Kids[3361 0 R 3365 0 R 3369 0 R] /Parent 3801 0 R >> endobj 3801 0 obj << /Type/Pages /Count 12 /Kids[3802 0 R 3803 0 R 3804 0 R 3805 0 R] /Parent 3795 0 R >> endobj 3373 0 obj << /Type/Page /Resources 3374 0 R /Contents[15 0 R 4 0 R 3375 0 R 17 0 R] /Parent 3807 0 R >> endobj 3377 0 obj << /Type/Page /Resources 3378 0 R /Contents[15 0 R 4 0 R 3379 0 R 17 0 R] /Parent 3807 0 R >> endobj 3381 0 obj << /Type/Page /Resources 3382 0 R /Contents[15 0 R 4 0 R 3383 0 R 17 0 R] /Parent 3807 0 R >> endobj 3807 0 obj << /Type/Pages /Count 3 /Kids[3373 0 R 3377 0 R 3381 0 R] /Parent 3806 0 R >> endobj 3385 0 obj << /Type/Page /Resources 3386 0 R /Contents[15 0 R 4 0 R 3387 0 R 17 0 R] /Parent 3808 0 R >> endobj 3389 0 obj << /Type/Page /Resources 3390 0 R /Contents[15 0 R 4 0 R 3391 0 R 17 0 R] /Parent 3808 0 R >> endobj 3393 0 obj << /Type/Page /Resources 3394 0 R /Contents[15 0 R 4 0 R 3401 0 R 17 0 R] /Parent 3808 0 R >> endobj 3808 0 obj << /Type/Pages /Count 3 /Kids[3385 0 R 3389 0 R 3393 0 R] /Parent 3806 0 R >> endobj 3404 0 obj << /Type/Page /Resources 3405 0 R /Contents[15 0 R 4 0 R 3406 0 R 17 0 R] /Parent 3809 0 R >> endobj 3408 0 obj << /Type/Page /Resources 3409 0 R /Contents[15 0 R 4 0 R 3410 0 R 17 0 R] /Parent 3809 0 R >> endobj 3412 0 obj << /Type/Page /Resources 3413 0 R /Contents[15 0 R 4 0 R 3414 0 R 17 0 R] /Parent 3809 0 R >> endobj 3809 0 obj << /Type/Pages /Count 3 /Kids[3404 0 R 3408 0 R 3412 0 R] /Parent 3806 0 R >> endobj 3416 0 obj << /Type/Page /Resources 3417 0 R /Contents[15 0 R 4 0 R 3418 0 R 17 0 R] /Parent 3810 0 R >> endobj 3420 0 obj << /Type/Page /Resources 3421 0 R /Contents[15 0 R 4 0 R 3422 0 R 17 0 R] /Parent 3810 0 R >> endobj 3424 0 obj << /Type/Page /Resources 3425 0 R /Contents[15 0 R 4 0 R 3426 0 R 17 0 R] /Parent 3810 0 R >> endobj 3810 0 obj << /Type/Pages /Count 3 /Kids[3416 0 R 3420 0 R 3424 0 R] /Parent 3806 0 R >> endobj 3806 0 obj << /Type/Pages /Count 12 /Kids[3807 0 R 3808 0 R 3809 0 R 3810 0 R] /Parent 3795 0 R >> endobj 3428 0 obj << /Type/Page /Resources 3429 0 R /Contents[15 0 R 4 0 R 3430 0 R 17 0 R] /Parent 3812 0 R >> endobj 3432 0 obj << /Type/Page /Resources 3433 0 R /Contents[15 0 R 4 0 R 3434 0 R 17 0 R] /Parent 3812 0 R >> endobj 3436 0 obj << /Type/Page /Resources 3437 0 R /Contents[15 0 R 4 0 R 3438 0 R 17 0 R] /Parent 3812 0 R >> endobj 3812 0 obj << /Type/Pages /Count 3 /Kids[3428 0 R 3432 0 R 3436 0 R] /Parent 3811 0 R >> endobj 3440 0 obj << /Type/Page /Resources 3441 0 R /Contents[15 0 R 4 0 R 3442 0 R 17 0 R] /Parent 3813 0 R >> endobj 3444 0 obj << /Type/Page /Resources 3445 0 R /Contents[15 0 R 4 0 R 3446 0 R 17 0 R] /Parent 3813 0 R >> endobj 3448 0 obj << /Type/Page /Resources 3449 0 R /Contents[15 0 R 4 0 R 3450 0 R 17 0 R] /Parent 3813 0 R >> endobj 3813 0 obj << /Type/Pages /Count 3 /Kids[3440 0 R 3444 0 R 3448 0 R] /Parent 3811 0 R >> endobj 3452 0 obj << /Type/Page /Resources 3453 0 R /Contents[15 0 R 4 0 R 3454 0 R 17 0 R] /Parent 3814 0 R >> endobj 3456 0 obj << /Type/Page /Resources 3457 0 R /Contents[15 0 R 4 0 R 3458 0 R 17 0 R] /Parent 3814 0 R >> endobj 3460 0 obj << /Type/Page /Resources 3461 0 R /Contents[15 0 R 4 0 R 3462 0 R 17 0 R] /Parent 3814 0 R >> endobj 3814 0 obj << /Type/Pages /Count 3 /Kids[3452 0 R 3456 0 R 3460 0 R] /Parent 3811 0 R >> endobj 3464 0 obj << /Type/Page /Resources 3465 0 R /Contents[15 0 R 4 0 R 3466 0 R 17 0 R] /Parent 3815 0 R >> endobj 3468 0 obj << /Type/Page /Resources 3469 0 R /Contents[15 0 R 4 0 R 3470 0 R 17 0 R] /Parent 3815 0 R >> endobj 3472 0 obj << /Type/Page /Resources 3473 0 R /Contents[15 0 R 4 0 R 3474 0 R 17 0 R] /Parent 3815 0 R >> endobj 3815 0 obj << /Type/Pages /Count 3 /Kids[3464 0 R 3468 0 R 3472 0 R] /Parent 3811 0 R >> endobj 3811 0 obj << /Type/Pages /Count 12 /Kids[3812 0 R 3813 0 R 3814 0 R 3815 0 R] /Parent 3795 0 R >> endobj 3795 0 obj << /Type/Pages /Count 48 /Kids[3796 0 R 3801 0 R 3806 0 R 3811 0 R] /Parent 3731 0 R >> endobj 3731 0 obj << /Type/Pages /Count 190 /Kids[3732 0 R 3753 0 R 3774 0 R 3795 0 R] /Parent 3 0 R >> endobj 3 0 obj << /Type/Pages /Count 757 /Kids[3476 0 R 3561 0 R 3646 0 R 3731 0 R] /MediaBox[0 0 612 792] >> endobj 15 0 obj << /Length 1 >> stream endstream endobj 17 0 obj << /Length 1 >> stream endstream endobj 4 0 obj << /Length 30 >> stream 1.00028 0 0 1.00028 72 720 cm endstream endobj 3816 0 obj << >> endobj 3817 0 obj null endobj 3818 0 obj << >> endobj 2 0 obj << /Type/Catalog /Pages 3 0 R /Outlines 3816 0 R /Threads 3817 0 R /Names 3818 0 R >> endobj xref 0 3819 0000000000 65535 f 0002680764 00000 n 0002798584 00000 n 0002798224 00000 n 0002798434 00000 n 0002680928 00000 n 0000004191 00000 n 0000000009 00000 n 0002372415 00000 n 0002372220 00000 n 0000001093 00000 n 0000002082 00000 n 0002377136 00000 n 0002376947 00000 n 0000002982 00000 n 0002798334 00000 n 0000003929 00000 n 0002798384 00000 n 0000004147 00000 n 0002681032 00000 n 0000004344 00000 n 0000004252 00000 n 0002681220 00000 n 0000013265 00000 n 0002384423 00000 n 0002384237 00000 n 0000004393 00000 n 0002392094 00000 n 0002391906 00000 n 0000005311 00000 n 0002403067 00000 n 0002402873 00000 n 0000006215 00000 n 0002410647 00000 n 0002410452 00000 n 0000007017 00000 n 0002414738 00000 n 0002414550 00000 n 0000007951 00000 n 0002434062 00000 n 0002433876 00000 n 0000008869 00000 n 0000009596 00000 n 0002451117 00000 n 0002450930 00000 n 0000010896 00000 n 0000011837 00000 n 0000013110 00000 n 0000013231 00000 n 0002681326 00000 n 0000017911 00000 n 0002455445 00000 n 0002455250 00000 n 0000013343 00000 n 0002474424 00000 n 0002474238 00000 n 0000014324 00000 n 0002482905 00000 n 0002482719 00000 n 0000015294 00000 n 0000016260 00000 n 0000017820 00000 n 0002681432 00000 n 0000020945 00000 n 0002487644 00000 n 0002487448 00000 n 0000017973 00000 n 0000018911 00000 n 0000020878 00000 n 0002681628 00000 n 0000021225 00000 n 0000021007 00000 n 0000021191 00000 n 0002681734 00000 n 0000023102 00000 n 0002500607 00000 n 0002500414 00000 n 0000021287 00000 n 0000022209 00000 n 0000023023 00000 n 0002681840 00000 n 0000024590 00000 n 0000023164 00000 n 0000024533 00000 n 0002682036 00000 n 0000026156 00000 n 0000024652 00000 n 0000026088 00000 n 0002682142 00000 n 0000027607 00000 n 0000026218 00000 n 0000027539 00000 n 0002682248 00000 n 0000029144 00000 n 0000027669 00000 n 0000029076 00000 n 0002682550 00000 n 0000030673 00000 n 0000029206 00000 n 0000030605 00000 n 0002682656 00000 n 0000032357 00000 n 0000030735 00000 n 0000032288 00000 n 0002682765 00000 n 0000033929 00000 n 0000032421 00000 n 0000033849 00000 n 0002682966 00000 n 0000036408 00000 n 0002511231 00000 n 0002511035 00000 n 0000033993 00000 n 0000034925 00000 n 0000036337 00000 n 0002683075 00000 n 0000037934 00000 n 0000036472 00000 n 0000037854 00000 n 0002683184 00000 n 0000039570 00000 n 0000037998 00000 n 0000039490 00000 n 0002683386 00000 n 0000041028 00000 n 0000039634 00000 n 0000040970 00000 n 0002683495 00000 n 0000041868 00000 n 0000041092 00000 n 0000041788 00000 n 0002683604 00000 n 0000042025 00000 n 0000041932 00000 n 0002683806 00000 n 0000044945 00000 n 0002522589 00000 n 0002522397 00000 n 0000042075 00000 n 0000043015 00000 n 0000044863 00000 n 0002683915 00000 n 0000048516 00000 n 0002535630 00000 n 0002535442 00000 n 0000045009 00000 n 0000046013 00000 n 0000048420 00000 n 0002684024 00000 n 0000050723 00000 n 0000048580 00000 n 0000050664 00000 n 0002684332 00000 n 0000054231 00000 n 0002549242 00000 n 0002549047 00000 n 0000050787 00000 n 0000051711 00000 n 0000054147 00000 n 0002684441 00000 n 0000056940 00000 n 0000054295 00000 n 0000056858 00000 n 0002684550 00000 n 0000058430 00000 n 0000057004 00000 n 0000058349 00000 n 0002684752 00000 n 0000061345 00000 n 0002552854 00000 n 0002552666 00000 n 0000058494 00000 n 0000059492 00000 n 0000061202 00000 n 0002684861 00000 n 0000064030 00000 n 0000061409 00000 n 0000063938 00000 n 0002684970 00000 n 0000082296 00000 n 0002561306 00000 n 0002561119 00000 n 0000064094 00000 n 0000064823 00000 n 0000065094 00000 n 0000081004 00000 n 0000082190 00000 n 0000082260 00000 n 0002685172 00000 n 0000084275 00000 n 0000082377 00000 n 0000084207 00000 n 0002685281 00000 n 0000086720 00000 n 0000084339 00000 n 0000086640 00000 n 0002685390 00000 n 0000087044 00000 n 0000086784 00000 n 0000087010 00000 n 0002685592 00000 n 0000087356 00000 n 0000087108 00000 n 0000087311 00000 n 0002685701 00000 n 0000087513 00000 n 0000087420 00000 n 0002685810 00000 n 0000090053 00000 n 0000087563 00000 n 0000089996 00000 n 0002686118 00000 n 0000094441 00000 n 0002577409 00000 n 0002577215 00000 n 0000090117 00000 n 0002594729 00000 n 0002594534 00000 n 0000090777 00000 n 0002608884 00000 n 0002608691 00000 n 0000091433 00000 n 0000092092 00000 n 0000094308 00000 n 0002686227 00000 n 0000096669 00000 n 0000094505 00000 n 0000096574 00000 n 0002686336 00000 n 0000099039 00000 n 0000096733 00000 n 0000098970 00000 n 0002686538 00000 n 0000105870 00000 n 0002617473 00000 n 0002617277 00000 n 0000099103 00000 n 0002619234 00000 n 0002619046 00000 n 0000100096 00000 n 0002621822 00000 n 0002621627 00000 n 0000101100 00000 n 0002624673 00000 n 0002624479 00000 n 0000102085 00000 n 0000103059 00000 n 0000105660 00000 n 0002686647 00000 n 0000107675 00000 n 0000105934 00000 n 0000107567 00000 n 0002686756 00000 n 0000110171 00000 n 0000107739 00000 n 0000110028 00000 n 0002686958 00000 n 0000112470 00000 n 0000110235 00000 n 0000112349 00000 n 0002687067 00000 n 0000114706 00000 n 0000112534 00000 n 0000114586 00000 n 0002687176 00000 n 0000117980 00000 n 0002626810 00000 n 0002626623 00000 n 0000114770 00000 n 0000115757 00000 n 0000117796 00000 n 0002687378 00000 n 0000120033 00000 n 0000118044 00000 n 0000119886 00000 n 0002687487 00000 n 0000123096 00000 n 0002633798 00000 n 0002633605 00000 n 0000120097 00000 n 0000121077 00000 n 0000122925 00000 n 0002687596 00000 n 0000126177 00000 n 0002637674 00000 n 0002637478 00000 n 0000123160 00000 n 0000124145 00000 n 0000125991 00000 n 0002688010 00000 n 0000128267 00000 n 0000126241 00000 n 0000128122 00000 n 0002688119 00000 n 0000130730 00000 n 0000128331 00000 n 0000130544 00000 n 0002688313 00000 n 0000133023 00000 n 0000130794 00000 n 0000132826 00000 n 0002688422 00000 n 0000135105 00000 n 0000133087 00000 n 0000134959 00000 n 0002688531 00000 n 0000137507 00000 n 0000135169 00000 n 0000137361 00000 n 0002688733 00000 n 0000140052 00000 n 0000137571 00000 n 0000139881 00000 n 0002688842 00000 n 0000142664 00000 n 0000140116 00000 n 0000142518 00000 n 0002688951 00000 n 0000145243 00000 n 0000142728 00000 n 0000145085 00000 n 0002689153 00000 n 0000147889 00000 n 0000145307 00000 n 0000147694 00000 n 0002689262 00000 n 0000151268 00000 n 0002639648 00000 n 0002639453 00000 n 0000147953 00000 n 0000148921 00000 n 0000151122 00000 n 0002689371 00000 n 0000154007 00000 n 0000151332 00000 n 0000153861 00000 n 0002689679 00000 n 0000156314 00000 n 0000154071 00000 n 0000156181 00000 n 0002689788 00000 n 0000159177 00000 n 0000156378 00000 n 0000159031 00000 n 0002689897 00000 n 0000161374 00000 n 0000159241 00000 n 0000161241 00000 n 0002690099 00000 n 0000163692 00000 n 0000161438 00000 n 0000163508 00000 n 0002690208 00000 n 0000168360 00000 n 0002643871 00000 n 0002643676 00000 n 0000163756 00000 n 0002652865 00000 n 0002652675 00000 n 0000164744 00000 n 0000165753 00000 n 0000168111 00000 n 0002690317 00000 n 0000171059 00000 n 0000168424 00000 n 0000170874 00000 n 0002690519 00000 n 0000173625 00000 n 0000171123 00000 n 0000173454 00000 n 0002690628 00000 n 0000175920 00000 n 0000173689 00000 n 0000175775 00000 n 0002690737 00000 n 0000177774 00000 n 0000175984 00000 n 0000177654 00000 n 0002690939 00000 n 0000180288 00000 n 0000177838 00000 n 0000180127 00000 n 0002691048 00000 n 0000184852 00000 n 0002655288 00000 n 0002655099 00000 n 0000180352 00000 n 0002657406 00000 n 0002657212 00000 n 0000181345 00000 n 0000182335 00000 n 0000184631 00000 n 0002691157 00000 n 0000187111 00000 n 0000184916 00000 n 0000186991 00000 n 0002691465 00000 n 0000189646 00000 n 0000187175 00000 n 0000189463 00000 n 0002691574 00000 n 0000192192 00000 n 0000189710 00000 n 0000192007 00000 n 0002691683 00000 n 0000194860 00000 n 0000192256 00000 n 0000194714 00000 n 0002691885 00000 n 0000197346 00000 n 0000194924 00000 n 0000197223 00000 n 0002691994 00000 n 0000199723 00000 n 0000197410 00000 n 0000199577 00000 n 0002692103 00000 n 0000202122 00000 n 0000199787 00000 n 0000201975 00000 n 0002692305 00000 n 0000206891 00000 n 0000202186 00000 n 0000204940 00000 n 0000205016 00000 n 0000206710 00000 n 0000206855 00000 n 0002692414 00000 n 0000209586 00000 n 0000206972 00000 n 0000209429 00000 n 0002692523 00000 n 0000211934 00000 n 0000209650 00000 n 0000211813 00000 n 0002692725 00000 n 0000214520 00000 n 0000211998 00000 n 0000214387 00000 n 0002692834 00000 n 0000216810 00000 n 0000214584 00000 n 0000216613 00000 n 0002692943 00000 n 0000218855 00000 n 0000216874 00000 n 0000218721 00000 n 0002693251 00000 n 0000221845 00000 n 0000218919 00000 n 0000221637 00000 n 0002693360 00000 n 0000224025 00000 n 0000221909 00000 n 0000223891 00000 n 0002693469 00000 n 0000226865 00000 n 0000224089 00000 n 0000226732 00000 n 0002693671 00000 n 0000229689 00000 n 0000226929 00000 n 0000229544 00000 n 0002693780 00000 n 0000232538 00000 n 0000229753 00000 n 0000232380 00000 n 0002693889 00000 n 0000234713 00000 n 0000232602 00000 n 0000234581 00000 n 0002694091 00000 n 0000241010 00000 n 0000234777 00000 n 0000236334 00000 n 0000236254 00000 n 0000237861 00000 n 0000237602 00000 n 0000237338 00000 n 0000237093 00000 n 0000236826 00000 n 0000236575 00000 n 0000239085 00000 n 0000240841 00000 n 0000240974 00000 n 0002694200 00000 n 0000243789 00000 n 0000241091 00000 n 0000243655 00000 n 0002694309 00000 n 0000245221 00000 n 0000243853 00000 n 0000245149 00000 n 0002694511 00000 n 0000245531 00000 n 0000245285 00000 n 0000245497 00000 n 0002694620 00000 n 0000247329 00000 n 0000245595 00000 n 0000247247 00000 n 0002694729 00000 n 0000249963 00000 n 0000247393 00000 n 0000249855 00000 n 0002695143 00000 n 0000252416 00000 n 0000250027 00000 n 0000252247 00000 n 0002695252 00000 n 0000255520 00000 n 0000252480 00000 n 0000253110 00000 n 0000253186 00000 n 0000253820 00000 n 0000253896 00000 n 0000255376 00000 n 0000255471 00000 n 0002695446 00000 n 0000257530 00000 n 0000255601 00000 n 0000257397 00000 n 0002695555 00000 n 0000259215 00000 n 0000257594 00000 n 0000259044 00000 n 0002695664 00000 n 0000261506 00000 n 0000259279 00000 n 0000261322 00000 n 0002695866 00000 n 0000263316 00000 n 0000261570 00000 n 0000263183 00000 n 0002695975 00000 n 0000266051 00000 n 0000263380 00000 n 0000265891 00000 n 0002696084 00000 n 0000268624 00000 n 0000266115 00000 n 0000268467 00000 n 0002696286 00000 n 0000270865 00000 n 0000268688 00000 n 0000270709 00000 n 0002696395 00000 n 0000273244 00000 n 0000270929 00000 n 0000273111 00000 n 0002696504 00000 n 0000275208 00000 n 0000273308 00000 n 0000275037 00000 n 0002696812 00000 n 0000277221 00000 n 0000275272 00000 n 0000277087 00000 n 0002696921 00000 n 0000279775 00000 n 0000277285 00000 n 0000279628 00000 n 0002697030 00000 n 0000282754 00000 n 0000279839 00000 n 0000282583 00000 n 0002697232 00000 n 0000284662 00000 n 0000282818 00000 n 0000284528 00000 n 0002697341 00000 n 0000287055 00000 n 0000284726 00000 n 0000286934 00000 n 0002697450 00000 n 0000290433 00000 n 0002659846 00000 n 0002659655 00000 n 0000287119 00000 n 0000288098 00000 n 0000290260 00000 n 0002697652 00000 n 0000293254 00000 n 0000290497 00000 n 0000293120 00000 n 0002697761 00000 n 0000295213 00000 n 0000293318 00000 n 0000295079 00000 n 0002697870 00000 n 0000297473 00000 n 0000295277 00000 n 0000297351 00000 n 0002698072 00000 n 0000299922 00000 n 0000297537 00000 n 0000299778 00000 n 0002698181 00000 n 0000301942 00000 n 0000299986 00000 n 0000301847 00000 n 0002698290 00000 n 0000304058 00000 n 0000302006 00000 n 0000303976 00000 n 0002698598 00000 n 0000306684 00000 n 0000304122 00000 n 0000306613 00000 n 0002698707 00000 n 0000308951 00000 n 0000306748 00000 n 0000308818 00000 n 0002698816 00000 n 0000311373 00000 n 0000309015 00000 n 0000311238 00000 n 0002699018 00000 n 0000313872 00000 n 0000311437 00000 n 0000313739 00000 n 0002699127 00000 n 0000316390 00000 n 0000313936 00000 n 0000316270 00000 n 0002699236 00000 n 0000319187 00000 n 0000316454 00000 n 0000319041 00000 n 0002699438 00000 n 0000321705 00000 n 0000319251 00000 n 0000321596 00000 n 0002699547 00000 n 0000323713 00000 n 0000321769 00000 n 0000323593 00000 n 0002699656 00000 n 0000326289 00000 n 0000323777 00000 n 0000326167 00000 n 0002699858 00000 n 0000328966 00000 n 0000326353 00000 n 0000328833 00000 n 0002699967 00000 n 0000331847 00000 n 0000329030 00000 n 0000331777 00000 n 0002700076 00000 n 0000334292 00000 n 0000331911 00000 n 0000334159 00000 n 0002700384 00000 n 0000338051 00000 n 0000334356 00000 n 0000336456 00000 n 0000336376 00000 n 0000336537 00000 n 0000337919 00000 n 0000338015 00000 n 0002700493 00000 n 0000340611 00000 n 0000338132 00000 n 0000340454 00000 n 0002700602 00000 n 0000343255 00000 n 0000340675 00000 n 0000343122 00000 n 0002700804 00000 n 0000345582 00000 n 0000343319 00000 n 0000345436 00000 n 0002700913 00000 n 0000349303 00000 n 0002663672 00000 n 0002663484 00000 n 0000345646 00000 n 0000346634 00000 n 0000349120 00000 n 0002701022 00000 n 0000351610 00000 n 0000349367 00000 n 0000351451 00000 n 0002701224 00000 n 0000355179 00000 n 0002669467 00000 n 0002669271 00000 n 0000351674 00000 n 0000352669 00000 n 0000354969 00000 n 0002701333 00000 n 0000357552 00000 n 0000355243 00000 n 0000357432 00000 n 0002701442 00000 n 0000360083 00000 n 0000357616 00000 n 0000359924 00000 n 0002701644 00000 n 0000362236 00000 n 0000360147 00000 n 0000362115 00000 n 0002701753 00000 n 0000364701 00000 n 0000362300 00000 n 0000364542 00000 n 0002701862 00000 n 0000366924 00000 n 0000364765 00000 n 0000366842 00000 n 0002702276 00000 n 0000369505 00000 n 0000366988 00000 n 0000369436 00000 n 0002702385 00000 n 0000371414 00000 n 0000369569 00000 n 0000371344 00000 n 0002702494 00000 n 0000373665 00000 n 0000371478 00000 n 0000373583 00000 n 0002702696 00000 n 0000376299 00000 n 0000373729 00000 n 0000376193 00000 n 0002702805 00000 n 0000379114 00000 n 0000376363 00000 n 0000379006 00000 n 0002702914 00000 n 0000381425 00000 n 0000379178 00000 n 0000381292 00000 n 0002703116 00000 n 0000383913 00000 n 0000381489 00000 n 0000383804 00000 n 0002703225 00000 n 0000390703 00000 n 0000383977 00000 n 0000384258 00000 n 0000386185 00000 n 0000386466 00000 n 0000388632 00000 n 0000390521 00000 n 0000390654 00000 n 0002703334 00000 n 0000394955 00000 n 0000390784 00000 n 0000391061 00000 n 0000393066 00000 n 0000394822 00000 n 0000394918 00000 n 0002703536 00000 n 0000398011 00000 n 0000395036 00000 n 0000397866 00000 n 0002703645 00000 n 0000599631 00000 n 0000398075 00000 n 0000398359 00000 n 0000497895 00000 n 0000498179 00000 n 0000597723 00000 n 0000599446 00000 n 0000599580 00000 n 0002703754 00000 n 0000602994 00000 n 0000599712 00000 n 0000600185 00000 n 0000600265 00000 n 0000600725 00000 n 0000602809 00000 n 0000602943 00000 n 0002704062 00000 n 0000605367 00000 n 0000603075 00000 n 0000605269 00000 n 0002704171 00000 n 0000607747 00000 n 0000605431 00000 n 0000607624 00000 n 0002704280 00000 n 0000611016 00000 n 0000607811 00000 n 0000608090 00000 n 0000608786 00000 n 0000610858 00000 n 0000610979 00000 n 0002704482 00000 n 0000613779 00000 n 0000611097 00000 n 0000613671 00000 n 0002704591 00000 n 0000722441 00000 n 0000613843 00000 n 0000614127 00000 n 0000616219 00000 n 0000616503 00000 n 0000720477 00000 n 0000722269 00000 n 0000722390 00000 n 0002704700 00000 n 0000725331 00000 n 0000722522 00000 n 0000725187 00000 n 0002704902 00000 n 0000727640 00000 n 0000725395 00000 n 0000727519 00000 n 0002705011 00000 n 0000730502 00000 n 0000727704 00000 n 0000730381 00000 n 0002705120 00000 n 0000733178 00000 n 0000730566 00000 n 0000733057 00000 n 0002705322 00000 n 0000737916 00000 n 0000733242 00000 n 0000733530 00000 n 0000734991 00000 n 0000735273 00000 n 0000736727 00000 n 0000737755 00000 n 0000737865 00000 n 0002705431 00000 n 0000741243 00000 n 0000737997 00000 n 0000741121 00000 n 0002705540 00000 n 0000744007 00000 n 0000741307 00000 n 0000743873 00000 n 0002705848 00000 n 0000746977 00000 n 0000744071 00000 n 0000746855 00000 n 0002705957 00000 n 0000749971 00000 n 0000747041 00000 n 0000749789 00000 n 0002706066 00000 n 0000757307 00000 n 0000750035 00000 n 0000750311 00000 n 0000751627 00000 n 0000751903 00000 n 0000755017 00000 n 0000757122 00000 n 0000757256 00000 n 0002706268 00000 n 0000759790 00000 n 0000757388 00000 n 0000759684 00000 n 0002706377 00000 n 0000762507 00000 n 0000759854 00000 n 0000762437 00000 n 0002706486 00000 n 0000765454 00000 n 0000762571 00000 n 0000765373 00000 n 0002706688 00000 n 0000766161 00000 n 0000765518 00000 n 0000766114 00000 n 0002706797 00000 n 0000768251 00000 n 0000766225 00000 n 0000768156 00000 n 0002706906 00000 n 0000770575 00000 n 0000768315 00000 n 0000770482 00000 n 0002707108 00000 n 0000773831 00000 n 0000770639 00000 n 0000773710 00000 n 0002707217 00000 n 0000776605 00000 n 0000773895 00000 n 0000776460 00000 n 0002707326 00000 n 0000779925 00000 n 0000776669 00000 n 0000779792 00000 n 0002707634 00000 n 0000783199 00000 n 0000779989 00000 n 0000781522 00000 n 0000781602 00000 n 0000783078 00000 n 0000783162 00000 n 0002707743 00000 n 0000785694 00000 n 0000783280 00000 n 0000785561 00000 n 0002707852 00000 n 0000789159 00000 n 0000785758 00000 n 0000789012 00000 n 0002708054 00000 n 0000791849 00000 n 0000789223 00000 n 0000791716 00000 n 0002708163 00000 n 0000794098 00000 n 0000791913 00000 n 0000793977 00000 n 0002708272 00000 n 0000796498 00000 n 0000794162 00000 n 0000796367 00000 n 0002708474 00000 n 0000799043 00000 n 0000796562 00000 n 0000798933 00000 n 0002708583 00000 n 0000801094 00000 n 0000799107 00000 n 0000800988 00000 n 0002708692 00000 n 0000803437 00000 n 0000801158 00000 n 0000803304 00000 n 0002708894 00000 n 0000805085 00000 n 0000803501 00000 n 0000804903 00000 n 0002709003 00000 n 0000807921 00000 n 0000805149 00000 n 0000807777 00000 n 0002709112 00000 n 0000810373 00000 n 0000807985 00000 n 0000810251 00000 n 0002709630 00000 n 0000813096 00000 n 0000810437 00000 n 0000812987 00000 n 0002709739 00000 n 0000815694 00000 n 0000813160 00000 n 0000815550 00000 n 0002709933 00000 n 0000819770 00000 n 0000815758 00000 n 0000817677 00000 n 0000817753 00000 n 0000819601 00000 n 0000819733 00000 n 0002710042 00000 n 0000823051 00000 n 0000819851 00000 n 0000821205 00000 n 0000821285 00000 n 0000822882 00000 n 0000823014 00000 n 0002710151 00000 n 0000825735 00000 n 0000823132 00000 n 0000825626 00000 n 0002710353 00000 n 0000827916 00000 n 0000825799 00000 n 0000827781 00000 n 0002710462 00000 n 0000830776 00000 n 0000827980 00000 n 0000830655 00000 n 0002710571 00000 n 0000833596 00000 n 0000830840 00000 n 0000833462 00000 n 0002710773 00000 n 0000835712 00000 n 0000833660 00000 n 0000835590 00000 n 0002710882 00000 n 0000838392 00000 n 0000835776 00000 n 0000838246 00000 n 0002710991 00000 n 0000840881 00000 n 0000838456 00000 n 0000840772 00000 n 0002711299 00000 n 0000842419 00000 n 0000840945 00000 n 0000842286 00000 n 0002711408 00000 n 0000845120 00000 n 0000842483 00000 n 0000845051 00000 n 0002711517 00000 n 0000862129 00000 n 0000845184 00000 n 0000845471 00000 n 0000860816 00000 n 0000861943 00000 n 0000862090 00000 n 0002711721 00000 n 0000864832 00000 n 0000862213 00000 n 0000864685 00000 n 0002711833 00000 n 0000867967 00000 n 0000864898 00000 n 0000867847 00000 n 0002711945 00000 n 0000870838 00000 n 0000868033 00000 n 0000870702 00000 n 0002712153 00000 n 0000874009 00000 n 0000870904 00000 n 0000873874 00000 n 0002712265 00000 n 0000877411 00000 n 0000874075 00000 n 0000877289 00000 n 0002712377 00000 n 0000881289 00000 n 0000877477 00000 n 0000879928 00000 n 0000880009 00000 n 0000881117 00000 n 0000881250 00000 n 0002712585 00000 n 0000884810 00000 n 0000881373 00000 n 0000884687 00000 n 0002712697 00000 n 0000887245 00000 n 0000884876 00000 n 0000887123 00000 n 0002712809 00000 n 0000889908 00000 n 0000887311 00000 n 0000889785 00000 n 0002713123 00000 n 0000892956 00000 n 0000889974 00000 n 0000892834 00000 n 0002713235 00000 n 0000895997 00000 n 0000893022 00000 n 0000895877 00000 n 0002713347 00000 n 0000898740 00000 n 0000896063 00000 n 0000898607 00000 n 0002713555 00000 n 0000901647 00000 n 0000898806 00000 n 0000901527 00000 n 0002713667 00000 n 0000904286 00000 n 0000901713 00000 n 0000904164 00000 n 0002713779 00000 n 0000905156 00000 n 0000904352 00000 n 0000905109 00000 n 0002713987 00000 n 0000905491 00000 n 0000905222 00000 n 0000905445 00000 n 0002714099 00000 n 0000905651 00000 n 0000905557 00000 n 0002714211 00000 n 0000908225 00000 n 0000905702 00000 n 0000908178 00000 n 0002714419 00000 n 0000910286 00000 n 0000908291 00000 n 0000910202 00000 n 0002714531 00000 n 0000912344 00000 n 0000910352 00000 n 0000912261 00000 n 0002714643 00000 n 0000914692 00000 n 0000912410 00000 n 0000914598 00000 n 0002714957 00000 n 0000917020 00000 n 0000914758 00000 n 0000916939 00000 n 0002715069 00000 n 0000919746 00000 n 0000917086 00000 n 0000918423 00000 n 0000918342 00000 n 0000918499 00000 n 0000919053 00000 n 0000919134 00000 n 0000919633 00000 n 0000919692 00000 n 0002715181 00000 n 0000921939 00000 n 0000919830 00000 n 0000921845 00000 n 0002715389 00000 n 0000924069 00000 n 0000922005 00000 n 0000923998 00000 n 0002715501 00000 n 0000926730 00000 n 0000924135 00000 n 0000926672 00000 n 0002715613 00000 n 0000931142 00000 n 0000926796 00000 n 0000927091 00000 n 0000929308 00000 n 0000931009 00000 n 0000931103 00000 n 0002715821 00000 n 0000934194 00000 n 0000931226 00000 n 0000934113 00000 n 0002715933 00000 n 0000938389 00000 n 0000934260 00000 n 0000934550 00000 n 0000936702 00000 n 0000938280 00000 n 0000938350 00000 n 0002716045 00000 n 0000940227 00000 n 0000938473 00000 n 0000940157 00000 n 0002716253 00000 n 0000942423 00000 n 0000940293 00000 n 0000942354 00000 n 0002716365 00000 n 0000944387 00000 n 0000942489 00000 n 0000944317 00000 n 0002716477 00000 n 0000946958 00000 n 0000944453 00000 n 0000946900 00000 n 0002716897 00000 n 0000949276 00000 n 0000947024 00000 n 0000949205 00000 n 0002717009 00000 n 0000949600 00000 n 0000949342 00000 n 0000949565 00000 n 0002717208 00000 n 0000951443 00000 n 0000949666 00000 n 0000951360 00000 n 0002717320 00000 n 0000954001 00000 n 0000951509 00000 n 0000953906 00000 n 0002717432 00000 n 0000957081 00000 n 0000954067 00000 n 0000956961 00000 n 0002717640 00000 n 0000959510 00000 n 0000957147 00000 n 0000959390 00000 n 0002717752 00000 n 0000961707 00000 n 0000959576 00000 n 0000961600 00000 n 0002717864 00000 n 0000964048 00000 n 0000961773 00000 n 0000963940 00000 n 0002718072 00000 n 0000966511 00000 n 0000964114 00000 n 0000966389 00000 n 0002718184 00000 n 0000968812 00000 n 0000966577 00000 n 0000968702 00000 n 0002718296 00000 n 0000971654 00000 n 0000968878 00000 n 0000971519 00000 n 0002718610 00000 n 0000973920 00000 n 0000971720 00000 n 0000973861 00000 n 0002718722 00000 n 0000976440 00000 n 0000973986 00000 n 0000976282 00000 n 0002718834 00000 n 0000979465 00000 n 0000976506 00000 n 0000979393 00000 n 0002719042 00000 n 0000981548 00000 n 0000979531 00000 n 0000981426 00000 n 0002719154 00000 n 0000983827 00000 n 0000981614 00000 n 0000983717 00000 n 0002719266 00000 n 0000986785 00000 n 0000983893 00000 n 0000986625 00000 n 0002719474 00000 n 0000989679 00000 n 0000986851 00000 n 0000989520 00000 n 0002719586 00000 n 0000992595 00000 n 0000989745 00000 n 0000992486 00000 n 0002719698 00000 n 0000995633 00000 n 0000992661 00000 n 0000995499 00000 n 0002719906 00000 n 0000998406 00000 n 0000995699 00000 n 0000998259 00000 n 0002720018 00000 n 0001001323 00000 n 0000998472 00000 n 0001001189 00000 n 0002720130 00000 n 0001003759 00000 n 0001001389 00000 n 0001003625 00000 n 0002720444 00000 n 0001005811 00000 n 0001003825 00000 n 0001005678 00000 n 0002720556 00000 n 0001007850 00000 n 0001005877 00000 n 0001007779 00000 n 0002720668 00000 n 0001010598 00000 n 0001007916 00000 n 0001010451 00000 n 0002720876 00000 n 0001013286 00000 n 0001010664 00000 n 0001013153 00000 n 0002720988 00000 n 0001016120 00000 n 0001013352 00000 n 0001015948 00000 n 0002721100 00000 n 0001018621 00000 n 0001016186 00000 n 0001018476 00000 n 0002721308 00000 n 0001021391 00000 n 0001018687 00000 n 0001021242 00000 n 0002721420 00000 n 0001023556 00000 n 0001021457 00000 n 0001023424 00000 n 0002721532 00000 n 0001027202 00000 n 0002671868 00000 n 0002671671 00000 n 0001023622 00000 n 0001024549 00000 n 0001027052 00000 n 0002721740 00000 n 0001029298 00000 n 0001027268 00000 n 0001029153 00000 n 0002721852 00000 n 0001031089 00000 n 0001029364 00000 n 0001031031 00000 n 0002721964 00000 n 0001033938 00000 n 0001031155 00000 n 0001033844 00000 n 0002722278 00000 n 0001036293 00000 n 0001034004 00000 n 0001036222 00000 n 0002722390 00000 n 0001038414 00000 n 0001036359 00000 n 0001038331 00000 n 0002722502 00000 n 0001040908 00000 n 0001038480 00000 n 0001040827 00000 n 0002722710 00000 n 0001044648 00000 n 0001040974 00000 n 0001042809 00000 n 0001042890 00000 n 0001044477 00000 n 0001044609 00000 n 0002722822 00000 n 0001047694 00000 n 0001044732 00000 n 0001047547 00000 n 0002722934 00000 n 0001049850 00000 n 0001047760 00000 n 0001049780 00000 n 0002723142 00000 n 0001052887 00000 n 0001049916 00000 n 0001050518 00000 n 0001050599 00000 n 0001052716 00000 n 0001052848 00000 n 0002723254 00000 n 0001057026 00000 n 0001052971 00000 n 0001054969 00000 n 0001055050 00000 n 0001056906 00000 n 0001056987 00000 n 0002723366 00000 n 0001061119 00000 n 0001057110 00000 n 0001059134 00000 n 0001059215 00000 n 0001060974 00000 n 0001061080 00000 n 0002723574 00000 n 0001076170 00000 n 0001061203 00000 n 0001063429 00000 n 0001063510 00000 n 0001073579 00000 n 0001070371 00000 n 0001073661 00000 n 0001072826 00000 n 0001072629 00000 n 0001071876 00000 n 0001071610 00000 n 0001070853 00000 n 0001070656 00000 n 0001071807 00000 n 0001074886 00000 n 0001076047 00000 n 0001076116 00000 n 0002723686 00000 n 0001080374 00000 n 0001076254 00000 n 0001078494 00000 n 0001078575 00000 n 0001080254 00000 n 0001080335 00000 n 0002723798 00000 n 0001101150 00000 n 0001080458 00000 n 0001088334 00000 n 0001087093 00000 n 0001088416 00000 n 0001087581 00000 n 0001087315 00000 n 0001087512 00000 n 0001089641 00000 n 0001098907 00000 n 0001096686 00000 n 0001098989 00000 n 0001098154 00000 n 0001097957 00000 n 0001097204 00000 n 0001096938 00000 n 0001097135 00000 n 0001100214 00000 n 0001101038 00000 n 0001101096 00000 n 0002724218 00000 n 0001123697 00000 n 0001101234 00000 n 0001112294 00000 n 0001109093 00000 n 0001112376 00000 n 0001111541 00000 n 0001111344 00000 n 0001110591 00000 n 0001110394 00000 n 0001109641 00000 n 0001109375 00000 n 0001109572 00000 n 0001113601 00000 n 0001121638 00000 n 0001118437 00000 n 0001121720 00000 n 0001120885 00000 n 0001120688 00000 n 0001119935 00000 n 0001119738 00000 n 0001118985 00000 n 0001118719 00000 n 0001118916 00000 n 0001122945 00000 n 0001123585 00000 n 0001123643 00000 n 0002724330 00000 n 0001126544 00000 n 0001123781 00000 n 0001126449 00000 n 0002724529 00000 n 0001129398 00000 n 0001126610 00000 n 0001129303 00000 n 0002724641 00000 n 0001132106 00000 n 0001129464 00000 n 0001131985 00000 n 0002724753 00000 n 0001144137 00000 n 0001132172 00000 n 0001140852 00000 n 0001138624 00000 n 0001140934 00000 n 0001140099 00000 n 0001139833 00000 n 0001139076 00000 n 0001138879 00000 n 0001140030 00000 n 0001142159 00000 n 0001143989 00000 n 0001144098 00000 n 0002724961 00000 n 0001146664 00000 n 0001144221 00000 n 0001146557 00000 n 0002725073 00000 n 0001148915 00000 n 0001146730 00000 n 0001148808 00000 n 0002725185 00000 n 0001151013 00000 n 0001148981 00000 n 0001150879 00000 n 0002725393 00000 n 0001153851 00000 n 0001151079 00000 n 0001153729 00000 n 0002725505 00000 n 0001156220 00000 n 0001153917 00000 n 0001156098 00000 n 0002725617 00000 n 0001158716 00000 n 0001156286 00000 n 0001158594 00000 n 0002725931 00000 n 0001161053 00000 n 0001158782 00000 n 0001160983 00000 n 0002726043 00000 n 0001162730 00000 n 0001161119 00000 n 0001162671 00000 n 0002726155 00000 n 0001164554 00000 n 0001162796 00000 n 0001164483 00000 n 0002726363 00000 n 0001166647 00000 n 0001164620 00000 n 0001166487 00000 n 0002726475 00000 n 0001169140 00000 n 0001166713 00000 n 0001169006 00000 n 0002726587 00000 n 0001170985 00000 n 0001169206 00000 n 0001170863 00000 n 0002726795 00000 n 0001172765 00000 n 0001171051 00000 n 0001172643 00000 n 0002726907 00000 n 0001174539 00000 n 0001172831 00000 n 0001174417 00000 n 0002727019 00000 n 0001177053 00000 n 0001174605 00000 n 0001176919 00000 n 0002727227 00000 n 0001179599 00000 n 0001177119 00000 n 0001179443 00000 n 0002727339 00000 n 0001181579 00000 n 0001179665 00000 n 0001181471 00000 n 0002727451 00000 n 0001183093 00000 n 0001181645 00000 n 0001183022 00000 n 0002727765 00000 n 0001185114 00000 n 0001183159 00000 n 0001185009 00000 n 0002727877 00000 n 0001187149 00000 n 0001185180 00000 n 0001187027 00000 n 0002727989 00000 n 0001188816 00000 n 0001187215 00000 n 0001188733 00000 n 0002728197 00000 n 0001191268 00000 n 0001188882 00000 n 0001191134 00000 n 0002728309 00000 n 0001193642 00000 n 0001191334 00000 n 0001193523 00000 n 0002728421 00000 n 0001196156 00000 n 0001193708 00000 n 0001196061 00000 n 0002728629 00000 n 0001198225 00000 n 0001196222 00000 n 0001198142 00000 n 0002728741 00000 n 0001200733 00000 n 0001198291 00000 n 0001200599 00000 n 0002728853 00000 n 0001203203 00000 n 0001200799 00000 n 0001203057 00000 n 0002729061 00000 n 0001205234 00000 n 0001203269 00000 n 0001205074 00000 n 0002729173 00000 n 0001208325 00000 n 0001205300 00000 n 0001208179 00000 n 0002729285 00000 n 0001211131 00000 n 0001208391 00000 n 0001211009 00000 n 0002729599 00000 n 0001213790 00000 n 0001211197 00000 n 0001213632 00000 n 0002729711 00000 n 0001215734 00000 n 0001213856 00000 n 0001215575 00000 n 0002729823 00000 n 0001218373 00000 n 0001215800 00000 n 0001218240 00000 n 0002730031 00000 n 0001220737 00000 n 0001218439 00000 n 0001220594 00000 n 0002730143 00000 n 0001223735 00000 n 0001220803 00000 n 0001223590 00000 n 0002730255 00000 n 0001226079 00000 n 0001223801 00000 n 0001225921 00000 n 0002730463 00000 n 0001228493 00000 n 0001226145 00000 n 0001228346 00000 n 0002730575 00000 n 0001230933 00000 n 0001228559 00000 n 0001230852 00000 n 0002730687 00000 n 0001233266 00000 n 0001230999 00000 n 0001233135 00000 n 0002730895 00000 n 0001235524 00000 n 0001233332 00000 n 0001235418 00000 n 0002731007 00000 n 0001237519 00000 n 0001235590 00000 n 0001237401 00000 n 0002731119 00000 n 0001239759 00000 n 0001237585 00000 n 0001239674 00000 n 0002731539 00000 n 0001242222 00000 n 0001239825 00000 n 0001242089 00000 n 0002731651 00000 n 0001245172 00000 n 0001242288 00000 n 0001245013 00000 n 0002731763 00000 n 0001247362 00000 n 0001245238 00000 n 0001247189 00000 n 0002731971 00000 n 0001249223 00000 n 0001247428 00000 n 0001249113 00000 n 0002732083 00000 n 0001250942 00000 n 0001249289 00000 n 0001250820 00000 n 0002732195 00000 n 0001252728 00000 n 0001251008 00000 n 0001252593 00000 n 0002732403 00000 n 0001255070 00000 n 0001252794 00000 n 0001254948 00000 n 0002732515 00000 n 0001257374 00000 n 0001255136 00000 n 0001257265 00000 n 0002732627 00000 n 0001259689 00000 n 0001257440 00000 n 0001259595 00000 n 0002732835 00000 n 0001260022 00000 n 0001259755 00000 n 0001259987 00000 n 0002732947 00000 n 0001261695 00000 n 0001260088 00000 n 0001261613 00000 n 0002733059 00000 n 0001264236 00000 n 0001261761 00000 n 0001264127 00000 n 0002733373 00000 n 0001270852 00000 n 0001264302 00000 n 0001267767 00000 n 0001266525 00000 n 0001267849 00000 n 0001267576 00000 n 0001267322 00000 n 0001267054 00000 n 0001266793 00000 n 0001266986 00000 n 0001267507 00000 n 0001269074 00000 n 0001270705 00000 n 0001270813 00000 n 0002733485 00000 n 0001273342 00000 n 0001270936 00000 n 0001273233 00000 n 0002733597 00000 n 0001276055 00000 n 0001273408 00000 n 0001275895 00000 n 0002733805 00000 n 0001278229 00000 n 0001276121 00000 n 0001278083 00000 n 0002733917 00000 n 0001283865 00000 n 0001278295 00000 n 0001280946 00000 n 0001280863 00000 n 0001279621 00000 n 0001281028 00000 n 0001280595 00000 n 0001280334 00000 n 0001280143 00000 n 0001279889 00000 n 0001280527 00000 n 0001280074 00000 n 0001282253 00000 n 0001283756 00000 n 0001283826 00000 n 0002734029 00000 n 0001285992 00000 n 0001283949 00000 n 0001285910 00000 n 0002734237 00000 n 0001288232 00000 n 0001286058 00000 n 0001288098 00000 n 0002734349 00000 n 0001290937 00000 n 0001288298 00000 n 0001290817 00000 n 0002734461 00000 n 0001293233 00000 n 0001291003 00000 n 0001293050 00000 n 0002734669 00000 n 0001295957 00000 n 0001293299 00000 n 0001295811 00000 n 0002734781 00000 n 0001299637 00000 n 0001296023 00000 n 0001297505 00000 n 0001297586 00000 n 0001299527 00000 n 0001299598 00000 n 0002734893 00000 n 0001301466 00000 n 0001299721 00000 n 0001301395 00000 n 0002735207 00000 n 0001303779 00000 n 0001301532 00000 n 0001303607 00000 n 0002735319 00000 n 0001305859 00000 n 0001303845 00000 n 0001305724 00000 n 0002735431 00000 n 0001307904 00000 n 0001305925 00000 n 0001307759 00000 n 0002735639 00000 n 0001310493 00000 n 0001307970 00000 n 0001310384 00000 n 0002735751 00000 n 0001312229 00000 n 0001310559 00000 n 0001312107 00000 n 0002735863 00000 n 0001316247 00000 n 0001312295 00000 n 0001314288 00000 n 0001314207 00000 n 0001314365 00000 n 0001316086 00000 n 0001316208 00000 n 0002736071 00000 n 0001318585 00000 n 0001316331 00000 n 0001318464 00000 n 0002736183 00000 n 0001321544 00000 n 0001318651 00000 n 0001321473 00000 n 0002736295 00000 n 0001324241 00000 n 0001321610 00000 n 0001324107 00000 n 0002736503 00000 n 0001326114 00000 n 0001324307 00000 n 0001325992 00000 n 0002736615 00000 n 0001328957 00000 n 0001326180 00000 n 0001328825 00000 n 0002736727 00000 n 0001330162 00000 n 0001329023 00000 n 0001330103 00000 n 0002737041 00000 n 0001332640 00000 n 0001330228 00000 n 0001332506 00000 n 0002737153 00000 n 0001335135 00000 n 0001332706 00000 n 0001335012 00000 n 0002737265 00000 n 0001337296 00000 n 0001335201 00000 n 0001337162 00000 n 0002737473 00000 n 0001339734 00000 n 0001337362 00000 n 0001339625 00000 n 0002737585 00000 n 0001341860 00000 n 0001339800 00000 n 0001341726 00000 n 0002737697 00000 n 0001343928 00000 n 0001341926 00000 n 0001343818 00000 n 0002737905 00000 n 0001346177 00000 n 0001343994 00000 n 0001346069 00000 n 0002738017 00000 n 0001348466 00000 n 0001346243 00000 n 0001348384 00000 n 0002738129 00000 n 0001351078 00000 n 0001348532 00000 n 0001350995 00000 n 0002738337 00000 n 0001352641 00000 n 0001351144 00000 n 0001352571 00000 n 0002738449 00000 n 0001354724 00000 n 0001352707 00000 n 0001354653 00000 n 0002738561 00000 n 0001357453 00000 n 0001354790 00000 n 0001357370 00000 n 0002739085 00000 n 0001359698 00000 n 0001357519 00000 n 0001359539 00000 n 0002739197 00000 n 0001362097 00000 n 0001359764 00000 n 0001361988 00000 n 0002739396 00000 n 0001365053 00000 n 0001362163 00000 n 0001364932 00000 n 0002739508 00000 n 0001367413 00000 n 0001365119 00000 n 0001367302 00000 n 0002739620 00000 n 0001370360 00000 n 0001367479 00000 n 0001370226 00000 n 0002739828 00000 n 0001373102 00000 n 0001370426 00000 n 0001372992 00000 n 0002739940 00000 n 0001375976 00000 n 0001373168 00000 n 0001375842 00000 n 0002740052 00000 n 0001378655 00000 n 0001376042 00000 n 0001378533 00000 n 0002740260 00000 n 0001381762 00000 n 0001378721 00000 n 0001381628 00000 n 0002740372 00000 n 0001384241 00000 n 0001381828 00000 n 0001384119 00000 n 0002740484 00000 n 0001386894 00000 n 0001384307 00000 n 0001386760 00000 n 0002740798 00000 n 0001389253 00000 n 0001386960 00000 n 0001389131 00000 n 0002740910 00000 n 0001391929 00000 n 0001389319 00000 n 0001391797 00000 n 0002741022 00000 n 0001395582 00000 n 0001391995 00000 n 0001392277 00000 n 0001392750 00000 n 0001395398 00000 n 0001395543 00000 n 0002741230 00000 n 0001399188 00000 n 0001395666 00000 n 0001395948 00000 n 0001396832 00000 n 0001399040 00000 n 0001399149 00000 n 0002741342 00000 n 0001402042 00000 n 0001399272 00000 n 0001401908 00000 n 0002741454 00000 n 0001405850 00000 n 0001402108 00000 n 0001402390 00000 n 0001403092 00000 n 0001405689 00000 n 0001405811 00000 n 0002741662 00000 n 0001408839 00000 n 0001405934 00000 n 0001408694 00000 n 0002741774 00000 n 0001411762 00000 n 0001408905 00000 n 0001411651 00000 n 0002741886 00000 n 0001418177 00000 n 0001411828 00000 n 0001412109 00000 n 0001413192 00000 n 0001413473 00000 n 0001414585 00000 n 0001414866 00000 n 0001415971 00000 n 0001417974 00000 n 0001418108 00000 n 0002742094 00000 n 0001421201 00000 n 0001418261 00000 n 0001421079 00000 n 0002742206 00000 n 0001423696 00000 n 0001421267 00000 n 0001423597 00000 n 0002742318 00000 n 0001427065 00000 n 0001423762 00000 n 0001424044 00000 n 0001424772 00000 n 0001426928 00000 n 0001427026 00000 n 0002742632 00000 n 0001430036 00000 n 0001427149 00000 n 0001429902 00000 n 0002742744 00000 n 0001433638 00000 n 0001430102 00000 n 0001430384 00000 n 0001431257 00000 n 0001433440 00000 n 0001433599 00000 n 0002742856 00000 n 0001436141 00000 n 0001433722 00000 n 0001435996 00000 n 0002743064 00000 n 0001438795 00000 n 0001436207 00000 n 0001438673 00000 n 0002743176 00000 n 0001441534 00000 n 0001438861 00000 n 0001441425 00000 n 0002743288 00000 n 0001445983 00000 n 0001441600 00000 n 0001441883 00000 n 0001443662 00000 n 0001445874 00000 n 0001445944 00000 n 0002743496 00000 n 0001448649 00000 n 0001446067 00000 n 0001448466 00000 n 0002743608 00000 n 0001451060 00000 n 0001448715 00000 n 0001450938 00000 n 0002743720 00000 n 0001458362 00000 n 0001451126 00000 n 0001451420 00000 n 0001457066 00000 n 0001458203 00000 n 0001458323 00000 n 0002743928 00000 n 0001460665 00000 n 0001458446 00000 n 0001460543 00000 n 0002744040 00000 n 0001463589 00000 n 0001460731 00000 n 0001463454 00000 n 0002744152 00000 n 0001465985 00000 n 0001463655 00000 n 0001465851 00000 n 0002744466 00000 n 0001468325 00000 n 0001466051 00000 n 0001468215 00000 n 0002744578 00000 n 0001470595 00000 n 0001468391 00000 n 0001470461 00000 n 0002744690 00000 n 0001473419 00000 n 0001470661 00000 n 0001473297 00000 n 0002744898 00000 n 0001475934 00000 n 0001473485 00000 n 0001475800 00000 n 0002745010 00000 n 0001478345 00000 n 0001476000 00000 n 0001478223 00000 n 0002745122 00000 n 0001481231 00000 n 0001478411 00000 n 0001481071 00000 n 0002745330 00000 n 0001483862 00000 n 0001481297 00000 n 0001483752 00000 n 0002745442 00000 n 0001486447 00000 n 0001483928 00000 n 0001486313 00000 n 0002745554 00000 n 0001489277 00000 n 0001486513 00000 n 0001489154 00000 n 0002745762 00000 n 0001496909 00000 n 0001489343 00000 n 0001489637 00000 n 0001495311 00000 n 0001496748 00000 n 0001496870 00000 n 0002745874 00000 n 0001499501 00000 n 0001496993 00000 n 0001499379 00000 n 0002745986 00000 n 0001502298 00000 n 0001499567 00000 n 0001502164 00000 n 0002746406 00000 n 0001503786 00000 n 0001502364 00000 n 0001503715 00000 n 0002746518 00000 n 0001504122 00000 n 0001503852 00000 n 0001504076 00000 n 0002746717 00000 n 0001504282 00000 n 0001504188 00000 n 0002746829 00000 n 0001507253 00000 n 0001504333 00000 n 0001507206 00000 n 0002746941 00000 n 0001508454 00000 n 0001507319 00000 n 0001508406 00000 n 0002747149 00000 n 0001510447 00000 n 0001508520 00000 n 0001510364 00000 n 0002747261 00000 n 0001513206 00000 n 0001510513 00000 n 0001513059 00000 n 0002747373 00000 n 0001516531 00000 n 0001513272 00000 n 0001516396 00000 n 0002747581 00000 n 0001519434 00000 n 0001516597 00000 n 0001519248 00000 n 0002747693 00000 n 0001522535 00000 n 0001519500 00000 n 0001522350 00000 n 0002747805 00000 n 0001524802 00000 n 0001522601 00000 n 0001524717 00000 n 0002748119 00000 n 0001527394 00000 n 0001524868 00000 n 0001527209 00000 n 0002748231 00000 n 0001529019 00000 n 0001527460 00000 n 0001528896 00000 n 0002748343 00000 n 0001531024 00000 n 0001529085 00000 n 0001530915 00000 n 0002748551 00000 n 0001532974 00000 n 0001531090 00000 n 0001532852 00000 n 0002748663 00000 n 0001534896 00000 n 0001533040 00000 n 0001534762 00000 n 0002748775 00000 n 0001536663 00000 n 0001534962 00000 n 0001536541 00000 n 0002748983 00000 n 0001539003 00000 n 0001536729 00000 n 0001538831 00000 n 0002749095 00000 n 0001541355 00000 n 0001539069 00000 n 0001541221 00000 n 0002749207 00000 n 0001543732 00000 n 0001541421 00000 n 0001543557 00000 n 0002749415 00000 n 0001545685 00000 n 0001543798 00000 n 0001545563 00000 n 0002749527 00000 n 0001547538 00000 n 0001545751 00000 n 0001547429 00000 n 0002749639 00000 n 0001549444 00000 n 0001547604 00000 n 0001549284 00000 n 0002749953 00000 n 0001551506 00000 n 0001549510 00000 n 0001551385 00000 n 0002750065 00000 n 0001554271 00000 n 0001551572 00000 n 0001552605 00000 n 0001552682 00000 n 0001554097 00000 n 0001554232 00000 n 0002750177 00000 n 0001556327 00000 n 0001554355 00000 n 0001556192 00000 n 0002750385 00000 n 0001558204 00000 n 0001556393 00000 n 0001558093 00000 n 0002750497 00000 n 0001560432 00000 n 0001558270 00000 n 0001560285 00000 n 0002750609 00000 n 0001562663 00000 n 0001560498 00000 n 0001562540 00000 n 0002750817 00000 n 0001564756 00000 n 0001562729 00000 n 0001564621 00000 n 0002750929 00000 n 0001566067 00000 n 0001564822 00000 n 0001565968 00000 n 0002751041 00000 n 0001568304 00000 n 0001566133 00000 n 0001568157 00000 n 0002751249 00000 n 0001570757 00000 n 0001568370 00000 n 0001570661 00000 n 0002751361 00000 n 0001572081 00000 n 0001570823 00000 n 0001572009 00000 n 0002751473 00000 n 0001572401 00000 n 0001572147 00000 n 0001572366 00000 n 0002751787 00000 n 0001574771 00000 n 0001572467 00000 n 0001574648 00000 n 0002751899 00000 n 0001577298 00000 n 0001574837 00000 n 0001577226 00000 n 0002752011 00000 n 0001579415 00000 n 0001577364 00000 n 0001579305 00000 n 0002752219 00000 n 0001582226 00000 n 0001579481 00000 n 0001582079 00000 n 0002752331 00000 n 0001584768 00000 n 0001582292 00000 n 0001582733 00000 n 0001582814 00000 n 0001584558 00000 n 0001584729 00000 n 0002752443 00000 n 0001587304 00000 n 0001584852 00000 n 0001587119 00000 n 0002752651 00000 n 0001589513 00000 n 0001587370 00000 n 0001589365 00000 n 0002752763 00000 n 0001591717 00000 n 0001589579 00000 n 0001591594 00000 n 0002752875 00000 n 0001594752 00000 n 0001591783 00000 n 0001592434 00000 n 0001592515 00000 n 0001594541 00000 n 0001594713 00000 n 0002753083 00000 n 0001596971 00000 n 0001594836 00000 n 0001596848 00000 n 0002753195 00000 n 0001599423 00000 n 0001597037 00000 n 0001597657 00000 n 0001597738 00000 n 0001599274 00000 n 0001599384 00000 n 0002753307 00000 n 0001602615 00000 n 0001599507 00000 n 0001602481 00000 n 0002753727 00000 n 0001606103 00000 n 0001602681 00000 n 0001605995 00000 n 0002753839 00000 n 0001607979 00000 n 0001606169 00000 n 0001607832 00000 n 0002754038 00000 n 0001611998 00000 n 0002673504 00000 n 0002673309 00000 n 0001608045 00000 n 0001608962 00000 n 0001611812 00000 n 0002754150 00000 n 0001614681 00000 n 0001612064 00000 n 0001614558 00000 n 0002754262 00000 n 0001617727 00000 n 0001614747 00000 n 0001615605 00000 n 0001615686 00000 n 0001617566 00000 n 0001617688 00000 n 0002754470 00000 n 0001619827 00000 n 0001617811 00000 n 0001619718 00000 n 0002754582 00000 n 0001622325 00000 n 0001619893 00000 n 0001622204 00000 n 0002754694 00000 n 0001625242 00000 n 0001622391 00000 n 0001625095 00000 n 0002754902 00000 n 0001627497 00000 n 0001625308 00000 n 0001627337 00000 n 0002755014 00000 n 0001629533 00000 n 0001627563 00000 n 0001629398 00000 n 0002755126 00000 n 0001632442 00000 n 0001629599 00000 n 0001632295 00000 n 0002755440 00000 n 0001634965 00000 n 0001632508 00000 n 0001632985 00000 n 0001633066 00000 n 0001633572 00000 n 0001633653 00000 n 0001634801 00000 n 0001634911 00000 n 0002755552 00000 n 0001638363 00000 n 0001635049 00000 n 0001635592 00000 n 0001635673 00000 n 0001638215 00000 n 0001638324 00000 n 0002755664 00000 n 0001640741 00000 n 0001638447 00000 n 0001640581 00000 n 0002755872 00000 n 0001642723 00000 n 0001640807 00000 n 0001642576 00000 n 0002755984 00000 n 0001644789 00000 n 0001642789 00000 n 0001644679 00000 n 0002756096 00000 n 0001646709 00000 n 0001644855 00000 n 0001646586 00000 n 0002756304 00000 n 0001648825 00000 n 0001646775 00000 n 0001648715 00000 n 0002756416 00000 n 0001651438 00000 n 0001648891 00000 n 0001651278 00000 n 0002756528 00000 n 0001653988 00000 n 0001651504 00000 n 0001653841 00000 n 0002756736 00000 n 0001656807 00000 n 0001654054 00000 n 0001656611 00000 n 0002756848 00000 n 0001660236 00000 n 0001656873 00000 n 0001660115 00000 n 0002756960 00000 n 0001662580 00000 n 0001660302 00000 n 0001662459 00000 n 0002757274 00000 n 0001664838 00000 n 0001662646 00000 n 0001664655 00000 n 0002757386 00000 n 0001666953 00000 n 0001664904 00000 n 0001666844 00000 n 0002757498 00000 n 0001669392 00000 n 0001667019 00000 n 0001669257 00000 n 0002757706 00000 n 0001671738 00000 n 0001669458 00000 n 0001671565 00000 n 0002757818 00000 n 0001674161 00000 n 0001671804 00000 n 0001674039 00000 n 0002757930 00000 n 0001676283 00000 n 0001674227 00000 n 0001676149 00000 n 0002758138 00000 n 0001678614 00000 n 0001676349 00000 n 0001678491 00000 n 0002758250 00000 n 0001680579 00000 n 0001678680 00000 n 0001680434 00000 n 0002758362 00000 n 0001682621 00000 n 0001680645 00000 n 0001682522 00000 n 0002758570 00000 n 0001685087 00000 n 0001682687 00000 n 0001684955 00000 n 0002758682 00000 n 0001687706 00000 n 0001685153 00000 n 0001687611 00000 n 0002758794 00000 n 0001688729 00000 n 0001687772 00000 n 0001688669 00000 n 0002759108 00000 n 0001689051 00000 n 0001688795 00000 n 0001689016 00000 n 0002759220 00000 n 0001691014 00000 n 0001689117 00000 n 0001690943 00000 n 0002759332 00000 n 0001693670 00000 n 0001691080 00000 n 0001693611 00000 n 0002759540 00000 n 0001695990 00000 n 0001693736 00000 n 0001695856 00000 n 0002759652 00000 n 0001698752 00000 n 0001696056 00000 n 0001696817 00000 n 0001696898 00000 n 0001698655 00000 n 0001698713 00000 n 0002759764 00000 n 0001701487 00000 n 0001698836 00000 n 0001701378 00000 n 0002759972 00000 n 0001703727 00000 n 0001701553 00000 n 0001703591 00000 n 0002760084 00000 n 0001706456 00000 n 0001703793 00000 n 0001706309 00000 n 0002760196 00000 n 0001709100 00000 n 0001706522 00000 n 0001708978 00000 n 0002760404 00000 n 0001711954 00000 n 0001709166 00000 n 0001711847 00000 n 0002760516 00000 n 0001714765 00000 n 0001712020 00000 n 0001714643 00000 n 0002760628 00000 n 0001717349 00000 n 0001714831 00000 n 0001717215 00000 n 0002761048 00000 n 0001719190 00000 n 0001717415 00000 n 0001719066 00000 n 0002761160 00000 n 0001721471 00000 n 0001719256 00000 n 0001721335 00000 n 0002761272 00000 n 0001723700 00000 n 0001721537 00000 n 0001723565 00000 n 0002761480 00000 n 0001726217 00000 n 0001723766 00000 n 0001726081 00000 n 0002761592 00000 n 0001728173 00000 n 0001726283 00000 n 0001728038 00000 n 0002761704 00000 n 0001730721 00000 n 0001728239 00000 n 0001730574 00000 n 0002761912 00000 n 0001731713 00000 n 0001730787 00000 n 0001731665 00000 n 0002762024 00000 n 0001733479 00000 n 0001731779 00000 n 0001733406 00000 n 0002762136 00000 n 0001736323 00000 n 0001733545 00000 n 0001736228 00000 n 0002762344 00000 n 0001740187 00000 n 0002677560 00000 n 0002677362 00000 n 0001736389 00000 n 0001737347 00000 n 0001737849 00000 n 0001737930 00000 n 0001740040 00000 n 0001740148 00000 n 0002762456 00000 n 0001743313 00000 n 0001740271 00000 n 0001741000 00000 n 0001741081 00000 n 0001743191 00000 n 0001743274 00000 n 0002762568 00000 n 0001745963 00000 n 0001743397 00000 n 0001744081 00000 n 0001744162 00000 n 0001745750 00000 n 0001745924 00000 n 0002762882 00000 n 0001747999 00000 n 0001746047 00000 n 0001747889 00000 n 0002762994 00000 n 0001750612 00000 n 0001748065 00000 n 0001750464 00000 n 0002763106 00000 n 0001753227 00000 n 0001750678 00000 n 0001753117 00000 n 0002763314 00000 n 0001754709 00000 n 0001753293 00000 n 0001754598 00000 n 0002763426 00000 n 0001757691 00000 n 0001754775 00000 n 0001755936 00000 n 0001756017 00000 n 0001757543 00000 n 0001757652 00000 n 0002763538 00000 n 0001759677 00000 n 0001757775 00000 n 0001759530 00000 n 0002763746 00000 n 0001761378 00000 n 0001759743 00000 n 0001761230 00000 n 0002763858 00000 n 0001763597 00000 n 0001761444 00000 n 0001763475 00000 n 0002763970 00000 n 0001764892 00000 n 0001763663 00000 n 0001764793 00000 n 0002764178 00000 n 0001772498 00000 n 0001764958 00000 n 0001765249 00000 n 0001771047 00000 n 0001772387 00000 n 0001772459 00000 n 0002764290 00000 n 0001774654 00000 n 0001772582 00000 n 0001774531 00000 n 0002764402 00000 n 0001777298 00000 n 0001774720 00000 n 0001777150 00000 n 0002764716 00000 n 0001779654 00000 n 0001777364 00000 n 0001779518 00000 n 0002764828 00000 n 0001781851 00000 n 0001779720 00000 n 0001781740 00000 n 0002764940 00000 n 0001783938 00000 n 0001781917 00000 n 0001783828 00000 n 0002765148 00000 n 0001785819 00000 n 0001784004 00000 n 0001785697 00000 n 0002765260 00000 n 0001787625 00000 n 0001785885 00000 n 0001787515 00000 n 0002765372 00000 n 0001789728 00000 n 0001787691 00000 n 0001789630 00000 n 0002765580 00000 n 0001791901 00000 n 0001789794 00000 n 0001791815 00000 n 0002765692 00000 n 0001793939 00000 n 0001791967 00000 n 0001793841 00000 n 0002765804 00000 n 0001795400 00000 n 0001794005 00000 n 0001795314 00000 n 0002766012 00000 n 0001797218 00000 n 0001795466 00000 n 0001797097 00000 n 0002766124 00000 n 0001812431 00000 n 0001797284 00000 n 0001797570 00000 n 0001810957 00000 n 0001812332 00000 n 0001812392 00000 n 0002766236 00000 n 0001827782 00000 n 0001812515 00000 n 0001812801 00000 n 0001826269 00000 n 0001827673 00000 n 0001827743 00000 n 0002766550 00000 n 0001842759 00000 n 0001827866 00000 n 0001828152 00000 n 0001841195 00000 n 0001842649 00000 n 0001842720 00000 n 0002766662 00000 n 0001845081 00000 n 0001842843 00000 n 0001844974 00000 n 0002766774 00000 n 0001847718 00000 n 0001845147 00000 n 0001847608 00000 n 0002766982 00000 n 0001849955 00000 n 0001847784 00000 n 0001849844 00000 n 0002767094 00000 n 0001852493 00000 n 0001850021 00000 n 0001852394 00000 n 0002767206 00000 n 0001854453 00000 n 0001852559 00000 n 0001854382 00000 n 0002767414 00000 n 0001857601 00000 n 0001854519 00000 n 0001857440 00000 n 0002767526 00000 n 0001859334 00000 n 0001857667 00000 n 0001859236 00000 n 0002767638 00000 n 0001860598 00000 n 0001859400 00000 n 0001860511 00000 n 0002767846 00000 n 0001862529 00000 n 0001860664 00000 n 0001862418 00000 n 0002767958 00000 n 0001864628 00000 n 0001862595 00000 n 0001864529 00000 n 0002768070 00000 n 0001866757 00000 n 0001864694 00000 n 0001866646 00000 n 0002768594 00000 n 0001868787 00000 n 0001866823 00000 n 0001868688 00000 n 0002768706 00000 n 0001871362 00000 n 0001868853 00000 n 0001871251 00000 n 0002768905 00000 n 0001873782 00000 n 0001871428 00000 n 0001873683 00000 n 0002769017 00000 n 0001875877 00000 n 0001873848 00000 n 0001875779 00000 n 0002769129 00000 n 0001877819 00000 n 0001875943 00000 n 0001877720 00000 n 0002769337 00000 n 0001879001 00000 n 0001877885 00000 n 0001878903 00000 n 0002769449 00000 n 0001880566 00000 n 0001879067 00000 n 0001880456 00000 n 0002769561 00000 n 0001883423 00000 n 0001880632 00000 n 0001883302 00000 n 0002769769 00000 n 0001886275 00000 n 0001883489 00000 n 0001886191 00000 n 0002769881 00000 n 0001888920 00000 n 0001886341 00000 n 0001888773 00000 n 0002769993 00000 n 0001891486 00000 n 0001888986 00000 n 0001889696 00000 n 0001889777 00000 n 0001891326 00000 n 0001891447 00000 n 0002770307 00000 n 0001894663 00000 n 0001891570 00000 n 0001894505 00000 n 0002770419 00000 n 0001896823 00000 n 0001894729 00000 n 0001896688 00000 n 0002770531 00000 n 0001898688 00000 n 0001896889 00000 n 0001898566 00000 n 0002770739 00000 n 0001901376 00000 n 0001898754 00000 n 0001899876 00000 n 0001899957 00000 n 0001901215 00000 n 0001901337 00000 n 0002770851 00000 n 0001904109 00000 n 0001901460 00000 n 0001904000 00000 n 0002770963 00000 n 0001906558 00000 n 0001904175 00000 n 0001904804 00000 n 0001904885 00000 n 0001906409 00000 n 0001906519 00000 n 0002771171 00000 n 0001909472 00000 n 0001906642 00000 n 0001906934 00000 n 0001907951 00000 n 0001909299 00000 n 0001909433 00000 n 0002771283 00000 n 0001911898 00000 n 0001909556 00000 n 0001911788 00000 n 0002771395 00000 n 0001914025 00000 n 0001911964 00000 n 0001913903 00000 n 0002771603 00000 n 0001916668 00000 n 0001914091 00000 n 0001916533 00000 n 0002771715 00000 n 0001919114 00000 n 0001916734 00000 n 0001918993 00000 n 0002771827 00000 n 0001919761 00000 n 0001919180 00000 n 0001919714 00000 n 0002772141 00000 n 0001920098 00000 n 0001919827 00000 n 0001920052 00000 n 0002772253 00000 n 0001920258 00000 n 0001920164 00000 n 0002772365 00000 n 0001922928 00000 n 0001920309 00000 n 0001922820 00000 n 0002772573 00000 n 0001926140 00000 n 0001922994 00000 n 0001924122 00000 n 0001924041 00000 n 0001924202 00000 n 0001926015 00000 n 0001926101 00000 n 0002772685 00000 n 0001929175 00000 n 0001926224 00000 n 0001929117 00000 n 0002772797 00000 n 0001929437 00000 n 0001929241 00000 n 0001929402 00000 n 0002773005 00000 n 0001931356 00000 n 0001929503 00000 n 0001931262 00000 n 0002773117 00000 n 0001933631 00000 n 0001931422 00000 n 0001933547 00000 n 0002773229 00000 n 0001936253 00000 n 0001933697 00000 n 0001936119 00000 n 0002773437 00000 n 0001938659 00000 n 0001936319 00000 n 0001938564 00000 n 0002773549 00000 n 0001941588 00000 n 0001938725 00000 n 0001941442 00000 n 0002773661 00000 n 0001943771 00000 n 0001941654 00000 n 0001943624 00000 n 0002773975 00000 n 0001946169 00000 n 0001943837 00000 n 0001946036 00000 n 0002774087 00000 n 0001948814 00000 n 0001946235 00000 n 0001948704 00000 n 0002774199 00000 n 0001951188 00000 n 0001948880 00000 n 0001951093 00000 n 0002774407 00000 n 0001953028 00000 n 0001951254 00000 n 0001952906 00000 n 0002774519 00000 n 0001954966 00000 n 0001953094 00000 n 0001954847 00000 n 0002774631 00000 n 0001957155 00000 n 0001955032 00000 n 0001957047 00000 n 0002774839 00000 n 0001959333 00000 n 0001957221 00000 n 0001959211 00000 n 0002774951 00000 n 0001961662 00000 n 0001959399 00000 n 0001961463 00000 n 0002775063 00000 n 0001963581 00000 n 0001961728 00000 n 0001963446 00000 n 0002775271 00000 n 0001966427 00000 n 0001963647 00000 n 0001966283 00000 n 0002775383 00000 n 0001969114 00000 n 0001966493 00000 n 0001968955 00000 n 0002775495 00000 n 0001971726 00000 n 0001969180 00000 n 0001971603 00000 n 0002775915 00000 n 0001974652 00000 n 0001971792 00000 n 0001974518 00000 n 0002776027 00000 n 0001977642 00000 n 0001974718 00000 n 0001977510 00000 n 0002776139 00000 n 0001980038 00000 n 0001977708 00000 n 0001979906 00000 n 0002776347 00000 n 0001982621 00000 n 0001980104 00000 n 0001982486 00000 n 0002776459 00000 n 0001985269 00000 n 0001982687 00000 n 0001985134 00000 n 0002776571 00000 n 0001987962 00000 n 0001985335 00000 n 0001987801 00000 n 0002776779 00000 n 0001990309 00000 n 0001988028 00000 n 0001990200 00000 n 0002776891 00000 n 0001992779 00000 n 0001990375 00000 n 0001992721 00000 n 0002777003 00000 n 0001994208 00000 n 0001992845 00000 n 0001994137 00000 n 0002777211 00000 n 0001994531 00000 n 0001994274 00000 n 0001994496 00000 n 0002777323 00000 n 0001996729 00000 n 0001994597 00000 n 0001996646 00000 n 0002777435 00000 n 0001999203 00000 n 0001996795 00000 n 0001999106 00000 n 0002777749 00000 n 0002001699 00000 n 0001999269 00000 n 0002001579 00000 n 0002777861 00000 n 0002004194 00000 n 0002001765 00000 n 0002004036 00000 n 0002777973 00000 n 0002006330 00000 n 0002004260 00000 n 0002006208 00000 n 0002778181 00000 n 0002008765 00000 n 0002006396 00000 n 0002008619 00000 n 0002778293 00000 n 0002012199 00000 n 0002008831 00000 n 0002010250 00000 n 0002012026 00000 n 0002012160 00000 n 0002778405 00000 n 0002014629 00000 n 0002012283 00000 n 0002014505 00000 n 0002778613 00000 n 0002016775 00000 n 0002014695 00000 n 0002016643 00000 n 0002778725 00000 n 0002019307 00000 n 0002016841 00000 n 0002019147 00000 n 0002778837 00000 n 0002021371 00000 n 0002019373 00000 n 0002021237 00000 n 0002779045 00000 n 0002023620 00000 n 0002021437 00000 n 0002023511 00000 n 0002779157 00000 n 0002025617 00000 n 0002023686 00000 n 0002025508 00000 n 0002779269 00000 n 0002028026 00000 n 0002025683 00000 n 0002027906 00000 n 0002779583 00000 n 0002030563 00000 n 0002028092 00000 n 0002030416 00000 n 0002779695 00000 n 0002032722 00000 n 0002030629 00000 n 0002032613 00000 n 0002779807 00000 n 0002035010 00000 n 0002032788 00000 n 0002034876 00000 n 0002780015 00000 n 0002037400 00000 n 0002035076 00000 n 0002037277 00000 n 0002780127 00000 n 0002039420 00000 n 0002037466 00000 n 0002039284 00000 n 0002780239 00000 n 0002041654 00000 n 0002039486 00000 n 0002041543 00000 n 0002780447 00000 n 0002044115 00000 n 0002041720 00000 n 0002043993 00000 n 0002780559 00000 n 0002046262 00000 n 0002044181 00000 n 0002046141 00000 n 0002780671 00000 n 0002047756 00000 n 0002046328 00000 n 0002047673 00000 n 0002780879 00000 n 0002048074 00000 n 0002047822 00000 n 0002048039 00000 n 0002780991 00000 n 0002050091 00000 n 0002048140 00000 n 0002049997 00000 n 0002781103 00000 n 0002052603 00000 n 0002050157 00000 n 0002052520 00000 n 0002781417 00000 n 0002055330 00000 n 0002052669 00000 n 0002055200 00000 n 0002781529 00000 n 0002057847 00000 n 0002055396 00000 n 0002057712 00000 n 0002781641 00000 n 0002060554 00000 n 0002057913 00000 n 0002060385 00000 n 0002781849 00000 n 0002063606 00000 n 0002060620 00000 n 0002063462 00000 n 0002781961 00000 n 0002066226 00000 n 0002063672 00000 n 0002066068 00000 n 0002782073 00000 n 0002068647 00000 n 0002066292 00000 n 0002068515 00000 n 0002782281 00000 n 0002071990 00000 n 0002068713 00000 n 0002070189 00000 n 0002070108 00000 n 0002070269 00000 n 0002071845 00000 n 0002071951 00000 n 0002782393 00000 n 0002075158 00000 n 0002072074 00000 n 0002073196 00000 n 0002073277 00000 n 0002074999 00000 n 0002075119 00000 n 0002782505 00000 n 0002078126 00000 n 0002075242 00000 n 0002077992 00000 n 0002782713 00000 n 0002080227 00000 n 0002078192 00000 n 0002080093 00000 n 0002782825 00000 n 0002083190 00000 n 0002080293 00000 n 0002083045 00000 n 0002782937 00000 n 0002085933 00000 n 0002083256 00000 n 0002085823 00000 n 0002783357 00000 n 0002088432 00000 n 0002085999 00000 n 0002088285 00000 n 0002783469 00000 n 0002091495 00000 n 0002088498 00000 n 0002091385 00000 n 0002783668 00000 n 0002094107 00000 n 0002091561 00000 n 0002093973 00000 n 0002783780 00000 n 0002096923 00000 n 0002094173 00000 n 0002096787 00000 n 0002783892 00000 n 0002099630 00000 n 0002096989 00000 n 0002099482 00000 n 0002784100 00000 n 0002102344 00000 n 0002099696 00000 n 0002102224 00000 n 0002784212 00000 n 0002105467 00000 n 0002102410 00000 n 0002103460 00000 n 0002103541 00000 n 0002105333 00000 n 0002105428 00000 n 0002784324 00000 n 0002108039 00000 n 0002105551 00000 n 0002107957 00000 n 0002784532 00000 n 0002109627 00000 n 0002108105 00000 n 0002109558 00000 n 0002784644 00000 n 0002112507 00000 n 0002109693 00000 n 0002112425 00000 n 0002784756 00000 n 0002114899 00000 n 0002112573 00000 n 0002114816 00000 n 0002785070 00000 n 0002117282 00000 n 0002114965 00000 n 0002117213 00000 n 0002785182 00000 n 0002120224 00000 n 0002117348 00000 n 0002120117 00000 n 0002785294 00000 n 0002121978 00000 n 0002120290 00000 n 0002121908 00000 n 0002785502 00000 n 0002123923 00000 n 0002122044 00000 n 0002123851 00000 n 0002785614 00000 n 0002126087 00000 n 0002123989 00000 n 0002125965 00000 n 0002785726 00000 n 0002128663 00000 n 0002126153 00000 n 0002128529 00000 n 0002785934 00000 n 0002131106 00000 n 0002128729 00000 n 0002130983 00000 n 0002786046 00000 n 0002133350 00000 n 0002131172 00000 n 0002133205 00000 n 0002786158 00000 n 0002136140 00000 n 0002133416 00000 n 0002136007 00000 n 0002786366 00000 n 0002139158 00000 n 0002136206 00000 n 0002139013 00000 n 0002786478 00000 n 0002141720 00000 n 0002139224 00000 n 0002141610 00000 n 0002786590 00000 n 0002143850 00000 n 0002141786 00000 n 0002143739 00000 n 0002786904 00000 n 0002145887 00000 n 0002143916 00000 n 0002145788 00000 n 0002787016 00000 n 0002148621 00000 n 0002145953 00000 n 0002148487 00000 n 0002787128 00000 n 0002150728 00000 n 0002148687 00000 n 0002150629 00000 n 0002787336 00000 n 0002153083 00000 n 0002150794 00000 n 0002152949 00000 n 0002787448 00000 n 0002155079 00000 n 0002153149 00000 n 0002154969 00000 n 0002787560 00000 n 0002157746 00000 n 0002155145 00000 n 0002157599 00000 n 0002787768 00000 n 0002160436 00000 n 0002157812 00000 n 0002158520 00000 n 0002158601 00000 n 0002160288 00000 n 0002160397 00000 n 0002787880 00000 n 0002162786 00000 n 0002160520 00000 n 0002162640 00000 n 0002787992 00000 n 0002165458 00000 n 0002162852 00000 n 0002165335 00000 n 0002788200 00000 n 0002168194 00000 n 0002165524 00000 n 0002168060 00000 n 0002788312 00000 n 0002171163 00000 n 0002168260 00000 n 0002171041 00000 n 0002788424 00000 n 0002188427 00000 n 0002171229 00000 n 0002171522 00000 n 0002186711 00000 n 0002188267 00000 n 0002188388 00000 n 0002788738 00000 n 0002190085 00000 n 0002188511 00000 n 0002190027 00000 n 0002788850 00000 n 0002192388 00000 n 0002190151 00000 n 0002192241 00000 n 0002788962 00000 n 0002194887 00000 n 0002192454 00000 n 0002194740 00000 n 0002789170 00000 n 0002197245 00000 n 0002194953 00000 n 0002197110 00000 n 0002789282 00000 n 0002200136 00000 n 0002197311 00000 n 0002200003 00000 n 0002789394 00000 n 0002202990 00000 n 0002200202 00000 n 0002202818 00000 n 0002789602 00000 n 0002248383 00000 n 0002203056 00000 n 0002203349 00000 n 0002247245 00000 n 0002248309 00000 n 0002248344 00000 n 0002789714 00000 n 0002250316 00000 n 0002248467 00000 n 0002250245 00000 n 0002789826 00000 n 0002253003 00000 n 0002250382 00000 n 0002252945 00000 n 0002790034 00000 n 0002255598 00000 n 0002253069 00000 n 0002255528 00000 n 0002790146 00000 n 0002258425 00000 n 0002255664 00000 n 0002258343 00000 n 0002790258 00000 n 0002261188 00000 n 0002258491 00000 n 0002261094 00000 n 0002790678 00000 n 0002263939 00000 n 0002261254 00000 n 0002263846 00000 n 0002790790 00000 n 0002266936 00000 n 0002264005 00000 n 0002266817 00000 n 0002790902 00000 n 0002269978 00000 n 0002267002 00000 n 0002269884 00000 n 0002791110 00000 n 0002273257 00000 n 0002270044 00000 n 0002273161 00000 n 0002791222 00000 n 0002275333 00000 n 0002273323 00000 n 0002275262 00000 n 0002791334 00000 n 0002277253 00000 n 0002275399 00000 n 0002277170 00000 n 0002791542 00000 n 0002279663 00000 n 0002277319 00000 n 0002279580 00000 n 0002791654 00000 n 0002282075 00000 n 0002279729 00000 n 0002282003 00000 n 0002791766 00000 n 0002284301 00000 n 0002282141 00000 n 0002284255 00000 n 0002791974 00000 n 0002287473 00000 n 0002284367 00000 n 0002287379 00000 n 0002792086 00000 n 0002287814 00000 n 0002287539 00000 n 0002287779 00000 n 0002792198 00000 n 0002289699 00000 n 0002287880 00000 n 0002289641 00000 n 0002792512 00000 n 0002291313 00000 n 0002289765 00000 n 0002291266 00000 n 0002792624 00000 n 0002291631 00000 n 0002291379 00000 n 0002291585 00000 n 0002792736 00000 n 0002291791 00000 n 0002291697 00000 n 0002792944 00000 n 0002293565 00000 n 0002291842 00000 n 0002293416 00000 n 0002793056 00000 n 0002295870 00000 n 0002293631 00000 n 0002295746 00000 n 0002793168 00000 n 0002298296 00000 n 0002295936 00000 n 0002298136 00000 n 0002793376 00000 n 0002300749 00000 n 0002298362 00000 n 0002300588 00000 n 0002793488 00000 n 0002302038 00000 n 0002300815 00000 n 0002301944 00000 n 0002793600 00000 n 0002304382 00000 n 0002302104 00000 n 0002304209 00000 n 0002793808 00000 n 0002306688 00000 n 0002304448 00000 n 0002306566 00000 n 0002793920 00000 n 0002309390 00000 n 0002306754 00000 n 0002309218 00000 n 0002794032 00000 n 0002311722 00000 n 0002309456 00000 n 0002311561 00000 n 0002794346 00000 n 0002313714 00000 n 0002311788 00000 n 0002313591 00000 n 0002794458 00000 n 0002316112 00000 n 0002313780 00000 n 0002315952 00000 n 0002794570 00000 n 0002318906 00000 n 0002316178 00000 n 0002318720 00000 n 0002794778 00000 n 0002321386 00000 n 0002318972 00000 n 0002321264 00000 n 0002794890 00000 n 0002323595 00000 n 0002321452 00000 n 0002323474 00000 n 0002795002 00000 n 0002337194 00000 n 0002323661 00000 n 0002323954 00000 n 0002327243 00000 n 0002327536 00000 n 0002333659 00000 n 0002333952 00000 n 0002336649 00000 n 0002337067 00000 n 0002337125 00000 n 0002795210 00000 n 0002337512 00000 n 0002337278 00000 n 0002337476 00000 n 0002795322 00000 n 0002339363 00000 n 0002337578 00000 n 0002339304 00000 n 0002795434 00000 n 0002342181 00000 n 0002339429 00000 n 0002342122 00000 n 0002795642 00000 n 0002344661 00000 n 0002342247 00000 n 0002344602 00000 n 0002795754 00000 n 0002345818 00000 n 0002344727 00000 n 0002345771 00000 n 0002795866 00000 n 0002347350 00000 n 0002345884 00000 n 0002347278 00000 n 0002796180 00000 n 0002349496 00000 n 0002347416 00000 n 0002349436 00000 n 0002796292 00000 n 0002351691 00000 n 0002349562 00000 n 0002351620 00000 n 0002796404 00000 n 0002353957 00000 n 0002351757 00000 n 0002353886 00000 n 0002796612 00000 n 0002355849 00000 n 0002354023 00000 n 0002355789 00000 n 0002796724 00000 n 0002356150 00000 n 0002355915 00000 n 0002356114 00000 n 0002796836 00000 n 0002357807 00000 n 0002356216 00000 n 0002357686 00000 n 0002797044 00000 n 0002360691 00000 n 0002357873 00000 n 0002360594 00000 n 0002797156 00000 n 0002363517 00000 n 0002360757 00000 n 0002363420 00000 n 0002797268 00000 n 0002366358 00000 n 0002363583 00000 n 0002366261 00000 n 0002797476 00000 n 0002369299 00000 n 0002366424 00000 n 0002369202 00000 n 0002797588 00000 n 0002370921 00000 n 0002369365 00000 n 0002370824 00000 n 0002797700 00000 n 0002372154 00000 n 0002370987 00000 n 0002372073 00000 n 0002709526 00000 n 0002687904 00000 n 0002682444 00000 n 0002681138 00000 n 0002681538 00000 n 0002681946 00000 n 0002682354 00000 n 0002684226 00000 n 0002682874 00000 n 0002683293 00000 n 0002683713 00000 n 0002684133 00000 n 0002686012 00000 n 0002684659 00000 n 0002685079 00000 n 0002685499 00000 n 0002685919 00000 n 0002687798 00000 n 0002686445 00000 n 0002686865 00000 n 0002687285 00000 n 0002687705 00000 n 0002695037 00000 n 0002689573 00000 n 0002688228 00000 n 0002688640 00000 n 0002689060 00000 n 0002689480 00000 n 0002691359 00000 n 0002690006 00000 n 0002690426 00000 n 0002690846 00000 n 0002691266 00000 n 0002693145 00000 n 0002691792 00000 n 0002692212 00000 n 0002692632 00000 n 0002693052 00000 n 0002694931 00000 n 0002693578 00000 n 0002693998 00000 n 0002694418 00000 n 0002694838 00000 n 0002702170 00000 n 0002696706 00000 n 0002695361 00000 n 0002695773 00000 n 0002696193 00000 n 0002696613 00000 n 0002698492 00000 n 0002697139 00000 n 0002697559 00000 n 0002697979 00000 n 0002698399 00000 n 0002700278 00000 n 0002698925 00000 n 0002699345 00000 n 0002699765 00000 n 0002700185 00000 n 0002702064 00000 n 0002700711 00000 n 0002701131 00000 n 0002701551 00000 n 0002701971 00000 n 0002709420 00000 n 0002703956 00000 n 0002702603 00000 n 0002703023 00000 n 0002703443 00000 n 0002703863 00000 n 0002705742 00000 n 0002704389 00000 n 0002704809 00000 n 0002705229 00000 n 0002705649 00000 n 0002707528 00000 n 0002706175 00000 n 0002706595 00000 n 0002707015 00000 n 0002707435 00000 n 0002709314 00000 n 0002707961 00000 n 0002708381 00000 n 0002708801 00000 n 0002709221 00000 n 0002738981 00000 n 0002716791 00000 n 0002711193 00000 n 0002709848 00000 n 0002710260 00000 n 0002710680 00000 n 0002711100 00000 n 0002713017 00000 n 0002711628 00000 n 0002712057 00000 n 0002712489 00000 n 0002712921 00000 n 0002714851 00000 n 0002713459 00000 n 0002713891 00000 n 0002714323 00000 n 0002714755 00000 n 0002716685 00000 n 0002715293 00000 n 0002715725 00000 n 0002716157 00000 n 0002716589 00000 n 0002724112 00000 n 0002718504 00000 n 0002717121 00000 n 0002717544 00000 n 0002717976 00000 n 0002718408 00000 n 0002720338 00000 n 0002718946 00000 n 0002719378 00000 n 0002719810 00000 n 0002720242 00000 n 0002722172 00000 n 0002720780 00000 n 0002721212 00000 n 0002721644 00000 n 0002722076 00000 n 0002724006 00000 n 0002722614 00000 n 0002723046 00000 n 0002723478 00000 n 0002723910 00000 n 0002731433 00000 n 0002725825 00000 n 0002724442 00000 n 0002724865 00000 n 0002725297 00000 n 0002725729 00000 n 0002727659 00000 n 0002726267 00000 n 0002726699 00000 n 0002727131 00000 n 0002727563 00000 n 0002729493 00000 n 0002728101 00000 n 0002728533 00000 n 0002728965 00000 n 0002729397 00000 n 0002731327 00000 n 0002729935 00000 n 0002730367 00000 n 0002730799 00000 n 0002731231 00000 n 0002738875 00000 n 0002733267 00000 n 0002731875 00000 n 0002732307 00000 n 0002732739 00000 n 0002733171 00000 n 0002735101 00000 n 0002733709 00000 n 0002734141 00000 n 0002734573 00000 n 0002735005 00000 n 0002736935 00000 n 0002735543 00000 n 0002735975 00000 n 0002736407 00000 n 0002736839 00000 n 0002738769 00000 n 0002737377 00000 n 0002737809 00000 n 0002738241 00000 n 0002738673 00000 n 0002768490 00000 n 0002746300 00000 n 0002740692 00000 n 0002739309 00000 n 0002739732 00000 n 0002740164 00000 n 0002740596 00000 n 0002742526 00000 n 0002741134 00000 n 0002741566 00000 n 0002741998 00000 n 0002742430 00000 n 0002744360 00000 n 0002742968 00000 n 0002743400 00000 n 0002743832 00000 n 0002744264 00000 n 0002746194 00000 n 0002744802 00000 n 0002745234 00000 n 0002745666 00000 n 0002746098 00000 n 0002753621 00000 n 0002748013 00000 n 0002746630 00000 n 0002747053 00000 n 0002747485 00000 n 0002747917 00000 n 0002749847 00000 n 0002748455 00000 n 0002748887 00000 n 0002749319 00000 n 0002749751 00000 n 0002751681 00000 n 0002750289 00000 n 0002750721 00000 n 0002751153 00000 n 0002751585 00000 n 0002753515 00000 n 0002752123 00000 n 0002752555 00000 n 0002752987 00000 n 0002753419 00000 n 0002760942 00000 n 0002755334 00000 n 0002753951 00000 n 0002754374 00000 n 0002754806 00000 n 0002755238 00000 n 0002757168 00000 n 0002755776 00000 n 0002756208 00000 n 0002756640 00000 n 0002757072 00000 n 0002759002 00000 n 0002757610 00000 n 0002758042 00000 n 0002758474 00000 n 0002758906 00000 n 0002760836 00000 n 0002759444 00000 n 0002759876 00000 n 0002760308 00000 n 0002760740 00000 n 0002768384 00000 n 0002762776 00000 n 0002761384 00000 n 0002761816 00000 n 0002762248 00000 n 0002762680 00000 n 0002764610 00000 n 0002763218 00000 n 0002763650 00000 n 0002764082 00000 n 0002764514 00000 n 0002766444 00000 n 0002765052 00000 n 0002765484 00000 n 0002765916 00000 n 0002766348 00000 n 0002768278 00000 n 0002766886 00000 n 0002767318 00000 n 0002767750 00000 n 0002768182 00000 n 0002798120 00000 n 0002775809 00000 n 0002770201 00000 n 0002768818 00000 n 0002769241 00000 n 0002769673 00000 n 0002770105 00000 n 0002772035 00000 n 0002770643 00000 n 0002771075 00000 n 0002771507 00000 n 0002771939 00000 n 0002773869 00000 n 0002772477 00000 n 0002772909 00000 n 0002773341 00000 n 0002773773 00000 n 0002775703 00000 n 0002774311 00000 n 0002774743 00000 n 0002775175 00000 n 0002775607 00000 n 0002783251 00000 n 0002777643 00000 n 0002776251 00000 n 0002776683 00000 n 0002777115 00000 n 0002777547 00000 n 0002779477 00000 n 0002778085 00000 n 0002778517 00000 n 0002778949 00000 n 0002779381 00000 n 0002781311 00000 n 0002779919 00000 n 0002780351 00000 n 0002780783 00000 n 0002781215 00000 n 0002783145 00000 n 0002781753 00000 n 0002782185 00000 n 0002782617 00000 n 0002783049 00000 n 0002790572 00000 n 0002784964 00000 n 0002783581 00000 n 0002784004 00000 n 0002784436 00000 n 0002784868 00000 n 0002786798 00000 n 0002785406 00000 n 0002785838 00000 n 0002786270 00000 n 0002786702 00000 n 0002788632 00000 n 0002787240 00000 n 0002787672 00000 n 0002788104 00000 n 0002788536 00000 n 0002790466 00000 n 0002789074 00000 n 0002789506 00000 n 0002789938 00000 n 0002790370 00000 n 0002798014 00000 n 0002792406 00000 n 0002791014 00000 n 0002791446 00000 n 0002791878 00000 n 0002792310 00000 n 0002794240 00000 n 0002792848 00000 n 0002793280 00000 n 0002793712 00000 n 0002794144 00000 n 0002796074 00000 n 0002794682 00000 n 0002795114 00000 n 0002795546 00000 n 0002795978 00000 n 0002797908 00000 n 0002796516 00000 n 0002796948 00000 n 0002797380 00000 n 0002797812 00000 n 0002798513 00000 n 0002798537 00000 n 0002798560 00000 n trailer << /Size 3819 /Root 2 0 R /Info 1 0 R >> startxref 2798685 %%EOF ocaml-book-1.0/en/main.html0000644000000000000000000001044007455136117012507 0ustar Developing applications with Objective Caml


This is a preliminary translation of the book Dveloppement d'applications avec Objective Caml by Emmanuel Chailloux, Pascal Manoury and Bruno Pagano, published by O'Reilly France.

Debian note: Please note that this english translation is only a draft version. If you would help translators the better you can do is to let they know you are reading this draft version. You can directly contact them at translators@pauillac.inria.fr or contact me instead.

The translation is available in several formats:

  • HTML for online reading.

  • PDF in one big file (3 megabytes).

The translation is not entirely finished yet; in particular, some solutions to exercises have not been translated yet, and proofreading is not completed. Work continues towards a definitive translation, but we hope that this early release of the translation can be useful.

If you notice obvious spelling or grammatical errors, please e-mail translators@pauillac.inria.fr.

This translation is the result of a collaborative effort involving about 60 volunteers worldwide. Their names are listed below. We thank Emmanuel Chailloux, Pascal Manoury and Bruno Pagano for providing us the complete LaTeX sources to the French original (and for having written it in the first place!), and O'Reilly France for their permission to distribute this translation, and their interest in this project.

Translators:
        Francisco Valverde Albacete        Joshua D. Guttman    
        Mark Andrew                        Theo Honohan         
        Martin Anlauf                      Jerzy Karczmarczuk   
        Christopher B. Browne              Xavier Leroy         
        David Casperson                    Markus Mottl         
        Gang Chen                          Charles Neveu        
        Harry Chomsky                      Tim Perkis           
        Ruchira Datta                      Alan Schmitt         
        Seth Delackner                     Paul Steckler        
        Patrick Doane                      Perdita Stevens      
        Andreas Eder                       Franois Thomasset   
        Manuel Fahndrich                   Hans-Joerg Tiede     
Project coordination:
        Ruchira Datta
        Joshua D. Guttman
        Benjamin C. Pierce
Proofreaders:
        Julian Assange                     Christopher League      
        Robert Bauer                       Ronald Legere           
        Will Benton                        Matthew W. Markland     
        Antony Courtney                    Markus Mottl            
        Thomas Valentino Crimi             Eric Merritt            
        Scott Cyphers                      William D. Neumann      
        Laura Dean                         John Prevost            
        Ken Dyck                           Brian Rogoff            
        Miles Egan                         Ken Rose                
        Jeremy Fincher                     George Richard Russell  
        Eric Frias                         Anders Selander         
        Brent Fulgham                      Rafael 'Dido' Sevilla   
        Jason Gibson                       Mark Shure              
        Dan Grossman                       John Max Skaller        
        John Heron                         Matt Sottile            
        Garry Hodgson                      Jason Voegele           
        Samin Ishtiaq                      Don Wakefield           
        Jeffrey Katcher                    Russle Brock Wilcox     
        Doug Landauer 
System administration:
        Xavier Leroy
ocaml-book-1.0/en/html/0000755000000000000000000000000007666671561011655 5ustar ocaml-book-1.0/en/html/book-ora161.html0000644000000000000000000017535507453055400014502 0ustar Fancy Robots Previous Contents Next

Fancy Robots

The example in this section illustrates the use of objects from the graphics library. We will revisit the concepts of simple inheritance, overriding methods and dynamic dispatch. We also see how parametric classes may be profitably used.

The application recognizes two principal categories of objects: a world and robots. The world represents a state space within which the robots evolve. We will have various classes of robots, each possessing its own strategy to move around in the world.

The principle of interaction between robots and the world here is extremely simple. The world is completely in control of the game: it asks, turn by turn, each of the robots if they know their next position. Each robot determines its next position fairly blindly. They do not know the geometry of the world, nor what other robots may be present. If the position requested by a robot is legal and open, the world will place the robot at that position.

The world displays the evolution of the robots via an interface. The (relative) complexity of the conception and development of this example is in the always-necessary separation between a behavior (here the evolution of the robots) and its interface (here the tracking of this evolution).

General Description
The application is developed in two stages.
  1. A group of definitions providing pure calculation classes for the world and for the diverse set of envisaged robots.

  2. A group of definitions using the preceding set, adding whatever is necessary to add in an interface.

    We provide two examples of such interfaces: a rudimentary text-based interface, and a more elaborate one using a graphical library.
In the first section, we provide the abstract definitions for the robots. Then (page ??), we provide the pure abstract definition for the world. In the next section (page ??), we introduce the text interface for the robots, and in the fourth section (page ??), the interface for the world. On page ?? we introduce a graphical interface for the robots and finally (page ??) we define a world for the graphical interface.

``Abstract'' Robots

The first thing to do is to examine robots abstractly, independent of any consideration of the environment in which they will move, that is to say, the interface that displays them.


# class virtual robot (i0:int) (j0:int) =
object
val mutable i = i0
val mutable j = j0
method get_pos = (i,j)
method set_pos (i', j') = i <- i'; j <- j'
method virtual next_pos : unit -> (int * int)
end ;;


A robot is an entity which knows, or believes it knows, its position (i and j), is capable of communicating that position to a requester (get_pos), is able to modify this knowledge if it knows precisely where it should be (set_pos) and may decide to move towards a new position (next_pos).




Figure 17.9: Hierarchy of pure robot classes


To improve the readability of the program, we define relative movements based on absolute directions:


# type dir = North | East | South | West | Nothing ;;

# let walk (x,y) = function
North -> (x,y+1) | South -> (x,y-1)
| West -> (x-1,y) | East -> (x+1,y)
| Nothing -> (x,y) ;;
val walk : int * int -> dir -> int * int = <fun>

# let turn_right = function
North -> East | East -> South | South -> West | West -> North | x -> x ;;
val turn_right : dir -> dir = <fun>


The schema is shown by the virtual class robots from which we define four distinct species of robots (see figure 17.9) to more precisely see their manner of motion:

  • Fixed robots which never move:

    # class fix_robot i0 j0 =
    object
    inherit robot i0 j0
    method next_pos() = (i,j)
    end ;;


  • Crazy robots which move at random:

    # class crazy_robot i0 j0 =
    object
    inherit robot i0 j0
    method next_pos () = ( i+(Random.int 3)-1 , j+(Random.int 3)-1 )
    end ;;


  • Obstinate robots which keep trying to advance in one direction whenever they are able to do so,


    # class obstinate_robot i0 j0 =
    object(self)
    inherit robot i0 j0
    val mutable wanted_pos = (i0,j0)
    val mutable dir = West

    method private set_wanted_pos d = wanted_pos <- walk (i,j) d
    method private change_dir = dir <- turn_right dir
    method next_pos () = if (i,j) = wanted_pos
    then let np = walk (i,j) dir in ( wanted_pos <- np ; np )
    else ( self#change_dir ; wanted_pos <- (i,j) ; (i,j) )
    end ;;


  • Interactive robots which obey the commands of an exterior operator:


    # class virtual interactive_robot i0 j0 =
    object(self)
    inherit robot i0 j0
    method virtual private get_move : unit -> dir
    method next_pos () = walk (i,j) (self#get_move ())
    end ;;
The case of the interactive robot is different from the others in that its behavior is controlled by an interface that permits communicating orders to it. To deal with this, we provide a virtual method to communicate this order. As a consequence, the class interactive_robot remains abstract.

Note that not only do the four specialized robot classes inherit from class robot, but also any others that have the same type. In effect, the only methods that we have added are the private methods that therefore do not appear in the type signatures of the instances of these classes (see page ??). This property is indispensable if we wish to consider all the robots to be objects of the same type.

Pure World

A pure world is a world that is independent of an interface. It is understood as the state space of positions which a robot may occupy. It takes the form of a grid of size l h, with a method is_legal to assure that a coordinate is a valid position in the world, and a method is_free indicates whether or not a robot occupies a given position.

In practice, a world manages the list of robots present on its surface while a method, add, allows new robots to enter the world.

Finally, a world is made visible by the method run, allowing the world to come to life.


# class virtual ['robot_type] world (l0:int) (h0:int) =
object(self)
val l = l0
val h = h0
val mutable robots = ( [] : 'robot_type list )
method add r = robots <- r::robots
method is_free p = List.for_all (fun r -> r#get_pos <> p) robots
method virtual is_legal : (int * int) -> bool

method private run_robot r =
let p = r#next_pos ()
in if (self#is_legal p) & (self#is_free p) then r#set_pos p

method run () =
while true do List.iter (function r -> self#run_robot r) robots done
end ;;
class virtual ['a] world :
int ->
int ->
object
constraint 'a =
< get_pos : int * int; next_pos : unit -> int * int;
set_pos : int * int -> unit; .. >
val h : int
val l : int
val mutable robots : 'a list
method add : 'a -> unit
method is_free : int * int -> bool
method virtual is_legal : int * int -> bool
method run : unit -> unit
method private run_robot : 'a -> unit
end


The Objective CAML type system does not permit leaving the types of robots undetermined (see page ??). To resolve this problem, we might consider restraining the type to those of the class robot. But that would forbid populating a world with objects other than those having exactly the same type as robot. As a result, we have instead decided to parameterize world with the type of the robots that populate it. We may thereby instantiate this type parameter with textual robots or graphical robots.

Textual Robots

Text Objects
To obtain robots controllable via a textual interface, we define a class of text objects (txt_object).


# class txt_object (s0:string) =
object
val name = s0
method get_name = name
end ;;


An Interface Class: Abstract Textual Robots
By double inheritance from robots and txt_object, we obtain the abstract class txt_robot of textual robots.


# class virtual txt_robot i0 j0 =
object
inherit robot i0 j0
inherit txt_object "Anonymous"
end ;;
class virtual txt_robot :
int ->
int ->
object
val mutable i : int
val mutable j : int
val name : string
method get_name : string
method get_pos : int * int
method virtual next_pos : unit -> int * int
method set_pos : int * int -> unit
end


This class defines a world with a textual interface (see page ??). The inhabitants of this world will not be objects of txt_robot (since this class is abstract) nor inheritors of this class. The class txt_robot is, in a way, an interface classe permitting the compiler to identify the method types (calculations and interfaces) of the inhabitants of the text interface world. The use of such a specification class provides the separation we wish to maintain between calculations and interface.

Concrete Text Robots
These are simply obtained via double inheritance; figure 17.10 shows the hierarchy of classes.




Figure 17.10: Hierarchy of classes for text mode robots



# class fix_txt_robot i0 j0 =
object
inherit fix_robot i0 j0
inherit txt_object "Fix robot"
end ;;

# class crazy_txt_robot i0 j0 =
object
inherit crazy_robot i0 j0
inherit txt_object "Crazy robot"
end ;;

# class obstinate_txt_robot i0 j0 =
object
inherit obstinate_robot i0 j0
inherit txt_object "Obstinate robot"
end ;;


The interactive robots require, for a workable implementation, defining their method of interacting with the user.


# class interactive_txt_robot i0 j0 =
object
inherit interactive_robot i0 j0
inherit txt_object "Interactive robot"
method private get_move () =
print_string "Which dir : (n)orth (e)ast (s)outh (w)est ? ";
match read_line() with
"n" -> North | "s" -> South
| "e" -> East | "w" -> West
| _ -> Nothing
end ;;


Textual World

The text interface world is derived from the pure world by:
  1. Inheritance from the generic class world by instantiating its type parameter with the class specified by txt_robot, and

  2. Redefinition of the method run to include the different textual methods.

# class virtual txt_world (l0:int) (h0:int) =
object(self)
inherit [txt_robot] world l0 h0 as super

method private display_robot_pos r =
let (i,j) = r#get_pos in Printf.printf "(%d,%d)" i j

method private run_robot r =
let p = r#next_pos ()
in if (self#is_legal p) & (self#is_free p)
then
begin
Printf.printf "%s is moving from " r#get_name ;
self#display_robot_pos r ;
print_string " to " ;
r#set_pos p;
self#display_robot_pos r ;
end
else
begin
Printf.printf "%s is staying at " r#get_name ;
self#display_robot_pos r
end ;
print_newline () ;
print_string"next - ";
ignore (read_line())

method run () =
let print_robot r =
Printf.printf "%s is at " r#get_name ;
self#display_robot_pos r ;
print_newline ()
in
print_string "Initial state :\n";
List.iter print_robot robots;
print_string "Running :\n";
super#run() (* 1 *)
end ;;


We direct the reader's attention to the call to run of the ancestor class (this method call is marked (* 1 *) in the code) in the redefinition of the same method. There we have an illustration of the two possible types of method dispatch: static or dynamic (see page ??). The call to super#run is static. This is why we name the superclass: to be able to call the methods when they are redefined. On the other hand, in super#run we find a call to self#run_robot. This is a dynamic dispatch; the method defined in class txt_world is executed, not that of world. Were the method from world executed, nothing would be displayed, and the method in txt_world would remain useless.

The planar rectangular text world
is obtained by implementing the final method that still remains abstract: is_legal.


# class closed_txt_world l0 h0 =
object(self)
inherit txt_world l0 h0
method is_legal (i,j) = (0<=i) & (i<l) & (0<=j) & (j<h)
end ;;





Figure 17.11: Hierarchy of classes in the textual planar rectangular world


We may proceed with a small essay in typing:

let w = new closed_txt_world 5 5
and r1 = new fix_txt_robot 3 3
and r2 = new crazy_txt_robot 2 2
and r3 = new obstinate_txt_robot 1 1
and r4 = new interactive_txt_robot 0 0
in w#add r1; w#add r2; w#add r3; w#add r4; w#run () ;;


We may skip, for the moment, the implementation of a graphical interface for our world of robots. In due course, we will obtain an application having an appearance like figure 17.12.




Figure 17.12: The graphical world of robots


Graphical Robots

We may implement robots in a graphical mode by following the same approach as with the text mode:
  1. define a generic graphical object,
  2. define an abstract class of graphical robots by double inheritance from robots and graphical objects (analogous to the interface class of page ??),
  3. define, through double inheritance, the particular behavior of robots.

Generic Graphical Objects

A simple graphical object is an object possessing a display method which takes, as arguments, the coordinates of a pixel and displays it.


# class virtual graph_object =
object
method virtual display : int -> int -> unit
end ;;


From this specification, it would be possible to implement graphical objects with extremely complex behavior. We will content ourselves for now with a class graph_item, displaying a bitmap that serves to represent the object.


# class graph_item x y im =
object (self)
val size_box_x = x
val size_box_y = y
val bitmap = im
val mutable last = None

method private erase = match last with
Some (x,y,img) -> Graphics.draw_image img x y
| None -> ()

method private draw i j = Graphics.draw_image bitmap i j
method private keep i j =
last <- Some (i,j,Graphics.get_image i j size_box_x size_box_y) ;

method display i j = match last with
Some (x,y,img) -> if x<>i || y<>j
then ( self#erase ; self#keep i j ; self#draw i j )
| None -> ( self#keep i j ; self#draw i j )
end ;;


An object of graph_item stores the portion of the image upon which it is drawn in order to restore it in subsequent redraws. In addition, if the image has not been moved, it will not be redrawn.


# let foo_bitmap = [|[| Graphics.black |]|] ;;
# class square_item x col =
object
inherit graph_item x x (Graphics.make_image foo_bitmap)
method private draw i j =
Graphics.set_color col ;
Graphics.fill_rect (i+1) (j+1) (x-2) (x-2)
end ;;

# class disk_item r col =
object
inherit graph_item (2*r) (2*r) (Graphics.make_image foo_bitmap)
method private draw i j =
Graphics.set_color col ;
Graphics.fill_circle (i+r) (j+r) (r-2)
end ;;

# class file_bitmap_item name =
let ch = open_in name
in let x = Marshal.from_channel ch
in let y = Marshal.from_channel ch
in let im = Marshal.from_channel ch
in let () = close_in ch
in object
inherit graph_item x y (Graphics.make_image im)
end ;;


We specialize the graph_item with instances of crosses, disks, and other bitmaps, read from a file.

The abstract graphical robot
is both a robot and a graphical object.


# class virtual graph_robot i0 j0 =
object
inherit robot i0 j0
inherit graph_object
end ;;


Graphical robots that are fixed, crazy, and obstinate
are specialized graphical objects.


# class fix_graph_robot i0 j0 =
object
inherit fix_robot i0 j0
inherit disk_item 7 Graphics.green
end ;;

# class crazy_graph_robot i0 j0 =
object
inherit crazy_robot i0 j0
inherit file_bitmap_item "crazy_bitmap"
end ;;

# class obstinate_graph_robot i0 j0 =
object
inherit obstinate_robot i0 j0
inherit square_item 15 Graphics.black
end ;;


The interactive graphical robot
uses the primitives key_pressed and read_key of module Graphics to determine its next move. We again see the key presses 8, 6, 2 and 4 on the numeric keypad (NumLock button active). In this manner, the user is not obliged to provide direction at each step in the simulation.


# class interactive_graph_robot i0 j0 =
object
inherit interactive_robot i0 j0
inherit file_bitmap_item "interactive_bitmap"
method private get_move () =
if not (Graphics.key_pressed ()) then Nothing
else match Graphics.read_key() with
'8' -> North | '2' -> South | '4' -> West | '6' -> East | _ -> Nothing
end ;;


Graphical World

We obtain a world with a graphical interface by inheriting from the pure world, instantiating the parameter 'a_robot with the graphical robot abstract class graph_robot. As with the text mode world, the graphical world provides its own method, run_robot, to implement the robot's behavior as well as the general activation method run.


# let delay x = let t = Sys.time () in while (Sys.time ()) -. t < x do () done ;;

# class virtual graph_world l0 h0 =
object(self)
inherit [graph_robot] world l0 h0 as super
initializer
let gl = (l+2)*15 and gh = (h+2)*15 and lw=7 and cw=7
in Graphics.open_graph (" "^(string_of_int gl)^"x"^(string_of_int gh)) ;
Graphics.set_color (Graphics.rgb 170 170 170) ;
Graphics.fill_rect 0 lw gl lw ;
Graphics.fill_rect (gl-2*lw) 0 lw gh ;
Graphics.fill_rect 0 (gh-2*cw) gl cw ;
Graphics.fill_rect lw 0 lw gh

method run_robot r = let p = r#next_pos ()
in delay 0.001 ;
if (self#is_legal p) & (self#is_free p)
then ( r#set_pos p ; self#display_robot r)

method display_robot r = let (i,j) = r#get_pos
in r#display (i*15+15) (j*15+15)

method run() = List.iter self#display_robot robots ;
super#run()
end ;;


Note that the graphical window is created at the time that an object of this class is initialized.

The rectangular planar graphical world
is obtained in much the same manner as with the rectangular planar textual world.


# class closed_graph_world l0 h0 =
object(self)
inherit graph_world l0 h0
method is_legal (i,j) = (0<=i) & (i<l) & (0<=j) & (j<h)
end ;;
class closed_graph_world :
int ->
int ->
object
val h : int
val l : int
val mutable robots : graph_robot list
method add : graph_robot -> unit
method display_robot : graph_robot -> unit
method is_free : int * int -> bool
method is_legal : int * int -> bool
method run : unit -> unit
method run_robot : graph_robot -> unit
end


We may then test the graphical application by typing in:


let w = new closed_graph_world 10 10 ;;
w#add (new fix_graph_robot 3 3) ;;
w#add (new crazy_graph_robot 2 2) ;;
w#add (new obstinate_graph_robot 1 1) ;;
w#add (new interactive_graph_robot 5 5) ;;
w#run () ;;


To Learn More

The implementation of the method run_robot in different worlds suggests that the robots are potentially able to move to any point on the world the moment it is empty and legal. Unfortunately, nothing prevents a robot from modifying its position arbitrarily; the world cannot prevent it. One remedy would consist of having robot positions being controlled by the world; when a robot attempts to move, the world verifies not only that the new position is legal, but also that it constitutes an authorized move. In that case, the robot must be capable of asking the world its actual position, with the result that the robot class must become dependent on the world's class. The robot class would take, as a type parameter, the world class.

This modification permits defining robots capable of querying the world in which they run, thus behaving as dependents of the world. We may then implement robots which follow or avoid other robots, try to block them, and so forth.

Another extension would be to permit robots to communicate with one another, exchanging information, perhaps constituting themselves into teams of robots.

The chapters of the next section allow making execution of robots independent from one another: by making use of Threads (see page ??), each may execute as a distinct process. They may profit from the possibilities of distributed computing (see ??) where the robots become clients executing on remote machines that announce their movements or request other information from a world that behaves as a server. This problem is dealt with on page ??.






Previous Contents Next ocaml-book-1.0/en/html/book-ora177.html0000644000000000000000000002450107453055400014473 0ustar Synchronous Communication Previous Contents Next

Synchronous Communication

Module Event from the thread library implements the communication of assorted values between two processes through particular ``communication channels''. The effective communication of the value is synchronized through send and receive events.

This model of communication synchronized by events allows the transfer through typed channels of the values of the language, including closures, objects, and events.

It is described in [Rep99].

Synchronization using Communication Events

The primitive communication events are:
  • send c v sends a value v on the channel c;
  • receive c receives a value on the channel c
So as to implement the physical action with which they are associated, two events should be synchronized. For this purpose, we introduce an operation of synchronization (sync) on events. The sending and receiving of a value are not effective unless the two communicating processes are in phase. If a single process wishes to synchronize itself, the operation gets blocked, waiting for the second process to perform its synchronization. This implies that a sender wishing to synchronize the sending of a value (sync (send c v)) can find itself blocked waiting for a synchronization from a receiver (sync (receive c)).

Transmitted Values

The communication channels through which the exchanged values travel are typed: Nothing prevents us from creating multiple channels for communicating each type of value. As this communication takes place between Objective CAML threads, any value of the language can be sent on a channel of the same type. This is useful for closures, objects, and also events, for a ``relayed'' synchronization request.

Module Event

The values encapsulated in communication events travel through communication channels of the abstract data type 'a channel. The creation function for channels is:

# Event.new_channel ;;
- : unit -> 'a Event.channel = <fun>


Send and receive events are created by a function call:

# Event.send ;;
- : 'a Event.channel -> 'a -> unit Event.event = <fun>
# Event.receive ;;
- : 'a Event.channel -> 'a Event.event = <fun>


We can consider the functions send and receive as constructors of the abstract type 'a event. The event constructed by send does not preserve the information about the type of the value to transmit (type unit Event.event). On the other hand, the receive event takes account of it to recover the value during a synchronization. These functions are non-blocking in the sense that the transmission of a value does not take place until the time of the synchronization of two processes by the function:

# Event.sync ;;
- : 'a Event.event -> 'a = <fun>
This function may be blocking for the sender and the receiver.

There is a non-blocking version:

# Event.poll ;;
- : 'a Event.event -> 'a option = <fun>


This function verifies that another process is waiting for synchronization.

If this is the case, it performs the transmissions, and returns the value Some v, if v is the value associated with the event, and None otherwise. The received message, extracted by the function sync, can be the result of a more or less complicated process, triggering other exchanges of messages.

Example of synchronization.
We define three threads. The first, t1, sends a chain of characters on channel c (function g) shared by all the processes. The two others t2 and t3 wait for a value on the same channel. Here are the functions executed by the different processes:


# let c = Event.new_channel ();;
val c : '_a Event.channel = <abstr>
# let f () =
let ids = string_of_int (Thread.id (Thread.self ()))
in print_string ("-------- before -------" ^ ids) ; print_newline() ;
let e = Event.receive c
in print_string ("-------- during -------" ^ ids) ; print_newline() ;
let v = Event.sync e
in print_string (v ^ " " ^ ids ^ " ") ;
print_string ("-------- after -------" ^ ids) ; print_newline() ;;
val f : unit -> unit = <fun>
# let g () =
let ids = string_of_int (Thread.id (Thread.self ()))
in print_string ("Start of " ^ ids ^ "\n");
let e2 = Event.send c "hello"
in Event.sync e2 ;
print_string ("End of " ^ ids) ;
print_newline () ;;
val g : unit -> unit = <fun>


The three processes are created and executed:

# let t1,t2,t3 = Thread.create f (), Thread.create f (), Thread.create g ();;
val t1 : Thread.t = <abstr>
val t2 : Thread.t = <abstr>
val t3 : Thread.t = <abstr>
# Thread.delay 1.0;;
Start of 5
-------- before -------6
-------- during -------6
hello 6 -------- after -------6
-------- before -------7
-------- during -------7
End of 5
- : unit = <unknown constructor>


The transmission may block. The trace of t1 is displayed after the synchronization traces of t2 and t3. Only one of the two processes t1 or t2 is really terminated, as the following calls show:

# Thread.kill t1;;
- : unit = ()
# Thread.kill t2;;
Uncaught exception: Failure("Thread.kill: killed thread")



Previous Contents Next ocaml-book-1.0/en/html/videoc.js0000644000000000000000000004445707666671561013502 0ustar /* $Id: videoc.js,v 1.1.1.1 2000/06/26 14:37:09 xleroy Exp $ * The JavaScript runtime library for VideoC. * * FUTURE adapt messages to the preferred language. * Make a hint popup above the nailed hints. * Make a popup with the usage if not used for some duration. */ window.videoc_version = ' $Id: videoc.js,v 1.1.1.1 2000/06/26 14:37:09 xleroy Exp $ '; // Check if the browser is known and at least at version 4 for Communicator and // IExplorer. The results are stored in the window object. { var ua = window.navigator.userAgent; // document.write("" + ua + ""); // For IE4.0, this gives Mozilla/4.0(compatible; MSIE 4.0;Windows 95) // For Communicator 4 on Windows95: Mozilla/4.02 [en] (Win95; I) // and on linux: Mozilla/4.04 [en] (X11; I; Linux 2.0.30 i586) // for Netscape on DECalpha: Mozilla/3.0 (X11; I; OSF1 V4.0 alpha) var tag = "MSIE "; var uai = ua.indexOf(tag); window.IExplorer = false; window.Mozilla = false; if ( uai > 0 ) { window.IExplorer = true; // This is IExplorer, fetch version number: window.version = parseInt(ua.substring(uai+tag.length, ua.indexOf(".", uai))); // document.write("\n\n IExplorer " + window.version + "\n\n"); // alert("\n\n IExplorer " + window.version + "\n\n"); } else { tag = "ozilla"; uai = ua.indexOf(tag); if ( uai > 0 ) { // This is Mozilla, fetch version number: window.Mozilla = true; window.version = parseInt(ua.substring(uai+tag.length+1, ua.indexOf(".", uai))); // document.write("\n\n Mozilla " + window.version + "\n\n"); // alert("\n\n Mozilla " + window.version + "\n\n"); } else { // Unknown browser alert("Votre butineur (" + ua + ") m'est inconnu. " + "Ce document souffrira de ne pas tre vu avec Communicator " + "ou IExplorer (version >= 4 tous les deux)."); } } } //{{{ // MOCHE // This height must be set up accordingly to the nav bar: var navBarHeight = 130; //}}} //{{{ Common stuff // To inspect an object: confirm(object_to_string(...)) function object_to_string(obj) { var result = ""; for (var i in obj) { result += "[." + i + " = " + obj[i] + "]"; if ( window.Mozilla ) { result += "\n"; } } return result; } // It is possible to store information from page to page within the // browser in the navigator object. All the information is stored in // an object. if ( typeof navigator.VideoC == "undefined" ) { navigator.VideoC = new Object(); }; // This function should be called whenever a page containing a searched // word is displayed and we want the word to be hightlighted. function afterWordSearchSubmission() { alert("pre-ok"); addHook("alert(\"post-ok\")"); } function addHook(expression) { navigator.VideoC.hook = expression; } // This function toggles the state of all the hints of the page. It // chooses at random some sensitive anchors and display their // associated hints. This may be interesting when to make users aware // of that possibility or to awake them. function show_hints_at_random (delay) { if ( delay > 0 ) { // sleep delay seconds } var i = random(document.hints.length); var anchor = document.hints[i][0]; // Create a pseudo event: use a method on hint instead ? var event = new Object; event.type = 'click'; event.target = new Object; event.target.hint = anchor; event.srcElement = event.target; toggleHint(event); } // a simple random function (see Knuth vol2, section 3.2.1.2 ex 1) var seed = 5772156648; function random (n) { var a = 3141592621; var c = 2718281829; var m = 10000000000; seed = (a * seed + c)%m; return seed%n; } // Make a message appear in the status bar. function showMessage (msg) { window.status = msg; } // Make a special message appear in the status bar. This is a // specialized function since a call to it appears in every // mouse-sensitive areas inserted by the annote tool. function ctsm() { showMessage("Click to see more"); } //}}} ------------------------------------------------------------------- //{{{ Netscape versions: // Makes the hint pop up until the mouse leaves the sensitive area. // A click makes the popup pinned down until another click. Show // the usage in the status zone of the window. function nc_toggleHint (e) { // receive the anchor as the target of the event then // retrieve the associated hint. var hint = e.target.hint; //confirm("Received event " + object_to_string(e.target.hint)); // DEBUG // Toggle the state of the hint: if ( hint.visibility == 'hidden' || hint.visibility == 'hide' ) { // Needed by netscape to prevent a right-ragged layer: //hint.bgColor = '#f5f5dc'; if ( ! hint.immotile ) { var hintX = e.layerX/2; var hintY = e.layerY+20; hint.moveTo(hintX, hintY); } hint.visibility = 'show'; window.status = 'Click to nail the hint'; } else { if ( hint.permanent ) { window.status = 'Click to remove the hint'; } else { hint.visibility = 'hidden'; } } // Click means: show/hide the hint permanently: if ( e.type == 'click' ) { hint.permanent = ! hint.permanent; if ( hint.permanent ) { window.status = 'Click to remove the hint'; } else { window.status = 'Removed!'; } } // Don't let these links be followed, they're not real anchors: return true; } // Link the sensitive area (ie the hint) from the anchor. A hint // may be reached from many anchors. function nc_initializeHint(hintId) { var hint = document.layers['l__' + hintId]; if ( hint == null ) return alert("Missing hint " + hintId); hint.visibility == 'hidden'; hint.permanent = false; hint.immotile = false; hint.name = hintId; var matches = 0; for (var i = 0 ; i < document.links.length ; i++ ) { var anchor = document.links[i]; //confirm("anchor[" + i + "]=" + object_to_string(anchor)); // DEBUG if ( anchor.protocol == 'javascript:' && anchor.pathname.indexOf(hintId)>=0 ) { matches++; nc_initializeHintWithAnchor(hint, anchor); }; } if ( matches == 0 ) { alert("No associated mouse-sensitive areas " + hintId); } return hint; } function nc_initializeHintWithAnchor(hint, anchor) { //confirm("hint= " + object_to_string(hint) + // ", anchor= " + object_to_string(anchor)); if ( anchor == null ) return alert("Missing sensitive anchor " + hint.name); anchor.hint = hint; var arr = new Array(anchor); if ( typeof hint.anchors == "undefined" ) { hint.anchors = arr; } else { hint.anchors = hint.anchors.concat(arr); } anchor.onMouseOver = nc_toggleHint; anchor.onMouseOut = nc_toggleHint; anchor.onClick = nc_toggleHint; return hint; } // NOTE: Related, credits and wordsearch appear with different widths // so I set their zIndexes with the smallest on top. // Prepare an already existing layer so it can receive the page // explaining related links (required/suggested). function nc_showRelated(url) { var layer = document.layers["RelatedLayer"]; if ( layer.visibility == "visible" || layer.visibility == "show" ) { //alert("nc_hideRelated"); // DEBUG layer.visibility = "hidden"; return false; } else { var newWidth = 2*getWindowWidth()/3; var xoffset = (getWindowWidth() - newWidth)/2; layer.moveToAbsolute(xoffset, navBarHeight); layer.clip.left = 2; layer.clip.top = 2; layer.clip.width = newWidth-2; if ( ! layer.alreadyLoaded ) { //layer.src = url; layer.load(url, newWidth); layer.zIndex = 2; layer.alreadyLoaded = true; //document.NCwordSearchForm.onSubmit = afterWordSearchSubmission; } layer.visibility = "show"; //alert(object_to_string(layer)); // DEBUG // Preempt the default href: return false; } } // Prepare an already existing layer so it can receive the page // that contains the credits of the current page. function nc_showCredits(url) { var layer = document.layers["CreditsLayer"]; if ( layer.visibility == "visible" || layer.visibility == "show" ) { layer.visibility = "hidden"; return false; } else { var newWidth = getWindowWidth()-40; var xoffset = (getWindowWidth() - newWidth)/2; layer.moveToAbsolute(xoffset, navBarHeight); layer.clip.left = 2; layer.clip.top = 2; layer.clip.width = newWidth-2; if ( ! layer.alreadyLoaded ) { layer.load(url, newWidth); layer.zIndex = 1; layer.alreadyLoaded = true; } layer.visibility = "show"; return false; } } // Prepare a layer to get a word and search for it. function nc_getWordAndSearch() { var layer = document.layers["WordSearchLayer"]; //confirm("nc_getWordAndSearch" + object_to_string(layer)); // DEBUG if ( layer.visibility == "visible" || layer.visibility == "show" ) { layer.visibility = "hidden"; return false; } else { var newWidth = 300; var xoffset = (getWindowWidth() - newWidth)/2; layer.moveToAbsolute(xoffset, navBarHeight); layer.clip.left = 0; layer.clip.top = 0; //layer.clip.width = newWidth; layer.visibility = "show"; layer.zIndex = 3; //alert(object_to_string(layer)); return false; } } //}}} ------------------------------------------------------------------- // {{{ Explorer versions: // Makes the hint pop up until the mouse leaves the sensitive area. // A click makes the popup pinned down until another click. It seems // there is a race condition where hint.style is reported not to be an // object !? function ie_toggleHint () { // receive the anchor as target. var anchor = event.srcElement; while ( anchor && anchor.tagName != "A" ) { anchor = anchor.parentElement; //confirm("event on " + object_to_string(anchor)); // DEBUG } // Don't let these links be followed, they're not real anchors: event.cancelBubble = true; if ( anchor ) { //confirm("event on " + object_to_string(anchor)); // DEBUG var hint = anchor.hint; //confirm("event on " + object_to_string(hint)); // DEBUG //confirm("event on " + object_to_string(hint.style)); // DEBUG // Toggle the state of the hint: if ( hint.style.visibility == 'hidden' || hint.style.visibility == "" ) { if ( ! hint.immotile ) { hint.style.pixelLeft = event.offsetX/2; hint.style.pixelTop = event.offsetY + 20; //confirm("Event is " + object_to_string(event)); // DEBUG }; hint.style.display = "block"; hint.style.visibility = 'visible'; hint.style.zIndex = 5; window.status = 'Click to nail the hint'; } else { if ( hint.permanent ) { window.status = 'Click (at the same place) to remove the hint'; } else { hint.style.visibility = 'hidden'; } } // Click means: show/hide the hint permanently: if ( event.type == 'click' ) { hint.permanent = ! hint.permanent; if ( hint.permanent ) { window.status = 'Click to remove the hint'; } else { window.status = 'Removed!'; } } return false; } else { return false; } } // Link the mouse-sensitive areas to their hint. function ie_initializeHint(hintId) { var hint = document.all('d__' + hintId); if ( hint == null ) alert("Missing DIV " + hintId); hint.permanent = false; hint.immotile = false; hint.name = hintId; //hint.anchors = new Array(); var matches = 0; for (var i = 0 ; i < document.links.length ; i++ ) { var anchor = document.links[i]; // confirm("anchor[" + i + "]=" + object_to_string(anchor)); // DEBUG if ( anchor.id.indexOf(hintId)>=0 ) { //confirm("got anchor " + hintId); // DEBUG matches++; ie_initializeHintWithAnchor(hint, anchor); }; } if ( matches == 0 ) { alert("No mouse-sensitive anchors for " + hintId); } return hint; } function ie_initializeHintWithAnchor(hint, anchor) { //confirm("hint= " + object_to_string(hint) + // ", anchor= " + object_to_string(anchor)); if ( anchor == null ) alert("Missing ANCHOR " + hint.name); anchor.hint = hint; var arr = new Array(anchor); //KO: the next instruction makes IExplorer abort!? //KO: hint.anchors = hint.anchors.concat(arr); //confirm("hint= " + object_to_string(hint)); // DEBUG // Attention, don't write onMouseOver but onmouseover instead! anchor.onmouseover = ie_toggleHint; anchor.onmouseout = ie_toggleHint; anchor.onclick = ie_toggleHint; hint.className = 'hint'; hint.style.display = 'none'; return hint; } function ie_showRelated(url) { var newStyle = "status,resizable,scrollbars,height=" + (getWindowHeight()*2/3) + ",width=" + (getWindowWidth()*3/4); var newWindow = window.open(url, "relatedWindow", newStyle); newWindow.focus(); } function ie_showCredits(url) { var newStyle = "status,resizable,scrollbars,height=" + (getWindowHeight()/2) + ",width=" + (getWindowWidth()*2/3); var newWindow = window.open(url, "creditsWindow", newStyle); newWindow.focus(); } function ie_getWordAndSearch() { var frame = document.all.WordSearchFrame; //confirm(object_to_string(frame)); // DEBUG if ( event ) { event.returnValue = false; } if ( frame.style.display != "none" ) { frame.style.display = "none"; return false; } else { // Center the button: var width = document.wordSearchForm.children[0].clientWidth; var xoffset = (getWindowWidth() - width)/2; frame.style.left = xoffset; frame.style.top = navBarHeight; frame.style.width = Math.max(width, 200); frame.style.display = "block"; frame.className = "wordSearch"; frame.zIndex = 3; return false; } } // }}} ------------------------------------------------------------------- // {{{ Final versions (independent of the browser): // These untested functions propose to leave hooks in the persistent part // of the runtime of javascript (persistent means visible from page to // page). A page may leave a function to be run by the next page to load. function runPreHook () { if ( (typeof navigator.VideoC != "undefined") && (typeof navigator.VideoC.preHook != "undefined") ) { eval(navigator.VideoC.preHook); delete navigator.VideoC.preHook; }; } function runPostHook () { if ( (typeof navigator.VideoC != "undefined") && (typeof navigator.VideoC.postHook != "undefined") ) { eval(navigator.VideoC.postHook); delete navigator.VideoC.postHook; }; } // This function was used to popup a small window, ask for a word and // ask the VideoC server for pages containing that word. function initializeJSforThatPage () { if ( window.IExplorer ) { // USELESS now: //var t = '
tag, it resets fonts, margins and // many things. So now the generation of a layer or div tag is // conditionalized over the kind of browser. // Netscape does not like more than one document.write. // Netscape does not execute instructions after a document.write. // Netscape does not like a document.write opening a tag closed by // another subsequent document.write. // Netscape does not like a document.write generating a '; document.write(tmp); } function initializePreviousHints() { //confirm("initializePreviousHints: " + currentHintIds.length); // DEBUG for ( var i=0 ; i xJHZج0vnt[[qu6 77 671t- 02h_(]LhO\hN¯ӈ˫ 1{`tLPJΓp tjC]y @u2j(c Ҏ9ɓ$nKd4!I5FN)n"D$yy]cVDTA#A7w2fV T~[ $K]USRim(^n(iGZ¨əs**6&bAAx)f+qXl ,ɄlYk؆)v+QU$.h)sHEjq G\Ǵܾq.=5If›oZmZhit| ch*:l<l3c@ 4Zl#QI5aEg,{ex'+͞$Mq8gYn>Y'[2lY{:ƭ*R oη49376mcbZkֹ`ղɚ ;C ۫ GhezלS/mѠ_g^ ]rˬ•?3앜ͥ9=aq>Ct0Owы&+ٶ: j%kq8]Y<=Ww:C'=31#6ꞟwzI='{jIe&[g5^=홿$.pٰ7/WS#KD#rz^SUqGе4TA=%ip7UFH_cj 8t 8s8r&go߁+cXq1"$ҁ 8N$-a`/,H#3Gg:Z}qY9+]47ib|s;0Au2Mpz3@Rhg}iyDžB @K6FcQ%bH Tgq9O'j -t PnPIVxU;7EiW'XVhcw5d X#?H`"D3qU3 c'kH>|A/htur/RhIW 1n)gSbDRpxH)4vnZX`6~}jZEBHF!-h;N\ u&8mq14}y4h~ Ij9O |Ɍ ‘iSҸeX)+IQ bG9AB NyJ iTI?SSv~(ӔC\I">`=bOYQ)8rytIynIawiwHPɕi}~XRQTYPԘyDzuF2ᒤ!R:#TZ-rXz"ZS^Z!dZfzhjlڦnpr:tZvzxz|ڧ~:Zzz>ب4eLZ^`yz?Qn%QEXGPz1D&<&v4ZSD*߈ZLI$j70rHFjB3L09.߸Z ;ocaml-book-1.0/en/html/book-ora091.html0000644000000000000000000000343707453055400014473 0ustar Summary Previous Contents Next

Summary

This chapter has presented the principal families of algorithms for automatic memory reclamation with the goal of detailing those used in Objective CAML. The Objective CAML garbage collector is an incremental garbage collector with two generations. It uses Mark&Sweep for the old generation, and Stop&Copy for the young generation. Two modules directly linked to the garbage collector allow control of the evolution of the heap. The Gc module allows analysis of the behavior of the garbage collector and modification of certain parameters with the goal of optimizing specific applications. With the Weak module one can save in arrays values that are potentially reclaimable, but which are still accessible. This module is useful for implementing a memory cache.


Previous Contents Next ocaml-book-1.0/en/html/book-ora039.gif0000644000000000000000000000713007452056111014267 0ustar GIF89aUUUrrr!,H0I8ͻ`(dih.s lpH,>b XIHZجv2U:ob9ݚz}ng~Oꎿ 8b::|~2moEKa98e0b0L4;<ڡnqG rX.`  *<…5b4-jd6e*u7d H\y9dI˃bɳH9QrI4& x7=evxjX[5O!JRرV˪Mt۵pKWܢ3`7oܔ[׽峯-8£w\mZ.P6YCg/Yh͊3^QinFN Vz.rudjjߞa&S%ݽqœ-rǫWO!/XBWOtOeR}LR;VJ~17FP(؀vI`k6F!94`:n~SxX%&a)ؚp dXmxAF>؈EJoMFpe7+p\v`){A$ Ore m2Ѡ)'sgЙWkigwbgBUpȤlZ1Pib s7:jz jupj0+i.:*)lcif,{, DhA99Ȧ:+9&nªi1Z"J|6(0hS˄{ 'Z` 0!vJS@+ z'3pʟcV쯵ij7CTsG+**.؄ 'L s< EҬ\;RAG /\ bvvM6BN Kx'Ձh6wpR GЫf|}@I;Gŀq0xo|ƩxQZ[x\njI{u3 w⠶ 8{;wƯ}rnL=Kbo=i9o_;_ӏ`@d+}[Mce"կu)z]?h>}W|arP^ez^LWGS{v2cJ$bgww[y!+Ak>H@.( ӂ|Ke%|*Wky606O7~ &z9)8cm&AS87r8~VoZv$EZKsl%Ug:+3T[xDAt| 񳄜5'uCW!"$ F, Wjxpx"p xq83(~a;L!B+2PZ;52 .hJ ۓ56"6Q/8ds' 4JT2|y 3;&^r`j\:<@5c3T+BB";5*Z4IsB&@ĥH|HQ,^ЧyJ5BJHc2A4Z8z2UӨQ3+@(5J*T)rԪ:א;ocaml-book-1.0/en/html/book-ora054.html0000644000000000000000000001351707453055377014507 0ustar To learn more Previous Contents Next

To learn more

Although graphics programming is naturally event-driven, the associated style of programming being imperative, it is not only possible but also often useful to introduce more functional operators to manipulate graphical objects. A good example comes from the use of the MLgraph library,

Link


http://www.pps.jussieu.fr/~cousinea/MLgraph/mlgraph.html
which implements the graphical model of PostScript and proposes functional operators to manipulate images. It is described in [CC92, CS94] and used later in [CM98] for the optimized placement of trees to construct drawings in the style of Escher.

One interesting characteristic of the Graphics library is that it is portable to the graphical interfaces of Windows, MacOS and Unix. The notion of virtual bitmaps can be found in several languages like Le_Lisp and more recently in Java. Unfortunately, the Graphics library in Objective CAML does not possess interactive components for the construction of interfaces. One of the applications described in part II of this book contains the first bricks of the Awi library. It is inspired by the Abstract Windowing Toolkit of the first versions of Java. One can perceive that it is relatively easy to extend the functionality of this library thanks to the existence of functional values in the language. Therefore chapter 16 compares the adaptation of object oriented programming and functional and modular programming for the construction of graphical interfaces. The example of Awi is functional and imperative, but it is also possible to only use the functional style. This is typically the case for purely functional languages. We cite the systems Fran and Fudget developed in Haskell and derivatives. The system Fran permits construction of interactive animations in 2D and 3D, which means with events between animated objects and the user.

Link


http://www.research.microsoft.com/~conal/fran/
The Fudget library is intended for the construction of graphical interfaces.

Link


http://www.cs.chalmers.se/ComputingScience/Research/Functional/Fudgets/


One of the difficulties when one wants to program a graphical interface for ones application is to know which of the numerous existing libraries to choose. It is not sufficient to determine the language and the system to fix the choice of the tool. For Objective CAML there exist several more or less complete ones:
  • the encapsulation of libX, for X-Windows;
  • the librt library, also for X-Windows;
  • ocamltk, an adaptation of Tcl/Tk, portable;
  • mlgtk, an adaptation of Gtk, portable.
We find the links to these developments in the ``Caml Hump'':

Link


http://caml.inria.fr/hump.html


Finally, we have only discussed programming in 2D. The tendency is to add one dimension. Functional languages must also respond to this necessity, perhaps in the model of VRML or the Java 3D-extension. In purely functional languages the system Fran offers interesting possibilities of interaction between sprites. More closely to Objective CAML one can use the VRcaML library or the development environment SCOL.

The VRcaML library was developed in the manner of MLgraph and integrates a part of the graphical model of VRML in Objective CAML.

Link


http://www.pps.jussieu.fr/~emmanuel/Public/enseignement/VRcaML
One can therefore construct animated scenes in 3D. The result is a VRML-file that can be directly visualized.

Still in the line of Caml, the language SCOL is a functional communication language with important libraries for 2D and 3D manipulations, which is intended as environment for people with little knowledge in computer science.

Link


http://www.cryo-networks.com
The interest in the language SCOL and its development environment is to be able to create distributed applications, e.g. client-server, thus facilitating the creation of Internet sites. We present distributed programming in Objective CAML in chapter 20.






Previous Contents Next ocaml-book-1.0/en/html/book-ora093.html0000644000000000000000000000212607453055401014470 0ustar Notes
1
In the online version of the book, the gray is slightly bluish.
2
Most values do not survive a single garbage collection.
3
The only exception in Objective CAML relates to arrays of floating point values (see chapter 12, page ??).
ocaml-book-1.0/en/html/book-ora199.html0000644000000000000000000002122107453055401014474 0ustar Elements of the evaluation Previous Contents Next

Elements of the evaluation

The Objective CAML distribution supplies two online compilers, one generating bytecodes and the other producing instructions for the most modern processors. The toplevel uses the bytecode compiler. Beyond that, the distribution offers numerous libraries in the form of modules and some tools for calculating dependencies between modules, for profiling, and for debugging. Finally, thanks to its interface with the C language, it is possible to link Objective CAML programs to C programs. Languages which similarly offer an interface with C, such as the JNI (Java Native Interface) of Java, become accessible in this way.

The reference manual gives the language syntax, and describes the development tools and the library signatures. This set (language, tools, libraries, documentation) makes up a development environment.

Language

Specification and implementation

There are two ways to approach a new language. A first way is to read the language specification to have a global vision. A second is to plunge into the language's user manual, following the concepts illustrated by the examples. Objective CAML has neither one, which makes a self-taught approach to it relatively difficult. The absence of a formal specification (such as SML has) or a descriptive one (such as ADA's) is a handicap for understanding how a program works. Another consequence of the lack of a specification is the impossibility of getting a language standard issued by ISO, ANSI, or IEEE. This strongly limits the construction of new implementations tailored for other environments. Fortunately INRIA's implementation is of high quality and best of all, the sources of the distribution can be downloaded.

Syntax

The particulars of syntax are a non-negligible difficulty in approaching Objective CAML. They can be explained by the functional origin of the language, but also by historical, that is to say anecdotal, factors.

The syntax of application of a function to its arguments is defined by simple juxtaposition, as in f 1 2. The lack of parentheses bothers the neophyte as much as the C programmer or the confirmed Lisp programmer. Nevertheless, this difficulty only arises when reading the code of a programmer who's stingy with parentheses. Nothing stops the neophyte Objective CAML programmer from using more explicit parenthesization and writing (f 1 2).

Beyond the functional core, Objective CAML adopts a syntax sometimes at odds with customary usage: access to array elements uses the notation t.(i) and not the usual brackets; method invocation is noted by a pound (# character) and not a dot, etc. These idiosyncrasies don't make it easier to get a grip on Objective CAML.

Finally, the syntax of Objective CAML and its ancestor Caml has undergone numerous modifications since their first implementation. Which hasn't pleaded in favor of the enduring nature of the developed applications.

To end on a more positive note, the pattern-matching syntax, inherited from the ML family, which Objective CAML incorporates, structures function definitions by case pleasingly and with simplicity.

Static typing

The fundamental character of the Objective CAML language resides in the language's static typing of expressions and declarations. This guarantees that no type error surfaces during program execution. Static type inference was conceived for the functional languages of the ML family, and Objective CAML has been able to maintain the greater part of this type discipline for the imperative and object-oriented extensions. However, in the object-oriented case, the programmer must sometimes give type inference a hand through explicit type constraints. Still, Objective CAML preserves static typing of expressions, and definitions, which provides an unsurpassed measure of execution safety: an Objective CAML program, will not return the ``method not found'' exception, which is not the case for dynamically typed object-oriented languages.

Objective CAML's parametric polymorphism of types allows the implementation of general algorithms. It is channeled into the object-oriented layer where parametric classes produce generic code, and not an expansion of code as generated by the templates of other languages. In itself, parametric polymorphism is an important component of code reusability.

The object-oriented extension adds a notion of inclusion polymorphism which is obtained by specifying subtyping relationships between objects. It reconciles code reusability, which constitutes the strength of the inheritance relationship between classes, with the security of static typing.

Libraries and tools

The libraries supplied with the distribution cover great needs. The programmer finds there, as a standard, the implementation of the most usual data structures with their basic functions. For example: stacks, queues, hash tables, AVL trees. More advanced tools can be found there as well, such as the treatment of data streams. These libraries are enriched in the course of successive language versions.

The Unix library allows lower-level programming as much for I/O as for process management. It is not identical for all platforms, which limits its use to some extent.

The exact arithmetic library and the regular expression library facilitate the development of specific applications.

Unfortunately the portable graphics library offers little functionality to support the construction of graphical user interfaces.

C libraries can easily be interfaced with the Objective CAML language. Here, the free availability of well-structured and duly commented sources definitely unlocks the potential for contact with the outside world and the various and sundry libraries to be found there.

Among the supplied tools, those for lexical and syntactic analysis, indispensable whenever dealing with complex textual data, are especially noteworthy. Based on the classic lex and yacc, they integrate perfectly with sum types and the functionality of Objective CAML, thus making them simpler to use than their predecessors.

Finally, no matter what soundness its features may bring, use of a language ``in actual practice'' never avoids the debugging phase of a program. The Objective CAML 2.04 distribution does not supply an IDE. Certainly, using the toplevel allows one to proceed rapidly to compilation and unit tests of functions (which is an undeniable advantage), but this usage will vary by platform and by programmer: cut-and-paste under X-Windows, calling the shell under emacs, requesting evaluation of a buffer under Windows. The next version (see appendix B) provides for the first time an environment for Unix containing a browser for interfaces and modules and a structured editor linked to the toplevel. Finally the distribution's debugger remains hard to use (in particular, because of the functional aspect and the language's parametric polymorphism) and limited to the Unix system.

Documentation

The documentation of the distribution consists of the reference manual in printable (PostScript) and online (HTML) format. This manual is not in any case an introduction to the language. On the contrary it is indispensable for finding out what's in the libraries or what parameters the commands take. Some pedagogical material can be found on Inria's Caml page, but it is mostly regarding the Caml-Light language. Thus the language is missing a complete tutorial manual, we hope that this book fills this gap.


Previous Contents Next ocaml-book-1.0/en/html/book-ora090.html0000644000000000000000000005656107453055400014500 0ustar Exercises Previous Contents Next

Exercises

Following the evolution of the heap

In order to follow the evolution of the heap, we suggest writing a function that keeps information on the heap in the form of a record with the following format:

# type tr_gc = {state : Gc.stat;
time : float; number : int};;
The time corresponds to the number of milliseconds since the program began and the number serves to distinguish between calls. We use the function Unix.time (see chapter 18, page ??) which gives the running time in milliseconds.
  1. Write a function trace_gc that returns such a record.

    # type tr_gc = {etat : Gc.stat; temps : float; numero : int} ;;
    type tr_gc = { etat: Gc.stat; temps: float; numero: int }
    # let trace_gc =
    let num = ref 0 in
    function () -> incr num;
    { etat = Gc.stat (); temps = Unix.time(); numero = !num} ;;
    val trace_gc : unit -> tr_gc = <fun>


  2. Modify this function so that it can save a value of type tr_gc in a file in the form of a persistant value. This new function needs an output channel in order to write. We use the Marshal module, described on page ??, to save the record.

    # let trace_out_gc oc =
    let trgc = trace_gc in
    Marshal.to_channel oc trgc [];;
    val trace_out_gc : out_channel -> unit = <fun>


  3. Write a stand-alone program, taking as input the name of a file containing records of type of tr_gc, and displaying the number of major and minor garbage collections.

    type tr_gc = {etat : Gc.stat; temps : float; numero : int} ;;

    let rec last l = match l with
    [] -> failwith "last"
    | [e] -> e
    | t::q -> last q;;

    let visu ltgc =
    let fst = List.hd ltgc
    and lst = last ltgc in
    let nb_minor = lst.etat.Gc.minor_collections - fst.etat.Gc.minor_collections
    and nb_major = lst.etat.Gc.major_collections - fst.etat.Gc.major_collections in

    Printf.printf "Nombre de Gc: mineur = %d, majeur = %d\n" nb_minor nb_major ;;


    let rec read_trace ic =
    try
    let tgc = ( (Marshal.from_channel ic) : tr_gc) in
    tgc :: (read_trace ic)
    with
    End_of_file -> close_in ic; [];;

    let usage () =
    Printf.printf "usage: visu filename\n";;

    let main () =
    if Array.length (Sys.argv) < 2 then usage()
    else
    let ltgc = read_trace (open_in Sys.argv.(1)) in
    visu ltgc;;


    main();;









  4. Test this program by creating a trace file at the interactive loop level.

Memory Allocation and Programming Styles

This exercise compares the effect of programming styles on the growth of the heap. To do this, we reconsider the exercise on prime numbers from chapter 8 page ??. We are trying to compare two versions, one tail-recursive and the other not, of the sieve of Eratosthenes.

  1. Write a tail-recursive function erart (this name needs fixing) that calculates the prime numbers in a given interval. Then write a function that takes an integer and returns the list of smaller prime numbers. fichier eras2.ml :

    (* fichier eras2.ml *)

    let erart l =
    let rec erart_aux l r = match l with
    [] -> List.rev r
    | p::q -> erart_aux (List.filter ( fun x -> x mod p <> 0) q) (p::r)
    in
    erart_aux l [] ;;

    let erart_go n =
    erart (Interval.interval (<) (fun x -> x + 1) 2 n) ;;





  2. By using the preceding functions, write a program (change the name) that takes the name of a file and a list of numbers on the command line and calculates, for each number given, the list of prime numbers smaller than it. This function creates a garbage collection trace in the indicated file. Trace commands from previous exercice are gathered in file trgc.ml fichier era2_main.ml :

    (* fichier : era2_main.ml *)

    open Trgc;;

    let usage () = Printf.printf "Usage: testgc filename n1 [n2 n3 ... np]n\n";;

    let main f =
    if Array.length (Sys.argv) < 3 then usage()
    else
    let fn = Sys.argv.(1)
    and vn = Array.map int_of_string
    (Array.sub Sys.argv 2 (Array.length Sys.argv - 2)) in
    let oc = open_out fn in
    Array.map (fun n ->
    let r = f n in
    trace_out_gc oc;
    r) vn ;
    close_out oc ;;






    fichier main_rt.ml :

    (* fichier main_rt.ml *)

    Era2_main.main Eras2.erart_go;;
    (* fichier trgc.ml *)

    type tr_gc = {etat : Gc.stat; temps : float; numero : int} ;;
    let trace_gc =
    let num = ref 0 in
    function () -> incr num;
    { etat = Gc.stat (); temps = Unix.time(); numero = !num} ;;

    let trace_out_gc oc =
    let trgc = trace_gc () in
    Marshal.to_channel oc trgc [];;


  3. Compile these files and create a stand-alone executable; test it with the following call, and display the result.
                                                               %
    erart trace_rt 3000 4000 5000 6000
    
    Compilation pour Unix :
    $ ocamlc -custom -o erart unix.cma interval.ml trgc.ml eras2.ml era2_main.ml main_rt.ml -cclib -lunix
    
    
    Test :
    $ erart traceRT 3000 4000 5000 6000
    
    Rsultats :
    $ visu traceRT 
    Nombre de Gc: mineur = 130, majeur = 22
    


  4. Do the same work for the non tail recursive function. On reprend le fichier eras.ml, de la page ??, contenant une fonction de calcul non rcursive terminale.

    Puis on cre le fichier main_nrt.ml suivant :
    (* fichier main_nrt.ml *)

    Era2_main.main Eras.era_go;;


    Compilation :
    $  ocamlc -custom -o eranrt unix.cma interval.ml trgc.ml eras.ml era2_main.ml main_nrt.ml -cclib -lunix
    
    Test :
    $ eranrt traceNRT 3000 4000 5000 6000
    
    Rsultats (version 2.04/Unix) :
    $ visu traceNRT 
    Nombre de Gc: mineur = 130, majeur = 10
    


  5. Compare trace results. Les deux programmes allouent les mmes donnes : ils ont le mme nombre de GC mineurs. La version rcursive terminale effectue plus de GC majeurs. En effet, l'accumulateur r de la fonction erart_rt est valu dans l'appel rcursif et sa tte de liste est range dans la zone des valeurs anciennes chaque GC mineur. Le filtrage des multiples d'un nombre peut entraner un GC mineur qui fait vieillir la liste filtre en cours de construction. Les anciennes listes filtres sont libres pendant un GC majeur. Ainsi l'espace mmoire de la gnration ancienne qui contient la liste des nombres premiers dj trouvs est plus petit ce qui augmente le nombre de GC majeurs.

    Ce cas est particulier

    L'criture d'une fonction rcursive sous forme rcursive terminale fait gagner en allocation de pile, mais pas forcment en nombre de GC.

Previous Contents Next ocaml-book-1.0/en/html/book-ora055.gif0000644000000000000000000001170707452056112014273 0ustar GIF89a?[}}}{{{yyywwwuuuqqqWWW333tttrrrnnnllljjj!R,?[R+`l*_)^']&\%[$Z#"Y!X WVUvH rXȰᔇŋ㠣" Cl@2 /$IYɲ%0c I悛8sɳ@ ѣH"XʴSJ իjׯŠ룬ٳҪUۣ۷ʝˣݻݻ߿La9+^)qHL9$PܠeK(]\dkIc~J~*1y. u뇳gg̽{bʔVnMHau]u! XY' -/lfRtq@a<l@(dTDVeH dXLP2'sTJgeuXfv\~d`9&ze65mj x¬ l[8km[["4ꨔJje^Xm*Y} j,亱F~Mh.ЈNC3ѐt-J[҆7NKҞ7 Qԋ.5W>հ5-Zzίw\vMbyNvfΎv-jvncw-r+Nѭvw-zw~x.[Ox3!. 83so8i+&7u ~'\/x˅Mru<3'r&^t9w^sHϷҙ?Q39.t[N~쫋@d7{>w7YT/YǟlH_< ǻҦ)G^w;oσȤ7rSuӟP^}1olև=m"ۺWwW2m|]+=Ͻ-[ӗxzϿ~~K}s/vY~$^O_^w~std|wki }vui} 8mofeWKyw·zRF%|sm"f'wf|gBƂ*s~~>؂7=Es@IFg9LUX|MXkHH7.x`Ki3f[_8gS5ȁֆ×gkX;OlefjDžO8xBh:|Yl yB+۹3y{1+k뭡˺ joGe$ƴ|.#Ik$:+kzfezR W+{ٻ;ۘX: [cörh ;z{~ǚmc c:WY幜K;i ii)V9؇., f#,<$F=i+e(,~N,LFl:'iJ!_g`lc\wghhxk\j(pr<ǀixuǣ~n}ȞȄ||ȤȊ|iȸȐŒ<jȘqɉɠf*MktU w,x&}(*,.02=4]6}8:<]ә@B=D]F}?IJ RNPR=T]V}XZMY-[[=d]f}hZZ mpr=t]\-W_m|6~׀-lxׁ}؈؊=Ճz؋ْ=وyԘٜٚٞ;ocaml-book-1.0/en/html/book-ora189.html0000644000000000000000000016376507453055401014517 0ustar Exercises Previous Contents Next

Exercises

The suggested exercises allow you to try different types of distributed applications. The first offers a new network service for setting the time on client machines. The second exercise shows how to use resources on different machines to distribute a calculation.

Service: Clock

This exercise consists of implementing a ``clock'' service that gives the time to any client. The idea is to have a reference machine to set the time for different machines on a network.

  1. Define a protocol for transmitting a date containing the day, month, hour, minute, and second.

    # type horloge = { jour:int; mois:int; heure:int; minute:int; seconde:int } ;;
    type horloge =
    { jour: int;
    mois: int;
    heure: int;
    minute: int;
    seconde: int }
    # let encode date =
    let str = String.create 5 in
    str.[0] <- char_of_int date.jour ;
    str.[1] <- char_of_int date.mois ;
    str.[2] <- char_of_int date.heure ;
    str.[3] <- char_of_int date.minute ;
    str.[4] <- char_of_int date.seconde ;
    str ;;
    val encode : horloge -> string = <fun>
    # let decode str =
    { jour = int_of_char str.[0] ;
    mois = int_of_char str.[1] ;
    heure = int_of_char str.[2] ;
    minute = int_of_char str.[3] ;
    seconde = int_of_char str.[4] } ;;
    val decode : string -> horloge = <fun>


  2. Write the function or the class for the service reusing one of the generic servers presented in the Chapter. The service sends date information over each accepted connection, then closes the socket. Nous utilisons la fonction main_serveur(20) :

    # main_serveur ;;
    - : (in_channel -> out_channel -> 'a) -> unit = <fun>

    # let horloge_service ic oc =
    try
    let date = Unix.localtime (Unix.time ()) in
    let date_horloge =
    { jour = date.Unix.tm_mday ;
    mois = date.Unix.tm_mon + 1 ;
    heure = date.Unix.tm_hour ;
    minute = date.Unix.tm_min ;
    seconde = date.Unix.tm_sec } in
    output_string oc (encode date_horloge) ;
    flush oc
    with exn -> print_endline "Fin du traitement"; flush stdout

    let main_horloge () = main_serveur horloge_service ;;
    val horloge_service : 'a -> out_channel -> unit = <fun>
    val main_horloge : unit -> unit = <fun>


  3. Write the client , which sets the clock every hour. Nous utilisons la fonction main_client20 :

    # main_client ;;
    - : (in_channel -> out_channel -> 'a) -> unit = <fun>

    # let client_horloge ic oc =
    let date = ref { jour=0; mois=0; heure=0; minute=0; seconde=0 } in
    try
    while true do
    let buffer = "xxxxx" in
    ignore (input ic buffer 0 5) ;
    date := decode buffer ;
    print_endline "BIP";
    flush stdout ;
    Unix.sleep 3600
    done
    with
    exn -> shutdown_connection ic ; raise exn ;;
    val client_horloge : in_channel -> 'a -> unit = <fun>

    # let main_horloge () = main_client client_horloge ;;
    val main_horloge : unit -> unit = <fun>


  4. Keep track of time differences when requests are sent. On peut mesurer le temps coul entre la demande de connexion et la rception de la rponse. On suppose que ce dlai est le double de celui mis par la rponse et on corrige le rsultat en consquence.

A Network Coffee Machine

We can build a little service that simulates a beverage vending machine. A summary description of the protocol between the client and service is as follows:
  • when it makes a connection, the client receives a list of available drinks;
  • it then sends to the server its beverage choice;
  • the server returns the price of the beverage;
  • the client sends the requested price, or some other sum;
  • the server responds with the name of the chosen beverage and shows the change tendered.
The server may also respond with an error message if it has not understood a request, does not have enough change, etc. A client request always contains just one piece of information.

The exchanges between client and server are in the form of strings of characters. The different components of a message are separated by two periods and all strings end in :$\n.

The service function communicates with the coffee machine by using a file to pass commands and a hash table for recovering drinks and change.

This exercise will make use of sockets, lightweight processes with a little concurrency, and objects.

  1. Rewrite the function establish_server using the primitives in ThreadUnix. On reprend les fonctions hostaddr et my_inet_addr de ce chapitre.

    # val hostaddr : string -> Unix.inet_addr = <fun>
    val my_inet_addr : unit -> Unix.inet_addr = <fun>

    let establish_server f saddr =
    let sock = ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
    Unix.bind sock saddr;
    Unix.listen sock 5;
    while true do
    let (s,_) = ThreadUnix.accept sock in
    let ic = Unix.in_channel_of_descr s
    and oc = Unix.out_channel_of_descr s in
    ignore (Thread.create (f ic) oc)
    done;;
    val establish_server :
    (in_channel -> out_channel -> 'a) -> Unix.sockaddr -> unit = <fun>


  2. Write two functions, get_request and send_answer. The first function reads and encodes a request and the second formats and sends a response beginning with a list of strings of characters.

    # let read fd =
    let buf = String.create 1024 in
    let n = ThreadUnix.read fd buf 0 1024 in
    let s = String.sub buf 0 n in
    s ;;
    val read : Unix.file_descr -> string = <fun>

    # let get_request fd =
    let s = read fd in
    match Str.split (Str.regexp "[:]") (String.sub s 0 (String.index s '$')) with
    [s1] -> s1
    | _ -> failwith "BadRequestFormat" ;;
    val get_request : Unix.file_descr -> string = <fun>
    On redfinit des fonctions d'entres-sorties utilisant celles de Threadunix puis on les utilise pour get_request et send_answer. Comme elle revient souvent, on dfinit galement une fonction send_cancel qui envoie un message d'erreur.

    # let write fd s =
    let leng = (String.length s) in
    let n = ThreadUnix.write fd s 0 leng in
    if n<leng then failwith "I/O error" ;;
    val write : Unix.file_descr -> string -> unit = <fun>

    # let send_answer fd ss =
    let rec mk_answer = function
    [] -> ":$\n"
    | [s] -> s ^ ":$\n"
    | s::ss -> s ^ ":" ^ (mk_answer ss)
    in
    write fd (mk_answer ss) ;;
    val send_answer : Unix.file_descr -> string list -> unit = <fun>

    # let send_cancel = let s = "cancel:$\n" in function fd -> write fd s ;;
    val send_cancel : Unix.file_descr -> unit = <fun>


  3. Write a class cmd_fifo to manage pending commands. Each new command is assigned a unique number. For this purpose, implement a class num_cmd_gen.

    # class cmd_fifo =
    object(self)
    val n = new num_cmd_gen
    val f = (Queue.create (): (int*int*int) Queue.t)
    val m = Mutex.create ()
    val c = Condition.create ()

    method add num_drink paid =
    let num_cmd = n#get() in
    Mutex.lock m ;
    Queue.add (num_cmd, num_drink, paid) f ;
    Mutex.unlock m ;
    Condition.signal c ;
    num_cmd

    method wait () =
    Mutex.lock m ;
    Condition.wait c m ;
    let cmd = Queue.take f in
    Mutex.unlock m ;
    cmd
    end ;;
    class cmd_fifo :
    object
    val c : Condition.t
    val f : (int * int * int) Queue.t
    val m : Mutex.t
    val n : num_cmd_gen
    method add : int -> int -> int
    method wait : unit -> int * int * int
    end

    # class num_cmd_gen =
    object
    val mutable x = 0
    val m = Mutex.create ()

    method get() =
    Mutex.lock m ;
    x <- x+1 ;
    let r = x in
    Mutex.unlock m ;
    r
    end ;;
    class num_cmd_gen :
    object val m : Mutex.t val mutable x : int method get : unit -> int end


  4. Write a class ready_table for stocking the machine with drinks.

    # class ready_table size =
    object
    val t = (Hashtbl.create size : (int, (string * int)) Hashtbl.t)
    val m = Mutex.create ()
    val c = Condition.create ()

    method add num_cmd num_drink change =
    Mutex.lock m ;
    Hashtbl.add t num_cmd (num_drink, change) ;
    Mutex.unlock m ;
    Condition.broadcast c

    method wait num_cmd =
    Mutex.lock m;
    while not(Hashtbl.mem t num_cmd) do Condition.wait c m done ;
    let cmd = Hashtbl.find t num_cmd in
    Hashtbl.remove t num_cmd ;
    Mutex.unlock m ;
    cmd
    end ;;
    class ready_table :
    int ->
    object
    val c : Condition.t
    val m : Mutex.t
    val t : (int, string * int) Hashtbl.t
    method add : int -> string -> int -> unit
    method wait : int -> string * int
    end


  5. Write the class machine that models the coffee machine. The class contains a method run that loops through the sequence: wait for a command, then execute it, as long as there remain drinks available. Define a type drink_descr indicating, for each drink: its name, the quantity in stock, the quantity that will remain after satisying pending commands, and its price. We can use an auxiliary function array_index which returns the index of the first element in a table satisfying a criterion passed as a parameter.

    # class machine (f_cmd0:cmd_fifo) (t_ready0:ready_table) =
    object(self)
    val f_cmd = f_cmd0
    val t_ready = t_ready0
    val mutable nb_available_drinks = 0
    val drinks_table =
    [| { name="cafe"; real_stock=10; virtual_stock=10; price=300 };
    { name="the"; real_stock=5; virtual_stock=5; price=250 };
    { name="chocolat"; real_stock=10; virtual_stock=10; price=250 } |]
    val mutable cash = 0
    val m = Mutex.create()

    initializer nb_available_drinks <- Array.length drinks_table

    method get_drink_price i = drinks_table.(i).price
    method get_drink_index s = array_index drinks_table (fun d -> d.name=s)

    method get_menu () =
    let f d ns = if d.real_stock > 0 then d.name::ns else ns in
    Array.fold_right f drinks_table []

    method cancel_cmd num_drink =
    let drink = drinks_table.(num_drink) in
    drink.virtual_stock <- drink.virtual_stock+1

    method set_cmd num_drink paid = f_cmd#add num_drink paid

    method wait_cmd num_cmd = t_ready#wait num_cmd

    method deliver_drink num_drink =
    let drink = drinks_table.(num_drink) in
    drink.real_stock <- drink.real_stock-1 ;
    if drink.real_stock = 0 then nb_available_drinks <- nb_available_drinks-1

    method run() =
    while nb_available_drinks>0 do
    let (num_cmd, num_drink, amount) = f_cmd#wait () in
    let drink = drinks_table.(num_drink) in
    let change = amount - drink.price in
    Mutex.lock m ;
    if (drink.virtual_stock > 0) & (cash >= change)
    then
    begin
    drink.virtual_stock <- drink.virtual_stock-1 ;
    cash <- cash + drink.price ;
    t_ready#add num_cmd drink.name change
    end
    else t_ready#add num_cmd "cancel" 0 ;
    Mutex.unlock m
    done
    end ;;
    class machine :
    cmd_fifo ->
    ready_table ->
    object
    val mutable cash : int
    val drinks_table : drink_descr array
    val f_cmd : cmd_fifo
    val m : Mutex.t
    val mutable nb_available_drinks : int
    val t_ready : ready_table
    method cancel_cmd : int -> unit
    method deliver_drink : int -> unit
    method get_drink_index : string -> int
    method get_drink_price : int -> int
    method get_menu : unit -> string list
    method run : unit -> unit
    method set_cmd : int -> int -> int
    method wait_cmd : int -> string * int
    end

    # type drink_descr =
    { name : string;
    mutable real_stock : int;
    mutable virtual_stock : int;
    price : int } ;;

    # let array_index t f =
    let i = ref 0 in
    let n = Array.length t in
    while (!i < n) & (not (f t.(!i))) do incr i done ;
    if !i=n then raise Not_found else !i ;;
    val array_index : 'a array -> ('a -> bool) -> int = <fun>


  6. Write the service function waiter.

    # let waiter mach ic oc =
    let f_in = Unix.descr_of_in_channel ic in
    let f_out = Unix.descr_of_out_channel oc in
    (try
    send_answer f_out (mach#get_menu()) ;
    let drink_name = get_request f_in in
    let num_drink = mach#get_drink_index drink_name in
    let drink_price = mach#get_drink_price num_drink in
    send_answer f_out [string_of_int drink_price] ;
    let paid = int_of_string (get_request f_in) in
    if paid < drink_price then failwith"NotEnough" ;
    let num_cmd = mach#set_cmd num_drink paid in
    let drink_name, change = mach#wait_cmd num_cmd in
    mach#deliver_drink num_drink;
    send_answer f_out [drink_name; (string_of_int change)]
    with
    Not_found -> send_cancel f_out
    | Failure("int_of_string") -> send_cancel f_out
    | Failure("I/O error") -> send_cancel f_out
    | Failure("NotEnough") -> send_cancel f_out
    | Failure("BadRequestFormat") -> send_cancel f_out
    );
    close_in ic ;
    flush oc ;
    close_out oc ;
    Thread.exit () ;;
    val waiter :
    < deliver_drink : 'a -> 'b; get_drink_index : string -> 'a;
    get_drink_price : 'a -> int; get_menu : unit -> string list;
    set_cmd : 'a -> int -> 'c; wait_cmd : 'c -> string * int; .. > ->
    in_channel -> out_channel -> unit = <fun>


  7. Write the principal function main that obtains a port number for the service from the command line and performs a number of initialization tasks. In particular, the coffee machine executes in a process.

    # let main () =
    if Array.length Sys.argv < 2
    then
    begin
    Printf.eprintf "usage : %s port\n" Sys.argv.(0) ;
    exit 1
    end
    else
    begin
    let port = int_of_string Sys.argv.(1) in
    let f_cmd = new cmd_fifo in
    let t_ready = new ready_table in
    let mach = new machine f_cmd (t_ready 13) in
    ignore (Thread.create mach#run ()) ;
    establish_server (waiter mach) (Unix.ADDR_INET (my_inet_addr (), port))
    end ;;
    val main : unit -> unit = <fun>

Previous Contents Next ocaml-book-1.0/en/html/book-ora153.html0000644000000000000000000011312707453055400014470 0ustar Comparison of Modules and Objects Previous Contents Next

Comparison of Modules and Objects

The main difference between modular programming and object programming in Objective CAML comes from the type system. In effect, programming with modules remains within the ML type system (i.e. parametric polymorphism code is executed for different types of parameter), while programming with objects entails an ad hoc polymorphism (in which the sending of a message to an object triggers the application of different pieces of code). This is particularly clear with subtyping. This extension of the ML type system can not be simulated in pure ML. It will always be impossible to construct heterogeneous lists without breaking the type system.

Modular programming and object programming are two safe (thanks to typing) approaches to the logical organisation of a program, permitting the reusability and the modifiability of software components. Programming with objects in Objective CAML allows parametric polymorphism (parameterized classes) and inclusion/subtype polymorphism (sending of messages) thanks to late binding and subtyping, with restrictions due to equality, facilitating incremental programming. Modular programming allows one to restrict parametric polymorphism and use immediate binding, which can be useful for conserving efficiency of execution.

The modular programming model permits the easy extension of functions on non-extensible recursive data types. If one wishes to add a case in a variant type, it will be necessary to modify a large part of the sources.
The object model of programming defines a set of recursive data types using classes. One interprets a class as a case of the data type.

Efficiency of Execution

Late binding corresponds to an indirection in the method table (see page ??). Just as the access to an instance variable from outside the class goes through a message dispatch, this accumulation of indirections can prove to be costly.

To show this loss of efficiency, we construct the following class hierarchy:

# class virtual test () =
object
method virtual sum : unit -> int
method virtual sum2 : unit -> int
end;;
# class a x =
object(self)
inherit test ()
val a = x
method a = a
method sum () = a
method sum2 () = self#a
end;;
# class b x y =
object(self)
inherit a x as super
val b = y
method b = b
method sum () = b + a
method sum2 () = self#b + super#sum2()
end;;


Now, we compare the execution time, on one hand of the dispatch of messages sum and sum2 to an instance of class b, and on the other hand of a call to the following function f.

# let f a b = a + b ;;
# let iter g a n = for i = 1 to n do ignore(g a) done ; g a ;;
# let go i j = match i with
1 -> iter (fun x -> x#sum()) (new b 1 2) j
| 2 -> iter (fun x -> x#sum2()) (new b 1 2) j
| 3 -> iter (fun x -> f 1 x) 2 j ;;

# go (int_of_string (Sys.argv.(1))) (int_of_string (Sys.argv.(2))) ;;


For 10 million iterations, we get the following results:
  bytecode native
case 1 07,5 s 0,6 s
case 2 15,0 s 2,3 s
case 3 06,0 s 0,3 s

This example has been constructed in order to show that late binding has a cost relative to the standard static binding. This cost depends on the quantity of calculation relative to the number of message dispatches in a function. The use of the native compiler reduces the calculation component without changing the indirection component of the test. We can see in case 2 that the multiple indirections at the dispatch of message sum2 have an ``incompressible'' cost.

Example: Graphical Interface

The AWI graphical library (see page ??) was designed using the functional/imperative core of the language. It is very easy to adapt it into module form. Each component becomes an independent module, thus permitting a harmonization of function names. To add a component, it is necessary to know the concrete type of its components. It is up to the new module to modify the fields necessary to describe its appearance and its behaviors.

The library can also be rewritten as an object. For this we construct the hierarchy of classes shown in figure 16.1.



Figure 16.1: Class hierarchy for AWI.


It is easier to add new components, thanks to inheritance, than when using modules; however, the absence of overloading still requires options to be encoded as method parameters. The use of the subtyping relation makes it easy to construct a list of the constituents of a container. Deferred linking selects the methods appropriate to the component. The interest of the object model also comes from the possibility of extending or modifying the graphics context, and the other types that are used, again thanks to inheritance. This is why the principal graphics libraries are organised according to the object model.

Translation of Modules into Classes

A simple module which only declares one type and does not have any type-independent polymorphic functions can be translated into a class. According to the nature of the type used (record type or variant type) one translates the module into a class in a different way.

Type Declarations

Record type.
A record type can be written directly in the form of a class in which every field of the record type becomes an instance variable.

Variant type.
A variant type translates into many classes, using the conceptual model of a ``composite''. An abstract class describes the operations (functions) on this type. Every branch of the variant type thus becomes a subclass of the abstract class, and implements the abstract methods for its branch. We no longer have pattern matching but instead choose the method specific to the branch.

Parameterized types.
Parameterized types are implemented by parameterized classes.

Abstract types.
We can consider a class as an abstract type. At no time is the internal state of the class visible outside its hierarchy. Nevertheless, nothing prevents us from defining a subclass in order to access the variables of the instances of a class.

Mutually recursive types.
The declarations of mutually recursive types are translated into declarations of mutually recursive classes.

Function Declarations

Those functions with parameters dependent on the module type, t, are translatable into methods. Functions in which t does not appear may be declared private inasmuch as their membership of the module is not directly linked to the type t. This has the added advantage that there is no problem if type variables appear in the type of the parameters. We are left with the problem of functions in which one parameter is of type t and another is of type 'a. These functions are very rare in the modules of the standard library. We can identify ``peculiar'' modules like Marshal or Printf which have non-standard typing, and modules (that operate) on linear structures like List. For this last, the function fold_left, of type ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a is difficult to translate, especially in a method of the class ['b] list because the type variable 'a is free and may not appear in the type of the method. Rather than adding a type parameter to the list class, it is preferable to break these functions out into new classes, parameterized by two type variables and having a list field.

Binary methods.
Binary methods do not pose any problem, outside subtyping.

Other declarations.
Declarations of non-functional values. We can accept the declaration of non-functional values outside classes. This is also true for exceptions.

Example: Lists with Iterator.
We are trying to translate a module with the following signature LIST into an object.


# module type LIST = sig
type 'a list = C0 | C1 of 'a * 'a list
val add : 'a list -> 'a -> 'a list
val length : 'a list -> int
val hd : 'a list -> 'a
val tl : 'a list -> 'a list
val append : 'a list -> 'a list -> 'a list
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
end ;;


First of all, we declare the abstract class 'a list corresponding to the definition of the type.

# class virtual ['a] list () =
object (self : 'b)
method virtual add : 'a -> 'a list
method virtual empty : unit -> bool
method virtual hd : 'a
method virtual tl : 'a list
method virtual length : unit -> int
method virtual append : 'a list -> 'a list
end ;;


Then we define the two subclasses c1_list and c0_list for each constituent of the variant type. Each of these classes should define the methods of the ancestor abstract class

# class ['a] c1_list (t, q) =
object (self )
inherit ['a] list () as super
val t = t
val q = q
method add x = new c1_list (x, (self : 'a #list :> 'a list))
method empty () = false
method length () = 1 + q#length()
method hd = t
method tl = q
method append l = new c1_list (t,q#append l)
end ;;
# class ['a] c0_list () =
object (self)
inherit ['a] list () as super
method add x = new c1_list (x, (self : 'a #list :> 'a list))
method empty () = true
method length () = 0
method hd = failwith "c0_list : hd"
method tl = failwith "c0_list : tl"
method append l = l
end ;;
# let l = new c1_list (4, new c1_list (7, new c0_list ())) ;;
val l : int list = <obj>


The function LIST.fold_left has not been incorporated into the list class to avoid introducing a new type parameter. We prefer to define the class fold_left to implement this method. For this, we use a functional instance variable (f).

# class virtual ['a,'b] fold_left () =
object(self)
method virtual f : 'a -> 'b -> 'a
method iter r (l : 'b list) =
if l#empty() then r else self#iter (self#f r (l#hd)) (l#tl)
end ;;
# class ['a,'b] gen_fl f =
object
inherit ['a,'b] fold_left ()
method f = f
end ;;


Thus we construct an instance of the class gen_fl for addition:

# let afl = new gen_fl (+) ;;
val afl : (int, int) gen_fl = <obj>
# afl#iter 0 l ;;
- : int = 11


Simulation of Inheritance with Modules

Thanks to the relation of inheritance between classes, we can retrieve in a subclass the collection of variable declarations and methods of the ancestor class. We can simulate this relation by using modules. The subclass which inherits is transformed into a parameterized module, of which the parameter is the ancestor class. Multiple inheritance increases the number of parameters of the module. We revisit the classic example of points and colored points, described in chapter 15, to translate it into modules.

The class point becomes the module Point with the following signature POINT.

# module type POINT =
sig
type point
val new_point : (int * int) -> point
val get_x : point -> int
val get_y : point -> int
val moveto : point -> (int * int) -> unit
val rmoveto : point -> (int * int) -> unit
val display : point -> unit
val distance : point -> float
end ;;


The class colored_point is transformed into a parameterized module ColoredPoint which has the signature POINT as its parameter.

# module ColoredPoint = functor (P : POINT) ->
struct
type colored_point = {p:P.point;c:string}
let new_colored_point p c = {p=P.new_point p;c=c}
let get_c self = self.c
(* begin *)
let get_x self = let super = self.p in P.get_x super
let get_y self = let super = self.p in P.get_y super
let moveto self = let super = self.p in P.moveto super
let rmoveto self = let super = self.p in P.rmoveto super
let display self = let super = self.p in P.display super
let distance self = let super = self.p in P.distance super
(* end *)
let display self =
let super = self.p in P.display super; print_string ("has color "^ self.c)
end ;;


The burden of ``inherited'' declarations can be lightened by an automatic translation procedure, or an extension of the language. Recursive method declarations can be written with a single let rec ... and. Multiple inheritance leads to functors with many parameters. The cost of redefinition is not greater than that of late binding.

Late binding is not implemented in this simulation. To achieve it, it is necessary to define a record in which each field corresponds to the type of its functions/methods.

Limitations of each Model

The functional/modular module offers a reassuring but rigid framework for the modifiability of code. Objective CAML's object model suffers from ``double vision'' of classes: structuring and type, implying the absence of overloading and the impossibility of imposing type constraints from an ancestor type on a descendant type.

Modules

The principal limitations of the functional/modular model arise from the difficulty of extending types. Although abstract types allow us to get away from the concrete representation of a type, their use in parameterized modules requires that type equalities between modules be indicated by hand, complicating the writing of signatures.

Recursive dependencies.
The dependence graph of the modules in an application is a directed acyclic graph (DAG). This implies on the one hand that there are no types that are mutually recursive between two modules, and on the other prevents the declaration of mutually recursive values.

Difficulties in writing signatures.
One of the attractions of type inference is that it is not necessary to specify the types of function parameters. The specification of signatures sacrifices this convenience. It becomes necessary to specify the types of the declarations of the signature ``by hand.'' One can use the -i option of the compiler ocamlc to display the type of all the global declarations in a .ml file and use this information to construct the signature of a module. In this case, we lose the ``software engineering'' discipline which consists of specifying the module before implementing it. In addition, if the signature and module undergo large changes, we will have to go back to editing the signature. Parameterized modules need signatures for their parameters and those should also be written by hand. Finally if we associate a functional signature with a parameterized module, it is impossible to recove the signature resulting from the application of the functor. This obliges us to mostly write non-functional signatures, leaving it until later to assemble them to construct a functional signature.

Import and export of modules.
The importation of the declarations of a simple module is achieved either by dot notation (Module.name) or directory by the name of a declaration (name) if the model has been opened (open Module). The declaration of the interface of the imported module is not directly exportable at the level of the module in process of being defined. It has access to these declarations, but they are not considered as declarations of the module. In order to do this it is necessary to declare, in the same way as the simulation of inheritance, imported values. The same is true for parameterized modules. The declarations of the module parameters are not considered as declarations of the current module.

Objects

The principle limitations of the Objective CAML object model arise from typing.
  • no methods containing parameters of free type;
  • difficulty of escaping from the type of a class in one of its methods;
  • absence of type constraint from the ancestor type on its descendant;
  • no overloading;
The most disconcerting point when you start with the object extension of Objective CAML is the impossibility of constructing methods containing a parameterized type in which the type parameter is free. The declaration of a class can be seen as the definition of a new type, and hence arises the general rule forbidding the presence of variables with free type in the declaration of a type. For this reason, parameterized classes are indispensable in the Objective CAML object model because they permit the linking of their type variables.

Absence of overloading.
The Objective CAML object model does not allow method overloading. As the type of an object corresponds to types of its methods, the fact of possessing many methods with the same name but different types would result in numerous ambiguities, due to parametric polymorphism, which the system could only resolve dynamically. This would be contradictory to the vision of totally static typing. We take a class example which has two message methods, the first having an integer parameter, and the second a float parameter. Let e be an instance of this class and f be the following function:

# let f x a = x#message a ;;
The calls f e 1 et f e 1.1 cannot be statically resolved because there is no information about the class example in the code of the function f.

An immediate consequence of this absence is the uniqueness of instance constructors. The declaration of a class indicates the parameters to supply to the creation function. This constructor is unique.

Initialization.
The initialization of instance variables declared in a class can be problematic when it should be calculated based on the values passed to the constructor.

Equality between instances.
The only equality which applies to objects is physical equality. Structural equality always returns false when it is applied to two physically different objects. This can be surprising inasmuch as two instances of the same class share the same method table. One can imagine a physical test on the method table and a structural test on the values (val) of objects. These are the implementation choices of the linear pattern-matching style.

Class hierarchy.
There is no class hierarchy in the language distribution. In fact the collection of libraries are supplied in the form of simple or parameterized modules. This demonstrates that the object extension of the language is still stabilizing, and makes little case for its extensive use.


Previous Contents Next ocaml-book-1.0/en/html/videoc-ocda.css0000644000000000000000000001250007452056113014521 0ustar /* $Id: videoc-ocda.css,v 1.1.1.1 2000/06/26 14:37:09 xleroy Exp $ * The cascading style sheet for VideoC. * * ATTENTION: Do not modify videoc.css but videoc.css.cpp instead! * Colors and fonts are defined in videoc.colors.cpp. */ /* $Id: videoc-ocda.css,v 1.1.1.1 2000/06/26 14:37:09 xleroy Exp $ * Colors for the style sheets of VideoC. * * They are used to regenerates the videoc*.css files. NOTE: Arial does not seem to use the ISO encoding: e' is correct but e` a` are incorrect. Revert to Times everywhere. */ /* end of videoc.colors.cpp */ BODY, .regularBody { font-family: "Times", serif ; margin-left: 20px; margin-right: 20px; color: black ; background-color: #ffffff ; } .topic, .subtopic, .subsubtopic, .lesson, .solution, .exotext { font-size: 80%; color: black ; font-size: medium; } A, .reference { color: seagreen; /*rgb(231,141,57) ;*/ font-weight: bold; } .bannerText { color: rgb(231,141,57) ; font-weight: bold; } .banner, .bannerSolution { color: rgb(231,141,57) ; font-weight: bold; align: center; border: solid 0 black ; padding: 0; } .tocAlphabet { align: center; font-size: 140%; } .detail { display: inline; } .relatedTable { align: center; margin-left: 10; margin-right: 10; margin-top: 5; } .requiredTable TH, .requiredTable TD { valign: top; align: center; } .suggestedTable TH, .suggestedTable TD { valign: top; align: center; } .related, .credits { font-family: "Times", serif ; display: block; font-size: 80%; align: center; width: 100%; margin-right: 50%; padding: 10px; border: solid 2px rgb(231,141,57) ; color: black ; background-color: rgb(53, 229, 175) ; } .related A, .credits A { color: black ; font-weight: bold; } .authorEmail, .careTakerEmail { width: 50%; align: right; font-family: "Times", serif ; font-size: 80%; color: red; } .suggested, .required { display: wrap; width: 100%; align: right; font-family: "Times", serif ; font-size: 100%; color: black ; } .wordSearch { border-width: 3px; border-color: blue; background-color: lightblue; } h1 { font-family: "Helvetica", serif ; display: block; align: center; width: 100%; padding: 10px; border-width: 3px; border-color: rgb(231,141,57) ; font-size: 200%; color: ivory; background-color: steelblue; /* PeMOC background rgb(231,141,57) ; color: #ffffff ; */ } .specialH1 { font-family: "Times", serif ; display: block; align: center; width: 100%; padding: 10px; border-width: 3px; border-color: rgb(231,141,57) ; font-size: 200%; color: black ; background-color: white; } h2, .topicHeader, .lessonHeader, .exerciseHeader, .answerHeader { font-family: "Helvetica", serif ; display: block; width: 100%; align: center; padding: 10px; font-size: 200%; color: ivory; /*gold; *//*steelblue; */ background-color: steelblue; /*rgb(213,141,57);*/ /*darkblue;*/ /* PeMOC rgb(231,141,57) ; color: #ffffff ; */ } h3, .subtopicHeader { display: block; align: center; width: 100%; padding: 5px; font-size: 140%; color: ivory; background-color: steelblue; /* color: rgb(231,141,57) ; */ /* background-color: #ffffff ;*/ } h4, .subsubtopicheader { display: block; align: center; width: 100%; padding: 1px; font-size: 100%; font-style: italic; color: ivory; background-color: steelblue; /* color: rgb(231,141,57) ; background-color: #ffffff ; */ } .message { align: center; padding: 10px; width: 66%; font-size: 130%; font-style: bolder; color: rgb(231,141,57) ; background-color: rgb(248,255,222) ; border: solid thick rgb(231,141,57) ; } /* The overall style for code excerpts. */ .codeInline { font-family: monospace; } .code, .c, .scheme, .tex, .perl, .sh, .asm, .makefile, .gdb, .shc, .syntax, .pascal, .texForAnnote, .texForSty, .schemeSlide, .vi { font-family: monospace; margin-left: 20; margin-right: 20; margin-top: 5; margin-bottom: 5; padding: 5px; color: red; background-color: rgb(248,255,222) ; border: solid thin rgb(231,141,57) ; } PRE { font-family: monospace; white-space: pre; margin-left: 20; margin-right: 20; margin-top: 5; margin-bottom: 5; padding: 5px; color: darkblue ; background-color: rgb(238,238,224); /*ivory;*/ /*beige;*/ /*sienna; *//*indianred; *//*orange;*/ /* PeMOC color: red; background-color: rgb(248,255,222) ; */ border: solid thin rgb(231,141,57) ; } .inclusion, .cInclusion, .texForAnnoteInclusion, .shInclusion, .perlInclusion, .texInclusion, .schemeSlideComment, .schemeInclusion, .schemeComment { color: purple; background-color: rgb(248,255,222) ; } /* Anchors that are mouse-sensitive should not be too emphasized * certainly not as a real anchor. */ .mousable { color: coral; /*seagreen; *//*steelblue;*/ /* PeMOC color: blue; */ text-decoration: none ! important; } /* The overall style for popups. Popups appear when moused over. * They stay or disappear, permanently, if clicked over. */ .hint { color: darkblue; padding: 5px; margin: 5px; font-size: medium; /* background-color: rgb(53, 229, 175) ;*/ background-color : orange; border: solid thin red; } /* For QCM */ .okChoice { color: green; margin-left: 20; } .koChoice { color: red; margin-left: 20; } /* end of videoc.css */ ocaml-book-1.0/en/html/book-ora089.html0000644000000000000000000004330407453055400014477 0ustar Module Weak Previous Contents Next

Module Weak

A weak pointer is a pointer to a region which the garbage collector may reclaim at any moment. It may be surprising to speak of a value that might disappear at any moment. In fact, we must see these weak pointers as a reservoir of values that may still be available. This turns out to be particularly useful when memory resources are small compared to the elements to be saved. The classic case is the management of a memory cache: a value may be lost, but it remains directly accessible as long as it exists.

In Objective CAML one cannot directly manipulate weak pointers, only arrays of weak pointers. The Weak module defines the abstract type 'a Weak.t, corresponding to the type 'a option array, a vector of weak pointers of type 'a. The concrete type 'a option is defined as follows:
type 'a option = None | Some of 'a;;
The main functions of this module are defined in figure 9.14.

function type
create int -> 'a t
set 'a t -> int -> 'a option -> unit
get 'a t -> int -> 'a option
check 'a t -> int -> bool

Figure 9.14: Main functions of the Weak module.


The create function allocates an array of weak pointers, each initialized to None. The set function puts a value of type 'a option at a specified index. The get function returns the value contained at index n in a table of weak pointers. The returned value is then referenced, and no longer reclaimable as long as this reference exists. To verify the effective existence of a value, one uses either the check function or pattern matching on the 'a option type's patterns. The former solution does not depend on the representation choice for weak pointers.

Standard functions for sequential structures also exist: length, for the length, and fill and blit for copies of parts of the array.

Example: an Image Cache

In an image-processing application, it is not rare to work on several images. When the user moves from one image to another, the first is saved to a file, and the other is loaded from another file. In general, only the names of the latest images processed are saved. In order to avoid overly frequent disk access while at the same time not using too much memory space, we use a memory cache which contains the last images loaded. The contents of the cache may be freed if necessary. We implement this with a table of weak pointers, leaving the decision of when to free the images up to the garbage collector. To load an image we first search the cache. If the image is there, it becomes the current image. If not, its file is read.

We define a table of images in the following manner:

# type table_of_images = {
size : int;
mutable ind : int;
mutable name : string;
mutable current : Graphics.color array array;
cache : ( string * Graphics.color array array) Weak.t } ;;
The field size gives the size of the table; the field ind gives the index of the current image; the field name, the name of the current image; the field current, the current image, and the field cache contains the array of weak pointers to the images. It contains the last images loaded and their names.

The function init_table initializes the table with its first image.

# let open_image filename =
let ic = open_in filename
in let i = ((input_value ic) : Graphics.color array array)
in ( close_in ic ; i ) ;;
val open_image : string -> Graphics.color array array = <fun>

# let init_table n filename =
let i = open_image filename
in let c = Weak.create n
in Weak.set c 0 (Some (filename,i)) ;
{ size=n; ind=0; name = filename; current = i; cache = c } ;;
val init_table : int -> string -> table_of_images = <fun>


The loading of a new image saves the current image in the table and loads the new one. To do this, we must first try to find the image in the cache.

# exception Found of int * Graphics.color array array ;;
# let search_table filename table =
try
for i=0 to table.size-1 do
if i<>table.ind then match Weak.get table.cache i with
Some (n,img) when n=filename -> raise (Found (i,img))
| _ -> ()
done ;
None
with Found (i,img) -> Some (i,img) ;;



# let load_table filename table =
if table.name = filename then () (* the image is the current image *)
else
match search_table filename table with
Some (i,img) ->
(* the image found becomes the current image *)
table.current <- img ;
table.name <- filename ;
table.ind <- i
| None ->
(* the image isn't in the cache, need to load it *)
(* find an empty spot in the cache *)
let i = ref 0 in
while (!i<table.size && Weak.check table.cache !i) do incr i done ;
(* if none are free, take a full slot *)
( if !i=table.size then i:=(table.ind+1) mod table.size ) ;
(* load the image here and make it the current one *)
table.current <- open_image filename ;
table.ind <- !i ;
table.name <- filename ;
Weak.set table.cache table.ind (Some (filename,table.current)) ;;
val load_table : string -> table_of_images -> unit = <fun>
The load_table function tests to see if the image requested is current. If not, it checks the cache to see if the image exists; if that fails, the function loads the image from disk. In either of the latter two cases, it makes the image become the current one.

To test this program, we use the following cache-printing function:

# let print_table table =
for i = 0 to table.size-1 do
match Weak.get table.cache ((i+table.ind) mod table.size) with
None -> print_string "[] "
| Some (n,_) -> print_string n ; print_string " "
done ;;
val print_table : table_of_images -> unit = <fun>


Then we test the following program:

# let t = init_table 10 "IMAGES/animfond.caa" ;;
val t : table_of_images =
{size=10; ind=0; name="IMAGES/animfond.caa";
current=
[|[|7372452; 7372452; 7372452; 7372452; 7372452; 7372452; 7372452;
7372452; 7372452; 7372452; 7372452; 7372452; 7505571; 7505571; ...|];
...|];
cache=...}
# load_table "IMAGES/anim.caa" t ;;
- : unit = ()
# print_table t ;;
IMAGES/anim.caa [] [] [] [] [] [] [] [] [] - : unit = ()


This cache technique can be adapted to various applications.






Previous Contents Next ocaml-book-1.0/en/html/book-ora109.html0000644000000000000000000000330507453055400014465 0ustar Summary Previous Contents Next

Summary

This chapter has introduced several Objective CAML tools for lexical analysis (lexing) and syntax analysis (parsing). We explored (in order of occurrence):
  • module Str to filter rational expressions;
  • module Genlex to easily build simple lexers;
  • the ocamllex tool, a typed integration of the lex tool;
  • the ocamlyacc tool, a typed integration of the yacc tool;
  • the use of streams to build top-down parsers, including contextual parsers.
Tools ocamllex and ocamlyacc were used to define a parser for the language Basic more easily maintained than that introduced in page ??.


Previous Contents Next ocaml-book-1.0/en/html/book-ora018.gif0000644000000000000000000000724207452056111014270 0ustar GIF89a 𪪪ddd!, H0I8{`(dihlp,ϬBx|臨pH, rl:ʧtJ֬v %p+.(2z8-ۍ~˳|!~k h gf^{ebǨ|aΝ{`Քz_ w\݅z|Ż[AU᫵Xo "L")Av.& q"Y rǑ/, *Ht0_ő(.h HVy4h+s4&̓5J#ZǨL? ;:xEJ-V_6ڈD6Flୃ +47N]+#3>ɘi83pWaTґMZͺְټMڸͻNȅO|iic Gd1bB gV阙k0Y Z \sPwȧ!8(Tc$Bd*CȞ 7h#z#=*:)<kUg ++N;.y%7J瘙r*l*c&ࢡʨ* %J jz _cVΚz6 4j;̺Ֆz/ X/ K f/ Ky* ,'>0*pK,L.\0eF# s{Kf۪ 2ƋR/0Z;!rnB#>ogg-bYs0Jp:Mf\dpqZrB׽t˽7>jv7WP~^h^wefTB aDIM4{'LA:K@%;F*S==6Ԧu1MhG*.գ3)TIEVuuO*QQxDN[ϣ(\vnr]C X)]d]]k\ؤ)+i Y4T {,ƆP-qʣ26GQcԠJiȂ 68bl6t|sT *du%ZqӪi{Z*Tn]Ilb5`o 7u{yMX=߁w-~ރg S8F_awB0CaXӉ=aR}g&c Ƶ0pplذ>qXi,d 똉H̚%3Y5Of,*KVᔳB.p^]u"fv|f4ٕ[|)g2u/(95Y>s|6,!s\ &f;5Ak.|R.%[k ҀYNz}uWڽN3_%GXɵVk:v+12SA6i'[ةtg]^v{%bMUc[Oǹ.Ҽ.i_~-mzm_ wٸL/ۃ[左+isok*SzK-u/qyK[䞵o4};gכھrny6Ws.AzT28)+B]SG@bowы x/ݵ0,KnTw~4fx?ݐװ!߇AHDXGgggBƀ?goFxhhid7uHfׁh|f|ifih烂iUGylwN7^k;xX%(x7zx\Cxyz4|MqJЦz($lV({Xisk_1GuUzlZ}xqhv;rFsݗp@nNVn~r yguOsx{HtBrcgyixr/we}kuzWv8:xwfmփ%s,{Cǃvwex^Dchz'dȈNֈehp7ŋEyṿyhQ8xH{苅x׎h膠81gC^K'w∈Yv*uvhYȇh7  Nyֈ_:6lhv )nok\_xuȒU_ط{ג}}h}v}LC|w?IJY{.fcWcjhTVl52fxgSloYnL0tvyxpz e}9egnAƕY]M'/eDw8#BHfY'X)x,0䂠9i-9>P[]Xyfmw_ȄǍWwik>z*yGHz48YMHĸfhݹwɌqP6Gc(,C:Hgyi7ZH'q"oṆW6מ ?Ƞ9X.Ip!9SɞSFe!::uȑZwx/ꞀJz(@ ɡ,*h3n2KZM(YIڟIՈْ饜HY)i)詝葍.vrJpZ9h2UZ:WdCju"YlJԓB(ZaWaOYBZ*SY@&֗Mbī_$ (YiyZy٬=|91c!zЭ:j ;ocaml-book-1.0/en/html/book-ora068.gif0000644000000000000000000000572207452056112014277 0ustar GIF89avUUU!,vڋ޼ KI L+ (L*fШBԪ-jӬ r2B*NlK{nt5R!pXhxC )9IYiy R IZ3j+3{Qn*ٵjɎ]{ցpҕW|!hի`u&p]VVȎ#udM2ʖ'\>GQϤKï 3$"$ԫUΑmr#g{mݷaY4Φvknw!/ D+ȞtpIBPSH1A`oko%_^4. gL  h]wuhbn%^ VFhuGcn(&tkxB~'^hYq A$1ʸ% f'eObe W9!]i"lNWvyVggppYb(Jgj6*hL9%dj)~^pJj"R%SA; kL0xE=ȵ)A&iE B,p8 DVF6[=L?챇 rT"5 y[͵njj&f{U5$,n.YdېQ \Ra ZpA%F*OVV#o 2zF9Jϧ =;HQITbՠLshFMB@ A`5gB!&^5$zSNPl+J^Uo8;L-y e 1l edf V͜7HNhUS@N qE4-Œ,F@9-6+ 6˂.CJ,9%xuK:utVH6ORNt5&55HP "'p&ȟdqu(-Ah?#Iբ'*JR(܇Cnܰ $.+:`!Ȱmc!FP?;UVui$9ZU+3 <d)haYv Ӑ#YZrt2=npUeUꋴRlZ1Э.6׹Ͻ.vmNZW=v]†{w^47}Nxk󢷧L/[l=~8 M ê;wN0^ WpD q-aEi1|} XP_Yt؏;htf .ȌiW{(aE'/|rEq+XZgX61eܭjXCGi&//GX!x&[^@;ȃz\5ͅN;DӹM\'qpiP8 t@꽧x֝t0_fyrHHQ$aO(U0ZH.C̈3ŦmLa&VMuK 7%qD#F|ρʰM;}+7 -_ \Vol؈Kq<䁹Oda< +gOd M#y(wӹ9kt|C?n #'+wk⬇d8 i=Mj )cC=4GWp9u 9( цKYmyߩ4!϶`&ۏVn$P=ӷi#3d|Y9X|j^AGj+'m|߫{ Y ^ o-'\(j#6c|1~C'!42|BF 3s*C@DX@DeFo v z'eA+8&V(turU9 77>hm#PsI7u8ȃsvuKp)#Gr'EUHpgG%r]xKa(cHehgi91R eWG/I+ӆ'/6~_ewvՖXUgdSnCRJ+${0׮A|N,J^PFτGAV&XpXGKo2uLYP?G0U0^xjuM)@}ۇgMy#@и1TҘm|3p({ Qx"n8Qm؇x6PI(`PSkG1q؈ ;ocaml-book-1.0/en/html/book-ora159.html0000644000000000000000000000422207453055400014471 0ustar Introduction Previous Contents Next

Introduction

This chapter illustrates program structure via two examples: the first uses a modular model; the second, an object model.

The first application provides a set of parametric modules for two player games. A functor implements the minimax-ab algorithm for the evaluation of a search tree. A second functor allows modifying the human/machine interface for the game. These parametric modules are then applied to two games: a vertical tic-tac-toe game, and another involving the construction of mystic ley-lines.

The second application constructs a world where robots evolve. The world and robots are structured as classes. The different behaviors of robots are obtained by inheritance from a common abstract class. It is then easy to define new behaviors. There, too, the human/machine interface may be modified.

Each of the applications, in its structure, contains reusable components. It is easy to construct a new two player game with different rules that uses the same base classes. Similarly, the general mechanism for the motion of robots in a world may be applied to new types of robots.




Previous Contents Next ocaml-book-1.0/en/html/book-ora065.gif0000644000000000000000000000453007452056112014270 0ustar GIF89a_UUU!,_ڋ޼HBʶKL x¢Lʦ)HԪj )d|VGǗwhT6hp2c)8IY#BZs Z;$@{c{t; iˀ<&ܜ`=i ] #ЪN^. ":4KmY#l _'=mlؤZ#O6/FrBhTg˘wŜ)礶 'C)hԨ:4f٦j6O+jf5w^ ғWĒʽqüO g_'] .`>a (a^w^"~b&R t)\2tW͈#5S>`H${ܬd dx-)eQie`e~ fbIfGv9 B0ffmqy#٩Ü]&Y)'j w:()($&RJ%NZj%Ij:yz(p%D$>$"Iku:6&<~\i䀽;KR:lva^N;#h{,in ͌"T>!%>[ f/l!(JnԯU&^Fy[ϗ1[Z SF|(&rvLAke<K0Ѽ֯|QBnSCqwY[*S x'*Eo-=vE3]mqns`9K }?]l/]|Ͱ=JR+ UO{Al9OR/`SG aQT 3S s] Q{3|uW5xwĂp/_XLR>ܴ0SfiyR4hx5e\Nf DCYkܠ8:x >z, 7HBH%k$ȌI$#QJ^ Ml]T $ aqB195-d奠:(гiV:3ɬJl.-H"}/Fe\яs]a\BNusS9| RCx☫,tyƪ1ϩ;} XQsx(=@u|& ]Hiq<=f(]J[Mʳ*]6 wE բ#OxU&c~ĦMju\W ְU8UFͫc\/ʺѩcAS}uw}D"bo\bue+jvg? Z4aiC(D敒LiaZ6-j_Re)]Y{tPmoJD"kB vpnrU[ Y̅% .w-(lrvM`ň)0Y}+x7%̜Zz +G(»떗1YQ_S> ݩMwe˜9JZ;LG 5{ I ?/4*.yݤgܵgJrI`h%x@3'ҦeMk)ĶQ^&y+\// 3 ryl=f2sMox{2pf )pf i͊Vo/cLkZ›k?O#Hԝ5&Hڲ'_ֱ>ti]ڶyk]Z3]l=p5Ld+ wCmi ؘ@5;N!lVۉJsU_8X َټUۖnp5<d숝[흵Ž&AǗ];Y_\,7ŻaSş*S|4vor47<2:r{N J<zЋnt#;J76RD֪k;ocaml-book-1.0/en/html/book-ora099.html0000644000000000000000000004411307453055400014477 0ustar Exercises Previous Contents Next

Exercises

Tracing Function Application

This exercise shows the evaluation of arguments at the moment of function application.
  1. Activate tracing of the function List.fold_left and evaluate the following expression:

    List.fold_left (-) 1 [2; 3; 4; 5];;
    What does the trace show you?

    # #trace List.fold_left ;;
    List.fold_left is now traced.

    # List.fold_left (-) 1 [2; 3; 4; 5] ;;
    List.fold_left <-- <fun>
    List.fold_left --> <fun>
    List.fold_left* <-- <poly>
    List.fold_left* --> <fun>
    List.fold_left** <-- [<poly>; <poly>; <poly>; <poly>]
    List.fold_left <-- <fun>
    List.fold_left --> <fun>
    List.fold_left* <-- <poly>
    List.fold_left* --> <fun>
    List.fold_left** <-- [<poly>; <poly>; <poly>]
    List.fold_left <-- <fun>
    List.fold_left --> <fun>
    List.fold_left* <-- <poly>
    List.fold_left* --> <fun>
    List.fold_left** <-- [<poly>; <poly>]
    List.fold_left <-- <fun>
    List.fold_left --> <fun>
    List.fold_left* <-- <poly>
    List.fold_left* --> <fun>
    List.fold_left** <-- [<poly>]
    List.fold_left <-- <fun>
    List.fold_left --> <fun>
    List.fold_left* <-- <poly>
    List.fold_left* --> <fun>
    List.fold_left** <-- []
    List.fold_left** --> <poly>
    List.fold_left** --> <poly>
    List.fold_left** --> <poly>
    List.fold_left** --> <poly>
    List.fold_left** --> <poly>
    - : int = -13


    La trace indique seulement le passage des arguments la fonction List.fold_left, mais n'affiche pas les valeurs des arguments. La nature polymorphe des paramtres de List.fold_left empche la trace de pouvoir afficher les valeurs des arguments passs.


  2. Define the function fold_left_int, identical to List.fold_left, but with type:

    (int -> int -> int) -> int -> int list -> int.
    Trace this function. Why is the output of the trace different?

# let rec fold_left_int f (r : int) (l : int list) =
match l with
[] -> r
| t::q -> fold_left_int f (f r t) q ;;
val fold_left_int : (int -> int -> int) -> int -> int list -> int = <fun>

# #trace fold_left_int ;;
fold_left_int is now traced.
# fold_left_int (-) 1 [2; 3; 4; 5] ;;
fold_left_int <-- <fun>
fold_left_int --> <fun>
fold_left_int* <-- 1
fold_left_int* --> <fun>
fold_left_int** <-- [2; 3; 4; 5]
fold_left_int <-- <fun>
fold_left_int --> <fun>
fold_left_int* <-- -1
fold_left_int* --> <fun>
fold_left_int** <-- [3; 4; 5]
fold_left_int <-- <fun>
fold_left_int --> <fun>
fold_left_int* <-- -4
fold_left_int* --> <fun>
fold_left_int** <-- [4; 5]
fold_left_int <-- <fun>
fold_left_int --> <fun>
fold_left_int* <-- -8
fold_left_int* --> <fun>
fold_left_int** <-- [5]
fold_left_int <-- <fun>
fold_left_int --> <fun>
fold_left_int* <-- -13
fold_left_int* --> <fun>
fold_left_int** <-- []
fold_left_int** --> -13
fold_left_int** --> -13
fold_left_int** --> -13
fold_left_int** --> -13
fold_left_int** --> -13
- : int = -13
# #untrace_all ;;
fold_left_int is no longer traced.
List.fold_left is no longer traced.
La fonction fold_left_int est monomorphe. Le mcanisme de trace connat le type des arguments et peut alors les afficher lors de leur passage.

Performance Analysis

We continue the exercise proposed in chapter 9, page ??, where we compared the evolution of the heap of two programs (one tail recursive and the other not) for calculating primes. This time we will compare the execution times of each function with the profiling tools. This exercise shows the importance of inline expansion (see chapter 7).

  1. Compile the two programs erart and eranrt with profiling options using the bytecode compiler and the native code compiler respectively.
    $ ocamlcp -custom -o era_rt_bc unix.cma interval.ml trgc.ml eras2.ml era2_main.ml main_rt.ml -cclib -lunix
    $ ocamlcp -custom -o era_nrt unix.cma interval.ml trgc.ml eras.ml era2_main.ml main_nrt.ml -cclib -lunix
    $ ocamlopt -p -o era_rt_nat unix.cmxa interval.ml trgc.ml eras2.ml era2_main.ml main_rt.ml -cclib -lunix
    $  ocamlopt -p -o era_nrt_nat unix.cmxa interval.ml trgc.ml eras.ml era2_main.ml main_nrt.ml -cclib -lunix
    


  2. Execute the programs passing them the numbers 3000 4000 5000 6000 on the command line.
    $ era_rt_bc traceRT-BC 3000 4000 5000 6000
    $ era_nrt_bc traceNRT-BC 3000 4000 5000 6000
    $ era_rt_nat traceRT-NAT 3000 4000 5000 6000
    $ era_nrt_nat traceNRT-NAT 3000 4000 5000 6000
    


  3. Visualize the results with the ocamlprof and gprof commands. What can you say about the results? Compilateur de code-octet :

    La commande ocamlprof lit les informations du fichier camlprof.dump. Si plusieurs excutions diffrentes ont t lances, seule l'information de cette dernire est accessible.

    Voici les informations ressorties pour la version rcursive terminale :
    $ ocamlprof eras2.ml
    (* fichier eras2.ml *)
    
    let erart l  = 
      (* 4 *) let rec erart_aux l r = (* 2436 *) match l with 
        [] -> (* 4 *) List.rev r
      | p::q -> (* 2432 *) erart_aux  (List.filter ( fun x -> (* 808410 *) x mod p <> 0) q) (p::r) 
      in 
        erart_aux l [] ;;
    
    let erart_go n = 
      (* 4 *) erart (Interval.interval (<)  (fun x -> (* 17992 *) x + 1) 2 n) ;;
    
    et pour la version rcursive terminale :
    $ ocamlprof eras.ml
    (* fichier eras.ml *)
    
    let rec eras l  = (* 2436 *) match l with 
      [] -> (* 4 *) []
    | p::q -> (* 2432 *) p:: (eras (List.filter ( fun x -> (* 808410 *) x mod p <> 0) q)) ;;
    
    let era_go n = 
      (* 4 *) eras (Interval.interval (<)  (fun x -> (* 17992 *) x + 1) 2 n) ;;
    
    On s'aperoit que dans les deux cas, il y a 2436 appels la fonction principale, dont 4 avec une liste vide et 2432 dans l'autre cas.

    Attention le profilage rend invalide de nombreuses optimisations du compilateur.

    Compilateur natif :

    L'excution d'un excutable autonome natif compil en mode -p produit un fichier gmon.out.

    $ gprof era_rt_nat 
    
    affiche tout d'abord le temps pass dans chaque fonction :
    Flat profile:
    
    Each sample counts as 0.01 seconds.
      %   cumulative   self              self     total           
     time   seconds   seconds    calls  us/call  us/call  name    
     44.44      0.12     0.12   808410     0.15     0.15  Eras2_fun_112
     18.52      0.17     0.05     2436    20.53    32.67  List_rev_append_57
     11.11      0.20     0.03     2432    12.34    73.68  List_find_213
      7.41      0.22     0.02      114   175.44   175.44  sweep_slice
      7.41      0.24     0.02       34   588.24   588.24  mark_slice
      3.70      0.25     0.01    76203     0.13     0.13  allocate_block
      3.70      0.26     0.01    64466     0.16     0.31  oldify
    ...
    
    puis le graphe d'appel.

    $ gprof era_nrt_nat 
    
    affiche tout d'abord le temps pass dans chaque fonction :
    Flat profile:
    
    Each sample counts as 0.01 seconds.
      %   cumulative   self              self     total           
     time   seconds   seconds    calls  us/call  us/call  name    
     42.42      0.14     0.14   808410     0.17     0.17  Eras_code_begin
     15.15      0.19     0.05     2432    20.56    95.63  List_find_213
     15.15      0.24     0.05     2432    20.56    39.31  List_rev_append_57
      6.06      0.26     0.02    93936     0.21     0.53  oldify
      6.06      0.28     0.02    74519     0.27     0.27  allocate_block
      6.06      0.30     0.02       37   540.54   540.54  mark_slice
      3.03      0.31     0.01    74519     0.13     0.40  alloc_shr
    ...
    
    L'outil ocamlprof compte le nombre d'appels ou de passage dans certaines parties d'une fonction sans indication de temps.

    Par contre , l'outil gprof est plus prcis car il indique les temps passs dans chaque fonction.

    Attention les versions compiles avec l'option -p effectuent un travail supplmentaire qui est perceptible lors de mesures de temps. Les versions .exe sont compiles sans cette option.
    $ time era_rt.exe a1 3000 4000 5000 6000
    0.230u 0.010s 0:00.25 96.0%     0+0k 0+0io 129pf+0w
    $ time era_rt_nat a2 3000 4000 5000 6000
    0.510u 0.010s 0:00.52 100.0%    0+0k 0+0io 134pf+0w
    $ time era_nrt.exe a3 3000 4000 5000 6000
    0.220u 0.020s 0:00.24 100.0%    0+0k 0+0io 130pf+0w
    $ time era_nrt_nat a4 3000 4000 5000 6000
    0.520u 0.010s 0:00.53 100.0%    0+0k 0+0io 134pf+0w
    $ visu a1
    Nombre de Gc: mineur = 131, majeur = 20
    $ visu a2
    Nombre de Gc: mineur = 131, majeur = 20
    $ visu a3
    Nombre de Gc: mineur = 131, majeur = 23
    $ visu a4
    Nombre de Gc: mineur = 131, majeur = 23
    
    $ gprof era_rt_nat 
    

Previous Contents Next ocaml-book-1.0/en/html/book-ora108.html0000644000000000000000000010723507453055400014473 0ustar Exercises Previous Contents Next

Exercises

Filtering Comments Out

Comments in Objective CAML are hierarchical. We can thus comment away sections of text, including those containing comments. A comment starts with characters (* and finishes with *). Here's an example:

(* comment spread
over several
lines *)

let succ x = (* successor function *)
x + 1;;

(* level 1 commented text
let old_succ y = (* level 2 successor function level 2 *)
y +1;;
level 1 *)
succ 2;;
The aim of this exercise is to create a new text without comments. You are free to choose whatever lexical analysis tool you wish.

  1. Write a lexer able to recognize Objective CAML comments. These start with a (* and end with a *). Your lexer should ensure comments are balanced, that is to say the number of comment openings equals the number of comment closings. We are not interested in other constructions in the language which may contain characters (* and *).
    (** fichier comment1.mll **)

    {
    let ignore_lex lb = ignore (Lexing.lexeme lb)
    let traite_normal = ignore_lex
    let traite_comment = ignore_lex
    exception Commentaires_mal_balances
    }

    rule normal = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; normal lexbuf }
    | "*)" { raise Commentaires_mal_balances }
    | _ { traite_normal lexbuf ; normal lexbuf }
    | eof { () }

    and commentaire = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; commentaire lexbuf }
    | "*)" { ignore_lex lexbuf }
    | _ { traite_comment lexbuf ; commentaire lexbuf }
    | eof { raise Commentaires_mal_balances }



  2. Write a program which takes a file, reads it, filters comments away and writes a new file with the remaining text.
    (** fichier comment2.mll **)
    {
    let ignore_lex lb = ignore (Lexing.lexeme lb)
    let sortie = ref stdout
    let init_sortie f = sortie := f
    let traite_normal lb = output_string !sortie (Lexing.lexeme lb)
    let traite_comment = ignore_lex
    exception Commentaires_mal_balances
    }

    rule normal = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; normal lexbuf }
    | "*)" { raise Commentaires_mal_balances }
    | _ { traite_normal lexbuf ; normal lexbuf }
    | eof { () }

    and commentaire = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; commentaire lexbuf }
    | "*)" { ignore_lex lexbuf }
    | _ { traite_comment lexbuf ; commentaire lexbuf }
    | eof { raise Commentaires_mal_balances }


    {

    let decommente src dest =
    let file_in = open_in src in
    let lb = Lexing.from_channel file_in in
    let file_out = open_out dest in
    init_sortie file_out ;
    normal lb ;
    close_in file_in ;
    close_out file_out ;;

    let usage () =
    print_string "comment2 filein fileout";
    print_newline() ;;


    let main () =
    if Array.length (Sys.argv) <> 3 then usage ()
    else decommente Sys.argv.(1) Sys.argv.(2) ;;

    main ();;
    }











  3. In Objective CAML character strings may contain any character, even the sequences (* and *). For example, character string "what(*ever te*)xt" should not be considered a comment. Modify your lexer to consider character strings.
    (** fichier comment3.mll **)
    {
    let ignore_lex lb = ignore (Lexing.lexeme lb)
    let sortie = ref stdout
    let init_sortie f = sortie := f
    let traite_normal lb = output_string !sortie (Lexing.lexeme lb)
    let traite_comment = ignore_lex
    exception Commentaires_mal_balances
    }

    rule normal = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; normal lexbuf }
    | "*)" { raise Commentaires_mal_balances }
    | '"' { traite_normal lexbuf ; chaine lexbuf ; normal lexbuf }
    | _ { traite_normal lexbuf ; normal lexbuf }
    | eof { () }

    and commentaire = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; commentaire lexbuf }
    | "*)" { ignore_lex lexbuf }
    | _ { traite_comment lexbuf ; commentaire lexbuf }
    | eof { raise Commentaires_mal_balances }

    and chaine = parse
    '"' { traite_normal lexbuf ; () }
    | "\\\"" { traite_normal lexbuf ; chaine lexbuf }
    | _ { traite_normal lexbuf ; chaine lexbuf }





  4. Use this new lexer to remove comments from an Objective CAML program .
    (** fichier comment4.mll **)
    {
    let ignore_lex lb = ignore (Lexing.lexeme lb)
    let sortie = ref stdout
    let init_sortie f = sortie := f
    let traite_normal lb = output_string !sortie (Lexing.lexeme lb)
    let traite_comment = ignore_lex
    exception Commentaires_mal_balances
    }

    rule normal = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; normal lexbuf }
    | "*)" { raise Commentaires_mal_balances }
    | '"' { traite_normal lexbuf ; chaine lexbuf ; normal lexbuf }
    | _ { traite_normal lexbuf ; normal lexbuf }
    | eof { () }

    and commentaire = parse
    "(*" { ignore_lex lexbuf ; commentaire lexbuf ; commentaire lexbuf }
    | "*)" { ignore_lex lexbuf }
    | _ { traite_comment lexbuf ; commentaire lexbuf }
    | eof { raise Commentaires_mal_balances }

    and chaine = parse
    '"' { traite_normal lexbuf ; () }
    | "\\\"" { traite_normal lexbuf ; chaine lexbuf }
    | _ { traite_normal lexbuf ; chaine lexbuf }


    {

    let decommente src dest =
    let file_in = open_in src in
    let lb = Lexing.from_channel file_in in
    let file_out = open_out dest in
    init_sortie file_out ;
    normal lb ;
    close_in file_in ;
    close_out file_out ;;

    let usage () =
    print_string "comment2 filein fileout";
    print_newline() ;;


    let main () =
    if Array.length (Sys.argv) <> 3 then usage ()
    else decommente Sys.argv.(1) Sys.argv.(2) ;;

    main ();;
    }

Evaluator

We will use ocamlyacc to implement an expression evaluator. The idea is to perform the evaluation of expressions directly in the grammar rules.

We choose a (completely parenthesized) prefix arithmetic expression language with variable arity operators. For example, expression (ADD e1 e2 .. en) is equivalent to e1 + e2 + .. + en. Plus and times operators are right-associative and subtraction and division are left-associative.
  1. Define in file opn_parser.mly the parsing and evaluation rules for an expression.

    %{
    let rec app_right f xs =
    match xs with
    [x] -> x
    | x::xs -> f x (app_right f xs)
    | _ -> failwith"missing argument" ;;
    let rec app_left f xs =
    match xs with
    [x] -> x
    | x1::x2::xs -> app_left f ((f x1 x2)::xs)
    | _ -> failwith"missing argument" ;;

    let t = Hashtbl.create 3 ;;

    (
    Hashtbl.add t "ADD" (app_right (+.));
    Hashtbl.add t "SUB" (app_left (-.));
    Hashtbl.add t "MUL" (app_right ( *.));
    Hashtbl.add t "DIV" (app_left (/.))
    ) ;;

    let apply o vs =
    try
    (Hashtbl.find t o) vs
    with
    Not_found -> (Printf.eprintf"Unknown operator %s\n" o; exit(1)) ;;
    %}

    %token Lpar Rpar
    %token <float> Num
    %token <string> Atom

    %start term
    %type <float> term
    %type <float list> terms

    %%
    term :
    Num { $1 }
    | Lpar Atom terms Rpar
    { (apply $2 $3) }
    ;
    terms :
    term { [$1] }
    | term terms { $1::$2 }
    ;
    %%


  2. Define in file opn_lexer.mll the lexical analysis of expressions.

    {
    open Opn_parser
    }

    rule lexer = parse
    [' ' '\n'] { lexer lexbuf }
    | '(' { Lpar }
    | ')' { Rpar }
    | '-'?['0'-'9']*'.'?['0'-'9']*
    { Num (float_of_string (Lexing.lexeme lexbuf)) }
    | ['A'-'z']+ { Atom (Lexing.lexeme lexbuf) }


  3. Write a simple main program opn which reads a line from standard input containing an expression and prints the result of evaluating the expression.

    open Opn_lexer ;;
    open Opn_parser ;;

    Printf.printf"? "; flush stdout;
    let buf = Lexing.from_string (input_line stdin) in
    Printf.printf "= %f\n" (Opn_parser.term Opn_lexer.lexer buf) ;;

Previous Contents Next ocaml-book-1.0/en/html/book-ora074.gif0000644000000000000000000000612507452056112014272 0ustar GIF89aUUU!,ڋ޼Hʶ L4 !L*ťJjaܮ cNgT[^dt7t8'G2(A&h p00YiӸ(:A79j9jQKvU|k|;,|L!\m=D MuM^Ε-+f^o~@y @L0qs/B4\1b%Z\XQNm1S ?լW~m4ش mܼ \l; m<{Õ"D׃߬[GpL $'ܩ#FeHmd v AJy|d)+җ.:opT0i r) { zL{|vMb+$<|r{ /0w|?ߤ7o}1?}/ޏOJU~6Frc~l;/Uf>ٿKdr? 倂H |d 8@]H\`x ff9!"(…!`vP)aVl ..;ġ1Q>DZH@DQ\7EI+(/t63|CLrч-V AY[h4P>8C>.2F 2G81аGE^N8d {HI0& $?"2}s#"19ʡdrC$jxJ(te1N.+&^br ;pI)|heM^۔A83qD5ÇNm[I4 v^@t 7Ej'p D 3GBEm䆒詏(432)Pt9 tWD-L}<?","O cP(|*fN`:J+銎 8Drbs2XďGUASٔ^PO9G}`H6OGӥ,]?h֏5pUTT"COd*(*6 $S,B=Oyj@'Y KTv}'"*Ml6KBiU o\*|˷߈s# Zz D,qn]MPMBv'W]KOƬw+{W+͜YBڱk|sWd{Vru&𘝀-=ѳ|qVG,f0c(//9c{T j0 9F*LصZ2zHdHٹC歑+GHjD1O6~Pqb&NOQgdۤzNFuMO Ѭ?U=~fMk krc8m654@ˍs+iY*wY2v9 }r.E1\̲D"z]X_"j9z%Ur<&6QIZ\ Wrh\һ&+iT7OxC pB3#* 5h)>bmяn+1Z۩aU3d6y˳}@,TA<4BǣK%j+@*Rb 9VQb:1e:-<|퍾}u^+1(fPNA&luW[Ny)^ypq~oaقWbg헠|º4K'tW~wuT4w~|Mfu hgmّCGhuj aVb/& +}7|6c7FV'VVP"( uaq#ax$.hO#u΢;"xz6xr(~7`ALR7;2ߧy[q6Hmo |g"Zqwf5%+<q"] mjl'('KuVՅ'oS#畁;qyhfń``wD7(!<*'Uք8 :fW^222{6*ć(s7+` |+/3*rtq茤# H=*g AEu 4Jf$MwCHKFPXn('mVt&!iD JOqrdSuh,`.zUZpӂ"w*8s^(K;Ňe1zA"1 W'UxȒnH2]03}}c29yFh'MHD` 唑uUYqਕ0]Y `IP;ocaml-book-1.0/en/html/previous_motif.gif0000644000000000000000000000047507452056124015405 0ustar GIF89app!# Imported from XPM image: prev.xpm!,@63333# B 0 A0 0 0 0 `0 `0 A   `0 `00000000000000000000000000000000000000000000  000 0000000000000000000000000000000` ;ocaml-book-1.0/en/html/book-ora129.html0000644000000000000000000000317507453055400014474 0ustar Chapter Outline Previous Contents Next

Chapter Outline

Section 1 illustrates Objective CAML modules on the example of the Stack module from the standard library, and develops an alternate implementation of this module with the same interface. Section 2 introduces the module language of Objective CAML in the case of simple modules, and shows some of its uses. In particular, we discuss type sharing between modules. Section 3 covers parameterized modules, which are called functors in Objective CAML. Finally, section 4 develops an extended example of modular programming: managing bank accounts with multiple views (the bank, the customer) and several parameters.


Previous Contents Next ocaml-book-1.0/en/html/book-ora061.gif0000644000000000000000000000353107452056112014264 0ustar GIF89a6 UUU!,6 ڋ޼jH扦ʦ LC@]~¢d% j׭ qyN\M;z^'4!T8h6#Іh9Hy(Y)rG0(Yz:@;bpJ{` ))K6#9 kY\묦L|k<*]mkܽM}ZJ.=Y*[{{k`˝@)రC*:@g}a41<"KNi2ʕLTH&1glf̈́:-'D=*SI1SSZ*Q3j5Fد59EڳoX} ͵CΤG9-7cL?Z)i U&&UFY9\a%ԶnLJr6kJ *(nh:)<`4/MQIO^*C):+ԻH"~C=[p.l5 -46$"V <92dw4#XP#@շt4 l m =S֤ ƔMջv|} Pӹwh7?-+lT?m`܏*C*'r~!z˅P|ΞClb5` "]A ^K$!4FB~*}}B dj(~sW0( wD}jyKHD$FQA`@?( Q[, D+&`bXE,^TԢ3+zcHGЄKt"D!$^3gOg&5xMX3kfRM8MS9tN+Mdg;Nw3=g=s9哞g?϶drmc GF"Ԑ\trA4,$E]Wȋbt5JC;Zьl=#"2c! 8ue {uøA5 0"U4N:ƊfAgRF6֫NAÆRRTPSVp OnF JmĺU:xLYƪ\湕s{ZX^bK;>6BF'{كbV@ 2c(Y˸YbVHYX&V\m{ZׂXmJ?!ҊVfI1궣r5\>хt)Z]^7v]~w/p/Yx;ocaml-book-1.0/en/html/book-ora047.gif0000644000000000000000000000216207452056111014266 0ustar GIF89atwww!,tڋؼH扦ʶLĢ&<*Lf JkϩUܮt l+l ޭ(8HCRw8Yiy(IiJp Pz'c (;[zkXk \{)\g L|*M=c}-ӭX|WL|n׮ )C.hޝ .~x Zq/?{'1s 9Vקbyu|tN#7ǐdK,=ƃ2Jk$PΛ{ٔQS{RTEƨ&o|ڕկ:Ы`Ն*, 6nޥ N7dAɶջZ;tGVV광tERH2ܷt_ffDCY?zwzJ]buȢc&mٶdk  {w{|nٱwソ[>{ys-_F~腁.x >Nx^nF"& މ*nb#b(c48&裈@Dnh&u$XVF3%FWnN Or fY^R_fJgb,|ik2*i'ssɖ+{"ӧcj9TKJb9;1Jˢ )gE#y F\WdGFmzsiUp.Š"`DPLo뒱"{쵏yƭa—QMEձ„p=Q^vh晫r [dT*\ ;eDRjv*5n 1

KJ^G\l۪2 y٩|N|a&DGiɱAʇ/GE3yG9kZc7y$3(n:.m.~a{ O||;ocaml-book-1.0/en/html/book-ora077.gif0000644000000000000000000000276707452056112014305 0ustar GIF89a!,H0I8ͻB(_ih Ktm8%p8Fr&;ϦtJv˅D7߮x gچ xKuW:F-?!O̱txv]^pJ 9 ,&30 \" U8≉{,j3CÍ 1`M •0t33I\ћOSU^Þ"{vڶMd7o'M{LS#?iA2؈2iAzzЩxc0ۢ2B($F)i:pVkmz[vknƒ[ԡf ;/Ền[\&Fc /t-m gL^q/8p{,o r\̛;7p @MI PGtdJ S'V XKMdtݵ`!؄mc6lcv]}AwM|5<303wm8BgYٍS11摇q yM nno!_1AOQϛao/C'$;:炬/g83̯o~Fs>f `@)pVs_Ot,x2,}pOM/t! ]ŏ-!g␆bL" *hش#q"<8r3gL/ׅQ+"cF8}h #/9ZG;"rui$' R򒘼%3N.b (1R<*3UR|,Y2 HHrq%/#`3,19c*ӏ:0wHyȌ& To Learn More Previous Contents Next


To Learn More

For a better understanding of the C language, especially argument passing and data representations, the book C: a reference manual [HS94] is highly recommended.

Concerning exceptions and garbage collection, several works add these missing features to C. The technical report [Rob89] describes an implementation of exceptions in C, based on open macros and on the setjmp and longjmp functions from the C library. Hans Boehm distributes a conservative collector with ambiguous roots that can be added (as a library) to any C program:

Link


http://www.hpl.hp.com/personal/Hans_Boehm/gc/


Concerning interoperability between Objective CAML and C, the tools described in this chapter are rather low-level and difficult to use. However, they give the programmer full control on copying or sharing of data structures between the two languages. A higher-level tool called CamlIDL is available; it automatically generates the Objective CAML ``stubs'' (encapsulation functions) for calling C functions and converting data types. The C types and functions are described in a language called IDL (Interface Definition Language), similar to a subset of C++ and C. This description is then passed through the CamlIDL compiler, which generates the corresponding .mli, .ml and .c files. This tool is distributed from the following page:

Link


http://caml.inria.fr/camlidl/


Other interfaces exist between Objective CAML and languages other than C. They are available on the ``Caml hump'' page:

Link


http://caml.inria.fr/hump.html
They include several versions of interfaces with Fortran, and also an Objective CAML bytecode interpreter written in Java.

Finally, interoperability between Objective CAML and other languages can also be achieved via data exchanges between separate programs, possibly over the network. This approach is described in the chapter on distributed programming (see chapter 20).






Previous Contents Next ocaml-book-1.0/en/html/book-ora022.gif0000644000000000000000000000333707452056111014264 0ustar GIF89a UUU!, ڋ޼{G扦:Lv Ǘ)#:u%giTw߱W~њHl6'mݚ_q$U ۍt:=P՜f|8̼?fu؇$A }| >xJ_|т{xؕP7?mHXjmT lWȃ3s]ؕ>u$2׊DDJП{e"_䄯hyRX%W[U%S^t&ޥ |uÚ[s\9bYIt7sg9&!  B mPzGs蛝楬d f`z߭:JhM( [ :*:lxzj dK+ 6 (jV5̸: nA>)*Xk #QVj.=KCZh0j'߉Hΰ۱#k;* \r&rqF1-[-Ҧ1/j z\mm-ȱ/lCR=XomiRZ^w4\Iݞi-1lL=p0z2 ^ơ)y)) 嗳7 ]Jnk ow޺~ܸ\::<޶>;}g5|zw,Jr)~[մ6om82*t^P;/{L@L(PЀԠc| ZЁ?{!@n% Oh>-] hyM"%m0>D{Ҷ CvІJsfJMC4~@mz+9 QDR`E^5BaF72Q8[a5JC3"xC2h68=#߸/Lrd i(~YKc!EJVisdGJaS#`ЕKa->Mn\+"9Y2!*#i]0-9Cd5iMhzTf3Lgr|48iNmHd7gbs) 5OtSK,6r( t Jψ:4LDS}^aF)N~!H)ÛY mѠ ޑW)EEl*mS&l Summary Previous Contents Next

Summary

This chapter presented the main system interface functions provided by the Unix module. Despite of its name, the module offers a large number of functions which can be used under Windows as well (see figure 18.1).
In the area of process creation, we did concentrate on the possibilities of communication between several Objective CAML programs running at the same time on the same machine. Operations handling lower level file access, signals and communication pipes have been discussed in detail.


Previous Contents Next ocaml-book-1.0/en/html/book-ora130.html0000644000000000000000000004345307453055400014467 0ustar Modules as Compilation Units Previous Contents Next

Modules as Compilation Units

The Objective CAML distribution includes a number of predefined modules. We saw in chapter 8 how to use these modules in a program. Here, we will show how users can define similar modules.

Interface and Implementation

The module Stack from the distribution provides the main functions on stacks, that is, queues with ``last in, first out'' discipline.

# let queue = Stack.create () ;;
val queue : '_a Stack.t = <abstr>
# Stack.push 1 queue ; Stack.push 2 queue ; Stack.push 3 queue ;;
- : unit = ()
# Stack.iter (fun n -> Printf.printf "%d " n) queue ;;
3 2 1 - : unit = ()


Since Objective CAML is distributed with full source code, we can look at the actual implementation of stacks.

ocaml-2.04/stdlib/stack.ml
type 'a t = { mutable c : 'a list }
exception Empty
let create () = { c = [] }
let clear s = s.c <- []
let push x s = s.c <- x :: s.c
let pop s = match s.c with hd::tl -> s.c <- tl; hd | [] -> raise Empty
let length s = List.length s.c
let iter f s = List.iter f s.c


We see that the type of stacks (written Stack.t outside the Stack module and just t inside) is a record with one mutable field containing a list. The list holds the contents of the stack, with the list head corresponding to the stack top. Stack operations are implemented as the basic list operations applied to the field of the record.

Armed with this insider's knowledge, we could try to access directly the list representing a stack. However, Objective CAML will not let us do this.

# let list = queue.c ;;
Characters 12-19:
Unbound label c
The compiler complains as if it did not know that Stack.t is a record type with a field c. It is actually the case, as we can see by looking at the interface of the Stack module.

ocaml-2.04/stdlib/stack.mli
(* Module [Stack]: last-in first-out stacks *)
(* This module implements stacks (LIFOs), with in-place modification. *)

type 'a t (* The type of stacks containing elements of type ['a]. *)

exception Empty (* Raised when [pop] is applied to an empty stack. *)

val create: unit -> 'a t
(* Return a new stack, initially empty. *)
val push: 'a -> 'a t -> unit
(* [push x s] adds the element [x] at the top of stack [s]. *)
val pop: 'a t -> 'a
(* [pop s] removes and returns the topmost element in stack [s],
or raises [Empty] if the stack is empty. *)
val clear : 'a t -> unit
(* Discard all elements from a stack. *)
val length: 'a t -> int
(* Return the number of elements in a stack. *)
val iter: ('a -> unit) -> 'a t -> unit
(* [iter f s] applies [f] in turn to all elements of [s],
from the element at the top of the stack to the element at the
bottom of the stack. The stack itself is unchanged. *)


In addition to comments documenting the functions of the module, this file lists explicitly the value, type and exception identifiers defined in the file stack.ml that should be visible to clients of the Stack module. More precisely, the interface declares the names and type specifications for these exported definitions. In particular, the type name t is exported, but the representation of this type (that is, as a record with one c field) is not given in this interface. Thus, clients of the Stack module do not know how the type Stack.t is represented, and cannot access directly values of this type. We say that the type Stack.t is abstract, or opaque.

The interface also declares the functions operating on stacks, giving their names and types. (The types must be provided explicitly so that the type checker can check that these functions are correctly used.) Declaration of values and functions in an interface is achieved via the following construct:

Syntax


val nom : type


Relating Interfaces and Implementations

As shown above, the Stack is composed of two parts: an implementation providing definitions, and an interface providing declarations for those definitions that are exported. All module components declared in the interface must have a matching definition in the implementation. Also, the types of values and functions as defined in the implementation must match the types declared in the interface.

The relationship between interface and implementation is not symmetrical. The implementation can contain more definitions than requested by the interface. Typically, the definition of an exported function can use auxiliary functions whose names will not appear in the interface. Such auxiliary functions cannot be called directly by a client of the module. Similarly, the interface can restrict the type of a definition. Consider a module defining the function id as the identity function (let id x = x). Its interface can declare id with the type int -> nt (instead of the more general 'a -> a). Then, clients of this module can only apply id to integers.

Since the interface of a module is clearly separated from its implementation, it becomes possible to have several implementations for the same interface, for instance to test different algorithms or data structures for the same operations. As an example, here is an alternate implementation for the Stack module, based on arrays instead of lists.


type 'a t = { mutable sp : int; mutable c : 'a array }
exception Empty
let create () = { sp=0 ; c = [||] }
let clear s = s.sp <- 0; s.c <- [||]
let size = 5
let increase s = s.c <- Array.append s.c (Array.create size s.c.(0))

let push x s =
if s.sp >= Array.length s.c then increase s ;
s.c.(s.sp) <- x ;
s.sp <- succ s.sp

let pop s =
if s.sp = 0 then raise Empty
else let x = s.c.(s.sp) in s.sp <- pred s.sp ; x

let length s = s.sp
let iter f s = for i = pred s.sp downto 0 do f s.sc.(i) done


This new implementation satisfies the requisites of the interface file stack.mli. Thus, it can be used instead of the predefined implementation of Stack in any program.

Separate Compilation

Like most modern programming languages, Objective CAML supports the decomposition of programs into multiple compilation units, separately compiled. A compilation unit is composed of two files, an implementation file (with extension .ml) and an interface file (with extension .mli). Each compilation unit is viewed as a module. Compiling the implementation file name.ml defines the module named Name1.

Values, types and exceptions defined in a module can be referenced either via the dot notation (Module.identifier), also known as qualified identifiers, or via the open construct.

      a.ml        b.ml
type t = { x:int ; y:int } ;; let val = { A.x = 1 ; A.y = 2 } ;;
let f c = c.x + c.y ;; A.f val ;;
  open A ;;
  f val ;;

An interface file (.mli file) must be compiled using the ocamlc -c command before any module that depends on this interface is compiled; this includes both clients of the module and the implementation file for this module as well.

If no interface file is provided for an implementation file, Objective CAML considers that the module exports everything; that is, all identifiers defined in the implementation file are present in the implicit interface with their most general types.

The linking phase to produce an executable file is performed as described in chapter 7: the ocamlc command (without the -c option), followed by the object files for all compilation units comprising the program. Warning: object files must be provided on the command line in dependency order. That is, if a module B references another module A, the object file a.cmo must precede b.cmo on the linker command line. Consequently, cross dependencies between two modules are forbidden.

For instance, to generate an executable file from the source files a.ml and b.ml, with matching interface files a.mli and b.mli, we issue the following commands:
> ocamlc -c a.mli
> ocamlc -c a.ml
> ocamlc -c b.mli
> ocamlc -c b.ml
> ocamlc a.cmo b.cmo
Compilation units, composed of one interface file and one implementation file, support separate compilation and information hiding. However, their abilities as a general program structuring tool are low. In particular, there is a one-to-one connection between modules and files, preventing a program to use simultaneously several implementations of a given interface, or also several interfaces for the same implementation. Nested modules and module parameterization are not supported either. To palliate those weaknesses, Objective CAML offers a module language, with special syntax and linguistic constructs, to manipulate modules inside the language itself. The remainder of this chapter introduces this module language.


Previous Contents Next ocaml-book-1.0/en/html/book-ora031.gif0000644000000000000000000000210607452056111014255 0ustar GIF89ajUUU!,jڋ޼{|H扦L-Ģ ̦ \BԪ#jn ߱[~Dϣ_'8Hx#)9IYiy *:JZjzz *K;kW{;+<TW~ xpde@1 %`!*6na%6W PyH]Hcc#/dVAz`7"ERT@w2'b DN|%XyQ(Ē 8sh#h '4dcx#Q) 7'jy=:xT[OX{#XFڦ}b@DYjuc9#0H*n'竞"ꂳ*#%z`fp樱ڊlrh?º8:r ^ Yc+:{j~\u%k겾oe{êm ĬH<(0Kpsq?<1 [2ٮ ˜qǶܯ8s[B.3L?MXPO-`N Wֶt,`db٨)lMp"ܠm'x|kߘ.%NK&G~bdMuz袏NXgS;ocaml-book-1.0/en/html/book-ora180.html0000644000000000000000000000334507453055400014470 0ustar Summary Previous Contents Next

Summary

This chapter tackled the topic of concurrent programming in which a number of processes interact, either through shared memory, or by synchronous communication. The first case represents concurrency for imperative programming. In particular, we have detailed the mechanisms of mutual exclusion whose use permits the synchronization of processes for access to shared memory. Synchronous communication offers a model for concurrency in functional programming. In particular, the possibility of sending closures and synchronization events on communication channels facilitates the composition of calculations carried out in different processes.

The processes used in this chapter are the threads of the Objective CAML Thread module.


Previous Contents Next ocaml-book-1.0/en/html/book-ora173.html0000644000000000000000000000714507453055400014474 0ustar Introduction Previous Contents Next

Introduction

Concurrency is the word used to describe causal independence between a number of actions, such as the execution of a number of instructions ``at the same time''. This is also the definition which we give of the term ``parallel'' in the introduction of this fourth part. The processes of the Unix library presented in the preceding chapter could be considered as concurrent to the extent that the Unix system provides the appearance of their simultaneous execution on a uniprocessor machine. But the notion of process and concurrency does not apply only to those obtained by the fork system call.

The Objective CAML language possesses a library for lightweight processes (threads.) The principle difference between a thread and a process is in the sharing or non-sharing of memory between the different child processes of the same program. Only the context of execution differs between two threads: the code and memory sections of the data are shared. Threads do not improve the execution time of an application. Their principal attraction is to make it possible to express the programming of concurrent algorithms within a language.

The nature of the chosen language, imperative or functional, affects the model of concurrency. For an imperative program, as every thread can modify the communal/shared memory, we are in a shared memory model. Communication between processes can be achieved by values written and read in this memory. For a purely functional program, that is to say, without side effects, even though the memory is shared, the calculations which each process executes do not act on this shared memory. In this case, the model used is that of separate memory and interaction between processes must be achieved by communication of values though channels.

The Objective CAML language implements both models in its thread library. The Thread module makes it possible to start new processes corresponding to a function call with its argument. Modules Mutex and Condition provide the synchronization tools for mutual exclusion and waiting on a condition. The Event model implements a means of communication of language values by events. These values can themselves be functional, thus making it possible to exchange calculations to be carried out between threads. As always in Objective CAML it is possible to mix the two models.

This library is portable to the different systems where OCAML runs. Unlike the Unix module, the Thread library facilitates the use of processes on machines that are not running Unix.


Previous Contents Next ocaml-book-1.0/en/html/book-ora035.gif0000644000000000000000000000641607452056111014271 0ustar GIF89aUUU999rrr!,x0I8ͻ`(dihJE jJkkpH8GE2L DZZجG, E& vn@@ ?-F.PwRssKrw w. zxiPvtjpDusyJ<~P0Tu6Gx:fJGg%ĦteζպGӖ}aPϴIa~ g8v*tE-b%) >IqK9sXqV STى`nuK4'jtQ iZ!.rիEꉫ6`Z ,C#bӪ buo1]K.Օ0׮߿} ,Xr+^̸ǐ#KLrcØ3{̎0ϝA,t2ҦNzpӯc\-;ڸsͻ Nȓ+_μУKNر?νS{3Gos~nCohY&68#@##31xV`Olaf!`!B@\XZqքE`(h:4n%^e$A$f7~@=2he IL5=cflDI%Q<&KzUpd?*艆rq텕(=Z(v 餕f*ZjLTdb}xFq*|`٬CI뮼k :AMT) fLXEݱ*찁b*~`V7S8{qX ,Bn@avcAn-'b"LdmF#1@!L15+{4̰H{ۯ[eh~'A[۲7p%곓?:M@>rƳ@355W'mj4*; `EKHL{HKuVi=>ڳ]ˑm3W7T%wÍbn2g-uA>ZcPޤsӭyZ'y?6YVWӹUe棖K;ߙc^^;Q<;lI_}ȴ}O6]~֮=LRV 9(XZ"*gO`<`&G0.7mDT65FE.ϩ޻U٘ABSBwAD&tUN|(7h jcQNP|$K'^j!rp% iE G6|̍QԟU(: &b" eh|^F%^cȔAcG 6 s`"ǏB@0D|6 &'9pplSE/iV/ʸ2\EΤA'G4{R Q#Y^:59̛ vky<^vK]S]4MxM]PuXBgaIқ4Iҩ3}g}N4pw5+,j!75eR=TmF!鯏KHg xxK|7{/jug macx&5t&3rwYg hQ$e+35D9Jp_H(f$rbd?Un3Հ5& Sb</V/Lpt/{IoTd1<0sc.AB'x|7/Sybh>?8Avf[J%Wai8ePz8ÇQ7J=VC+/;w9aUxzU)Df8\k41Tq|?^5M8x||ECo)" (J;z(g]}l:'8BS<ʸ8~Ҍ4y5Ȍh)g(nXKX9(%\18?8O\w(hHX}6x3X_7?By 7nc)V9h(9d@‘Ӓ.(mz5Y:Q0ARӓ>I<ߣm0S3)BBLO2N3ϣAXYNڡ'"m\Õ(Y9fmYio׊Z\B2`eTRYviQyh7b@w@,l疃Y&]AYkxYJ_Vwė&fbR2 D]y,~1'xpbc ](zUz aĩ3HșX:z9ܸyY ;ocaml-book-1.0/en/html/book-ora029.html0000644000000000000000000001014307453055377014501 0ustar Order of Evaluation of Arguments Previous Contents Next

Order of Evaluation of Arguments

In a pure functional language, the order of evaluation of the arguments does not matter. As there is no modification of memory state and no interruption of the calculation, there is no risk of the calculation of one argument influencing another. On the other hand, in Objective CAML, where there are physically modifiable values and exceptions, there is a danger in not taking account of the order of evaluation of arguments. The following example is specific to version 2.04 of Objective CAML for Linux on Intel hardware:

# let new_print_string s = print_string s; String.length s ;;
val new_print_string : string -> int = <fun>
# (+) (new_print_string "Hello ") (new_print_string "World!") ;;
World!Hello - : int = 12
The printing of the two strings shows that the second string is output before the first.

It is the same with exceptions:

# try (failwith "function") (failwith "argument") with Failure s -> s;;
- : string = "argument"


If you want to specify the order of evaluation of arguments, you have to make local declarations forcing this order before calling the function. So the preceding example can be rewritten like this:

# let e1 = (new_print_string "Hello ")
in let e2 = (new_print_string "World!")
in (+) e1 e2 ;;
Hello World!- : int = 12


In Objective CAML, the order of evaluation of arguments is not specified. As it happens, today all implementations of Objective CAML evaluate arguments from left to right. All the same, making use of this implementation feature could turn out to be dangerous if future versions of the language modify the implementation.

We come back to the eternal debate over the design of languages. Should certain features of the language be deliberately left unspecified---should programmers be asked not to use them, on pain of getting different results from their program according to the compiler implementation? Or should everything be specified---should programmers be allowed to use the whole language, at the price of complicating compiler implementation, and forbidding certain optimizations?


Previous Contents Next ocaml-book-1.0/en/html/book-ora003.html0000644000000000000000000001663407453055377014504 0ustar Description of the language Previous Contents Next

Description of the language

Objective CAML is a functional language:
it manipulates functions as values in the language. These can in turn be passed as arguments to other functions or returned as the result of a function call.

Objective CAML is statically typed:
verification of compatibility between the types of formal and actual parameters is carried out at program compilation time. From then on it is not necessary to perform such verification during the execution of the program, which increases its efficiency. Moreover, verification of typing permits the elimination of most errors introduced by typos or thoughtlessness and contributes to execution safety.

Objective CAML has parametric polymorphism:
a function which does not traverse the totality of the structure of one of its arguments accepts that the type of this argument is not fully determined. In this case this parameter is said to be polymorphic. This feature permits development of generic code usable for different data structures, such that the exact representation of this structure need not be known by the code in question. The typing algorithm is in a position to make this distinction.

Objective CAML has type inference:
the programmer need not give any type information within the program. The language alone is in charge of deducing from the code the most general type of the expressions and declarations therein. This inference is carried out jointly with verification, during program compilation.

Objective CAML is equipped with an exception mechanism:
it is possible to interrupt the normal execution of a program in one place and resume at another place thanks to this facility. This mechanism allows control of exceptional situations, but it can also be adopted as a programming style.

Objective CAML has imperative features:
I/O, physical modification of values and iterative control structures are possible without having recourse to functional programming features. Mixture of the two styles is acceptable, and offers great development flexibility as well as the possibility of defining new data structures.

Objective CAML executes (threads):
the principal tools for creation, synchronization, management of shared memory, and interthread communication are predefined.

Objective CAML communicates on the Internet:
the support functions needed to open communication channels between different machines are predefined and permit the development of client-server applications.

Numerous libraries are available for Objective CAML:
classic data structures, I/O, interfacing with system resources, lexical and syntactic analysis, computation with large numbers, persistent values, etc.

A programming environment is available for Objective CAML:
including interactive toplevel, execution trace, dependency calculation and profiling.

Objective CAML interfaces with the C language:
by calling C functions from an Objective CAML program and vice versa, thus permitting access to numerous C libraries.

Three execution modes are available for Objective CAML:
interactive by means of an interactive toplevel, compilation to bytecodes interpreted by a virtual machine, compilation to native machine code. The programmer can thus choose between flexibility of development, portability of object code between different architectures, or performance on a given architecture.

Structure of a program

Development of important applications requires the programmer or the development team to consider questions of organization and structure. In Objective CAML, two models are available with distinct advantages and features.
The parameterized module model:
data and procedures are gathered within a single entity with two facets: the code proper, and its interface. Communication between modules takes place via their interface. The description of a type may be hidden, not appearing in the module interface. These abstract data types facilitate modifications of the internal implementation of a module without affecting other modules which use it. Moreover, modules can be parameterized by other modules, thus increasing their reusability.

The object model:
descriptions of procedures and data are gathered into entities called classes; an object is an instance (value) of a class. Interobject communication is implemented through ``message passing'', the receiving object determines upon execution (late binding) the procedure corresponding to the message. In this way, object-oriented programming is ``data-driven''. The program structure comes from the relationships between classes; in particular inheritance lets one class be defined by extending another. This model allows concrete, abstract and parameterized classes. Furthermore, it introduces polymorphism of inclusion by defining the subtyping relationship between classes.

The choice between these two models allows great flexibility in the logical organization of an application and facilitates its maintenance and evolution. There is a duality between these two models. One cannot add data fields to a module type (no extensibility of data), but one can add new procedures (extensibility of procedures) acting on data. In the object model, one can add subclasses of a class (extensibility of data) for dealing with new cases, but one cannot add new procedures visible from the ancestor class (no extensibility of procedures). Nevertheless the combination of the two offers new possibilities for extending data and procedures.

Safety and efficiency of execution

Objective CAML bestows excellent execution safety on its programs without sacrificing their efficiency. Fundamentally, static typing is a guarantee of the absence of run-time type errors and makes useful static information available to the compiler without burdening performance with dynamic type tests. These benefits also extend to the object-oriented language features. Moreover, the built-in garbage collector adds to the safety of the language system. Objective CAML's is particularly efficient. The exception mechanism guarantees that the program will not find itself in an inconsistent state after a division by zero or an access outside the bounds of an array.


Previous Contents Next ocaml-book-1.0/en/html/book-ora195.html0000644000000000000000000016463307453055401014507 0ustar The Robots of Dawn Previous Contents Next

The Robots of Dawn

As we promised in the last application of the third part (page ??), we will now revisit the problem of robots in order to treat it in a distributed framework where the world is a server and where each robot is an independent process capable of being executed on a remote machine.

This application is a good summary of the possibilities of the Objective CAML language because we will utilize and combine the majority of its features. In addition to the distributed model which is imposed on us by the exercise, we will make use of concurrency to construct a server in which multiple connections will be handled independently while all sharing a single memory representation of the ``world''. All access to and modification of the state of affairs of the world will therefore have to be protected by critical sections.

In order to reuse as much as possible the code that we have already built for robots in one section, and the client-server architecture of another section, we will use functors and inheritance of classes at the same time.

This application is quite minimal, but we will see that its architecture lends itself particularly well to extensions in multiple directions.

World-Server

We take a representation of the world similar to that which we developed in Part III. The world is a grid of finite size, and each cell of the grid can be occupied by only one robot. A robot is identified by its name and by its position; the world is determined by its size and by the robots that live in it. This information is represented by the following types:


# type position = { x:int ; y:int } ;;

# type robot_info = { name : string ; mutable pos : position }
type world_info = { length : int ; width : int ;
mutable robots : robot_info list } ;;


The world will have to serve two sorts of clients:
  • passive clients which simply observe the positions of various robots. They will allow us to build the clients in charge of displays. We will call them spies.
  • active clients, able to ask the server to move robots and thus modify its state.
These two categories of clients and their behavior will determine the collection of messages exchanged by the server and clients.

When a client connects, it declares itself passive (Spy) or active (Enter). A spy receives as response to its connection the global state of the world. Then, it is kept informed of all changes. However, it cannot submit any requests. A robot which connects must supply its characteristics (its name and its initial position); the world then confirms its arrival. Then, it can request information: its own position (GetPos) or the list of robots that surround it (Look). It can also instruct the world to move it. The protocol of requests to the world from distributed robots is represented by the following type:

# type query =
| Spy (* initial declaration requests *)
| Enter of robot_info

| Move of position (* robot requests *)
| GetPos
| Look of int

| World of world_info (* messages delivered by the world *)
| Pos of robot_info
| Exit of robot_info ;;


From this protocol, using the functors from the ``distributed toolbox'' of the previous chapter, we immediately derive the generic server.

# module Pquery = Make_Protocol (struct type t = query end ) ;;
# module Squery = Server (Pquery) ;;


Now we need only specify the behavior of the server by implementing the method process to handle both the data that represent the world and the data for managing connections.

More precisely, the server contains a variable world (of type world_info) which is protected by the lock sem (of type Mutex.t). It also contains a variable spies which is a list of queues of messages to send to observers, with one queue per spy. To activate the processes in charge of sending these messages, the server also maintains a signal (of type Condition.t).

We provide an auxiliary function dist to calculate the distance between two positions:

# let dist p q = max (abs (p.x-q.x)) (abs (p.y-q.y)) ;;
val dist : position -> position -> int = <fun>


The function critical encapsulates the calculation of a value within a critical section:

# let critical m f a =
Mutex.lock m ; let r = f a in Mutex.unlock m ; r ;;
val critical : Mutex.t -> ('a -> 'b) -> 'a -> 'b = <fun>


Here is the definition of the class server implementing the world-server. It is long, but we will follow it up with a step-by-step explanation.

# class server l w n np =
object (self)
inherit [query] Squery.server n np
val world = { length=l ; width=w ; robots=[] }
val sem = Mutex.create ()
val mutable spies = []
val signal = Condition.create ()

method lock = Mutex.lock sem
method unlock = Mutex.unlock sem

method legal_pos p = p.x>=0 && p.x<l && p.y>=0 && p.y<w

method free_pos p =
let is_not_here r = r.pos.x<>p.x || r.pos.y<>p.y
in critical sem (List.for_all is_not_here) world.robots

method legal_move r p =
let dist1 p = (dist r.pos p) <= 1
in (critical sem dist1 p) && self#legal_pos p && self#free_pos p


method queue_message mes =
List.iter (Queue.add mes) spies ;
Condition.broadcast signal

method trace_loop s q =
let foo = Mutex.create () in
let f () =
try
spies <- q :: spies ;
self#send s (World world) ;
while true do
while Queue.length q = 0 do Condition.wait signal foo done ;
self#send s (Queue.take q)
done
with _ -> spies <- List.filter ((!=) q) spies ;
Unix.close s
in ignore (Thread.create f ())

method remove_robot r =
self#lock ;
world.robots <- List.filter ((<>) r) world.robots ;
self#queue_message (Exit {r with name=r.name}) ;
self#unlock

method try_move_robot r p =
if self#legal_move r p
then begin
self#lock ;
r.pos <- p ;
self#queue_message (Pos {r with name=r.name}) ;
self#unlock
end

method process_robot s r =
let f () =
try
world.robots <- r :: world.robots ;
self#send s (Pos r) ;
self#queue_message (Pos r) ;
while true do
Thread.delay 0.5 ;
match self#receive s with
Move p -> self#try_move_robot r p
| GetPos -> self#send s (Pos r)
| Look d ->
self#lock ;
let dist p = max (abs (p.x-r.pos.x)) (abs (p.y-r.pos.y)) in
let l = List.filter (fun x -> (dist x.pos)<=d) world.robots
in self#send s (World { world with robots = l }) ;
self#unlock
| _ -> ()
done
with _ -> self#unlock ;
self#remove_robot r ;
Unix.close s
in ignore (Thread.create f ())

method process s =
match self#receive s with
Spy -> self#trace_loop s (Queue.create ())
| Enter r ->
( if not (self#legal_pos r.pos && self#free_pos r.pos) then
let i = ref 0 and j = ref 0 in
( try
for x=0 to l do
for y=0 to w do
let p = { x=x ; y=y }
in if self#legal_pos p && self#free_pos p
then ( i:=x ; j:=y; failwith "process" )
done done ;
Unix.close s
with Failure "process" -> r.pos <- { x= !i ; y= !j } )) ;
self#process_robot s r
| _ -> Unix.close s

end ;;
class server :
int ->
int ->
int ->
int ->
object
val nb_pending : int
val port_num : int
val sem : Mutex.t
val signal : Condition.t
val sock : Unix.file_descr
val mutable spies : Pquery.t Queue.t list
val world : world_info
method free_pos : position -> bool
method legal_move : robot_info -> position -> bool
method legal_pos : position -> bool
method lock : unit
method process : Unix.file_descr -> unit
method process_robot : Unix.file_descr -> robot_info -> unit
method queue_message : Pquery.t -> unit
method receive : Unix.file_descr -> Pquery.t
method remove_robot : robot_info -> unit
method send : Unix.file_descr -> Pquery.t -> unit
method start : unit
method trace_loop : Unix.file_descr -> Pquery.t Queue.t -> unit
method try_move_robot : robot_info -> position -> unit
method unlock : unit
end


The method process starts out by distinguishing between the two types of client. Depending on whether the client is active or passive, it invokes a processing method called: trace_loop for an observer, process_robot for a robot. In the second case, it checks that the initial position proposed by the client is compatible with the state of the world; if not, it finds a valid initial position. The remainder of the code can be divided into four categories:
  1. General methods: these are methods which we developed in Part III for general worlds. Mainly, it is a matter of verifying that a displacement is legal for a given robot.
  2. Management of observers: each observer is associated with a socket through which it is sent data, with a queue containing all the messages which have not yet been sent to it, and with a process. The method trace_loop is an infinite loop that empties the queue of messages by sending them; it goes to sleep when the queue is empty. The queues are filled, all at the same time, by the method queue_message. Note that after appending a message, the activation signal is sent to all processes.
  3. Management of robots: here again, each robot is associated with a dedicated process. The method process_robot is an infinite loop: it waits for a request, processes it, and responds if necessary. Then it resumes waiting for the next request. Note that it is these robot-management methods which issue calls to the method queue_message when the state of the world has been modified. If the connection with a robot is lost---that is, if an exception is raised while waiting for a request---the robot is considered to have terminated and its departure is signaled to the observers.
  4. Inherited methods: these are the methods of the generic server obtained by application of the functor Server to the protocol of our application.

Observer-client

The functor Client gives us generic functions for connecting with a server according to the particular protocol that concerns us here.

# module Cquery = Client (Pquery) ;;
module Cquery :
sig
module Com :
sig
val send : Unix.file_descr -> Pquery.t -> unit
val receive : Unix.file_descr -> Pquery.t
end
val connect : string -> int -> Unix.file_descr
val emit_simple : string -> int -> Pquery.t -> unit
val emit_answer : string -> int -> Pquery.t -> Pquery.t
end


The behavior of a spy is simple: it connects to the server and displays the information that the server sends it. The spy includes three display functions which we provide below:

# let display_robot r =
Printf.printf "The robot %s is located at (%d,%d)\n" r.name r.pos.x r.pos.y ;
flush stdout ;;
val display_robot : robot_info -> unit = <fun>

# let display_exit r = Printf.printf "The robot %s has terminated\n" r.name ;
flush stdout ;;
val display_exit : robot_info -> unit = <fun>

# let display_world w =
Printf.printf "The world is a grid of size %d by %d \n" w.length w.width ;
List.iter display_robot w.robots ;
flush stdout ;;
val display_world : world_info -> unit = <fun>


The primary function of the spy-client is:

# let trace_client name port =
let sock = Cquery.connect name port
in Cquery.Com.send sock Spy ;
( match Cquery.Com.receive sock with
World w -> display_world w
| _ -> failwith "the server did not follow the protocol" ) ;
while true do
match Cquery.Com.receive sock with
Pos r -> display_robot r
| Exit r -> display_exit r
|_ -> failwith "the server did not follow the protocol"
done ;;
val trace_client : string -> int -> unit = <fun>


There are two ways of constructing a graphical display. The first is simple but not very efficient: since the server sends the complete set of information when a connection is established, one can simply open a new connection at regular intervals, display the world in its entirety, and close the connection. The other approach involves using the information sent by the server to maintain a copy of the state of the world. It is then easy to display only the modifications to the state upon reception of messages. It is this second solution which we have implemented.

Robot-Client

As we defined them in the previous chapter (cf. page ??), the robots conform to the following signature.


# module type ROBOT =
sig
class robot : int -> int ->
object
val mutable i : int
val mutable j : int
method get_pos : int * int
method next_pos : unit -> int * int
method set_pos : int * int -> unit
end
end ;;


The part that we wish to save from the various classes is that which necessarily varies from one type of robot to another and which defines its behavior: the method next_pos.

In addition, we need a method for connecting the robot to the world (start) and a loop that alternately calculates a new position and communicates with the server to submit the chosen position.

We define a functor which, when given a class implementing a virtual robot (that is, conforming to the signature ROBOT), creates, by inheritance, a new class containing the proper methods to make an autonomous client out of the robot.


# module RobotClient (R : ROBOT) =
struct
class robot robname x y hostname port =
object (self)
inherit R.robot x y as super
val mutable socket = Unix.stderr
val mutable rob = { name=robname ; pos={x=x;y=y} }

method private adjust_pos r =
rob.pos <- r.pos ; i <- r.pos.x ; j <- r.pos.y

method get_pos =
Cquery.Com.send socket GetPos ;
match Cquery.Com.receive socket with
Pos r -> self#adjust_pos r ; super#get_pos
| _ -> failwith "the server did not follow the protocol"

method set_pos =
failwith "the method set_pos cannot be used"

method start =
socket <- Cquery.connect hostname port ;
Cquery.Com.send socket (Enter rob) ;
match Cquery.Com.receive socket with
Pos r -> self#adjust_pos r ; self#run
| _ -> failwith "the server did not follow the protocol"

method run =
while true do
let (x,y) = self#next_pos ()
in Cquery.Com.send socket (Move {x=x;y=y}) ;
ignore (self#get_pos)
done
end
end ;;
module RobotClient :
functor(R : ROBOT) ->
sig
class robot :
string ->
int ->
int ->
string ->
int ->
object
val mutable i : int
val mutable j : int
val mutable rob : robot_info
val mutable socket : Unix.file_descr
method private adjust_pos : robot_info -> unit
method get_pos : int * int
method next_pos : unit -> int * int
method run : unit
method set_pos : int * int -> unit
method start : unit
end
end


Notice that the method get_pos has been redefined as a query to the server: the instance variables i and j are not reliable, because they can be modified without the consent of the world. For the same reason, the use of set_pos has been made invalid: calling it will always raise an exception. This policy may seem severe, but it's a good bet that if this method were used by next_pos then a discrepancy would appear between the real position (as known by the server) and the supposed position (as known by the client).

We use the functor RobotClient to create various classes corresponding to the various robots.


# module Fix = RobotClient (struct class robot = fix_robot end) ;;
# module Crazy = RobotClient (struct class robot = crazy_robot end) ;;
# module Obstinate = RobotClient (struct class robot = obstinate_robot end) ;;


The following small program provides a way to launch the server and the various clients from the command line. The argument passed to the program specifies which one to launch.

# let port = 1200 in
if Array.length Sys.argv >=2 then
match Sys.argv.(1) with
"1" -> let s = new server 25 30 port 10 in s#start
| "2" -> trace_client "localhost" port
| "3" -> let o = new Fix.robot "fix" 10 10 "localhost" port in o#start
| "4" -> let o = new Crazy.robot "crazy" 10 10 "localhost" port in o#start
| "5" -> let o = new Obstinate.robot "obstinate" 10 10 "localhost" port
in o#start
| _ -> () ;;


To Learn More

The world of robots stimulates the imagination. With the elements already given here, one can easily create an ``intelligent robot'' which is both a robot and a spy. This allows the various inhabitants of the world to cooperate. One can then extend the application to obtain a small action game like ``chickens-foxes-snakes'' in which the foxes chase the chickens, the snakes chase the foxes and the chickens eat the snakes.


Previous Contents Next ocaml-book-1.0/en/html/book-ora055.html0000644000000000000000000000144007453055401014464 0ustar Notes
1
Abstract types hide the internal representation of their values. The declaration of such types will be presented in chapter 14.
ocaml-book-1.0/en/html/book-ora025.html0000644000000000000000000000340007453055377014473 0ustar Plan of the Chapter Previous Contents Next

Plan of the Chapter

This chapter continues the presentation of the basic elements of the Objective CAML language begun in the previous chapter, but this time focusing on imperative constructions. There are five sections. The first is the most important; it presents the different modifiable data structures and describes their memory representation. The second describes the basic I/O of the language, rather briefly. The third section is concerned with the new iterative control structures. The fourth section discusses the impact of imperative features on the execution of a program, and in particular on the order of evaluation of the arguments of a function. The final section returns to the calculator example from the last chapter, to turn it into a calculator with a memory.


Previous Contents Next ocaml-book-1.0/en/html/book-ora208.html0000644000000000000000000002567407453055401014503 0ustar Cyclic types Previous Contents Next

Cyclic types

In Objective CAML, it is possible to declare recursive data structures: such a structure may contain a value with precisely the same structure.

 
# type sum_ex1 = Ctor of sum_ex1 ;;
type sum_ex1 = | Ctor of sum_ex1

# type record_ex1 = { field : record_ex1 } ;;
type record_ex1 = { field: record_ex1 }


How to build values with such types is not obvious, since we need a value before building one! The recursive declaration of values allows to get out of this vicious circle.


# let rec sum_val = Ctor sum_val ;;
val sum_val : sum_ex1 = Ctor (Ctor (Ctor (Ctor (Ctor ...))))

# let rec val_record_1 = { field = val_record_2 }
and val_record_2 = { field = val_record_1 } ;;
val val_record_1 : record_ex1 = {field={field={field={field={field=...}}}}}
val val_record_2 : record_ex1 = {field={field={field={field={field=...}}}}}


Arbitrary planar trees can be represented by such a data structure.


# type 'a tree = Vertex of 'a * 'a tree list ;;
type 'a tree = | Vertex of 'a * 'a tree list
# let height_1 = Vertex (0,[]) ;;
val height_1 : int tree = Vertex (0, [])
# let height_2 = Vertex (0,[ Vertex (1,[]); Vertex (2,[]); Vertex (3,[]) ]) ;;
val height_2 : int tree =
Vertex (0, [Vertex (1, []); Vertex (2, []); Vertex (3, [])])
# let height_3 = Vertex (0,[ height_2; height_1 ]) ;;
val height_3 : int tree =
Vertex
(0,
[Vertex (0, [Vertex (...); Vertex (...); Vertex (...)]); Vertex (0, [])])

(* same with a record *)
# type 'a tree_rec = { label:'a ; sons:'a tree_rec list } ;;
type 'a tree_rec = { label: 'a; sons: 'a tree_rec list }
# let hgt_rec_1 = { label=0; sons=[] } ;;
val hgt_rec_1 : int tree_rec = {label=0; sons=[]}
# let hgt_rec_2 = { label=0; sons=[hgt_rec_1] } ;;
val hgt_rec_2 : int tree_rec = {label=0; sons=[{label=0; sons=[]}]}


We might think that an enumerated type with only one constructor is not useful, but by default, Objective CAML does not accept recursive type abbreviations.
 
# type 'a tree = 'a * 'a tree list ;;
Characters 7-34:
The type abbreviation tree is cyclic


We can define values with such a structure, but they do not have the same type.

# let tree_1 = (0,[]) ;;
val tree_1 : int * 'a list = 0, []
# let tree_2 = (0,[ (1,[]); (2,[]); (3,[]) ]) ;;
val tree_2 : int * (int * 'a list) list = 0, [1, []; 2, []; 3, []]
# let tree_3 = (0,[ tree_2; tree_1 ]) ;;
val tree_3 : int * (int * (int * 'a list) list) list =
0, [0, [...; ...; ...]; 0, []]


In the same way, Objective CAML is not able to infer a type for a function whose argument is a value of this form.

# let max_list = List.fold_left max 0 ;;
val max_list : int list -> int = <fun>

# let rec height = function
Vertex (_,[]) -> 1
| Vertex (_,sons) -> 1 + (max_list (List.map height sons)) ;;
val height : 'a tree -> int = <fun>


# let rec height2 = function
(_,[]) -> 1
| (_,sons) -> 1 + (max_list (List.map height2 sons)) ;;
Characters 95-99:
This expression has type 'a list but is here used with type
('b * 'a list) list


The error message tells us that the function height2 could be typed, if we had type equality between 'a and 'b * 'a list, and precisely this equality was denied to us in the declaration of the type abbreviation tree.

However, object typing allows to build values, whose type is cyclic. Let us consider the following function, and try to guess its type.

# let f x = x#copy = x ;;
The type of x is a class with method copy. The type of this method should be the same as that of x, since equality is tested between them. So, if foo is the type of x, it has the form: < copy : foo ; .. >. From what has been said above, the type of this function is cyclic, and it should be rejected; but it is not:

# let f x = x#copy = x ;;
val f : (< copy : 'a; .. > as 'a) -> bool = <fun>
Objective CAML does accept this function, and notes the type cyclicity using as, which identifies 'a with a type containing 'a.

In fact, the problems are the same, but by default, Objective CAML will not accept such types unless objects are concerned. The function height is typable if it gives a cyclicity on the type of an object.


# let rec height a = match a#sons with
[] -> 1
| l -> 1 + (max_list (List.map height l)) ;;
val height : (< sons : 'a list; .. > as 'a) -> int = <fun>



Previous Contents Next ocaml-book-1.0/en/html/book-ora198.html0000644000000000000000000000557207453055401014506 0ustar Introduction Previous Contents Next

Introduction

Having reached this point, the reader should no longer doubt the richness of Objective CAML. This language rests on a functional and imperative core, and it integrates the two major application organization models: modules and objects. While presented as libraries, threads are an attractive part of the language. The system primitives, portable for the most part, complete the language with all the possibilities offered by distributed programming. These different programming paradigms are shaped within the general framework of static typing with inference. For all that, these elements do not, in themselves, settle the question of Objective CAML's relevance for developing applications, or more prosaically, ``is it a good language?''

None of the following classic arguments can be used in its favor:
  • (marketing development) ``it's a good language because clients buy it'';
  • (historical development) ``it's a good language because thousands of lines of code have already been written in it'';
  • (systems development) ``it's a good language because the Unix or Windows systems are written in it'';
  • (beacon application development) ``it's a good language because such-and-such application is written in it'';
  • (standardization development) ``it's a good language because it has an ISO specification''.
We'll review one last time the various features of the language, but this time from the angle of its relevance for answering a development team's needs. The criteria selected to make up the elements of our evaluation take into account the intrinsic qualities of the language, its development environment, the contributions of its community and the significant applications which have been achieved. Finally we'll compare Objective CAML with several similar functional languages as well as the object-oriented language Java in order to underscore the main differences.


Previous Contents Next ocaml-book-1.0/en/html/book-ora059.html0000644000000000000000000025710207453055400014477 0ustar Minesweeper Previous Contents Next

Minesweeper

Let us briefly recall the object of this game: to explore a mine field without stepping on one. A mine field is a two dimensional array (a matrix) where some cells contain hidden mines while others are empty. At the beginning of the game, all the cells are closed and the player must open them one after another. The player wins when he opens all the cells that are empty.

Every turn, the player may open a cell or flag it as containing a mine. If he opens a cell that contains a mine, it blows up and the player loses. If the cell is empty, its appearance is modified and the number of mines in the 8 neighbor cells is displayed (thus at most 8). If the player decides to flag a cell, he cannot open it until he removes the flag.



Figure 6.5: Screenshot.


We split the implementation of the game into three parts.
  1. The abstract game, including the internal representation of the mine field as well as the functions manipulating this representation.
  2. The graphical part of the game, including the function for displaying cells.
  3. The interaction between the program and the player.

The abstract mine field

This part deals with the mine field as an abstraction only, and does not address its display.



Configuration
A mine field is defined by its dimensions and the number of mines it contains. We group these three pieces of data in a record and define a default configuration: 1010 cells and 15 mines.

# type config = {
nbcols : int ;
nbrows : int ;
nbmines : int };;
# let default_config = { nbcols=10; nbrows=10; nbmines=15 } ;;


The mine field
It is natural to represent the mine field as a two dimensional array. However, it is still necessary to specify what the cells are, and what information their encoding should provide. The state of a cell should answer the following questions:
  • is there a mine in this cell?
  • is this cell opened (has it been seen)?
  • is this cell flagged?
  • how many mines are there in neighbor cells?
The last item is not mandatory, as it is possible to compute it when it is needed. However, it is simpler to do this computation once at the beginning of the game.

We represent a cell with a record that contains these four pieces of data.

# type cell = {
mutable mined : bool ;
mutable seen : bool ;
mutable flag : bool ;
mutable nbm : int
} ;;
The two dimensional array is an array of arrays of cells:

# type board = cell array array ;;


An iterator
In the rest of the program, we often need to iterate a function over all the cells of the mine field. To do it generically, we define the operator iter_cells that applies the function f, given as an argument, to each cell of the board defined by the configuration cf.

# let iter_cells cf f =
for i=0 to cf.nbcols-1 do for j=0 to cf.nbrows-1 do f (i,j) done done ;;
val iter_cells : config -> (int * int -> 'a) -> unit = <fun>


This is a good example of a mix between functional and imperative programming styles, as we use a higher order function (a function taking another function as an argument) to iterate a function that operates through side effects (as it returns no value).

Initialization
We randomly choose which cells are mines. If c and r are respectively the number of columns and rows of the mine field, and m the number of mines, we need to generate m different numbers between 1 and c r. We suppose that m c r to define the algorithm, but the program using it will need to check this condition.

The straightforward algorithm consists of starting with an empty list, picking a random number and putting it in the list if it is not there already, and repeating this until the list contains m numbers. We use the following functions from the Random and Sys modules:
  • Random.int: int -> int, picks a number between 0 and n-1 (n is the argument) according to a random number generator;
  • Random.init: int -> unit, initializes the random number generator;
  • Sys.time: unit -> float, returns the number of milliseconds of processor time the program used since it started. This function will be used to initialize the random number generator with a different seed for each game.
The modules containing these functions are described in more details in chapter 8, pages ?? and ??.

The random mine placement function receives the number of cells (cr) and the number of mines to place (m), and returns a list of linear positions for the m mines.

# let random_list_mines cr m =
let cell_list = ref []
in while (List.length !cell_list) < m do
let n = Random.int cr in
if not (List.mem n !cell_list) then cell_list := n :: !cell_list
done ;
!cell_list ;;
val random_list_mines : int -> int -> int list = <fun>


With such an implementation, there is no upper bound on the number of steps the function takes to terminate. If the random number generator is reliable, we can only insure that the probability it does not terminate is zero. However, all experimental uses of this function have never failed to terminate. Thus, even though it is not guaranteed that it will terminate, we will use it to generate the list of mined cells.

We need to initialize the random number generator so that each run of the game does not use the same mine field. We use the processor time since the beginning of the program execution to initialize the random number generator.

# let generate_seed () =
let t = Sys.time () in
let n = int_of_float (t*.1000.0)
in Random.init(n mod 100000) ;;
val generate_seed : unit -> unit = <fun>
In practice, a given program very often takes the same execution time, which results in a similar result for generate_seed for each run. We ought to use the Unix.time function (see chapter 18).

We very often need to know the neighbors of a given cell, during the initialization of the mine field as well as during the game. Thus we write a neighbors function. This function must take into account the side and corner cells that have fewer neighbors than the middle ones (function valid).

# let valid cf (i,j) = i>=0 && i<cf.nbcols && j>=0 && j<cf.nbrows ;;
val valid : config -> int * int -> bool = <fun>
# let neighbors cf (x,y) =
let ngb = [x-1,y-1; x-1,y; x-1,y+1; x,y-1; x,y+1; x+1,y-1; x+1,y; x+1,y+1]
in List.filter (valid cf) ngb ;;
val neighbors : config -> int * int -> (int * int) list = <fun>
The initialize_board function creates the initial mine field. It proceeds in four steps:
  1. generation of the list of mined cells;
  2. creation of a two dimensional array containing different cells;
  3. setting of mined cells in the board;
  4. computation of the number of mines in neighbor cells for each cell that is not mined.
The function initialize_board uses a few local functions that we briefly describe.
cell_init
: creates an initial cell value;
copy_cell_init
: puts a copy of the initial cell value in a cell of the board;
set_mined
: puts a mine in a cell;
count_mined_adj
: computes the number of mines in the neighbors of a given cell;
set_count
: updates the number of mines in the neighbors of a cell if it is not mined.

# let initialize_board cf =
let cell_init () = { mined=false; seen=false; flag=false; nbm=0 } in
let copy_cell_init b (i,j) = b.(i).(j) <- cell_init() in
let set_mined b n = b.(n / cf.nbrows).(n mod cf.nbrows).mined <- true
in
let count_mined_adj b (i,j) =
let x = ref 0 in
let inc_if_mined (i,j) = if b.(i).(j).mined then incr x
in List.iter inc_if_mined (neighbors cf (i,j)) ;
!x
in
let set_count b (i,j) =
if not b.(i).(j).mined
then b.(i).(j).nbm <- count_mined_adj b (i,j)
in
let list_mined = random_list_mines (cf.nbcols*cf.nbrows) cf.nbmines in
let board = Array.make_matrix cf.nbcols cf.nbrows (cell_init ())
in iter_cells cf (copy_cell_init board) ;
List.iter (set_mined board) list_mined ;
iter_cells cf (set_count board) ;
board ;;
val initialize_board : config -> cell array array = <fun>


Opening a cell
During a game, when the player opens a cell whose neighbors are empty (none contains a mine), he knows that he can open the neighboring cells without risk, and he can keep opening cells as long as he opens cells without any mined neighbor. In order to relieve the player of this boring process (as it is not challenging at all), our Minesweeper opens all these cells itself. To this end, we write the function cells_to_see that returns a list of all the cells to open when a given cell is opened.

The algorithm needed is simple to state: if the opened cell has some neighbors that contain a mine, then the list of cells to see consists only of the opened cell; otherwise, the list of cells to see consists of the neighbors of the opened cell, as well as the lists of cells to see of these neighbors. The difficulty is in writing a program that does not loop, as every cell is a neighbor of any of its neighbors. We thus need to avoid processing the same cell twice.
To remember which cells were processed, we use the array of booleans visited. Its size is the same as the mine field. The value true for a cell of this array denotes that it was already visited. We recurse only on cells that were not visited.

We use the auxiliary function relevant that computes two sublists from the list of neighbors of a cell. Each one of these lists only contains cells that do not contain a mine, that are not opened, that are not flagged by the player, and that were not visited. The first sublist is the list of neighboring cells who have at least one neighbor containing a mine; the second sublist is the list of neighboring cells whose neighbors are all empty. As these lists are computed, all these cells are marked as visited. Notice that flagged cells are not processed, as a flag is meant to prevent opening a cell.

The local function cells_to_see_rec implements the recursive search loop. It takes as an argument the list of cells to visit, updates it, and returns the list of cells to open. This function is called with the list consisting only of the cell being opened, after it is marked as visited.

# let cells_to_see bd cf (i,j) =
let visited = Array.make_matrix cf.nbcols cf.nbrows false in
let rec relevant = function
[] -> ([],[])
| ((x,y) as c)::t ->
let cell=bd.(x).(y)
in if cell.mined || cell.flag || cell.seen || visited.(x).(y)
then relevant t
else let (l1,l2) = relevant t
in visited.(x).(y) <- true ;
if cell.nbm=0 then (l1,c::l2) else (c::l1,l2)
in
let rec cells_to_see_rec = function
[] -> []
| ((x,y) as c)::t ->
if bd.(x).(y).nbm<>0 then c :: (cells_to_see_rec t)
else let (l1,l2) = relevant (neighbors cf c)
in (c :: l1) @ (cells_to_see_rec (l2 @ t))
in visited.(i).(j) <- true ;
cells_to_see_rec [(i,j)] ;;
val cells_to_see :
cell array array -> config -> int * int -> (int * int) list = <fun>


At first sight, the argument of cells_to_see_rec may grow between two consecutive calls, although the recursion is based on this argument. It is legitimate to wonder if this function always terminates.
The way the visited array is used guarantees that a visited cell cannot be in the result of the relevant function. Also, all the cells to visit come from the result of the relevant function. As the relevant function marks as visited all the cells it returns, it returns each cell at most once, thus a cell may be added to the list of cells to visit at most once. The number of cells being finite, we deduce that the function terminates.

Except for graphics, we are done with our Minesweeper. Let us take a look at the programming style we have used. Mutable structures (arrays and mutable record fields) make us use an imperative style of loops and assignments. However, to deal with auxiliary issues, we use lists that are processed by functions written in a functional style. Actually, the programming style is a consequence of the data structure that it manipulates. The function cells_to_see is a good example: it processes lists, and it is natural to write it in a functional style. Nevertheless, we use an array to remember the cells that were already processed, and we update this array imperatively. We could use a purely functional style by using a list of visited cells instead of an array, and check if a cell is in the list to see if it was visited. However, the cost of such a choice is important (looking up an element in a list is linear in the size of the list, whereas accessing an array element takes constant time) and it does not make the program simpler.

Displaying the Minesweeper game

This part depends on the data structures representing the state of the game (see page ??). It consists of displaying the different components of the Minesweeper window, as shown in figure 6.6. To this end, we use the box drawing functions seen on page ??.



Figure 6.6: The main window of Minesweeper.


The following parameters characterize the components of the graphical window.


# let b0 = 3 ;;
# let w1 = 15 ;;
# let w2 = w1 ;;
# let w4 = 20 + 2*b0 ;;
# let w3 = w4*default_config.nbcols + 2*b0 ;;
# let w5 = 40 + 2*b0 ;;
 

# let h1 = w1 ;;
# let h2 = 30 ;;
# let h3 = w5+20 + 2*b0 ;;
# let h4 = h2 ;;
# let h5 = 20 + 2*b0 ;;
# let h6 = w5 + 2*b0 ;;
 

We use them to extend the basic configuration of our Minesweeper board (value of type config). Below, we define a record type window_config. The cf field contains the basic configuration. We associate a box with every component of the display: main window (field main_box), mine field (field field_box), dialog window (field dialog_box) with two sub-boxes (fields d1_box and d2_box), flagging button (field flag_box) and current cell (field current_box).

# type window_config = {
cf : config ;
main_box : box_config ;
field_box : box_config ;
dialog_box : box_config ;
d1_box : box_config ;
d2_box : box_config ;
flag_box : box_config ;
mutable current_box : box_config ;
cell : int*int -> (int*int) ;
coor : int*int -> (int*int)
} ;;


Moreover, a record of type window_config contains two functions:
  • cell: takes the coordinates of a cell and returns the coordinates of the corresponding box;
  • coor: takes the coordinates of a pixel of the window and returns the coordinates of the corresponding cell.
Configuration
We now define a function that builds a graphical configuration (of type window_config) according to a basic configuration (of type config) and the parameters above. The values of the parameters of some components depend on the value of the parameters of other components. For instance, the global box width depends on the mine field width, which, in turn, depends on the number of columns. To avoid computing the same value several times, we incrementally create the components. This initialization phase of a graphical configuration is always a little tedious when there is no adequate primitive or tool available.

# let make_box x y w h bw r =
{ x=x; y=y; w=w; h=h; bw=bw; r=r; b1_col=gray1; b2_col=gray3; b_col=gray2 } ;;
val make_box : int -> int -> int -> int -> int -> relief -> box_config =
<fun>
# let make_wcf cf =
let wcols = b0 + cf.nbcols*w4 + b0
and hrows = b0 + cf.nbrows*h5 + b0 in
let main_box = let gw = (b0 + w1 + wcols + w2 + b0)
and gh = (b0 + h1 + hrows + h2 + h3 + h4 + b0)
in make_box 0 0 gw gh b0 Top
and field_box = make_box w1 h1 wcols hrows b0 Bot in
let dialog_box = make_box ((main_box.w - w3) / 2)
(b0+h1+hrows+h2)
w3 h3 b0 Bot
in
let d1_box = make_box (dialog_box.x + b0) (b0 + h1 + hrows + h2)
((w3-w5)/2-(2*b0)) (h3-(2*b0)) 5 Flat in
let flag_box = make_box (d1_box.x + d1_box.w)
(d1_box.y + (h3-h6) / 2) w5 h6 b0 Top in
let d2_box = make_box (flag_box.x + flag_box.w)
d1_box.y d1_box.w d1_box.h 5 Flat in
let current_box = make_box 0 0 w4 h5 b0 Top
in { cf = cf;
main_box = main_box; field_box=field_box; dialog_box=dialog_box;
d1_box=d1_box;
flag_box=flag_box; d2_box=d2_box; current_box = current_box;
cell = (fun (i,j) -> ( w1+b0+w4*i , h1+b0+h5*j)) ;
coor = (fun (x,y) -> ( (x-w1)/w4 , (y-h1)/h5 )) } ;;
val make_wcf : config -> window_config = <fun>


Cell display
We now need to write the functions to display the cells in their different states. A cell may be open or closed and may contain some information. We always display (the box corresponding with) the current cell in the game configuration (field cc_bcf).

We thus write two functions modifying the configuration of the current cell; one closing it, the other opening it.

# let close_ccell wcf i j =
let x,y = wcf.cell (i,j)
in wcf.current_box <- {wcf.current_box with x=x; y=y; r=Top} ;;
val close_ccell : window_config -> int -> int -> unit = <fun>
# let open_ccell wcf i j =
let x,y = wcf.cell (i,j)
in wcf.current_box <- {wcf.current_box with x=x; y=y; r=Flat} ;;
val open_ccell : window_config -> int -> int -> unit = <fun>


Depending on the game phase, we may need to display some information on the cells. We write, for each case, a specialized function.
  • Display of a closed cell:

    # let draw_closed_cc wcf i j =
    close_ccell wcf i j;
    draw_box wcf.current_box ;;
    val draw_closed_cc : window_config -> int -> int -> unit = <fun>


  • Display of an opened cell with its number of neighbor mines:

    # let draw_num_cc wcf i j n =
    open_ccell wcf i j ;
    draw_box wcf.current_box ;
    if n<>0 then draw_string_in_box Center (string_of_int n)
    wcf.current_box Graphics.white ;;
    val draw_num_cc : window_config -> int -> int -> int -> unit = <fun>


  • Display of a cell containing a mine:

    # let draw_mine_cc wcf i j =
    open_ccell wcf i j ;
    let cc = wcf.current_box
    in draw_box wcf.current_box ;
    Graphics.set_color Graphics.black ;
    Graphics.fill_circle (cc.x+cc.w/2) (cc.y+cc.h/2) (cc.h/3) ;;
    val draw_mine_cc : window_config -> int -> int -> unit = <fun>


  • Display of a flagged cell containing a mine:

    # let draw_flag_cc wcf i j =
    close_ccell wcf i j ;
    draw_box wcf.current_box ;
    draw_string_in_box Center "!" wcf.current_box Graphics.blue ;;
    val draw_flag_cc : window_config -> int -> int -> unit = <fun>


  • Display of a wrongly flagged cell:

    # let draw_cross_cc wcf i j =
    let x,y = wcf.cell (i,j)
    and w,h = wcf.current_box.w, wcf.current_box.h in
    let a=x+w/4 and b=x+3*w/4
    and c=y+h/4 and d=y+3*h/4
    in Graphics.set_color Graphics.red ;
    Graphics.set_line_width 3 ;
    Graphics.moveto a d ; Graphics.lineto b c ;
    Graphics.moveto a c ; Graphics.lineto b d ;
    Graphics.set_line_width 1 ;;
    val draw_cross_cc : window_config -> int -> int -> unit = <fun>
During the game, the choice of the display function to use is done by:

# let draw_cell wcf bd i j =
let cell = bd.(i).(j)
in match (cell.flag, cell.seen , cell.mined ) with
(true,_,_) -> draw_flag_cc wcf i j
| (_,false,_) -> draw_closed_cc wcf i j
| (_,_,true) -> draw_mine_cc wcf i j
| _ -> draw_num_cc wcf i j cell.nbm ;;
val draw_cell : window_config -> cell array array -> int -> int -> unit =
<fun>


A specialized function displays all the cells at the end of the game. It is slightly different from the previous one as all the cells are taken as opened. Moreover, a red cross indicates the empty cells where the player wrongly put a flag.

# let draw_cell_end wcf bd i j =
let cell = bd.(i).(j)
in match (cell.flag, cell.mined ) with
(true,true) -> draw_flag_cc wcf i j
| (true,false) -> draw_num_cc wcf i j cell.nbm; draw_cross_cc wcf i j
| (false,true) -> draw_mine_cc wcf i j
| (false,false) -> draw_num_cc wcf i j cell.nbm ;;
val draw_cell_end : window_config -> cell array array -> int -> int -> unit =
<fun>


Display of the other components
The state of the flagging mode is indicated by a box that is either at the bottom or on top and that contain either the word ON or OFF:

# let draw_flag_switch wcf on =
if on then wcf.flag_box.r <- Bot else wcf.flag_box.r <- Top ;
draw_box wcf.flag_box ;
if on then draw_string_in_box Center "ON" wcf.flag_box Graphics.red
else draw_string_in_box Center "OFF" wcf.flag_box Graphics.blue ;;
val draw_flag_switch : window_config -> bool -> unit = <fun>


We display the purpose of the flagging button above it:

# let draw_flag_title wcf =
let m = "Flagging" in
let w,h = Graphics.text_size m in
let x = (wcf.main_box.w-w)/2
and y0 = wcf.dialog_box.y+wcf.dialog_box.h in
let y = y0+(wcf.main_box.h-(y0+h))/2
in Graphics.moveto x y ;
Graphics.draw_string m ;;
val draw_flag_title : window_config -> unit = <fun>


During the game, the number of empty cells left to be opened and the number of cells to flag are displayed in the dialog box, to the left and right of the flagging mode button.

# let print_score wcf nbcto nbfc =
erase_box wcf.d1_box ;
draw_string_in_box Center (string_of_int nbcto) wcf.d1_box Graphics.blue ;
erase_box wcf.d2_box ;
draw_string_in_box Center (string_of_int (wcf.cf.nbmines-nbfc)) wcf.d2_box
( if nbfc>wcf.cf.nbmines then Graphics.red else Graphics.blue ) ;;
val print_score : window_config -> int -> int -> unit = <fun>


To draw the initial mine field, we need to draw (number of rows) (number of columns) times the same closed cell. It is always the same drawing, but it may take a long time, as it is necessary to draw a rectangle as well as four trapezoids. To speed up this initialization, we draw only one cell, take the bitmap corresponding to this drawing, and paste this bitmap into every cell.

# let draw_field_initial wcf =
draw_closed_cc wcf 0 0 ;
let cc = wcf.current_box in
let bitmap = draw_box cc ; Graphics.get_image cc.x cc.y cc.w cc.h in
let draw_bitmap (i,j) = let x,y=wcf.cell (i,j)
in Graphics.draw_image bitmap x y
in iter_cells wcf.cf draw_bitmap ;;
val draw_field_initial : window_config -> unit = <fun>


At the end of the game, we open the whole mine field while putting a red cross on cells wrongly flagged:

# let draw_field_end wcf bd =
iter_cells wcf.cf (fun (i,j) -> draw_cell_end wcf bd i j) ;;
val draw_field_end : window_config -> cell array array -> unit = <fun>


Finally, the main display function called at the beginning of the game opens the graphical context and displays the initial state of all the components.

# let open_wcf wcf =
Graphics.open_graph ( " " ^ (string_of_int wcf.main_box.w) ^ "x" ^
(string_of_int wcf.main_box.h) ) ;
draw_box wcf.main_box ;
draw_box wcf.dialog_box ;
draw_flag_switch wcf false ;
draw_box wcf.field_box ;
draw_field_initial wcf ;
draw_flag_title wcf ;
print_score wcf ((wcf.cf.nbrows*wcf.cf.nbcols)-wcf.cf.nbmines) 0 ;;
val open_wcf : window_config -> unit = <fun>


Notice that all the display primitives are parameterized by a graphical configuration of type window_config. This makes them independent of the layout of the components of our Minesweeper. If we wish to modify the layout, the code still works without any modification, only the configuration needs to be updated.

Interaction with the player

We now list what the player may do:

  • he may click on the mode box to change mode (opening or flagging),
  • he may click on a cell to open it or flag it,
  • he may hit the 'q' key to quit the game.
Recall that a Graphic event (Graphics.event) must be associated with a record (Graphics.status) that contains the current information on the mouse and keyboard when the event occurs. An interaction with the mouse may happen on the mode button, or on a cell of the mine field. Every other mouse event must be ignored. In order to differentiate these mouse events, we create the type:

# type clickon = Out | Cell of (int*int) | SelectBox ;;


Also, pressing the mouse button and releasing it are two different events. For a click to be valid, we require that both events occur on the same component (the flagging mode button or a cell of the mine field).

# let locate_click wcf st1 st2 =
let clickon_of st =
let x = st.Graphics.mouse_x and y = st.Graphics.mouse_y
in if x>=wcf.flag_box.x && x<=wcf.flag_box.x+wcf.flag_box.w &&
y>=wcf.flag_box.y && y<=wcf.flag_box.y+wcf.flag_box.h
then SelectBox
else let (x2,y2) = wcf.coor (x,y)
in if x2>=0 && x2<wcf.cf.nbcols && y2>=0 && y2<wcf.cf.nbrows
then Cell (x2,y2) else Out
in
let r1=clickon_of st1 and r2=clickon_of st2
in if r1=r2 then r1 else Out ;;
val locate_click :
window_config -> Graphics.status -> Graphics.status -> clickon = <fun>


The heart of the program is the event waiting and processing loop defined in the function loop. It is similar to the function skel described page ??, but specifies the mouse events more precisely. The loop ends when:
  • the player presses the q or Q key, meaning that he wants to end the game;
  • the player opens a cell containing a mine, then he loses;
  • the player has opened all the cell that are empty, then he wins the game.
We gather in a record of type minesw_cf the information useful for the interface:

# type minesw_cf =
{ wcf : window_config; bd : cell array array;
mutable nb_flagged_cells : int;
mutable nb_hidden_cells : int;
mutable flag_switch_on : bool } ;;
The meaning of the fields is:
  • wcf: the graphical configuration;
  • bd: the board;
  • flag_switch_on: a boolean indicating whether flagging mode or opening mode is on;
  • nb_flagged_cells: the number of flagged cells;
  • nb_hidden_cells: the number of empty cells left to open;
The main loop is implemented this way:

# let loop d f_init f_key f_mouse f_end =
f_init ();
try
while true do
let st = Graphics.wait_next_event
[Graphics.Button_down;Graphics.Key_pressed]
in if st.Graphics.keypressed then f_key st.Graphics.key
else let st2 = Graphics.wait_next_event [Graphics.Button_up]
in f_mouse (locate_click d.wcf st st2)
done
with End -> f_end ();;
val loop :
minesw_cf ->
(unit -> 'a) -> (char -> 'b) -> (clickon -> 'b) -> (unit -> unit) -> unit =
<fun>


The initialization function, cleanup function and keyboard event processing function are very simple.

# let d_init d () = open_wcf d.wcf
let d_end () = Graphics.close_graph()
let d_key c = if c='q' || c='Q' then raise End;;
val d_init : minesw_cf -> unit -> unit = <fun>
val d_end : unit -> unit = <fun>
val d_key : char -> unit = <fun>


However, the mouse event processing function requires the use of some auxiliary functions:
  • flag_cell: when clicking on a cell with flagging mode on.
  • ending: when ending the game. The whole mine field is revealed, we display a message indicating whether the game was won or lost, and we wait for a mouse or keyboard event to quit the application.
  • reveal: when clicking on a cell with opening mode on (i.e. flagging mode off).

# let flag_cell d i j =
if d.bd.(i).(j).flag
then ( d.nb_flagged_cells <- d.nb_flagged_cells -1;
d.bd.(i).(j).flag <- false )
else ( d.nb_flagged_cells <- d.nb_flagged_cells +1;
d.bd.(i).(j).flag <- true );
draw_cell d.wcf d.bd i j;
print_score d.wcf d.nb_hidden_cells d.nb_flagged_cells;;
val flag_cell : minesw_cf -> int -> int -> unit = <fun>

# let ending d str =
draw_field_end d.wcf d.bd;
erase_box d.wcf.flag_box;
draw_string_in_box Center str d.wcf.flag_box Graphics.black;
ignore(Graphics.wait_next_event
[Graphics.Button_down;Graphics.Key_pressed]);
raise End;;
val ending : minesw_cf -> string -> 'a = <fun>

# let reveal d i j =
let reveal_cell (i,j) =
d.bd.(i).(j).seen <- true;
draw_cell d.wcf d.bd i j;
d.nb_hidden_cells <- d.nb_hidden_cells -1
in
List.iter reveal_cell (cells_to_see d.bd d.wcf.cf (i,j));
print_score d.wcf d.nb_hidden_cells d.nb_flagged_cells;
if d.nb_hidden_cells = 0 then ending d "WON";;
val reveal : minesw_cf -> int -> int -> unit = <fun>


The mouse event processing function matches a value of type clickon.

# let d_mouse d click = match click with
Cell (i,j) ->
if d.bd.(i).(j).seen then ()
else if d.flag_switch_on then flag_cell d i j
else if d.bd.(i).(j).flag then ()
else if d.bd.(i).(j).mined then ending d "LOST"
else reveal d i j
| SelectBox ->
d.flag_switch_on <- not d.flag_switch_on;
draw_flag_switch d.wcf d.flag_switch_on
| Out -> () ;;
val d_mouse : minesw_cf -> clickon -> unit = <fun>


To create a game configuration, three parameters are needed: the number of columns, the number of rows, and the number of mines.

# let create_minesw nb_c nb_r nb_m =
let nbc = max default_config.nbcols nb_c
and nbr = max default_config.nbrows nb_r in
let nbm = min (nbc*nbr) (max 1 nb_m) in
let cf = { nbcols=nbc ; nbrows=nbr ; nbmines=nbm } in
generate_seed () ;
let wcf = make_wcf cf in
{ wcf = wcf ;
bd = initialize_board wcf.cf;
nb_flagged_cells = 0;
nb_hidden_cells = cf.nbrows*cf.nbcols-cf.nbmines;
flag_switch_on = false } ;;
val create_minesw : int -> int -> int -> minesw_cf = <fun>


The launch function creates a configuration according to the numbers of columns, rows, and mines, before calling the main event processing loop.

# let go nbc nbr nbm =
let d = create_minesw nbc nbr nbm in
loop d (d_init d) d_key (d_mouse d) (d_end);;
val go : int -> int -> int -> unit = <fun>
The function call go 10 10 10 builds and starts a game of the same size as the one depicted in figure 6.5.

Exercises

This program can be built as a standalone executable program. Chapter 7 explains how to do this. Once it is done, it is useful to be able to specify the size of the game on the command line. Chapter 8 describes how to get command line arguments in an Objective CAML program, and applies it to our minesweeper (see page ??).

Another possible extension is to have the machine play to discover the mines. To do this, one needs to be able to find the safe moves and play them first, then compute the probabilities of presence of a mine and open the cell with the smallest probability.




Previous Contents Next ocaml-book-1.0/en/html/book-ora193.html0000644000000000000000000000444507453055401014477 0ustar Introduction Previous Contents Next

Introduction

The first application is really a toolbox to facilitate the construction of client-server applications which transmit Objective CAML values. To build an application using the toolbox, one need only implement serialization functions for the values to be transmitted, then apply a functor to obtain an abstract class for the server, then add the application's processing function by means of inheritance.

The second application revisits the robot simulation, presented on page ??, and adapts it to the client-server model. The server represents the world in which the robot clients move around. We thus simulate distributed memory shared by a group of clients possibly located on various machines on the network.

The third application is an implementation of some small HTTP servers (called servlets). A server knows how to respond to an HTTP request such as a request to retrieve an HTML page. Moreover, it is possible to pass values in these requests using the CGI format of HTTP servers. We will use this functionality right away to construct a server for requests on the association database, described on page ??. As a client, we will use a Web browser to which we will send an initial page containing the query form.


Previous Contents Next ocaml-book-1.0/en/html/book-ora075.gif0000644000000000000000000001167307452056112014277 0ustar GIF89aycUUU999!,ycX0I8ͻ`(dihp,tmxl ,vƤr6$tJZzvL^d@4-x$Еm9#y~'|vtoX>iL!.j v..)R m ,&RfYD% ϩ~O< LUR!>p,$!jRRhԮq]HtX0"1}pq͡V_ BŠfVŊLǐ#C+q㻗Z937) iP;%^4ӏS:omr+^h޽/IcoWJ- _":m9t-TG'H.}ȧĮ1}|G$.b Q\wahVх]}:suvbdw/a#<(?3bX$A"vYms=R c@LYq%'EshX> vt04[MiۓAf8By$43`)GAȟ餢@&nє&_UBB PXXbx΂U' ZpmX&:~mDS XP+b ([RII۷Z+nmaj.*Cko ;˒yl8Υ32Bp2>zXJTܫ[@Sk1Wi5"02&Spr9^i>˫}ǟߟ}V^ovU$PЁ$X ς `८SM0(( <@S'\bX0a '!=qH"gGTTC*hBxE.qâF9*p@+F LmFBFGLģ 8H8Zr̺!JёZ$)N^2c}L%s:&4hQnge)5XШQ% OI^N,#%\R=dbYYrTf.%ckⲖ|&uLA;g&us,PӔ5U%Es*|`G52, Tr5u ;~ZuS)o4]k"ZIkت;SǜpcV=E\"NT;ΛEċNE _1O(.6`U%q>n<3 ·-H&͡ɶĿNt+UttE:EoÒo@]OY_ʃ +q_/oo3߿7Ww(7Q7H؀GԷ"qzv|֗~hif@w{kD|QX1"k)hpGkW(h-:Xf.؃p698|'~6HzǀgDh|BAOy.^g_('HEr ā_j}VmFgqt]8AH uh7K?%kW3yU`jvqz}` n p&URn2o \2c} h XnnQ(,U8^oY nmSSQ\Q Ih(Y=>,'g8 s !0ht;gpR9x06CdH %W sBs5 ԦmJS?sF irix;' " HW[MǐF"9PNq0u93jt)xؑ vz7 r>pg~-vSpFxarWw7yW%}7&j\Ȅ~×fHHgXJ{Q2{z8(p)*myrő- w~}Oz9aeYi+`>)W(InɘHy'EؙYi~h9 a"'ٜ"ًҹ~y?ɝZ YG?/.0ةs*4;{Z[Jא:lU*yzzb\K{ѨO (Yq\%HRo]vA:xu+ %ѵKe7jK>F{h~)6xx[$K ++k&{k^-j {;KH{΋?97䪼[؛( ȫ16[;劯 {˻;f웾Kݫ˽+K+˴[J{\%{ llk l ڛ{/'L0,$:T"V| Z|"Y2ŕ©$kDg̚BS Ǚ:s\u_!vq5K{JƐՊ!)lX!ھ{U:( (Y&(',+-s2n:}: $@G@C~;ocaml-book-1.0/en/html/book-ora140.html0000644000000000000000000004123207453055400014461 0ustar Classes, Objects, and Methods Previous Contents Next

Classes, Objects, and Methods

The object-oriented extension of Objective CAML is integrated with the functional and imperative kernels of the language, as well as with its type system. Indeed, this last point is unique to the language. Thus we have an object-oriented, statically typed language, with type inference. This extension allows definition of classes and instances, class inheritance (including multiple inheritance), parameterized classes, and abstract classes. Class interfaces are generated from their definition, but may be made more precise through a signature, similarly to what is done for modules.

Object-Oriented Terminology

We summarize below the main object-oriented programming terms.
class:
a class describes the contents of the objects that belong to it: it describes an aggregate of data fields (called instance variables), and defines the operations (called methods).
object:
an object is an element (or instance) of a class; objects have the behaviors of their class. The object is the actual component of programs, while the class specifies how instances are created and how they behave.
method:
a method is an action which an object is able to perform.
sending a message
sending a message to an object means asking the object to execute or invoke one of its methods.

Class Declaration

The simplest syntax for defining a class is as follows. We shall develop this definition throughout this chapter.

Syntax


class name p1 ...pn =
  object
      :
    instance variables
      :
    methods
      :
  end

p1, ..., pn are the parameters for the constructor of the class; they are omitted if the class has no parameters.

An instance variable is declared as follows:

Syntax


val name = expr
or
val mutable name = expr

When a data field is declared mutable, its value may be modified. Otherwise, the value is always the one that was computed when expr was evaluated during object creation.

Methods are declared as follows:

Syntax


method name p1 ...pn = expr


Other clauses than val and method can be used in a class declaration: we shall introduce them as needed.

Our first class example.
We start with the unavoidable class point:
  • the data fields x and y contain the coordinates of the point,
  • two methods provide access to the data fields (get_x and get_y),
  • two displacement methods (moveto: absolute displacement) and (rmoveto: relative displacement),
  • one method presents the data as a string (to_string),
  • one method computes the distance to the point from the origin (distance).

# class point (x_init,y_init) =
object
val mutable x = x_init
val mutable y = y_init
method get_x = x
method get_y = y
method moveto (a,b) = x <- a ; y <- b
method rmoveto (dx,dy) = x <- x + dx ; y <- y + dy
method to_string () =
"( " ^ (string_of_int x) ^ ", " ^ (string_of_int y) ^")"
method distance () = sqrt (float(x*x + y*y))
end ;;
Note that some methods do not need parameters; this is the case for get_x and get_y. We usually access instance variables with parameterless methods.

After we declare the class point, the system prints the following text:

class point :
int * int ->
object
val mutable x : int
val mutable y : int
method distance : unit -> float
method get_x : int
method get_y : int
method moveto : int * int -> unit
method rmoveto : int * int -> unit
method to_string : unit -> string
end


This text contains two pieces of information. First, the type for objects of the class; this type will be abbreviated as point. The type of an object is the list of names and types of methods in its class. In our example, point is an abbreviation for:
  
< distance : unit -> unit; get_x : int; get_y : int;
moveto : int * int -> unit; rmoveto : int * int -> unit;
to_string : unit -> unit >
Next, we have a constructor for instances of class point, whose type is int*int -> oint. The constructor allows us to construct point objects (well just say ``points'' to be brief) from the initial values provided as arguments. In this case, we construct a point from a pair of integers (meaning the initial position). The constructor point is used with the keyword new.

It is possible to define class types:

# type simple_point = < get_x : int; get_y : int; to_string : unit -> unit > ;;
type simple_point = < get_x : int; get_y : int; to_string : unit -> unit >


Note


Type point does not repeat all the informations shown after a class declaration. Instance variables are not shown in the type. Only methods have access to these instance variables.


Warning


A class declaration is a type declaration. As a consequence, it cannot contain a free type variable.


We will come back to this point later when we deal with type constraints (page ??) and parameterized classes (page ??).

A Graphical Notation for Classes

We adapt the UML notation for the syntax of Objective CAML types. Classes are denoted by a rectangle with three parts:
  • the top part shows the name of the class,
  • the middle part lists the attributes (data fields) of a class instance,
  • the bottom part shows the methods of an instance of the class.
Figure 15.1 gives an example of the graphical representation for the class caml.



Figure 15.1: Graphical representation of a class.


Type information for the fields and methods of a class may be added.

Instance Creation

An object is a value of a class, called an instance of the class. Instances are created with the generic construction primitive new, which takes the class and initialization values as arguments.

Syntax


new name expr1 ...exprn
The following example creates several instances of class point, from various initial values.

# let p1 = new point (0,0);;
val p1 : point = <obj>
# let p2 = new point (3,4);;
val p2 : point = <obj>
# let coord = (3,0);;
val coord : int * int = 3, 0
# let p3 = new point coord;;
val p3 : point = <obj>


In Objective CAML, the constructor of a class is unique, but you may define your own specific function make_point for point creation:

# let make_point x = new point (x,x) ;;
val make_point : int -> point = <fun>
# make_point 1 ;;
- : point = <obj>


Sending a Message

The notation # is used to send a message to an object. 2

Syntax


obj1#name p1 ...pn
The message with method name ``name'' is sent to the object obj. The arguments p1, ..., pn are as expected by the method name. The method must be defined by the class of the object, i.e. visible in the type. The types of arguments must conform to the types of the formal parameters. The following example shows several queries performed on objects from the class point.

# p1#get_x;;
- : int = 0
# p2#get_y;;
- : int = 4
# p1#to_string();;
- : string = "( 0, 0)"
# p2#to_string();;
- : string = "( 3, 4)"
# if (p1#distance()) = (p2#distance())
then print_string ("That's just chance\n")
else print_string ("We could bet on it\n");;
We could bet on it
- : unit = ()


From the type point of view, objects of type point can be used by polymorphic functions of Objective CAML, just as any other value in the language:

# p1 = p1 ;;
- : bool = true
# p1 = p2;;
- : bool = false
# let l = p1::[];;
val l : point list = [<obj>]
# List.hd l;;
- : point = <obj>


Warning


Object equality is defined as physical equality.


We shall clarify this point when we study the subtyping relation (page ??).


Previous Contents Next ocaml-book-1.0/en/html/book-ora088.html0000644000000000000000000001525207453055400014477 0ustar Module Gc Previous Contents Next

Module Gc

The Gc module lets one obtain statistics about the heap and gives control over its evolution as well as allowing the activation of various garbage collector phases. Two concrete record types are defined: stat and control. The fields of type control are modifiable; whereas those of stat are not. The latter simply reflect the state of the heap at a given moment.

The fields of a stat mainly contain counters indicating:
  • the number of garbage collections: minor_collections, major_collections and compactions;
  • the number of words allocated and transfered since the beginning of the program: minor_words, promoted_words, and major_words.
The fields of the record control are:
  • minor_heap_size, which defines the size of the zone allotted to the younger generation;
  • major_heap_increment, which defines the increment applied to the growth of the region for the older generation;
  • space_overhead, which defines the percentage of the memory used beyond which a major garbage collection is begun (the default value is 42);
  • max_overhead, which defines the connection between free memory and occupied memory after which compactification is activated. A value of 0 causes a systematic compactification after every major garbage collection. The maximal value of 1000000 inhibits compactification.
  • verbose is an integer parameter governing the tracing of the activities of the garbage collector.
Functions manipulating the types stat and control are given in figure 9.13.

stat unit -> stat
print_stat out_channel -> unit
get unit control
set control -> unit

Figure 9.13: Control and statistical functions for the heap.


The following functions, of type unit -> unit, force the execution of one or more stages of the Objective CAML garbage collector: minor (stage 1), major (stages 1 and 2), full_major (stages 1, 2 and 3) and compact (stages 1, 2, 3 and 4).

Examples

Here is what the Gc.stat call shows:

# Gc.stat();;
- : Gc.stat =
{Gc.minor_words=549290; Gc.promoted_words=60620; Gc.major_words=204615;
Gc.minor_collections=17; Gc.major_collections=2; Gc.heap_words=190464;
Gc.heap_chunks=3; Gc.live_words=52727; Gc.live_blocks=12959;
Gc.free_words=31155; Gc.free_blocks=84; Gc.largest_free=17899;
Gc.fragments=3; Gc.compactions=0}


We see the number of executions of each phase: minor garbage collection, major garbage collection, compaction, as well as the number of words handled by the different memory spaces. Calling compact forces the four stages of the garbage collector, causing the heap statistics to be modified (see the call of Gc.stat).

# Gc.compact();;
- : unit = ()
# Gc.stat();;
- : Gc.stat =
{Gc.minor_words=555758; Gc.promoted_words=61651; Gc.major_words=205646;
Gc.minor_collections=18; Gc.major_collections=4; Gc.heap_words=190464;
Gc.heap_chunks=3; Gc.live_words=130637; Gc.live_blocks=30770;
Gc.free_words=59827; Gc.free_blocks=1; Gc.largest_free=59827;
Gc.fragments=0; Gc.compactions=1}


The fields GC.minor_collections and compactions are incremented by 1, whereas the field Gc.major_collections is incremented by 2. All of the fields of type GC.control are modifiable. For them to be taken into account, we must use the function Gc.set, which takes a value of type control and modifies the behavior of the garbage collector.

For example, the field verbose may take a value from 0 to 127, controlling 7 different indicators.

# c.Gc.verbose <- 31;;
Characters 1-2:
This expression has type int * int but is here used with type Gc.control
# Gc.set c;;
Characters 7-8:
This expression has type int * int but is here used with type Gc.control
# Gc.compact();;
- : unit = ()


which prints:
<>Starting new major GC cycle
allocated_words = 329
extra_heap_memory = 0u
amount of work to do = 3285u
Marking 1274 words
!Starting new major GC cycle
Compacting heap...
done.
The different phases of the garbage collector are indicated as well as the number of objects processed.


Previous Contents Next ocaml-book-1.0/en/html/book-ora207.html0000644000000000000000000000326607453055401014473 0ustar Introduction Previous Contents Next

Introduction

Objective CAML's type system would be much simpler if the language were purely functionnal. Alas, language extensions entail extensions to the type language, and to the inference mechanism, of which we saw the illustration with the weak type variables (see page ??), made unavoidable by imperative extensions.

Object typing introduces the notion of cyclic type, associated with the keyword as (see page ??), which can be used independently of any concept of object oriented programming. The present appendix describes this extension of the type language, available through an option of the compiler.


Previous Contents Next ocaml-book-1.0/en/html/book-ora051.html0000644000000000000000000007002407453055377014500 0ustar A Graphical Calculator Previous Contents Next

A Graphical Calculator

Let's consider the calculator example as described in the preceding chapter on imperative programming (see page ??). We will give it a graphical interface to make it more usable as a desktop calculator.

The graphical interface materializes the set of keys (digits and functions) and an area for displaying results. Keys can be activated using the graphical interface (and the mouse) or by typing on the keyboard. Figure 5.9 shows the interface we are about to construct.


Figure 5.9: Graphical calculator.


We reuse the functions for drawing boxes as described on page ??. We define the following type:

# type calc_state =
{ s : state; k : (box_config * key * string ) list; v : box_config } ;;
It contains the state of the calculator, the list of boxes corresponding to the keys and the visualization box. We plan to construct a calculator that is easily modifiable. Therefore, we parameterize the construction of the interface with an association list:

# let descr_calc =
[ (Digit 0,"0"); (Digit 1,"1"); (Digit 2,"2"); (Equals, "=");
(Digit 3,"3"); (Digit 4,"4"); (Digit 5,"5"); (Plus, "+");
(Digit 6,"6"); (Digit 7,"7"); (Digit 8,"8"); (Minus, "-");
(Digit 9,"9"); (Recall,"RCL"); (Div, "/"); (Times, "*");
(Off,"AC"); (Store, "STO"); (Clear,"CE/C")
] ;;


Generation of key boxes
At the beginning of this description we construct a list of key boxes. The function gen_boxes takes as parameters the description (descr), the number of the column (n), the separation between boxes (wsep), the separation between the text and the borders of the box (wsepint) and the size of the board (wbord). This function returns the list of key boxes as well as the visualization box. To calculate these placements, we define the auxiliary functions max_xy for calculating the maximal size of a list of complete pairs and max_lbox for calculating the maximal positions of a list of boxes.

# let gen_xy vals comp o =
List.fold_left (fun a (x,y) -> comp (fst a) x,comp (snd a) y) o vals ;;
val gen_xy : ('a * 'a) list -> ('b -> 'a -> 'b) -> 'b * 'b -> 'b * 'b = <fun>
# let max_xy vals = gen_xy vals max (min_int,min_int);;
val max_xy : (int * int) list -> int * int = <fun>
# let max_boxl l =
let bmax (mx,my) b = max mx b.x, max my b.y
in List.fold_left bmax (min_int,min_int) l ;;
val max_boxl : box_config list -> int * int = <fun>


Here is the principal function gen_boxes for creating the interface.

# let gen_boxes descr n wsep wsepint wbord =
let l_l = List.length descr in
let nb_lig = if l_l mod n = 0 then l_l / n else l_l / n + 1 in
let ls = List.map (fun (x,y) -> Graphics.text_size y) descr in
let sx,sy = max_xy ls in
let sx,sy= sx+wsepint ,sy+wsepint in
let r = ref [] in
for i=0 to l_l-1 do
let px = i mod n and py = i / n in
let b = { x = wsep * (px+1) + (sx+2*wbord) * px ;
y = wsep * (py+1) + (sy+2*wbord) * py ;
w = sx; h = sy ; bw = wbord;
r=Top;
b1_col = gray1; b2_col = gray3; b_col =gray2}
in r:= b::!r
done;
let mpx,mpy = max_boxl !r in
let upx,upy = mpx+sx+wbord+wsep,mpy+sy+wbord+wsep in
let (wa,ha) = Graphics.text_size " 0" in
let v = { x=(upx-(wa+wsepint +wbord))/2 ; y= upy+ wsep;
w=wa+wsepint; h = ha +wsepint; bw = wbord *2; r=Flat ;
b1_col = gray1; b2_col = gray3; b_col =Graphics.black}
in
upx,(upy+wsep+ha+wsepint+wsep+2*wbord),v,
List.map2 (fun b (x,y) -> b,x,y ) (List.rev !r) descr;;
val gen_boxes :
('a * string) list ->
int ->
int ->
int -> int -> int * int * box_config * (box_config * 'a * string) list =
<fun>


Interaction
Since we would also like to reuse the skeleton proposed on page ?? for interaction, we define the functions for keyboard and mouse control, which are integrated in this skeleton. The function for controlling the keyboard is very simple. It passes the translation of a character value of type key to the function transition of the calculator and then displays the text associated with the calculator state.

# let f_key cs c =
transition cs.s (translation c);
erase_box cs.v;
draw_string_in_box Right (string_of_int cs.s.vpr) cs.v Graphics.white ;;
val f_key : calc_state -> char -> unit = <fun>


The control of the mouse is a bit more complex. It requires verification that the position of the mouse click is actually in one of the key boxes. For this we first define the auxiliary function mem, which verifies membership of a position within a rectangle.

# let mem (x,y) (x0,y0,w,h) =
(x >= x0) && (x< x0+w) && (y>=y0) && ( y<y0+h);;
val mem : int * int -> int * int * int * int -> bool = <fun>
# let f_mouse cs x y =
try
let b,t,s =
List.find (fun (b,_,_) ->
mem (x,y) (b.x+b.bw,b.y+b.bw,b.w,b.h)) cs.k
in
transition cs.s t;
erase_box cs.v;
draw_string_in_box Right (string_of_int cs.s.vpr ) cs.v Graphics.white
with Not_found -> ();;
val f_mouse : calc_state -> int -> int -> unit = <fun>


The function f_mouse looks whether the position of the mouse during the click is reallydwell within one of the boxes corresponding to a key. If it is, it passes the corresponding key to the transition function and displays the result, otherwise it will not do anything.

The function f_exc handles the exceptions which can arise during program execution.

# let f_exc cs ex =
match ex with
Division_by_zero ->
transition cs.s Clear;
erase_box cs.v;
draw_string_in_box Right "Div 0" cs.v (Graphics.red)
| Invalid_key -> ()
| Key_off -> raise End
| _ -> raise ex;;
val f_exc : calc_state -> exn -> unit = <fun>


In the case of a division by zero, it restarts in the initial state of the calculator and displays an error message on its screen. Invalid keys are simply ignored. Finally, the exception Key_off raises the exception End to terminate the loop of the skeleton.

Initialization and termination
The initialization of the calculator requires calculation of the window size. The following function creates the graphical information of the boxes from a key/text association and returns the size of the principal window.

# let create_e k =
Graphics.close_graph ();
Graphics.open_graph " 10x10";
let mx,my,v,lb = gen_boxes k 4 4 5 2 in
let s = {lcd=0; lka = false; loa = Equals; vpr = 0; mem = 0} in
mx,my,{s=s; k=lb;v=v};;
val create_e : (key * string) list -> int * int * calc_state = <fun>


The initialization function makes use of the result of the preceding function.

# let f_init mx my cs () =
Graphics.close_graph();
Graphics.open_graph (" "^(string_of_int mx)^"x"^(string_of_int my));
Graphics.set_color gray2;
Graphics.fill_rect 0 0 (mx+1) (my+1);
List.iter (fun (b,_,_) -> draw_box b) cs.k;
List.iter
(fun (b,_,s) -> draw_string_in_box Center s b Graphics.black) cs.k ;
draw_box cs.v;
erase_box cs.v;
draw_string_in_box Right "hello" cs.v (Graphics.white);;
val f_init : int -> int -> calc_state -> unit -> unit = <fun>


Finally the termination function closes the graphical window.

# let f_end e () = Graphics.close_graph();;
val f_end : 'a -> unit -> unit = <fun>


The function go is parameterized by a description and starts the interactive loop.

# let go descr =
let mx,my,e = create_e descr in
skel (f_init mx my e) (f_end e) (f_key e) (f_mouse e) (f_exc e);;
val go : (key * string) list -> unit = <fun>


The call to go descr_calc corresponds to the figure 5.9.






Previous Contents Next ocaml-book-1.0/en/html/book-ora217.html0000644000000000000000000000371307453055401014471 0ustar Contacts Previous Contents

Contacts

Liste de diffusion par messagerie lectronique

Si vous souhaitez recevoir priodiquement les annonces de nouveaux produits O'Reilly en franais, il vous suffit de souscrire un abonnement la liste d'annonces
parutions-oreilly
Merci d'expdier en ce cas un message lectronique majordomo@ora.de contenant (dans le corps du message) :

subscribe parutions-oreilly votre_adresse_email

Exemple :
subscribe parutions-oreilly jean.dupond@ici.fr
Cette liste ne vhicule que des annonces et non des discussions, vous ne pourrez par consquent pas y poster. Son volume ne dpasse pas quatre messages par mois.

En cas de problme technique crire
parutions-oreilly-owner@ora.de

Site Web

Notre site Web http://www.editions-oreilly.fr/ diffuse diverses informations :
  • le catalogue des produits proposs par les ditions O'Reilly,
  • les errata de nos ouvrages,
  • des archives abritant les exemples,
  • la liste des revendeurs.

Previous Contents ocaml-book-1.0/en/html/book-ora001.gif0000644000000000000000000003253207452056110014257 0ustar GIF89aû{{{th!p==!l=Jw===Fwt1W1Sh_N{(c(_tp(h$htǁ{p1_wp1[ccc{{___5Sw!h5W5SBJw$c(19{wp===9NЌptl_{,c111,_{ǿph[wlhW(h!!!5[5W{tlwthwph=N{=J{9Jwwǁ==,cc[J_WF_SF=N$cwt,[p,W{=={$lp$h{t1_{!l!ht5W1Swpc==BJ{5Ntpctlc{,cpl_,_{w(_lh[!tBB!p(h$h1[$,51W9S{{9J{{w,,,lcWhcSh_Sc[N(c_WJ_SJ{wl9N9JÑ{w,_t,[$l,h!,# H*\ȰÇ#JHŋ3jȑb" CIɓ(S\ɲ˗0cʜI͛8sl @@ JѣH*]ʴӧPJJիX6g`~CϔhN[0jzժݻxػ߿VbÇeY̸cN"GfBJNhys%n=ӨSNb˞=$s'Ļ߽?ަq)_С(zuسkߎ 3o9_^=-YeO}?" X$ .ÃF(U=( jT!׉qV^,׋{a"㌘؍88< )D^bHqƒLY E)bW&m\6)bRb jf u)tYgtM7u|٧v'bF&6(eD*)-VJf(jjꩢ4*P8!*Q(FPY¦Er!l/6׳68m^Dfmg߆+.ۺnV=ڛosFu=0l1 l1Q "q:1Tz5: @A'ib@^E(jBnՕ4W0H66x#icD>$_$K:-L[%X%_i|g yzR`g(كz>*Nj)QLƟ1)D Ժ@! la*E'481.r%܉$90esdBը03P:Iv]pG/RC_4>}ת p?u(*W@X WMi8Ke:04rAvnCd4tS]$иŘZB#ev'}Fij̅.E6SՆ!gk׬qglH4zɧm{>MU^ (u(#|F"(cYLwGŒI-R*ȋ1uK9STp^ TdXdN LY9@M- P&%Af.T7e.+\J9_uKea wKa*vT5+X oٸ `q!JT9Tw`G׳G>:VgU۱ aScVuxke+5Uu]CZz`{{O_>=,LZ1,^rlLAB]HtFcv5 74dYGFr]~#\*I.ݖ|V+}Ka[˜3UԻk8g3d'N[|Z7s!=ٸgo MgB 3YS;)<]k]Eanuů=gQ z=ο{ܲ_}Ix(}7^?>K^pG;nzޣ'J_s>?rC_|^ճէϿb~Wz~z H{t'~1G~hs_Wwǀ؁ t$X&xL瀓~s؂}0q/x8:<؃>@B8ԆFxHcyJ؄NGH{))1Y5A(S(}28cĂ`5qgZhh6vGZpPxxvgvm?ϴ@ц?!r8zh|-5GuQ'ukSXwB'xhxVC QxTxu#wYe?|_8b8iW'GF=H@qFHz} 7Uȋhq{ z}uk8n ?q^7DPSaT؈7zWzhY'֘?g+ͨ7v!Xftv@8a+ ?,ӐcXhHuɌ@a?Ww)|8|$9X!)rxZZPXc87yuvijQQS9QهQVɆZ xٕ:ؓZ7x~iɖgʈ!蓗^?TȓRw0HiǘH,Ӎ1)z7{1uc~w8FwLtWɈZID>Xٝ¸}W2BIy?W+-|1hVhfh8ZXFɟ' E~xj?ڡ 9$$z(j*wZ9 ^yF6z8T٠<ڣ>B:D:@zHڣELjIPM:TQz@ʤh(MZ^z b:dZfzhh lڦnZhjr:fvzIzk~||[ i]ڨ*J:Zizکj:zЩiʩz*ꨴZڥʦz_Z*Je2ںڭC:1ߊ嚮:<bຮ:p'QZw:v.jrz B'dpZٚzT*5p(,jtTB#v$[r/ %/+ @`%@pVYг@oRfVBk0pW0д]#R;SEZ b!^=A`f GpE`>}P |k&0#q,P,KT b b 10xw`vP 0 [p_[0[;k!+qʰ 4P*"$bi3C#V`&k$Vv d.܋PZB s&&5bWDvV>x)CgV7"p"*R yCԵ@0 c 0Z0 ekj˶p+PyO8;;K { K`뺰+ .`+KS  )`;"[dL:`Ka a˽N5Cf2kR5f׳="XkK|6 96H  gKk{>uP"L‚K‰˸-0l{á;{ۺ{)ď% ѱ_cT{:(Ke*n-[03{7jWҳYBE'+brO LT;V+?+zT^9S]90 < 9 bk̶nktk}k†+.{2ʝ<>LA ~ l;ʌђK<Û8-G7Շ/]"ӿ˥ u MzT,AIF;2BB;+-!;K&VCvfCaRRg<` ПGE}Q|K}b>ґmғ*һxp 00_S#TtdHAݼ&^ŵԷM}.iqoL]qdvx֠l~*ci}݇jmHY9u]c x=Y{؃] lئL`cN}Ҕ߂kxm=ޯ- d=}ipK&#ϋ#[IBK۵R+EDmf{em)̭}9<БF\E@')O,o1oÏM%(]],W:G2% -~Ԍ6j&OUV|HX0plE}Ju0o^7NrTABXQ`O ]mV4.6s=/%ulWV\iv6!\$[MqEwq>ұtsY[]]EwOٿ'Yއ'5K6m9]p҉w&򾇗[on }ӵnzwEY~zU߿za=u @9u%%t*+u+AN͎DA?Nx1{`"B.ƆI֚ء,س6zyo_E0npe+Ŧ๯SP_AJ[\z@t25vKl #۸Fy"$!ΰȱȭo}21Ǐ룟29d Erxd&-سiphd(E IK%2Mz;BβcM+% fˆɲ"3X8PҘǼr1T"i$8H:ӚL2'yJlZ l^ Xb49{*mRySj Dꐜbe6qp5ibeӁbV~+WZSjic &u=UmlywUb-'5"f,bT+& _ +JZmMi[*]I-u ѷo9սzp,"#<Ƒ%hp;\uX]8sލA%^ Ls%/^W|YC*wpumUܲ vś0_@Kdzs5fu9Ț]_+#v.}1rh\s}ɼ1Oq۫"WďsPv27rGNouy}yCg+t[ r\zM~N|Clh:%MCѫO'_hU~t}XMmi"q39w-v{ntq}N [ ]Nv}NcTߜcc朗9;3 3|h3f94]nz=O[gL}_|K֟/<zG`;s<σA:+<{< >7AAB۾  ?",3$+8Bҫ>;K+C-Cc{3:DB|%@T{W3SD?+TD,?@<> C[%8l67#[—q@/L),@',KؓSPTQT??a+b4R@KdZ|ERD-E;|ICD*El]|FDFScFEFh]tL;BBDqloFG?~4st/c4HtĤ=C@H@|A)sAO@P\BHRȏ|FhGŋܴl̿FȺHpnE{ECɆ<Į4ʣ4I|ֺ"dIGDHYܮKA#C!6$Tlˋó H$ \KJCE|˹K$;w k &ò,-uELJIǿǖE<;ʫwlxIɄG{4̾BL"d LJAjzlM8 N $ɎdJLF?TItFд-dUVjsBJZSV=U]Źyrk= Sm$%-X Ġ9T7T]WvuפiREᗅ5x5֔V$5X.%XeqeX^T_Xcw0WE֙MY[{MSSW])2*zՆUޜQoUQ˭TYR:=*`۱%۱۳E۴Ťe۶[۸]%ۋ ڌ]J٦ٻY=Z;-Z VM_-JDžȅߡʵ.]%E\eݏ4U*٥ڵ]j}+%5EUeu^-' W1K%5E_پZ|75Q " ^_^6>]_EK5N^ ^Ճ&ޘ $RhҮE`6N`ؿYYpZ_a$fbUm(_n.['.b.`~ުTT-4V5&0nōY[&b).c)Ve]Cc6&B6 vZH`=HI TF.cN:f;c;O]֨2K@^ wg߉)gj[kF6GuYYGffބ^nng}wi^f*F~߇IFꥆWhj^il`?jDj&y`kjoV\pXߜ낾~k gX^NlnkU{뒎k鿦m16WS (MN/FFmhhjӞNbNV8!dlߞdvZ^?6af^mܶdž@>m^lv+.Ⱦf.d&n6YVc^AVnnnĶSg&.Vo2^efno~m2konmkR|.l qWq&o6ͮQnppbp^l)lq76fc͞㍦Wm]N~yrxpo#'sNrvq!?q2ws%&'(qe~y@8j9~j@DiS:o./muhprsFfpE7%tGmSwuMu~tvvo;t^oVp'p<]ƅvNvOvvL?d7aWg}:mJwfK|nw(OP+c7`GwdҶt}o~R,_ex'ffysu@dWximaxiu=*,k{vwgng_nb/oS/yt%Z;oq'u?zEO]zqծ7ly|yv'0~xxu =oOtU8roŇppLz|7'|5(VOɧY_Hx\ygﳇG||'evWwħ}~w1OyWV>~Ux٧}2|C'oj/}^nr~pk?}s/soCyxrrsG,H „ 2lC'RhQ Č72G)iҤNqӥ%̘2gҬi&Μ:wl'P)MH)R]0%ȨRRj*֬Z$ydʕ-MLj,ڴjײe-\7f|)I6}/.,+]"aY!-Ȓƭ\6ivm@^4<4MT:Ut|i6ܺe=T^Lem8ʱJҧݽ{nO%u9nV%c8go]7F h/" 7Aez [PU H`rءU桔Kxm ~5^8#XsV|tx$Iv#bAXQ#OA~5$(1wsQy&(ZDq9adMRY]a޹zhBLݞ{gbi߅`kcfV棡&eyh\nʩj** *½("蝌6*e3YYWZNY,ʢny)ꬸV٪⚸; ;l|n;0;ֽ&MZ!~mQ\1~JzӶm.bl'gL~2N!|3`? ۰fmE#=Pq 35^Wm"_ʻkmL;8J5=)7XŇ 'ۼ}- 7ϯKZci6޻?(nd量ᚷ 0z.d=i{^ݸ Ju1Iv?؁,NQJ>Qt L06ljKl<]w{ xhs ن6CF<"%2N|"()RV"HEVχj AmO-0Ҩ5n|#(9ұvr=~'MdⳠ/䃾X&2~&S?[^"dǤ7md-k)$3'f7v֗e3xt Q4djvG6q\ %O ]|uюnt=jˬ&ޗ#>)/w~5EEi馇"~˓S?Y1k.{N1 XS~}ms6-qG97ӭu~7-yӻ7}7;ocaml-book-1.0/en/html/book-ora163.html0000644000000000000000000000555707453055400014500 0ustar Introduction Previous Contents Next

Introduction

This chapter approaches two important aspects of the interface between a programming language and the operating system: communication and processes. The Sys module presented in chapter 8 has already shown how to pass values to a program and how to start a program from another one. The goal of this chapter is to discuss the notions of processes and communication between processes.

The term ``process'' is used for an executing program. Processes are the main components of a parallel application. We introduce processes in the classical way originating from the Unix system. In this context a process is created by another process, establishing a parent-child relationship between them. This relationship allows the parent to wait for the child to terminate, as well as to set up privileged communications between the two. The underlying model of parallelism is that of distributed memory.

The term ``communication'' covers three aspects:
  • input and output via file descriptors. The notion of file descriptors under Unix has a much broader meaning than the simple reading or writing of data from or to a storage medium. We will see this in chapter 20, where programs running on different machines are communicating via such descriptors;
  • the use of pipes between processes which allow the exchange of data using the principle of waiting queues;
  • the generation and handling of signals, which allow a simple interaction between processes.
The functions presented in this chapter are similar to those in the Unix module which accompanies the Objective CAML distribution. The terminology and the notions come from the Unix world. But many of the functions of this module can also be used under Windows. Later we will indicate the applicability of the presented functions.


Previous Contents Next ocaml-book-1.0/en/html/book-ora026.html0000644000000000000000000006674507453055377014521 0ustar Modifiable Data Structures Previous Contents Next

Modifiable Data Structures

Values of the following types: vectors, character strings, records with mutable fields, and references are the data structures whose parts can be physically modified.

We have seen that an Objective CAML variable bound to a value keeps this value to the end of its lifetime. You can only modify this binding with a redefinition---in which case we are not really talking about the ``same'' variable; rather, a new variable of the same name now masks the old one, which is no longer directly accessible, but which remains unchanged. With modifiable values, you can change the value associated with a variable without having to redeclare the latter. You have access to the value of a variable for writing as well as for reading.

Vectors

Vectors, or one dimensional arrays, collect a known number of elements of the same type. You can write a vector directly by listing its values between the symbols [| and |], separated by semicolons as for lists.

# let v = [| 3.14; 6.28; 9.42 |] ;;
val v : float array = [|3.14; 6.28; 9.42|]
The creation function Array.create takes the number of elements in the vector and an initial value, and returns a new vector.

# let v = Array.create 3 3.14;;
val v : float array = [|3.14; 3.14; 3.14|]


To access or modify a particular element, you give the index of that element:

Syntax


expr1 . ( expr2 )

Syntax


expr1 . ( expr2 ) <- expr3


expr1 should be a vector (type array) whose values have type expr3. The expression expr2 must, of course, have type int. The modification is an expression of type unit. The first element of a vector has index 0 and the index of the last element is the length of the vector minus 1. The parentheses around the index expression are required.


# v.(1) ;;
- : float = 3.14
# v.(0) <- 100.0 ;;
- : unit = ()
# v ;;
- : float array = [|100; 3.14; 3.14|]


If the index used to access an element in an array is outside the range of indices of the array, an exception is raised at the moment of access.

# v.(-1) +. 4.0;;
Uncaught exception: Invalid_argument("Array.get")
This check is done during program execution, which can slow it down. Nevertheless it is essential, in order to avoid writing to memory outside the space allocated to a vector, which would cause serious execution errors.

The functions for manipulating arrays are part of the Array module in the standard library. We'll describe them in chapter 8 (page ??). In the examples below, we will use the following three functions from the Array module:

  • create which creates an array of the given size with the given initial value;
  • length which gives the length of a vector;
  • append which concatenates two vectors.

Sharing of Values in a Vector

All the elements of a vector contain the value that was passed in when it was created. This implies a sharing of this value, if it is a structured value. For example, let's create a matrix as a vector of vectors using the function create from the Array module.

# let v = Array.create 3 0;;
val v : int array = [|0; 0; 0|]
# let m = Array.create 3 v;;
val m : int array array = [|[|0; 0; 0|]; [|0; 0; 0|]; [|0; 0; 0|]|]





Figure 3.1: Memory representation of a vector sharing its elements.


If you modify one of the fields of vector v, which was used in the creation of m, then you automatically modify all the ``rows'' of the matrix together (see figures 3.1 and 3.2).

# v.(0) <- 1;;
- : unit = ()
# m;;
- : int array array = [|[|1; 0; 0|]; [|1; 0; 0|]; [|1; 0; 0|]|]





Figure 3.2: Modification of shared elements of a vector.


Duplication occurs if the initialization value of the vector (the second argument passed to Array.create) is an atomic value and there is sharing if this value is a structured value.

Values whose size does not exceed the standard size of Objective CAML values---that is, the memory word---are called atomic values. These are the integers, characters, booleans, and constant constructors. The other values---structured values---are represented by a pointer into a memory area. This distinction is detailed in chapter 9 (page ??).
Vectors of floats are a special case. Although floats are structured values, the creation of a vector of floats causes the the initial value to be copied. This is for reasons of optimization. Chapter 12, on the interface with the C language (page ??), describes this special case.

Non-Rectangular Matrices

A matrix, a vector of vectors, does not need not to be rectangular. In fact, nothing stops you from replacing one of the vector elements with a vector of a different length. This is useful to limit the size of such a matrix. The following value t constructs a triangular matrix for the coefficients of Pascal's triangle.

# let t = [|
[|1|];
[|1; 1|];
[|1; 2; 1|];
[|1; 3; 3; 1|];
[|1; 4; 6; 4; 1|];
[|1; 5; 10; 10; 5; 1|]
|] ;;
val t : int array array =
[|[|1|]; [|1; 1|]; [|1; 2; 1|]; [|1; 3; 3; 1|]; [|1; 4; 6; 4; ...|]; ...|]
# t.(3) ;;
- : int array = [|1; 3; 3; 1|]
In this example, the element of vector t with index i is a vector of integers with size i+1. To manipulate such matrices, you have to calculate the size of each element vector.

Copying Vectors

When you copy a vector, or when you concatenate two vectors, the result obtained is a new vector. A modification of the original vectors does not result in the modification of the copies, unless, as usual, there are shared values.

# let v2 = Array.copy v ;;
val v2 : int array = [|1; 0; 0|]
# let m2 = Array.copy m ;;
val m2 : int array array = [|[|1; 0; 0|]; [|1; 0; 0|]; [|1; 0; 0|]|]
# v.(1)<- 352;;
- : unit = ()
# v2;;
- : int array = [|1; 0; 0|]
# m2 ;;
- : int array array = [|[|1; 352; 0|]; [|1; 352; 0|]; [|1; 352; 0|]|]
We notice in this example that copying m only copies the pointers to v. If one of the elements of v is modified, m2 is modified too.

Concatenation creates a new vector whose size is equal to the sum of the sizes of the two others.

# let mm = Array.append m m ;;
val mm : int array array =
[|[|1; 352; 0|]; [|1; 352; 0|]; [|1; 352; 0|]; [|1; 352; 0|];
[|1; 352; ...|]; ...|]
# Array.length mm ;;
- : int = 6
# m.(0) <- Array.create 3 0 ;;
- : unit = ()
# m ;;
- : int array array = [|[|0; 0; 0|]; [|1; 352; 0|]; [|1; 352; 0|]|]
# mm ;;
- : int array array =
[|[|1; 352; 0|]; [|1; 352; 0|]; [|1; 352; 0|]; [|1; 352; 0|];
[|1; 352; ...|]; ...|]


On the other hand, modification of v, a value shared by m and mm, does affect both these matrices.

# v.(1) <- 18 ;;
- : unit = ()
# mm;;
- : int array array =
[|[|1; 18; 0|]; [|1; 18; 0|]; [|1; 18; 0|]; [|1; 18; 0|]; [|1; 18; ...|];
...|]


Character Strings

Character strings can be considered a special case of vectors of characters. Nevertheless, for efficient memory usage1 their type is specialized. Moreover, access to their elements has a special syntax:

Syntax


expr1 . [expr2]
The elements of a character string can be physically modified:

Syntax


expr1 . [expr2] <- expr3



# let s = "hello";;
val s : string = "hello"
# s.[2];;
- : char = 'l'
# s.[2]<-'Z';;
- : unit = ()
# s;;
- : string = "heZlo"


Mutable Fields of Records

Fields of a record can be declared mutable. All you have to do is to show this in the declaration of the type of the record using the keyword mutable.

Syntax


type name = { ...; mutable namei : t ; ...}


Here is a small example defining a record type for points in the plane:

# type point = { mutable xc : float; mutable yc : float } ;;
type point = { mutable xc: float; mutable yc: float }
# let p = { xc = 1.0; yc = 0.0 } ;;
val p : point = {xc=1; yc=0}


Thus the value of a field which is declared mutable can be modified using the syntax:

Syntax


expr1 . name <- expr2


The expression expr1 should be a record type which has the field name. The modification operator returns a value of type unit.

# p.xc <- 3.0 ;;
- : unit = ()
# p ;;
- : point = {xc=3; yc=0}


We can write a function for moving a point by modifying its components. We use a local declaration with pattern matching in order to sequence the side-effects.

# let moveto p dx dy =
let () = p.xc <- p.xc +. dx
in p.yc <- p.yc +. dy ;;
val moveto : point -> float -> float -> unit = <fun>
# moveto p 1.1 2.2 ;;
- : unit = ()
# p ;;
- : point = {xc=4.1; yc=2.2}


It is possible to mix mutable and non-mutable fields in the definition of a record. Only those specified as mutable may be modified.

# type t = { c1 : int; mutable c2 : int } ;;
type t = { c1: int; mutable c2: int }
# let r = { c1 = 0; c2 = 0 } ;;
val r : t = {c1=0; c2=0}
# r.c1 <- 1 ;;
Characters 0-9:
The label c1 is not mutable
# r.c2 <- 1 ;;
- : unit = ()
# r ;;
- : t = {c1=0; c2=1}


On page ?? we give an example of using records with modifiable fields and arrays to implement a stack structure.

References

Objective CAML provides a polymorphic type ref which can be seen as the type of a pointer to any value; in Objective CAML terminology we call it a reference to a value. A referenced value can be modified. The type ref is defined as a record with one modifiable field:

type 'a ref = {mutable contents:'a}
This type is provided as a syntactic shortcut. We construct a reference to a value using the function ref. The referenced value can be reached using the prefix function (!). The function modifying the content of a reference is the infix function (:=).

# let x = ref 3 ;;
val x : int ref = {contents=3}
# x ;;
- : int ref = {contents=3}
# !x ;;
- : int = 3
# x := 4 ;;
- : unit = ()
# !x ;;
- : int = 4
# x := !x+1 ;;
- : unit = ()
# !x ;;
- : int = 5


Polymorphism and Modifiable Values

The type ref is parameterized. This is what lets us use it to create references to values of any type whatever. However, it is necessary to place certain restrictions on the type of referenced values; we cannot allow the creation of a reference to a value with a polymorphic type without taking some precautions.

Let us suppose that there were no restriction; then someone could declare:
let x = ref [] ;;
Then the variable x would have type 'a list ref and its value could be modified in a way which would be inconsistent with the strong static typing of Objective CAML:

x := 1 :: !x ;;
x := true :: !x ;;
Thus we would have one and the same variable having type int list at one moment and bool list the next.

In order to avoid such a situation, Objective CAML's type inference mechanism uses a new category of type variables: weak type variables. Syntactically, they are distinguished by the underscore character which prefixes them.

# let x = ref [] ;;
val x : '_a list ref = {contents=[]}
The type variable '_a is not a type parameter, but an unknown type awaiting instantiation; the first use of x after its declaration fixes the value that '_a will take in all types that depend on it, permanently.

# x := 0::!x ;;
- : unit = ()
# x ;;
- : int list ref = {contents=[0]}
From here onward, the variable x has type int list ref.

A type containing an unknown is in fact monomorphic even though its type has not been specified. It is not possible to instantiate this unknown with a polymorphic type.


# let x = ref [] ;;
val x : '_a list ref = {contents=[]}
# x := (function y -> ())::!x ;;
- : unit = ()
# x ;;
- : ('_a -> unit) list ref = {contents=[<fun>]}
In this example, even though we have instantiated the unknown type with a type which is a priori polymorphic ('a -> unit), the type has remained monomorphic with a new unknown type.

This restriction of polymorphism applies not only to references, but to any value containing a modifiable part: vectors, records having at least one field declared mutable, etc. Thus all the type parameters, even those which have nothing to do with a modifiable part, are weak type variables.

# type ('a,'b) t = { ch1 :'a list ; mutable ch2 : 'b list } ;;
type ('a, 'b) t = { ch1: 'a list; mutable ch2: 'b list }
# let x = { ch1 = [] ; ch2 = [] } ;;
val x : ('_a, '_b) t = {ch1=[]; ch2=[]}


Warning


This modification of the typing of application has consequences for pure functional programs.


Likewise, when you apply a polymorphic value to a polymorphic function, you get a weak type variable, because you must not exclude the possibility that the function may construct physically modifiable values. In other words, the result of the application is always monomorphic.

# (function x -> x) [] ;;
- : '_a list = []


You get the same result with partial application:

# let f a b = a ;;
val f : 'a -> 'b -> 'a = <fun>
# let g = f 1 ;;
val g : '_a -> int = <fun>


To get a polymorphic type back, you have to abstract the second argument of f and then apply it:

# let h x = f 1 x ;;
val h : 'a -> int = <fun>


In effect, the expression which defines h is the functional expression function x -> f 1 x. Its evaluation produces a closure which does not risk producing a side effect, because the body of the function is not evaluated.

In general, we distinguish so-called ``non-expansive'' expressions, whose calculation we are sure carries no risk of causing a side effect, from other expressions, called ``expansive.'' Objective CAML's type system classifies expressions of the language according to their syntactic form:
  • ``non-expansive'' expressions include primarily variables, constructors of non-mutable values, and abstractions;
  • ``expansive'' expressions include primarily applications and constructors of modifiable values. We can also include here control structures like conditionals and pattern matching.

Previous Contents Next ocaml-book-1.0/en/html/book-ora172.html0000644000000000000000000000313107453055401014463 0ustar Notes
1
Under Unix each user belongs to one or more user groups, which allows the organization of their rights.
2
The type file_perm is an alias for the type int.
3
The options and the behavior of this command are not standardized. The given example may not be reproducible.
4
Molire, Le Bourgeois Gentilhome, Acte II, scne 4.

Link


http://www.site-moliere.com/pieces/bourgeoi.htm
5
We recall that the signals are handled in an asynchronous way. So, if two children die one after the other, it is possible that the signal of the first has not been handled.
ocaml-book-1.0/en/html/book-ora094.html0000644000000000000000000000454207453055400014474 0ustar Introduction Previous Contents Next

Introduction

Program analysis tools provide supplementary information to the programmer in addition to the feedback from the compiler and the linker. Some of these tools perform a static analysis, i.e. they look at the code (either as text or in the form of a syntax tree) and determine certain properties like interdependency of modules or uncaught exceptions. Other tools perform a dynamic analysis, i.e. they look at the flow of execution. Analysis tools are useful for determining the number of calls to certain functions, getting a trace of the flow of arguments, or determining the time spent in certain parts of the program. Some are interactive, like the tools for debugging. In this case program execution is modified to account for user interaction. It is then possible to set breakpoints, in order to look at values or to restart program execution with different arguments.

The Objective CAML distribution includes such tools. Some of them have rather unusual characteristics, mostly dealing with static typing. It is, in fact, this static typing that guarantees the absence of type errors during program execution and enables the compiler to produce efficient code with a small memory footprint. Typing information is partly lost for constructed Objective CAML values. This creates certain difficulties, e.g. the impossibility of showing the arguments of polymorphic functions.


Previous Contents Next ocaml-book-1.0/en/html/book-ora005.html0000644000000000000000000000140707453055401014462 0ustar Notes
1
Institut National de Recherche en Informatique et Automatique (National Institute for Research in Automation and Information Technology).
ocaml-book-1.0/en/html/book-ora016.html0000644000000000000000000024200507453055377014501 0ustar Type declarations and pattern matching Previous Contents Next

Type declarations and pattern matching

Although Objective CAML's predefined types permit the construction of data structures from tuples and lists, one needs to be able to define new types to describe certain data structures. In Objective CAML, type declarations are recursive and may be parameterized by type variables, in the same vein as the type 'a list already encountered. Type inference takes these new declarations into account to produce the type of an expression. The construction of values of these new types uses the constructors described in their definition. A special feature of languages in the ML family is pattern matching. It allows simple access to the components of complex data structures. A function definition most often corresponds to pattern matching over one of its parameters, allowing the function to be defined by cases.

First of all we present pattern matching over the predefined types, and then go on to describe the various ways to declare structured types and how to construct values of such types, as well as how to access their components through pattern matching.

Pattern matching

A pattern is not strictly speaking an Objective CAML expression. It's more like a correct (syntactically, and from the point of view of types) arrangement of elements such as constants of the primitive types (int, bool, char, ..), variables, constructors, and the symbol _ called the wildcard pattern. Other symbols are used in writing patterns. We will introduce them to the extent needed.

Pattern matching applies to values. It is used to recognize the form of this value and lets the computation be guided accordingly, associating with each pattern an expression to compute.

Syntax


match expr with
| p1 -> expr1
:
| pn -> exprn

The expression expr is matched sequentially to the various patterns p1, ..., pn. If one of the patterns (for example pi) is consistent with the value of expr then the corresponding computation branch (expri) is evaluated. The various patterns pi are of the same type. The same goes for the various expressions expri. The vertical bar preceding the first pattern is optional.

Examples

Here are two ways to define by pattern matching a function imply of type (bool * bool) -> bool implementing logical implication. A pattern which matches pairs has the form ( , ).

The first version of imply enumerates all possible cases, as a truth table would:

# let imply v = match v with
(true,true) -> true
| (true,false) -> false
| (false,true) -> true
| (false,false) -> true;;
val imply : bool * bool -> bool = <fun>


By using variables which group together several cases, we obtain a more compact definition.

# let imply v = match v with
(true,x) -> x
| (false,x) -> true;;
val imply : bool * bool -> bool = <fun>
These two versions of imply compute the same function. That is, they return the same values for the same inputs.

Linear pattern

A pattern must necessarily be linear, that is, no given variable can occur more than once inside the pattern being matched. Thus, we might have hoped to be able to write:

# let equal c = match c with
(x,x) -> true
| (x,y) -> false;;
Characters 35-36:
This variable is bound several times in this matching
But this would have required the compiler to know how to carry out equality tests. Yet this immediately raises numerous problems. If we accept physical equality between values, we get a system which is too weak, incapable of recognizing the equality between two occurrences of the list [1; 2], for example. If we decide to use structural equality, we run the risk of having to traverse, ad infinitum, circular structures. Recursive functions, for example, are circular structures, but we can construct recursive, hence circular, values which are not functions as well (see page ??).

Wildcard pattern

The symbol _ matches all possible values. It is called a wildcard pattern. It can be used to match complex types. We use it, for example, to further simplify the definition of the function imply:

# let imply v = match v with
(true,false) -> false
| _ -> true;;
val imply : bool * bool -> bool = <fun>


A definition by pattern matching must handle the entire set of possible cases of the values being matched. If this is not the case, the compiler prints a warning message:

# let is_zero n = match n with 0 -> true ;;
Characters 17-40:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
1
val is_zero : int -> bool = <fun>


Indeed if the actual parameter is different from 0 the function doesn't know what value to return. So the case analysis can be completed using the wildcard pattern.

# let is_zero n = match n with
0 -> true
| _ -> false ;;
val is_zero : int -> bool = <fun>


If, at run-time, no pattern is selected, then an exception is raised. Thus, one can write:

# let f x = match x with 1 -> 3 ;;
Characters 11-30:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
0
val f : int -> int = <fun>
# f 1 ;;
- : int = 3
# f 4 ;;
Uncaught exception: Match_failure("", 11, 30)
The Match_Failure exception is raised by the call to f 4, and if it is not handled induces the computation in progress to halt (see ??)

Combining patterns

Combining several patterns lets us obtain a new pattern which can match a value according to one or another of the original patterns. The syntactic form is as follows:

Syntax


p1 | ...| pn
It constructs a new pattern by combining the patterns p1, ...and pn. The only strong constraint is that all naming is forbidden within these patterns. So each one of them must contain only constant values or the wildcard pattern. The following example demonstrates how to verify that a character is a vowel.
 
# let is_a_vowel c = match c with
'a' | 'e' | 'i' | 'o' | 'u' | 'y' -> true
| _ -> false ;;
val is_a_vowel : char -> bool = <fun>
# is_a_vowel 'i' ;;
- : bool = true
# is_a_vowel 'j' ;;
- : bool = false


Pattern matching of a parameter

Pattern matching is used in an essential way for defining functions by cases. To make writing these definitions easier, the syntactic construct function allows pattern matching of a parameter:

Syntax


function | p1 -> expr1
  | p2 -> expr2
    :
  | pn -> exprn

The vertical bar preceding the first pattern is optional here as well. In fact, like Mr. Jourdain, each time we define a function, we use pattern matching7. Indeed, the construction function x -> expression, is a definition by pattern matching using a single pattern reduced to one variable. One can make use of this detail with simple patterns as in:

# let f = function (x,y) -> 2*x + 3*y + 4 ;;
val f : int * int -> int = <fun>


In fact the form
function p1 -> expr1 | ...| pn -> exprn
is equivalent to
function expr -> match expr with p1 -> expr1 | ...| pn -> exprn

Using the equivalence of the declarations mentioned on page ??, we write:

# let f (x,y) = 2*x + 3*y + 4 ;;
val f : int * int -> int = <fun>
But this natural way of writing is only possible if the value being matched belongs to a type having only a single constructor. If such is not the case, the pattern matching is not exhaustive:

# let is_zero 0 = true ;;
Characters 13-21:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
1
val is_zero : int -> bool = <fun>


Naming a value being matched

During pattern matching, it is sometimes useful to name part or all of the pattern. The following syntactic form introduces the keyword as which binds a name to a pattern.

Syntax


( p as name )
This is useful when one needs to take apart a value while still maintaining its integrity. In the following example, the function min_rat gives the smaller rational of a pair of rationals. The latter are each represented by a numerator and denominator in a pair.


# let min_rat pr = match pr with
((_,0),p2) -> p2
| (p1,(_,0)) -> p1
| (((n1,d1) as r1), ((n2,d2) as r2)) ->
if (n1 * d2 ) < (n2 * d1) then r1 else r2;;
val min_rat : (int * int) * (int * int) -> int * int = <fun>
To compare two rationals, it is necessary to take them apart in order to name their numerators and denominators (n1, n2, d1 and d2), but the initial pair (r1 or r2) must be returned. The as construct allows us to name the parts of a single value in this way. This lets us avoid having to reconstruct the rational returned as the result.

Pattern matching with guards

Pattern matching with guards corresponds to the evaluation of a conditional expression immediately after the pattern is matched. If this expression comes back true, then the expression associated with that pattern is evaluated, otherwise pattern matching continues with the following pattern.

Syntax


match expr with
:
| pi when condi -> expri
:



The following example uses two guards to test equality of two rationals.

# let eq_rat cr = match cr with
((_,0),(_,0)) -> true
| ((_,0),_) -> false
| (_,(_,0)) -> false
| ((n1,1), (n2,1)) when n1 = n2 -> true
| ((n1,d1), (n2,d2)) when ((n1 * d2) = (n2 * d1)) -> true
| _ -> false;;
val eq_rat : (int * int) * (int * int) -> bool = <fun>
If the guard fails when the fourth pattern is matched, matching continues with the fifth pattern.

Note


The verification carried out by Objective CAML as to whether the pattern matching is exhaustive assumes that the conditional expression in the guard may be false. Consequently, it does not count this pattern since it is not possible to know, before execution, whether the guard will be satisfied or not.
It won't be possible to detect that the pattern matching in the following example is exhaustive.

# let f = function x when x = x -> true;;
Characters 10-40:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
_
val f : 'a -> bool = <fun>


Pattern matching on character intervals

In the context of pattern matching on characters, it is tedious to construct the combination of all the patterns corresponding to a character interval. Indeed, if one wishes to test a character or even a letter, one would need to write 26 patterns at a minimum and combine them. For characters, Objective CAML permits writing patterns of the form:

Syntax


'c1' .. 'cn'
It is equivalent to the combination: 'c1' | 'c2' | ...| 'cn'.

For example the pattern '0' .. '9' corresponds to the pattern '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'. The first form is nicer to read and quicker to write.

Warning


This feature is among the extensions to the language and may change in future versions.
Using combined patterns and intervals, we define a function categorizing characters according to several criteria.

# let char_discriminate c = match c with
'a' | 'e' | 'i' | 'o' | 'u' | 'y'
| 'A' | 'E' | 'I' | 'O' | 'U' | 'Y' -> "Vowel"
| 'a'..'z' | 'A'..'Z' -> "Consonant"
| '0'..'9' -> "Digit"
| _ -> "Other" ;;
val char_discriminate : char -> string = <fun>
It should be noted that the order of the groups of patterns has some significance. Indeed, the second set of patterns includes the first, but it is not examined until after the check on the first.

Pattern matching on lists

As we have seen, a list can be:
  • either empty (the list is of the form []),
  • or composed of a first element (its head) and a sublist (its tail). The list is then of the form h::t.
These two possible ways of writing a list can be used as patterns and allow pattern matching on a list.

# let rec size x = match x with
[] -> 0
| _::tail_x -> 1 + (size tail_x) ;;
val size : 'a list -> int = <fun>
# size [];;
- : int = 0
# size [7;9;2;6];;
- : int = 4


So we can redo the examples described previously (see page ??) using pattern matching, such as iteration over lists for example.

# let rec fold_left f a = function
[] -> a
| head::tail -> fold_left f (f a head) tail ;;
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a = <fun>
# fold_left (+) 0 [8;4;10];;
- : int = 22


Value declaration through pattern matching

Value declaration in fact uses pattern matching. The declaration let x = 18 matches the value 18 with the pattern x. Any pattern is allowed as the left-hand side of a declaration; the variables in the pattern are bound to the values which they match.

# let (a,b,c) = (1, true, 'A');;
val a : int = 1
val b : bool = true
val c : char = 'A'
# let (d,c) = 8, 3 in d + c;;
- : int = 11
The scope of pattern variables is the usual static scope for local declarations. Here, c remains bound to the value 'A'.

# a + (int_of_char c);;
- : int = 66


As with any kind of pattern matching, value declaration may not be exhaustive.

# let [x;y;z] = [1;2;3];;
Characters 5-12:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]
val x : int = 1
val y : int = 2
val z : int = 3
# let [x;y;z] = [1;2;3;4];;
Characters 4-11:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]
Uncaught exception: Match_failure("", 4, 11)


Any pattern is allowed, including constructors, wildcards and combined patterns.

# let head :: 2 :: _ = [1; 2; 3] ;;
Characters 5-19:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]
val head : int = 1
# let _ = 3. +. 0.14 in "PI" ;;
- : string = "PI"


This last example is of little use in the functional world insofar as the computed value 3.14 is not named and so is lost.

Type declaration

Type declarations are another possible ingredient in an Objective CAML phrase. They support the definition of new types corresponding to the original data structures used in a program. There are two major families of types: product types for tuples or records; and sum types for unions.

Type declarations use the keyword type.

Syntax


type name = typedef ;;
In contrast with variable declarations, type declarations are recursive by default. That is, type declarations, when combined, support the declaration of mutually recursive types.

Syntax


type name1 = typedef1
and name2 = typedef2
  :    
and namen = typedefn ;;

Type declarations can be parameterized by type variables. A type variable name always begins with an apostrophe (the ' character):

Syntax


type 'a name = typedef ;;
When there are several of them, the type parameters are declared as a tuple in front of the name of the type:

Syntax


type ('a1 ...'an) name = typedef ;;
Only the type parameters defined on the left-hand side of the declaration may appear on the right-hand side.

Note


Objective CAML's type printer renames the type parameters encountered; the first is called 'a, the second 'b and so forth.


One can always define a new type from one or more existing types.

Syntax


type name = type expression
This is useful for constraining a type which one finds too general.

# type 'param paired_with_integer = int * 'param ;;
type 'a paired_with_integer = int * 'a
# type specific_pair = float paired_with_integer ;;
type specific_pair = float paired_with_integer


Nevertheless without type constraints, inference will produce the most general type.

# let x = (3, 3.14) ;;
val x : int * float = 3, 3.14


But one can use a type constraint to see the desired name appear:

# let (x:specific_pair) = (3, 3.14) ;;
val x : specific_pair = 3, 3.14


Records

Records are tuples, each of whose fields is named in the same way as the Pascal record or the C struct. A record always corresponds to the declaration of a new type. A record type is defined by the declaration of its name and the names and types of each of its fields.

Syntax


type name = { name1 : t1; ...; namen : tn } ;;
We can define a type representing complex numbers by:

# type complex = { re:float; im:float } ;;
type complex = { re: float; im: float }


The creation of a value of record type is done by giving a value to each of its fields (in arbitrary order).

Syntax


{ namei1 = expri1; ...; namein = exprin } ;;
For example, we create a complex number with real part 2. and imaginary part 3.:

# let c = {re=2.;im=3.} ;;
val c : complex = {re=2; im=3}
# c = {im=3.;re=2.} ;;
- : bool = true


In the case where some fields are missing, the following error is produced:

# let d = { im=4. } ;;
Characters 9-18:
Some labels are undefined


A field can be accessed in two ways: by the dot notation or by pattern matching on certain fields.

The dot notation syntax is as usual:

Syntax


expr.name
The expression expr must be of a record type containing a field name.

Pattern matching a record lets one retrieve the value bound to several fields. A pattern to match a record has the following syntax:

Syntax


{ namei = pi ; ...; namej = pj }
The patterns are to the right of the = sign (pi, ..., pj). It is not necessary to make all the fields of a record appear in such a pattern.

The function add_complex accesses fields through the dot notation, while the function mult_complex accesses them through pattern matching.

# let add_complex c1 c2 = {re=c1.re+.c2.re; im=c1.im+.c2.im};;
val add_complex : complex -> complex -> complex = <fun>
# add_complex c c ;;
- : complex = {re=4; im=6}
# let mult_complex c1 c2 = match (c1,c2) with
({re=x1;im=y1},{re=x2;im=y2}) -> {re=x1*.x2-.y1*.y2;im=x1*.y2+.x2*.y1} ;;
val mult_complex : complex -> complex -> complex = <fun>
# mult_complex c c ;;
- : complex = {re=-5; im=12}


The advantages of records, as opposed to tuples, are at least twofold:
  • descriptive and distinguishing information thanks to the field names: in particular this allows pattern matching to be simplified;
  • access in an identical way, by name, to any field of the record whatsoever: the order of the fields no longer has significance, only their names count.
The following example shows the ease of accessing the fields of records as opposed to tuples:

# let a = (1,2,3) ;;
val a : int * int * int = 1, 2, 3
# let f tr = match tr with x,_,_ -> x ;;
val f : 'a * 'b * 'c -> 'a = <fun>
# f a ;;
- : int = 1
# type triplet = {x1:int; x2:int; x3:int} ;;
type triplet = { x1: int; x2: int; x3: int }
# let b = {x1=1; x2=2; x3=3} ;;
val b : triplet = {x1=1; x2=2; x3=3}
# let g tr = tr.x1 ;;
val g : triplet -> int = <fun>
# g b ;;
- : int = 1


For pattern matching, it is not necessary to indicate all the fields of the record being matched. The inferred type is then that of the last field.

# let h tr = match tr with {x1=x} -> x;;
val h : triplet -> int = <fun>
# h b;;
- : int = 1


There is a construction which lets one create a record identical to another except for some fields. It is often useful for records containing many fields.

Syntax


{ name with namei= expri ; ...; namej=exprj}



# let c = {b with x1=0} ;;
val c : triplet = {x1=0; x2=2; x3=3}
A new copy of the value of b is created where only the field x1 has a new value.

Warning


This feature is among the extensions to the language and may change in future versions.


Sum types

In contrast with tuples or records, which correspond to a Cartesian product, the declaration of a sum type corresponds to a union of sets. Different types (for example integers or character strings) are gathered into a single type. The various members of the sum are distinguished by constructors, which support on the one hand, as their name indicates, construction of values of this type and on the other hand, thanks to pattern matching, access to the components of these values. To apply a constructor to an argument is to indicate that the value returned belongs to this new type.

A sum type is declared by giving the names of its constructors and the types of their eventual arguments.

Syntax


type name = ...
  | Namei ...
  | Namej of tj ...
  | Namek of tk * ...* tl ...;;



A constructor name is a particular identifier:

Warning


The names of constructors always begin with a capital letter.


Constant constructors

A constructor which doesn't expect an argument is called a constant constructor. Constant constructors can subsequently be used directly as a value in the language, as a constant.

# type coin = Heads | Tails;;
type coin = | Heads | Tails
# Tails;;
- : coin = Tails
The type bool can be defined in this way.

Constructors with arguments

Constructors can have arguments. The keyword of indicates the type of the constructor's arguments. This supports the gathering into a single type of objects of different types, each one being introduced with a particular constructor.

Here is a classic example of defining a datatype to represent the cards in a game, here Tarot8. The types suit and card are defined in the following way:

# type suit = Spades | Hearts | Diamonds | Clubs ;;
# type card =
King of suit
| Queen of suit
| Knight of suit
| Knave of suit
| Minor_card of suit * int
| Trump of int
| Joker ;;


The creation of a value of type card is carried out through the application of a constructor to a value of the appropriate type.

# King Spades ;;
- : card = King Spades
# Minor_card(Hearts, 10) ;;
- : card = Minor_card (Hearts, 10)
# Trump 21 ;;
- : card = Trump 21


And here, for example, is the function all_cards which constructs a list of all the cards of a suit passed as a parameter.

# let rec interval a b = if a = b then [b] else a::(interval (a+1) b) ;;
val interval : int -> int -> int list = <fun>
# let all_cards s =
let face_cards = [ Knave s; Knight s; Queen s; King s ]
and other_cards = List.map (function n -> Minor_card(s,n)) (interval 1 10)
in face_cards @ other_cards ;;
val all_cards : suit -> card list = <fun>
# all_cards Hearts ;;
- : card list =
[Knave Hearts; Knight Hearts; Queen Hearts; King Hearts;
Minor_card (Hearts, 1); Minor_card (Hearts, 2); Minor_card (Hearts, 3);
Minor_card (Hearts, ...); ...]


To handle values of sum types, we use pattern matching. The following example constructs conversion functions from values of type suit and of type card to character strings (type string):

# let string_of_suit = function
Spades -> "spades"
| Diamonds -> "diamonds"
| Hearts -> "hearts"
| Clubs -> "clubs" ;;
val string_of_suit : suit -> string = <fun>
# let string_of_card = function
King c -> "king of " ^ (string_of_suit c)
| Queen c -> "queen of " ^ (string_of_suit c)
| Knave c -> "knave of " ^ (string_of_suit c)
| Knight c -> "knight of " ^ (string_of_suit c)
| Minor_card (c, n) -> (string_of_int n) ^ " of "^(string_of_suit c)
| Trump n -> (string_of_int n) ^ " of trumps"
| Joker -> "joker" ;;
val string_of_card : card -> string = <fun>
Lining up the patterns makes these functions easy to read.

The constructor Minor_card is treated as a constructor with two arguments. Pattern matching on such a value requires naming its two components.

# let is_minor_card c = match c with
Minor_card v -> true
| _ -> false;;
Characters 41-53:
The constructor Minor_card expects 2 argument(s),
but is here applied to 1 argument(s)


To avoid having to name each component of a constructor, one declares it to have a single argument by parenthesizing the corresponding tuple type. The two constructors which follow are pattern-matched differently.

# type t =
C of int * bool
| D of (int * bool) ;;
# let access v = match v with
C (i, b) -> i,b
| D x -> x;;
val access : t -> int * bool = <fun>


Recursive types

Recursive type definitions are indispensable in any algorithmic language for describing the usual data structures (lists, heaps, trees, graphs, etc.). To this end, in Objective CAML type definition is recursive by default, in contrast with value declaration (let).

Objective CAML's predefined type of lists only takes a single parameter. One may wish to store values of belonging to two different types in a list structure, for example, integers (int) or characters (char). In this case, one defines:

# type int_or_char_list =
Nil
| Int_cons of int * int_or_char_list
| Char_cons of char * int_or_char_list ;;

# let l1 = Char_cons ( '=', Int_cons(5, Nil) ) in
Int_cons ( 2, Char_cons ( '+', Int_cons(3, l1) ) ) ;;
- : int_or_char_list =
Int_cons (2, Char_cons ('+', Int_cons (3, Char_cons ('=', Int_cons (...)))))


Parametrized types

A user can equally well declare types with parameters. This lets us generalize the example of lists containing values of two different types.

# type ('a, 'b) list2 =
Nil
| Acons of 'a * ('a, 'b) list2
| Bcons of 'b * ('a, 'b) list2 ;;

# Acons(2, Bcons('+', Acons(3, Bcons('=', Acons(5, Nil))))) ;;
- : (int, char) list2 =
Acons (2, Bcons ('+', Acons (3, Bcons ('=', Acons (...)))))


One can, obviously, instantiate the parameters 'a and 'b with the same type.

# Acons(1, Bcons(2, Acons(3, Bcons(4, Nil)))) ;;
- : (int, int) list2 = Acons (1, Bcons (2, Acons (3, Bcons (4, Nil))))


This use of the type list2 can, as in the preceding example, serve to mark even integers and odd integers. In this way we extract the sublist of even integers in order to construct an ordinary list.

# let rec extract_odd = function
Nil -> []
| Acons(_, x) -> extract_odd x
| Bcons(n, x) -> n::(extract_odd x) ;;
val extract_odd : ('a, 'b) list2 -> 'b list = <fun>
The definition of this function doesn't give a single clue as to the nature of the values stored in the structure. That is why its type is parameterized.

Scope of declarations

Constructor names obey the same scope discipline as global declarations: a redefinition masks the previous one. Nevertheless values of the masked type still exist. The interactive toplevel does not distinguish these two types in its output. Whence some unclear error messages.

In this first example, the constant constructor Nil of type int_or_char has been masked by the constructor declarations of the type ('a, 'b) list2.

# Int_cons(0, Nil) ;;
Characters 13-16:
This expression has type ('a, 'b) list2 but is here used with type
int_or_char_list


This second example provokes a rather baffling error message, at least the first time it appears. Let the little program be as follows:

# type t1 = Empty | Full;;
type t1 = | Empty | Full
# let empty_t1 x = match x with Empty -> true | Full -> false ;;
val empty_t1 : t1 -> bool = <fun>
# empty_t1 Empty;;
- : bool = true


Then, we redeclare the type t1:

# type t1 = {u : int; v : int} ;;
type t1 = { u: int; v: int }
# let y = { u=2; v=3 } ;;
val y : t1 = {u=2; v=3}


Now if we apply the function empty_t1 to a value of the new type t1, we get the following error message:

# empty_t1 y;;
Characters 10-11:
This expression has type t1 but is here used with type t1
The first occurrence of t1 represents the first type defined, while the second corresponds to the second type.

Function types

The type of the argument of a constructor may be arbitrary. In particular, it may very well contain a function type. The following type constructs lists, all of whose elements except the last are function values.

# type 'a listf =
Val of 'a
| Fun of ('a -> 'a) * 'a listf ;;
type 'a listf = | Val of 'a | Fun of ('a -> 'a) * 'a listf


Since function values are values which can be manipulated in the language, we can construct values of type listf:

# let eight_div = (/) 8 ;;
val eight_div : int -> int = <fun>
# let gl = Fun (succ, (Fun (eight_div, Val 4))) ;;
val gl : int listf = Fun (<fun>, Fun (<fun>, Val 4))
and functions which pattern-match such values:
 
# let rec compute = function
Val v -> v
| Fun(f, x) -> f (compute x) ;;
val compute : 'a listf -> 'a = <fun>
# compute gl;;
- : int = 3


Example: representing trees

Tree structures come up frequently in programming. Recursive types make it easy to define and manipulate such structures. In this subsection, we give two examples of tree structures.

Binary trees
We define a binary tree structure whose nodes are labelled with values of a single type by declaring:

# type 'a bin_tree =
Empty
| Node of 'a bin_tree * 'a * 'a bin_tree ;;


We use this structure to define a little sorting program using binary search trees. A binary search tree has the property that all the values in the left branch are less than that of the root, and all those of the right branch are greater. Figure 2.5 gives an example of such a structure over the integers. The empty nodes (constructor Empty) are represented there by little squares; the others (constructor Node), by a circle in which is inscribed the stored value.




Figure 2.5: Binary search tree.


A sorted list is extracted from a binary search tree via an inorder traversal carried out by the following function:


# let rec list_of_tree = function
Empty -> []
| Node(lb, r, rb) -> (list_of_tree lb) @ (r :: (list_of_tree rb)) ;;
val list_of_tree : 'a bin_tree -> 'a list = <fun>


To obtain a binary search tree from a list, we define an insert function.

# let rec insert x = function
Empty -> Node(Empty, x, Empty)
| Node(lb, r, rb) -> if x < r then Node(insert x lb, r, rb)
else Node(lb, r, insert x rb) ;;
val insert : 'a -> 'a bin_tree -> 'a bin_tree = <fun>


The function to transform a list into a tree is obtained by iterating the function insert.

# let rec tree_of_list = function
[] -> Empty
| h::t -> insert h (tree_of_list t) ;;
val tree_of_list : 'a list -> 'a bin_tree = <fun>


The sort function is then simply the composition of the functions tree_of_list and list_of_tree.

# let sort x = list_of_tree (tree_of_list x) ;;
val sort : 'a list -> 'a list = <fun>
# sort [5; 8; 2; 7; 1; 0; 3; 6; 9; 4] ;;
- : int list = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9]


General planar trees
In this part, we use the following predefined functions from the List module (see page ??):
  • List.map: which applies a function to all the elements of a list and returns the list of results;
  • List.fold_left: which is an equivalent version of the function fold_left defined on page ??;
  • List.exists: which applies a boolean-valued function to all the elements of a list; if one of these applications yields true then the result is true, otherwise the function returns false.
A general planar tree is a tree whose number of branches is not fixed a priori; to each node is associated a list of branches whose length may vary.

# type 'a tree = Empty
| Node of 'a * 'a tree list ;;
The empty tree is represented by the value Empty. A leaf is a node without branches either of the form Node(x,[]), or of the degenerate form Node(x, [Empty;Empty; ..]). It is then relatively easy to write functions to manipulate these trees, e.g., to determine whether an element belongs to a tree or compute the height of the tree.

To test membership of an element e, we use the following algorithm: if the tree is empty then e does not belong to this tree, otherwise e belongs to the tree if and only if either it is equal to the label of the root, or it belongs to one of its branches.

# let rec belongs e = function
Empty -> false
| Node(v, bs) -> (e=v) or (List.exists (belongs e) bs) ;;
val belongs : 'a -> 'a tree -> bool = <fun>


To compute the height of a tree, we use the following definition: an empty tree has height 0, otherwise the height of the tree is equal to the height of its highest subtree plus 1.

# let rec height =
let max_list l = List.fold_left max 0 l in
function
Empty -> 0
| Node (_, bs) -> 1 + (max_list (List.map height bs)) ;;
val height : 'a tree -> int = <fun>


Recursive values which are not functions

Recursive declaration of non-function values allows the construction of circular data structures.

The following declaration constructs a circular list with one element.

# let rec l = 1::l ;;
val l : int list =
[1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; ...]


Application of a recursive function to such a list risks looping until memory overflows.

# size l ;;
Stack overflow during evaluation (looping recursion?).


Structural equality remains usable with such lists only when physical equality is first verified:

# l=l ;;
- : bool = true


In short, if you define a new list, even an equal one, you must not use the structural equality test on pain of seeing your program loop indefinitely. So we don't recommend attempting to evaluate the following example:
let rec l2 = 1::l2 in l=l2 ;;
On the other hand, physical equality always remains possible.

# let rec l2 = 1::l2 in l==l2 ;;
- : bool = false


The predicate == tests equality of an immediate value or sharing of a structured object (equality of the address of the value). We will use it to verify that in traversing a list we don't retraverse a sublist which was already examined. First of all, we define the function memq, which verifies the presence of an element in the list by relying on physical equality. It is the counterpart to the function mem which tests structural equality; these two functions belong to the module List.

# let rec memq a l = match l with
[] -> false
| b::l -> (a==b) or (memq a l) ;;
val memq : 'a -> 'a list -> bool = <fun>


The size computation function is redefined, storing the list of lists already examined and halting if a list is encountered a second time.

# let special_size l =
let rec size_aux previous l = match l with
[] -> 0
| _::l1 -> if memq l previous then 0
else 1 + (size_aux (l::previous) l1)
in size_aux [] l ;;
val special_size : 'a list -> int = <fun>
# special_size [1;2;3;4] ;;
- : int = 4
# special_size l ;;
- : int = 1
# let rec l1 = 1::2::l2 and l2 = 1::2::l1 in special_size l1 ;;
- : int = 4



Previous Contents Next ocaml-book-1.0/en/html/book-ora028.html0000644000000000000000000007770107453055377014515 0ustar Control Structures Previous Contents Next

Control Structures

Input-output and modifiable values produce side-effects. Their use is made easier by an imperative programming style furnished with new control structures. We present in this section the sequence and iteration structures.

We have already met the conditional control structure on page ??, whose abbreviated form if then patterns itself on the imperative world. We will write, for example:

# let n = ref 1 ;;
val n : int ref = {contents=1}
# if !n > 0 then n := !n - 1 ;;
- : unit = ()


Sequence

The first of the typically imperative structures is the sequence. This permits the left-to-right evaluation of a sequence of expressions separated by semicolons.

Syntax


expr1 ; ...; exprn
A sequence of expressions is itself an expression, whose value is that of the last expression in the sequence (here, exprn). Nevertheless, all the expressions are evaluated, and in particular their side-effects are taken into account.

# print_string "2 = "; 1+1 ;;
2 = - : int = 2


With side-effects, we get back the usual construction of imperative languages.

# let x = ref 1 ;;
val x : int ref = {contents=1}
# x:=!x+1 ; x:=!x*4 ; !x ;;
- : int = 8


As the value preceding a semicolon is discarded, Objective CAML gives a warning when it is not of type unit.

# print_int 1; 2 ; 3 ;;
Characters 14-15:
Warning: this expression should have type unit.
1- : int = 3


To avoid this message, you can use the function ignore:

# print_int 1; ignore 2; 3 ;;
1- : int = 3


A different message is obtained if the value has a functional type, as Objective CAML suspects that you have forgotten a parameter of a function.

# let g x y = x := y ;;
val g : 'a ref -> 'a -> unit = <fun>
# let a = ref 10;;
val a : int ref = {contents=10}
# let u = 1 in g a ; g a u ;;
Characters 13-16:
Warning: this function application is partial,
maybe some arguments are missing.
- : unit = ()
# let u = !a in ignore (g a) ; g a u ;;
- : unit = ()


As a general rule we parenthesize sequences to clarify their scope. Syntactically, parenthesizing can take two forms:

Syntax


( expr )

Syntax


begin expr end
We can now write the Higher/Lower program from page ?? more naturally:

# let rec hilo n =
print_string "type a number: ";
let i = read_int () in
if i = n then print_string "BRAVO\n\n"
else
begin
if i < n then print_string "Higher\n" else print_string "Lower\n" ;
hilo n
end ;;
val hilo : int -> unit = <fun>


Loops

The iterative control structures are also from outside the functional world. The conditional expression for repeating, or leaving, a loop does not make sense unless there can be a physical modification of the memory which permits its value to change. There are two iterative control structures in Objective CAML: the for loop for a bounded iteration and the while loop for a non-bounded iteration. The loop structures themselves are expressions of the language. Thus they return a value: the constant () of type unit.

The for loop can be rising (to) or falling (downto) with a step of one.

Syntax


for name = expr1 to expr2 do expr3 done
for name = expr1 downto expr2 do expr3 done

The expressions expr1 and expr2 are of type int. If expr3 is not of type unit, the compiler produces a warning message.

# for i=1 to 10 do print_int i; print_string " " done; print_newline() ;;
1 2 3 4 5 6 7 8 9 10
- : unit = ()
# for i=10 downto 1 do print_int i; print_string " " done; print_newline() ;;
10 9 8 7 6 5 4 3 2 1
- : unit = ()


The non-bounded loop is the ``while'' loop whose syntax is:

Syntax


while expr1 do expr2 done
The expression expr1 should be of type bool. And, as for the for loop, if expr2 is not of type unit, the compiler produces a warning message.

# let r = ref 1
in while !r < 11 do
print_int !r ;
print_string " " ;
r := !r+1
done ;;
1 2 3 4 5 6 7 8 9 10 - : unit = ()


It is important to understand that loops are expressions like the previous ones which calculate the value () of type unit.

# let f () = print_string "-- end\n" ;;
val f : unit -> unit = <fun>
# f (for i=1 to 10 do print_int i; print_string " " done) ;;
1 2 3 4 5 6 7 8 9 10 -- end
- : unit = ()
Note that the string "-- end\n" is output after the integers from 1 to 10 have been printed: this is a demonstration that the arguments (here the loop) are evaluated before being passed to the function.

In imperative programming, the body of a loop (expr2) does not calculate a value, but advances by side effects. In Objective CAML, when the body of a loop is not of type unit the compiler prints a warning, as for the sequence:

# let s = [5; 4; 3; 2; 1; 0] ;;
val s : int list = [5; 4; 3; 2; 1; 0]
# for i=0 to 5 do List.tl s done ;;
Characters 17-26:
Warning: this expression should have type unit.
- : unit = ()


Example: Implementing a Stack

The data structure 'a stack will be implemented in the form of a record containing an array of elements and the first free position in this array. Here is the corresponding type:

# type 'a stack = { mutable ind:int; size:int; mutable elts : 'a array } ;;
The field size contains the maximal size of the stack.

The operations on these stacks will be init_stack for the initialization of a stack, push for pushing an element onto a stack, and pop for returning the top of the stack and popping it off.

# let init_stack n = {ind=0; size=n; elts =[||]} ;;
val init_stack : int -> 'a stack = <fun>
This function cannot create a non-empty array, because you would have to provide it with the value with which to construct it. This is why the field elts gets an empty array.

Two exceptions are declared to guard against attempts to pop an empty stack or to add an element to a full stack. They are used in the functions pop and push.

# exception Stack_empty ;;
# exception Stack_full ;;

# let pop p =
if p.ind = 0 then raise Stack_empty
else (p.ind <- p.ind - 1; p.elts.(p.ind)) ;;
val pop : 'a stack -> 'a = <fun>
# let push e p =
if p.elts = [||] then
(p.elts <- Array.create p.size e;
p.ind <- 1)
else if p.ind >= p.size then raise Stack_full
else (p.elts.(p.ind) <- e; p.ind <- p.ind + 1) ;;
val push : 'a -> 'a stack -> unit = <fun>


Here is a small example of the use of this data structure:

# let p = init_stack 4 ;;
val p : '_a stack = {ind=0; size=4; elts=[||]}
# push 1 p ;;
- : unit = ()
# for i = 2 to 5 do push i p done ;;
Uncaught exception: Stack_full
# p ;;
- : int stack = {ind=4; size=4; elts=[|1; 2; 3; 4|]}
# pop p ;;
- : int = 4
# pop p ;;
- : int = 3


If we want to prevent raising the exception Stack_full when attempting to add an element to the stack, we can enlarge the array. To do this the field size must be modifiable too:

# type 'a stack =
{mutable ind:int ; mutable size:int ; mutable elts : 'a array} ;;
# let init_stack n = {ind=0; size=max n 1; elts = [||]} ;;
# let n_push e p =
if p.elts = [||]
then
begin
p.elts <- Array.create p.size e;
p.ind <- 1
end
else if p.ind >= p.size then
begin
let nt = 2 * p.size in
let nv = Array.create nt e in
for j=0 to p.size-1 do nv.(j) <- p.elts.(j) done ;
p.elts <- nv;
p.size <- nt;
p.ind <- p.ind + 1
end
else
begin
p.elts.(p.ind) <- e ;
p.ind <- p.ind + 1
end ;;
val n_push : 'a -> 'a stack -> unit = <fun>


All the same, you have to be careful with data structures which can expand without bound. Here is a small example where the initial stack grows as needed.

# let p = init_stack 4 ;;
val p : '_a stack = {ind=0; size=4; elts=[||]}
# for i = 1 to 5 do n_push i p done ;;
- : unit = ()
# p ;;
- : int stack = {ind=5; size=8; elts=[|1; 2; 3; 4; 5; 5; 5; 5|]}
# p.stack ;;
Characters 0-7:
Unbound label stack


It might also be useful to allow pop to decrease the size of the stack, to reclaim unused memory.

Example: Calculations on Matrices

In this example we aim to define a type for matrices, two-dimensional arrays containing floating point numbers, and to write some operations on the matrices. The monomorphic type mat is a record containing the dimensions and the elements of the matrix. The functions create_mat, access_mat, and mod_mat are respectively the functions for creation, accessing an element, and modification of an element.

# type mat = { n:int; m:int; t: float array array };;
type mat = { n: int; m: int; t: float array array }
# let create_mat n m = { n=n; m=m; t = Array.create_matrix n m 0.0 } ;;
val create_mat : int -> int -> mat = <fun>
# let access_mat m i j = m.t.(i).(j) ;;
val access_mat : mat -> int -> int -> float = <fun>
# let mod_mat m i j e = m.t.(i).(j) <- e ;;
val mod_mat : mat -> int -> int -> float -> unit = <fun>
# let a = create_mat 3 3 ;;
val a : mat = {n=3; m=3; t=[|[|0; 0; 0|]; [|0; 0; 0|]; [|0; 0; 0|]|]}
# mod_mat a 1 1 2.0; mod_mat a 1 2 1.0; mod_mat a 2 1 1.0 ;;
- : unit = ()
# a ;;
- : mat = {n=3; m=3; t=[|[|0; 0; 0|]; [|0; 2; 1|]; [|0; 1; 0|]|]}


The sum of two matrices a and b is a matrix c  such that   cij = aij + bij.

# let add_mat p q =
if p.n = q.n && p.m = q.m then
let r = create_mat p.n p.m in
for i = 0 to p.n-1 do
for j = 0 to p.m-1 do
mod_mat r i j (p.t.(i).(j) +. q.t.(i).(j))
done
done ;
r
else failwith "add_mat : dimensions incompatible";;
val add_mat : mat -> mat -> mat = <fun>
# add_mat a a ;;
- : mat = {n=3; m=3; t=[|[|0; 0; 0|]; [|0; 4; 2|]; [|0; 2; 0|]|]}


The product of two matrices a and b is a matrix c  such that cij = k=1k=ma aik. bkj

# let mul_mat p q =
if p.m = q.n then
let r = create_mat p.n q.m in
for i = 0 to p.n-1 do
for j = 0 to q.m-1 do
let c = ref 0.0 in
for k = 0 to p.m-1 do
c := !c +. (p.t.(i).(k) *. q.t.(k).(j))
done;
mod_mat r i j !c
done
done;
r
else failwith "mul_mat : dimensions incompatible" ;;
val mul_mat : mat -> mat -> mat = <fun>
# mul_mat a a;;
- : mat = {n=3; m=3; t=[|[|0; 0; 0|]; [|0; 5; 2|]; [|0; 2; 1|]|]}



Previous Contents Next ocaml-book-1.0/en/html/book-ora038.html0000644000000000000000000007011607453055377014507 0ustar Which Style to Choose? Previous Contents Next

Which Style to Choose?

This is no matter of religion or esthetics; a priori neither style is prettier or holier than the other. On the contrary, one style may be more adequate than the other depending on the problem to be solved.

The first rule to apply is the rule of simplicity. Whether the algorithm to use implemented is written in a book, or whether its seed is in the mind of the programmer, the algorithm is itself described in a certain style. It is natural to use the same style when implementing it.

The second criterion of choice is the efficiency of the program. One may say that an imperative program (if well written) is more efficient that its functional analogue, but in very many cases the difference is not enough to justify complicating the code to adopt an imperative style where the functional style would be natural. The function map in the previous section is a good example of a problem naturally expressed in the functional style, which gains nothing from being written in the imperative style.

Sequence or Composition of Functions

We have seen that as soon as a program causes side effects, it is necessary to determine precisely the order of evaluation for the elements of the program. This can be done in both styles:
functional:
using the fact that Objective CAML is a strict language, which means that the argument is evaluated before applying the function. The expression (f (g x)) is computed by first evaluating (g x), and then passing the result as argument to f. With more complex expressions, we can name an intermediate result with the let in construction, but the idea remains the same: let aux=(g x) in (f aux).
imperative:
using sequences or other control structures (loops). In this case, the result is not the value returned by a function, but its side effects on memory: aux:=(g x) ; (f !aux).
Let us examine this choice of style on an example. The quick sort algorithm, applied to a vector, is described recursively as follows:
  1. Choose a pivot: This is the index of an element of the vector;
  2. Permute around the pivot: Permute the elements of the vector so elements less than the value at the pivot have indices less than the pivot, and vice versa;
  3. sort the subvectors obtained on each side of the pivot, using the same algorithm: The subvector preceding the pivot and the subvector following the pivot.
The choice of algorithm, namely to modify a vector so that its elements are sorted, incites us to use an imperative style at least to manipulate the data.

First, we define a function to permute two elements of a vector:

# let permute_element vec n p =
let aux = vec.(n) in vec.(n) <- vec.(p) ; vec.(p) <- aux ;;
val permute_element : 'a array -> int -> int -> unit = <fun>


The choice of a good pivot determines the efficiency of the algorithm, but we will use the simplest possible choice here: return the index of the first element of the (sub)vector.

# let choose_pivot vec start finish = start ;;
val choose_pivot : 'a -> 'b -> 'c -> 'b = <fun>


Let us write the algorithm that we would like to use to permute the elements of the vector around the pivot.
  1. Place the pivot at the beginning of the vector to be permuted;
  2. Initialize i to the index of the second element of the vector;
  3. Initialize j to the index of the last element of the vector;
  4. If the element at index j is greater than the pivot, permute it with the element at index i and increment i; otherwise, decrement j;
  5. While i<j, repeat the previous operation;
  6. At this stage, every element with index <i (or equivalently, j) is less than the pivot, and all others are greater; if the element with index i is less than the pivot, permute it with the pivot; otherwise, permute its predecessor with the pivot.
In implementing this algorithm, it is natural to adopt imperative control structures.

# let permute_pivot vec start finish ind_pivot =
permute_element vec start ind_pivot ;
let i = ref (start+1) and j = ref finish and pivot = vec.(start) in
while !i < !j do
if vec.(!j) >= pivot then decr j
else
begin
permute_element vec !i !j ;
incr i
end
done ;
if vec.(!i) > pivot then decr i ;
permute_element vec start !i ;
!i
;;
val permute_pivot : 'a array -> int -> int -> int -> int = <fun>
In addition to its effects on the vector, this function returns the index of the pivot as its result.

All that remains is to put together the different stages and add the recursion on the sub-vectors.

# let rec quick vec start finish =
if start < finish
then
let pivot = choose_pivot vec start finish in
let place_pivot = permute_pivot vec start finish pivot in
quick (quick vec start (place_pivot-1)) (place_pivot+1) finish
else vec ;;
val quick : 'a array -> int -> int -> 'a array = <fun>


We have used the two styles here. The chosen pivot serves as argument to the permutation around this pivot, and the index of the pivot after the permutation is an argument to the recursive call. By contrast, the vector obtained after the permutation is not returned by the permute_pivot function; instead, this result is produced by side effect. However, the quick function returns a vector, and the sorting of sub-vectors is obtained by composition of recursive calls.

The main function is:

# let quicksort vec = quick vec 0 ((Array.length vec)-1) ;;
val quicksort : 'a array -> 'a array = <fun>
It is a polymorphic function because the order relation < on vector elements is itself polymorphic.

# let t1 = [|4;8;1;12;7;3;1;9|] ;;
val t1 : int array = [|4; 8; 1; 12; 7; 3; 1; 9|]
# quicksort t1 ;;
- : int array = [|1; 1; 3; 4; 7; 8; 9; 12|]
# t1 ;;
- : int array = [|1; 1; 3; 4; 7; 8; 9; 12|]
# let t2 = [|"the"; "little"; "cat"; "is"; "dead"|] ;;
val t2 : string array = [|"the"; "little"; "cat"; "is"; "dead"|]
# quicksort t2 ;;
- : string array = [|"cat"; "dead"; "is"; "little"; "the"|]
# t2 ;;
- : string array = [|"cat"; "dead"; "is"; "little"; "the"|]


Shared or Copy Values

When the values that we manipulate are not mutable, it does not matter whether they are shared or not.

# let id x = x ;;
val id : 'a -> 'a = <fun>
# let a = [ 1; 2; 3 ] ;;
val a : int list = [1; 2; 3]
# let b = id a ;;
val b : int list = [1; 2; 3]
Whether b is a copy of the list a or the very same list makes no difference, because these are intangible values anyway. But if we put modifiable values in place of integers, we need to know whether modifying one value causes a change in the other.

The implementation of polymorphism in Objective CAML causes immediate values to be copied, and structured values to be shared. Even though arguments are always passed by value, only the pointer to a structured value is copied. This is the case even in the function id:

# let a = [| 1 ; 2 ; 3 |] ;;
val a : int array = [|1; 2; 3|]
# let b = id a ;;
val b : int array = [|1; 2; 3|]
# a.(1) <- 4 ;;
- : unit = ()
# a ;;
- : int array = [|1; 4; 3|]
# b ;;
- : int array = [|1; 4; 3|]
We have here a genuine programming choice to decide which is the most efficient way to represent a data structure. On one hand, using mutable values allows manipulations in place, which means without allocation, but requires us to make copies sometimes when immutable data would have allowed sharing. We illustrate this here with two ways to implement lists.

# type 'a list_immutable = LInil | LIcons of 'a * 'a list_immutable ;;
# type 'a list_mutable = LMnil | LMcons of 'a * 'a list_mutable ref ;;


The immutable lists are strictly equivalent to lists built into Objective CAML, while the mutable lists are closer to the style of C, in which a cell is a value together with a reference to the following cell.

With immutable lists, there is only one way to write concatenation, and it requires duplicating the structure of the first list; by contrast, the second list may be shared with the result.

# let rec concat l1 l2 = match l1 with
LInil -> l2
| LIcons (a,l11) -> LIcons(a, (concat l11 l2)) ;;
val concat : 'a list_immutable -> 'a list_immutable -> 'a list_immutable =
<fun>

# let li1 = LIcons(1, LIcons(2, LInil))
and li2 = LIcons(3, LIcons(4, LInil)) ;;
val li1 : int list_immutable = LIcons (1, LIcons (2, LInil))
val li2 : int list_immutable = LIcons (3, LIcons (4, LInil))
# let li3 = concat li1 li2 ;;
val li3 : int list_immutable =
LIcons (1, LIcons (2, LIcons (3, LIcons (4, LInil))))
# li1==li3 ;;
- : bool = false
# let tlLI l = match l with
LInil -> failwith "Liste vide"
| LIcons(_,x) -> x ;;
val tlLI : 'a list_immutable -> 'a list_immutable = <fun>
# tlLI(tlLI(li3)) == li2 ;;
- : bool = true
From these examples, we see that the first cells of li1 and li3 are distinct, while the second half of li3 is exactly li2.

With mutable lists, we have a choice between modifying arguments (function concat_share) and creating a new value (function concat_copy).

# let rec concat_copy l1 l2 = match l1 with
LMnil -> l2
| LMcons (x,l11) -> LMcons(x, ref (concat_copy !l11 l2)) ;;
val concat_copy : 'a list_mutable -> 'a list_mutable -> 'a list_mutable =
<fun>
This first solution, concat_copy, gives a result similar to the previous function, concat. A second solution shares its arguments with its result fully:

# let concat_share l1 l2 =
match l1 with
LMnil -> l2
| _ -> let rec set_last = function
LMnil -> failwith "concat_share : impossible case!!"
| LMcons(_,l) -> if !l=LMnil then l:=l2 else set_last !l
in
set_last l1 ;
l1 ;;
val concat_share : 'a list_mutable -> 'a list_mutable -> 'a list_mutable =
<fun>
Concatenation with sharing does not require any allocation, and therefore does not use the constructor LMcons. Instead, it suffices to cause the last cell of the first list to point to the second list. However, this version of concatenation has the potential weakness that it alters arguments passed to it.

# let lm1 = LMcons(1, ref (LMcons(2, ref LMnil)))
and lm2 = LMcons(3, ref (LMcons(4, ref LMnil))) ;;
val lm1 : int list_mutable =
LMcons (1, {contents=LMcons (2, {contents=LMnil})})
val lm2 : int list_mutable =
LMcons (3, {contents=LMcons (4, {contents=LMnil})})
# let lm3 = concat_share lm1 lm2 ;;
val lm3 : int list_mutable =
LMcons (1, {contents=LMcons (2, {contents=LMcons (...)})})
We do indeed obtain the expected result for lm3. However, the value bound to lm1 has been modified.

# lm1 ;;
- : int list_mutable =
LMcons (1, {contents=LMcons (2, {contents=LMcons (...)})})
This may therefore have consequences on the rest of the program.

How to Choose your Style

In a purely functional program, side effects are forbidden, and this excludes mutable data structures, exceptions, and input/output. We prefer, though, a less restrictive definition of the functional style, saying that functions that do not modify their global environment may be used in a functional style. Such a function may manipulate mutable values locally, and may therefore be written in an imperative style, but must not modify global variables, nor its arguments. We permit them to raise exceptions in addition. Viewed from outside, these functions may be considered ``black boxes.'' Their behavior matches a function written in a purely functional style, apart from being able of breaking control flow by raising an exception. In the same spirit, a mutable value which can no longer be modified after initialization may be used in a functional style.

On the other hand, a program written in an imperative style still benefits from the advantages provided by Objective CAML: static type safety, automatic memory management, the exception mechanism, parametric polymorphism, and type inference.

The choice between the imperative and functional styles depends on the application to be developed. We may nevertheless suggest some guidelines based on the character of the application, and the criteria considered important in the development process.
  • choice of data structures: The choice whether to use mutable data structures follows from the style of programming adopted. Indeed, the functional style is essentially incompatible with modifying mutable values. By contrast, constructing and traversing objects are the same whatever their status. This touches the same issue as ``modification in place vs copying'' on page ??; we return to it again in discussing criteria of efficiency.
  • required data structures: If a program must modify mutable data structures, then the imperative style is the only one possible. If, on the other hand, you just have to traverse values, then adopting the functional style guarantees the integrity of the data.
    Using recursive data structures requires the use of functions that are themselves recursive. Recursive functions may be defined using either of the two styles, but it is often easier to understand the creation of a value following a recursive definition, which corresponds to a functional approach, than to repeat the recursive processing on this element. The functional style allows us to define generic iterators over the structure of data, which factors out the work of development and makes it faster.

  • criteria of efficiency: Modification in place is far more efficient than creating a value. When code efficiency is the preponderant criterion, it will usually tip the balance in favor of the imperative style. We note however that the need to avoid sharing values may turn out to be a very hard task, and in the end costlier than copying the values to begin with.
    Being purely functional has a cost. Partial application and using functions passed as arguments from other functions has an execution cost greater than total application of a function whose declaration is visible. Using this eminently functional feature must thus be avoided in those portions of a program where efficiency is crucial.

  • development criteria: the higher level of abstraction of functional programs permits them to be written more quickly, leading to code that is more compact and contains fewer errors than the equivalent imperative code, which is generally more verbose. The functional style is better suited to the constraints imposed by developing substantial applications. Since each function is not dependent upon its evaluation context, functional can be easily divided into small units that can be examined separately; as a consequence, the code is easier to read.
    Programs written using the functional style are more easily reusable because of its better modularity, and because functions may be passed as arguments to other functions.
These remarks show that it is often a good idea to mix the two programming styles within the same application. The functional programming style is faster to develop and confers a simpler organization to an application. However, portions whose execution time is critical repay being developed in a more efficient imperative style.


Previous Contents Next ocaml-book-1.0/en/html/book-ora151.html0000644000000000000000000000705307453055400014466 0ustar Introduction Previous Contents Next

Introduction

Chapters 14 and 15 respectively presented two models of application organisation: The functional/modular model and the object model. These two models address, each in its own way, the needs of application development:

  • logical organisation of a program: module or class;
  • separate compilation: simple module;
  • abstract data types: module (abstract type) or object;
  • reuse of components: functors/sharing of types with parametric polymorphism or inheritance/subtyping with parameterized classes;
  • modifiability of components: late binding (object).
The development of a modular application begins by dividing it into logical units: modules. This is followed by the actualization of their specification by writing their signature, and finally by implementation. During the implementation of a module, it may be necessary to modify its signature or that of its parameters; it is then necessary to modify their sources. This is unsatisfactory if the same module is already used by another application. Nevertheless, this process offers a strict and reassuring framework for the programmer.

In the object model, the analysis of a problem results in the description of the relations between classes. If, later on, a class does not provide the required functionality, it is always possible to extend it by subclassing. This process permits the reuse of large hierarchies of classes without modifying their sources, and thus not modifying the behavior of an application that uses them, either. Unfortunately, this technique leads to code bloat, and poses difficulties of duplication with multiple inheritance.

Many problems necessitate recursive data types and operations which manipulate values of these types. It often happens that the problem evolves, sometimes in the course of implementation, sometimes during maintenance, requiring an extension of the types and operations. Neither of these two models permits extension in both ways. In the functional/modular model, types are not extensible, but one can create new functions (operations) on the types. In the object model, one can extend the objects, but not the methods (by creating a new subclass on an abstract class which implements its methods.) In this respect, the two models are duals.

The advantage of uniting these two models in the same language is to be able to choose the most appropriate model for the resolution of the problem in question, and to mix them in order to overcome the limitations of each model.


Previous Contents Next ocaml-book-1.0/en/html/book-ora150.html0000644000000000000000000000217607453055401014467 0ustar Notes
1
A number of notations exist for describing relations, e.g. UML (Unified Modeling Language).
2
In most object-oriented languages, a dot notation is used. However, the dot notation was already used for records and modules, so a new symbol was needed.
3
The private of Objective CAML corresponds to protected of Objective C, C++ and Java
ocaml-book-1.0/en/html/book-ora081.gif0000644000000000000000000000477407452056112014300 0ustar GIF89a'UUU999!,'H0I8ͻ H&PJ-óZ(~6mH_8cO[ʘ4IlZWzxLvlzn|N~Ar~Zd| A[*Ľ "--Ո((%Z-̡A޸ Isw^|IVC@q~f|8Gf>Xqu9RبrVVpMǜ55Q/,*hӧDA 5)իbJI+ׯuY%X˶۷pʝK7N6t{ڳe?̶#/ }p ib%6 \As5?|x%bŨFR (\纤LΌZ0&?r[,k&"{,fFq6Mii5..[&kVιh8{xrogM5 y3SӸ3ATQ"μ 2z3@H2"R3"0SJ#'EԄ51Έ̹: C XL4J:#DC(7A[ZTtԚaϬ% Cpi>TQJ)qg?EE nIL'_/uSOQ #]D ~.#^*{߳%L+g&*5T6⑭ {7Vm r{֨G>tU 3ny~u1*RpaQ0Wqp XnVmEƦtL2VI9`Mmu[Ln+|<.$=lZA&%_ gJ*1% K*QK}YcZ+^kKMAu1[ -Y6Ց+}5j57uӂ'NVt xcq?nrlN9ssn#ߟ←駣N⫳Sz`TN;Cz#|zy3|./O-Gك?\h'{o?ɳP_ ?=/ v?E{N`p  [ '$a8hB$|c(担(@^u%9 nD+$opGğ'q`uq`l"W(Jq؁ьkcD1ʑYc Fq|L@NkD!ٹ"r  /4%xIEVfJ#7FGR$c' T[+X3j2;egy?ڲ̥'oQr/KOhl&cJdldrIm^ദ$Lq64g 6HF\7yL}$d00,?[(ԟ5ġ *4=62z*Ϟ3"3^4i95xtI')ѧ49WrS,)M©2Sa ~߰xԖfzKBƾtJ9ж2Dg(rљӪ+(Ej05y.&-`!WNELEʾꆫ+樂ܩ3oNbtd7[Nt*gyբ?-eT֚MQںC#mݖ4}-oJZ6rclo+sopjv:fkޖnv.׻fe`ǥ=u偠SYWˀ䋇MŮ9c"3=jÃ3Pgq;ocaml-book-1.0/en/html/book-ora037.gif0000644000000000000000000000623407452056111014271 0ustar GIF89aUUUrrr!,H0I8ͻ`(dih .s lpH#A1MeqJZlDlVNK.FkL1<+V< 8a::zw|2lGR,J`980a0K4 <߷˞fR A^hHa"q9dPŇA.֚A#{qF#CH9dg\P&>OΤ%&OF?Ee =[;IjiBG9QU*WujV86`oX56Sm=97 \Ivrw[r}}ٴpbk 6l'fDr!x&#5% QaڧmC׾m8D+?GxDW69_OeR[<hB^=ʛrqC'/z}C\2-爁`E c =%"T U}q! ^t8څ R,f]@`1cH-娆9 H2j85^-3a%b_pRbdM-d[7I# F&'VJW(6'}"ڹ[yvT$y:%p'8*餔Vj饘#*Jrb a+* jg2LlO޾ ɀ̄2^AvCLTVxk: Wml &9srIw4E ޳@a< ecZܖ B@#`}jcOD%kݽ ĈQI擻mmpe&WrIl^twB%p}Vn4v]d08c 's޵O@is8CYhY !BXH|†$06ւ1 D"T*%Ҵoj]bRBENuG%'Nr *(?@" ɽkݒU`j |0SY Bas./ARK]Џ ^يlU!K񓖛#71˧ t&nc[$?yJg?5Z(FIs4E18(Ne&RJrɥV`"L{3*<,AHКQtvS"Ta:i@7`,8kTrQ'L?^=j<ꗰ2ƬxZJϒ5=ŕլγO2]]V[ڌw\GGUwN386ŒeUB㌚5Eg6d\27ev?iiM 23]FwJiҶlir{,5{\J`nsE;VLt]bUnrsKj gl\ak^%o4Ƞ5nub1[JY_@"p\_‡503W!N~0J86Ux pI5òUcGL+k,\gJ;q w#Fs",JfjmQrP_⹞0en1gQJ1s甆35#JݬR|!ssyGznST?Ё^ xH  K FMT4-j~)¥驇jZ6b-4Ӥ5kU+ýl~t5lc . iS{^izmn6"6-u~ejyKRE}{tt ! |V-Kdr W63!qsOxpqwUr>95畧.9c.a\ _{@yn{fMҗtK!kMunضPM{~u[U|5j\}vq|N$vOd)A uqS]Czޑ<χ~+($yֶ/wⷼ7d_ٻz72_~X`E$~~ ؀v@'KC}(UN 8<Ɓwp(5Fx'0],vg2y o(6xtB8DXgtHJL؄! 0kRXUW\XFe_!c`@XaxϷl/nXsV!hrz }8Ň^w8i]؈h88H[;&3艢'XlH hw_e"Xb劺S(qXsmXH~w#hUQpM@삍mSM_R'pxw 2;.A CI/2@2"#Ia0I6N g;yb#6?cQyc5y@?5wqq ؋x(U#f]9!#z}r<Ӑ բ-,i*B.$e#9mi"⇓ۘh2 1Q7b ؒ Object-oriented languages: comparison with Java Previous Contents Next

Object-oriented languages: comparison with Java

Although Objective CAML sprang from the functional world, it is necessary to compare its object-oriented extension to an important representative of the object-oriented languages. We pick the Java language which, while similar from the point of view of its implementation, differs strongly in its object model and its type system.

The Java language is an object-oriented language developed by the SUN corporation. The main site for access to the language is the following:

Link


http://java.sun.com


Main characteristics

The Java language is a language with classes. Inheritance is simple and allows redefinition or overloading of inherited methods. Typing is static. An inherited class is in a subtyping relationship with its ancestor class.

Java does not have parameterized classes. One gets two types of polymorphism: ad hoc by overloading, and of inclusion by redefinition.

It is multi-threading and supports the development of distributed application whether using sockets or by invoking methods of remote objects (Remote Method Invocation).

The principles of its implementation are close to those of Objective CAML. A Java program is compiled to a virtual machine (JVM). The loading of code is dynamic. The code produced is independent of machine architectures, being interpreted by a virtual machine. The basic datatypes are specified in such a way as to guarantee the same representation on all architectures. The runtime is equipped with a GC.

Java has important class libraries (around 600 with the JDK, which are supplemented by many independent developments). The main libraries concern graphical interfaces and I/O operations integrating communication between machines.

Differences with Objective CAML

The main differences between Java and Objective CAML come from their type system, from redefinition and from overloading of methods. Redefinition of an inherited method must use parameters of exactly the same type. Method overloading supports switching the method to use according to the types of the method call parameters. In the following example class B inherits from class A. Class B redefines the first version of the to_string method, but overloads the second version. Moreover the eq method is overloaded since the type of the parameter (here B) is not equal to the type of the parameter of the inherited method (here A). In the end class B has two eq methods and two to_string methods.
class A {
  boolean eq (A o) {  return true;}
  String to_string (int n ) { }
}

class B extends A {
  boolean eq (B o) {  return true;}
  String to_string (int n ) { }
  String to_string (float x, float y)
}
Although binding is late, overload resolution, that is determination of the type of the method to use, is carried out on compilation.

The second important difference derives from the possibility of casting the type of an object, as would be done in C. In the following example, two objects a and b are defined, of class A and B respectively. Then three variables c, d and e are declared while imposing a type constraint on the affected values.
{
  A a = new A ();
  B b = new B ();
  A c = (A) b;
  B d = (B) c;
  B e = (B) a;
}
Since the type of b is a subtype of the type of a, the cast from b to c is accepted. In this case the type constraint may be omitted. On the other hand the two following constraints require a dynamic type test to be carried out to guarantee that the values in c and in a do in fact correspond to objects of class B. In this program this is true for c, but false for a. So this last case raises an exception. While this is useful, in particular for graphical interfaces, these type constraints can lead to exceptions being raised during exception due to erroneous use of types. In this Java is a language typed partly statically and partly dynamically. Moreover the absence of parameterized classes quite often obliges one to use this feature to write generic classes.


Previous Contents Next ocaml-book-1.0/en/html/book-ora029.gif0000644000000000000000000000624007452056111014267 0ustar GIF89aʽ}}}{}{{y׮!A,AHBB6%CD EE!F"З؂ۦ߮Ჱ庺҂@4H YȐᎇ!HD1qく ?Ir$(OXɒe0_Isf8oɓg@JHZXP^JuXbʕ낯`K,hXvm=|P` 6oEw2dɒ)S\3f͚9shI37esժYvliӶm.޼ :+{,,*l,:',J7;,ZG{--jW :i6[n;|Cpr1sI7]e]m }xfy詷^{]ᖛ}=q\r2לsE u5QNT!z=D)#6yEiB)0#dcc cCvBRX `U$=S& @Wb[v".YifoœtD':h (tWXFi CJ+'H f0^:cij䩩.$@r-+z+A*B lFIi☣Z'*9՚nzהI9Qp{,6rz/ þ)h ;->Æp]VfkhInرr(#23lJ.:'snЄKt 3rwrhNmmVs,%Gd8 }ؼFm/J,N 5)kH5.>[+n~%0jvyLtbtzgjajO]{󸹞e;;GOK_]ݞm.}~|Ͷ~{r>lAɚb2a%o֞["I]cj{\=%w Oj XB@AQDQwAe1dL!Aʄwӄ/O}akՇ ph?mm{/s#b0ԬyW_x~=`1Z -È1*w;ӧ$L$"gF2$@JZҒ Ȥ&PNzғ(EyR8H*UVL,cZڲȥ.s^җ0b&H2Ɂf:ә4ЁjZӚȦ6Ƀnz8rH:9vӝ<)zӞȧ>~p@JЂMBІ:ʆD'JъZͨF7юz HGJҒ(MJWRM4J,Lҗ8$tJQ(M PB4i@*U>TG զժW-*MZUZ5TѪZeY:jd+HU6b+X*׾nE +\׺o_kQ.UEX;TLefO:zvhGKҚMjWkڶUkg9ԭb?!pԷ-'kn/AG43,tY5S;أVnu;]S{W*65q*_e"ޚX敫kovഺenhkkTտ啬wʷ ~_ 6~!߯ޥ0"%1xU_ 0:Fvnek|&du Y+w[:(nyL gכ-Wʽm<`w*sx2r-Y[VBˆNF;ѐ'MJ[Ҙδ7N{ӠGMRԨNWj"zV)|M2u[ C$l;KG{ְqBFZs k9оi-}k;;2F6sn^9>7mu3>lvێoX}&x^n yxvn]pJ' GNFuD+8nn|[mpGY/^k88*tBWOy- ٬$٭gm #?-΃.dYљYF : ײ惯{=\g]vp~w~y:%ϽwkFǽ/j ÿi6/keO\f?ٟk嵭uh?g_`]#}'vc7x (~ɶsUg瀲eo7y4pkI|welyo'fxy_0 mxWnv2ȂF<w)yE džrCK؄NPR8R|Y\Gm@hs~$zA^}ZU\h_Of4|iH&Tgy-w>|8x8tkXe&]gh E]n` u8HVKP8(hR(؉-(QP8tHg{WdYhV]=ȋ6拇(_Fg{p 8XeXR;ocaml-book-1.0/en/html/book-ora060.gif0000644000000000000000000000722607452056112014270 0ustar GIF89aUUUrrr!,H0I8ͻ`(dIhlp,tmߵ:pTxȤl:%ǧt:F2*.n-%<<{"w]kFO _ |ÂbuZΌanޮx% '!i%e8싥O?? m Bta+RRcHdedE9<C(QLXQ =J'#Č,Ƭx3&w蜵%Đ"Y3 %U >գWR+O䑒1@(ЄfZ*Vdܹr׉nt!b{0;v&Ie5Edl eWˍ#6<3ԱeVT Q]uif n\p)J1D׹7.,IWbED\/<|sq~ab-xuLg[bA 3;R5?B# YJdG6$=BL)eX^٥m_&b Upfk&U"Z*Y'wc{اwIhvhIXAģF*I饘fFn2ږiJjAh*Wޚ kv3 ,4+AKt_hqUʑm:GlD3e62nˢI6ަ*Kj)cD,{#x$C9 - ,"[0O.c\-q~|"r'ÊV of|jsGBηQ%,Tm"d%4UzUQ!]4g3Nlr2\@3EZ;A=jp7=wbJFjJK3O'K9 [cQ4dy@ծҎG;ߦ=* Qmۭ-A1dpwmrs$'Nlw%gE.}S<(K̥ԕ* OpK ם?hO 6ZC`n@50o{8 J ,AIQ GhN\(RC~X4ÂK ~ e۟sEDE}5Kb>)jbxH;Z1{X@@*LEc<ӥE8{Kc؊6bt48YàM@D##ɦ5gJP/$} +wN!@q}_`5*o=sX#aGQdkQQ,b2:@ )c Ϙ՞$'7YzGVO; B=[.ghvsp} ge@8p0q 7I5nR&ǥnj;'x45sȡ8y,ʾ54rn TTbK Sc<,3h[(ΘTd6 }pJDz5(&U.4?ʜi:$O%fjSNLE}c!\7T=oTfe5*W֧u_OuԵw-F~ob\WiaۖX&.6QI=&͘b @hW*dh+JZ 瑃N>D¶JTCtPk"yehB`ABmMAXr0(Qyү]a8,K׉'v.j\r\%4z%pb1:CN{f:LsդKz`gh;tkC Z`|or0܊N'S=f8I"HaFk9Soؑg`s%v68dV\t@=c)izKFI q!/e2L(gޥ_*bЛ9iȡf"mZC!GT9Vߒ\UF}vU.K(Q*lŕYڞ-\IuUѻ~#Z Gw*dG6Hl(Tz ]H`l[6l:TZmbrJ5F4yHROj.Uu&N6L=^q-xU^pg8' NwwykѦ~g½ ft2ż ^ in/J=k68n:c/Qx紜{ wXйaCnƾf~[=$䁦B79E^ûc :#tnn:3>2 y[|Ho/gg~ѿU;eQ}:c61)Pl{|:ux2}z:?18OY+F%]lsIԋA]tPe٦e˙矕h%gR;35T:HVw]i@ SnS` M Kz i0likdm^ `SEHXѦXW=X?X/CXAXEGXIXlKYM*Eu6STm<y3Hjqo1ϲ}gw3YHs HH'NVH3].S]QN\W4"]gkS^er]UJi0P, JGdG'.t()fUfzӇ*}<Z({\m^j68X7=G;824-4;͈xƸ| cGɷ Xdd48;_bexK|׍&dcxQW~9h?p*cv~<OSl~g^hv!YnpOV*,c2In4 G3y-#\G";/8AyGDD7S0YC>!G3n:OM?5qBUR8׈<&rH4[|r1%a_VX{'`u Ib䀴Y20}IHVWHzʨ uٔ׎;8|/ƍcvT;P(؁}h a0/Uy]!bƚ05sٛIwIh܉: ~U;v\[ÑY#9mHKF9oO9m JVڠ :@zjcxw3793FW(z$Vinzw3]!TCIHI E!G':1IjՀY,ڢыp`Ms4KcJT~tjO: Y7c`Iݔcg~WAQh50}x3"GGeHHWvkycV$Y禩ZiaQQYZ fX{Hv5r#0g˕r)_㐴*uZ媚ZGz Һ xتBպ܊' ;ocaml-book-1.0/en/html/book-ora175.html0000644000000000000000000003240207453055400014470 0ustar Concurrent Processes Previous Contents Next

Concurrent Processes

With an application composed of many concurrent processes, we lose the convenience offered by the determinism of sequential programs. For processes sharing the same zone of memory, the result of the following program cannot be deduced from reading it.

main program
let x = ref 1;;
process P process Q
x := !x + 1;; x := !x * 2;;
At the end of the execution of P and Q, the reference x can point to 2, 3 or 4, depending on the order of execution of each process.

This indeterminism applies also to terminations. When the memory state depends on the execution of each parallel process, an application can fail to terminate on a particular execution, and terminate on another. To provide some control over the execution, the processes must be synchronized.

For processes using distinct memory areas, but communicating between each other, their interaction depends on the type of communication. We introduce for the following example two communication primitives: send which sends a value, showing the destination, and receive which receives a value from a process. Let P and Q be two communicating processes:
process P process Q
let x = ref 1;; let y = ref 1;;
send(Q,!x); y := !y + 3;
x := !x * 2; y := !y + receive(P);
send(Q,!x); send(P,!y);
x := !x + receive(Q); y := !y + receive(P);
In the case of a transient communication, process Q can miss the messages of P. We fall back into the non-determinism of the preceding model.

For an asynchronous communication, the medium of the communication channel stores the different values that have been transmitted. Only reception is blocking. Process P can be waiting for Q, even if the latter has not yet read the two messages from P. However, this does not prevent it from transmitting.

We can classify concurrent applications into five categories according to the program units that compose them:
  1. unrelated;
  2. related, but without synchronization;
  3. related, with mutual exclusion;
  4. related, with mutual exclusion and communication;
  5. related, without mutual exclusion, and with synchronous communication.
The difficulty of implementation comes principally from these last categories. Now we will see how to resolve these difficulties by using the Objective CAML libraries.

Compilation with Threads

The Objective CAML thread library is divided into five modules, of which the first four each define an abstract type:
  • module Thread: creation and execution of threads. (type Thread.t);
  • module Mutex: creation, locking and release of mutexes. (type Mutex.t);
  • module Condition: creation of conditions (signals), waiting and waking up on a condition (type Condition.t);
  • module Event: creation of communication channels (type 'a Event.channel), the values which they carry (type 'a Event.event), and communication functions.
  • module ThreadUnix: redefinitions of I/O functions of module Unix so that they are not blocking.
This library is not part of the execution library of Objective CAML. Its use requires the option -custom both for compiling programs and for constructing a new toplevel by using the commands:
$ ocamlc -thread -custom threads.cma  files.ml -cclib -lthreads
$ ocamlmktop -tread -custom -o threadtop thread.cma -cclib -lthreads
The Threads library is not usable with the native compiler unless the platform implements threads conforming to the POSIX 10031. Thus we compile executables by adding the libraries unix.a and pthread.a:

$ ocamlc -thread -custom threads.cma files.ml -cclib -lthreads \
  -cclib -lunix -cclib -lpthread
$ ocamltop -thread -custom threads.cma files.ml -cclib -lthreads \
  -cclib -lunix -cclib -lpthread
$ ocamlcopt -thread threads.cmxa files.ml -cclib -lthreads \
  -cclib -lunix -cclib -lpthread

Module Thread

The Objective CAML Thread module contains the primitives for creation and management of threads. We will not make an exhaustive presentation, for instance the operations of file I/O have been described in the preceding chapter.

A thread is created through a call to:

# Thread.create ;;
- : ('a -> 'b) -> 'a -> Thread.t = <fun>
The first argument, of type 'a -> 'b, corresponds to the function executed by the created process; the second argument, of type 'a, is the argument required by the executed function; the result of the call is the descriptor associated with the process. The process thus created is automatically destroyed when the associated function terminates.

Knowing its descriptor, we can ask for the execution of a process and wait for it to finish by using the function join. Here is a usage example:

# let f_proc1 () = for i=0 to 10 do Printf.printf "(%d)" i; flush stdout done;
print_newline() ;;
val f_proc1 : unit -> unit = <fun>
# let t1 = Thread.create f_proc1 () ;;
val t1 : Thread.t = <abstr>
# Thread.join t1 ;;
(0)(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)
- : unit = <unknown constructor>


Warning


The result of the execution of a process is not recovered by the parent process, but lost when the child process terminates.


We can also brutally interrupt the execution of a process of which we know the descriptor with the function kill. For instance, we create a process which is immediately interrupted:

# let n = ref 0 ;;
val n : int ref = {contents=0}
# let f_proc1 () = while true do incr n done ;;
val f_proc1 : unit -> unit = <fun>
# let go () = n := 0 ;
let t1 = Thread.create f_proc1 ()
in Thread.kill t1 ;
Printf.printf "n = %d\n" !n ;;
val go : unit -> unit = <fun>
# go () ;;
n = 0
- : unit = ()


A process can put an end to its own activity by the function:

# Thread.exit ;;
- : unit -> unit = <fun>


It can suspend its activity for a given time by a call to:

# Thread.delay ;;
- : float -> unit = <fun>


The argument stands for the number of seconds to wait.

Let us consider the previous example, and add timing. We create a first process t1 of which the associated function f_proc2 creates in its turn a process t2 which executes f_proc1, then f_proc2 delays for d seconds, and then terminates t2. On termination of t1, we print the contents of n.

# let f_proc2 d =
n := 0 ;
let t2 = Thread.create f_proc1 ()
in Thread.delay d ;
Thread.kill t2 ;;
val f_proc2 : float -> unit = <fun>
# let t1 = Thread.create f_proc2 0.25
in Thread.join t1 ; Printf.printf "n = %d\n" !n ;;
n = 128827
- : unit = ()



Previous Contents Next ocaml-book-1.0/en/html/book-ora132.html0000644000000000000000000005103407453055400014463 0ustar Parameterized Modules Previous Contents Next

Parameterized Modules

Parameterized modules are to modules what functions are to base values. Just like a function returns a new value from the values of its parameters, a parameterized module builds a new module from the modules given as parameters. Parameterized modules are also called functors.

The addition of functors to the module language increases the opportunities for code reuse in structures.

Functors are defined using a function-like syntax:

Syntax


functor ( Name : signature ) -> structure



# module Couple = functor ( Q : sig type t end ) ->
struct type couple = Q.t * Q.t end ;;
module Couple :
functor(Q : sig type t end) -> sig type couple = Q.t * Q.t end


As for functions, syntactic sugar is provided for defining and naming a functor:

Syntax


module Name1 ( Name2 : signature ) = structure

# module Couple ( Q : sig type t end ) = struct type couple = Q.t * Q.t end ;;
module Couple :
functor(Q : sig type t end) -> sig type couple = Q.t * Q.t end


A functor can take several parameters:

Syntax


functor ( Name1 : signature1 ) ->   :
functor ( Namen : signaturen ) ->   structure

The syntactic sugar for defining and naming a functor extends to multiple-argument functors:

Syntax


module Name (Name1 : signature1 ) ...( Namen : signaturen ) =
  structure



The application of a functor to its arguments is written thus:

Syntax


module Name = functor ( structure1 ) ...( structuren )
Note that each parameter is written between parentheses. The result of the application can be either a simple module or a partially applied functor, depending on the number of parameters of the functor.

Warning


There is no equivalent to functors at the level of signature: it is not possible to build a signature by application of a ``functorial signature'' to other signatures.


A closed functor is a functor that does not reference any module except its parameters. Such a closed functor makes its communications with other modules entirely explicit. This provides maximal reusability, since the modules it references are determined at application time only. There is a strong parallel between a closed function (without free variables) and a closed functor.

Functors and Code Reuse

The Objective CAML standard library provides three modules defining functors. Two of them take as argument a module implementing a totally ordered data type, that is, a module with the following signature:

# module type OrderedType =
sig
type t
val compare: t -> t -> int
end ;;
module type OrderedType = sig type t val compare : t -> t -> int end


Function compare takes two arguments of type t and returns a negative integer if the first is less than the second, zero if both are equal, and a positive integer if the first is greater than the second. Here is an example of totally ordered type: pairs of integers equipped with lexicographic ordering.


# module OrderedIntPair =
struct
type t = int * int
let compare (x1,x2) (y1,y2) =
if x1 < y1 then -1
else if x1 > y1 then 1
else if x2 < y2 then -1
else if x2 > y2 then 1
else 0
end ;;
module OrderedIntPair :
sig type t = int * int val compare : 'a * 'b -> 'a * 'b -> int end


The functor Make from module Map returns a module that implements association tables whose keys are values of the ordered type passed as argument. This module provides operations similar to the operations on association lists from module List, but using a more efficient and more complex data structure (balanced binary trees).


# module AssocIntPair = Map.Make (OrderedIntPair) ;;
module AssocIntPair :
sig
type key = OrderedIntPair.t
and 'a t = 'a Map.Make(OrderedIntPair).t
val empty : 'a t
val add : key -> 'a -> 'a t -> 'a t
val find : key -> 'a t -> 'a
val remove : key -> 'a t -> 'a t
val mem : key -> 'a t -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val map : ('a -> 'b) -> 'a t -> 'b t
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
end


The Make functor allows to construct association tables over any key type for which we can write a compare function.

The standard library module Set also provides a functor named Make taking an ordered type as argument and returning a module implementing sets of sets of values of this type.

# module SetIntPair = Set.Make (OrderedIntPair) ;;
module SetIntPair :
sig
type elt = OrderedIntPair.t
and t = Set.Make(OrderedIntPair).t
val empty : t
val is_empty : t -> bool
val mem : elt -> t -> bool
val add : elt -> t -> t
val singleton : elt -> t
val remove : elt -> t -> t
val union : t -> t -> t
val inter : t -> t -> t
val diff : t -> t -> t
val compare : t -> t -> int
val equal : t -> t -> bool
val subset : t -> t -> bool
val iter : (elt -> unit) -> t -> unit
val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a
val cardinal : t -> int
val elements : t -> elt list
val min_elt : t -> elt
val max_elt : t -> elt
val choose : t -> elt
end


The type SetIntPair.t is the type of sets of integer pairs, with all the usual set operations provided in SetIntPair, including a set comparison function SetIntPair.compare. To illustrate the code reuse made possible by functors, we now build sets of sets of integer pairs.

# module SetofSet = Set.Make (SetIntPair) ;;

# let x = SetIntPair.singleton (1,2) ;; (* x = { (1,2) } *)
val x : SetIntPair.t = <abstr>
# let y = SetofSet.singleton SetIntPair.empty ;; (* y = { {} } *)
val y : SetofSet.t = <abstr>
# let z = SetofSet.add x y ;; (* z = { {(1,2)} ; {} } *)
val z : SetofSet.t = <abstr>


The Make functor from module Hashtbl is similar to that from the Map module, but implements (imperative) hash tables instead of (purely functional) balanced trees. The argument to Hashtbl.Make is slightly different: in addition to the type of the keys for the hash table, it must provide an equality function testing the equality of two keys (instead of a full-fledged comparison function), plus a hash function, that is, a function associating integers to keys.


# module type HashedType =
sig
type t
val equal: t -> t -> bool
val hash: t -> int
end ;;
module type HashedType =
sig type t val equal : t -> t -> bool val hash : t -> int end
# module IntMod13 =
struct
type t = int
let equal = (=)
let hash x = x mod 13
end ;;
module IntMod13 :
sig type t = int val equal : 'a -> 'a -> bool val hash : int -> int end
# module TblInt = Hashtbl.Make (IntMod13) ;;
module TblInt :
sig
type key = IntMod13.t
and 'a t = 'a Hashtbl.Make(IntMod13).t
val create : int -> 'a t
val clear : 'a t -> unit
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_all : 'a t -> key -> 'a list
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
end


Local Module Definitions

The Objective CAML core language allows a module to be defined locally to an expression.

Syntax


let module Name = structure
  in expr



For instance, we can use the Set module locally to write a sort function over integer lists, by inserting each list element into a set and finally converting the set to the sorted list of its elements.

# let sort l =
let module M =
struct
type t = int
let compare x y =
if x < y then -1 else if x > y then 1 else 0
end
in
let module MSet = Set.Make(M)
in MSet.elements (List.fold_right MSet.add l MSet.empty) ;;
val sort : int list -> int list = <fun>

# sort [ 5 ; 3 ; 8 ; 7 ; 2 ; 6 ; 1 ; 4 ] ;;
- : int list = [1; 2; 3; 4; 5; 6; 7; 8]


Objective CAML does not allow a value to escape a let module expression if the type of the value is not known outside the scope of the expression.

# let test =
let module Foo =
struct
type t
let id x = (x:t)
end
in Foo.id ;;
Characters 15-101:
This `let module' expression has type Foo.t -> Foo.t
In this type, the locally bound module name Foo escapes its scope



Previous Contents Next ocaml-book-1.0/en/html/book-ora112.html0000644000000000000000000001020607453055400014455 0ustar Introduction Previous Contents Next

Introduction

Developing programs in a given language very often requires one to integrate libraries written in other languages. The two main reasons for this are:
  • to use libraries that cannot be written in the language, thus extending its functionality;
  • to use high-performance libraries already implemented in another language.
A program then becomes an assembly of software components written in various languages, where each component has been written in the language most appropriate for the part of the problem it addresses. Those software components interoperate by exchanging values and requesting computations.

The Objective CAML language offers such a mechanism for interoperability with the C language. This mechanism allows Objective CAML code to call C functions with Caml-provided arguments, and to get back the result of the computation in Objective CAML. The converse is also possible: a C program can trigger an Objective CAML computation, then work on its result.

The choice of C as interoperability language is justified by the following reasons:
  • it is a standardized language (ISO C);
  • C is a popular implementation language for operating systems (Unix, Windows, MacOS, etc.);
  • a great many libraries are written in C;
  • most programming languages offer a C interface, thus it is possible to interface Objective CAML with these languages by going through C.
The C language can therefore be viewed as the esperanto of programming languages.

Cooperation between C and Objective CAML raises a number of difficulties that we review below.
  • Machine representation of data
    For instance, values of base types (int, char, float) have different machine representations in the two languages. This requires conversion between the representations, in both directions. The same holds for data structures such as records, sum types1, or arrays.

  • The Objective CAML garbage collector
    Standard C does not provide garbage collection. (However, garbage collectors are easily written in C.) Moreover, calling a C function from Objective CAML must not modify the memory in ways incompatible with the Objective CAML GC.
  • Aborted computations
    Standard C does not support exceptions, and provides different mechanisms for aborting computations. This complicates Objective CAML's exception handling.
  • Sharing common resources
    For instance, files and other input-output devices are shared between Objective CAML and C, but each language maintains its own input-output buffers. This may violate the proper sequencing of input-output operations in mixed programs.
Programs written in Objective CAML benefit from the safety of static typing and automatic memory management. This safety must not be compromised by improper use of C libraries and interfacing with other languages through C. The programmer must therefore adhere to rather strict rules to ensure that both languages coexist peacefully.


Previous Contents Next ocaml-book-1.0/en/html/book-ora016.gif0000644000000000000000000007524607452056110014276 0ustar GIF89anDvU?Ʋ}3.(y[JD.&Q* ƙوŵ& 2"]H[au{,*$Y.,FJDlhwlS5]ɢfºN]pyyWDDY=P}JUlf(W9JN=D}ƴʈ LR{3.H9 9S7UH1_YHpa1HvaN &ƢѶ}ft_ŽÎlP= 03*99JfNLDJR@wR[ !Y꽛p}fwyl䮝y{yl[uhWFYjl=(Ct_AfH&NUWѮ{auQbS}["JD5NsС.PԻ&3CdRphP9B;dѝֻ`yNjqw;;fU(@U5A[ @__w5wAcݬθ]=䰌wLe{!S]jcd5ʢlyLʽdWۙÎ_vd{]Q{"3b9J3fN;qjtbN779fYT.yur¥hfW]Yj֧֟*CpLa®!5}pP1F* !;(`wtf""Յyjylu[j$˝yj9Ljy"(9̻Ȇy$ƬF;.]jN9"lݥbt=.lP(?9*!, H*\ȰÇ#J\ȥ"%}BkIb D DQ1/ );*0 ijMmmIR$RH&eF.L5J]͎&RW"5jHcGbE[6Vu"ZQjS ŶE)SI lȓLX$VǐY(H!1, 9aRl5tnEmN5M RѠ =TlR5>6ܕr#G.+Iۓ}UQPĊQdYM%76M8)_"ݖ#JU\rc HTbULluS:1@Rbt>hJEfeUfѢO^>XREVr~'!Tbum&نqQ{"&-PaYHi=&yT9mU]ZցPx0KԄ&-g46ag v6Zݥ f}4YfVTPJYX X=YN= FҗU2GI'XK)Jp8'g1IcY'RuIZFar[j#[Q()}5:ma8[ n8ˊ+Y 5Rvfa6TkO6OuŽD[-QdUQ7:G5R[)(Po՞d& ng̸ yS@k]Qe'ܘ1*2l+ed{vg<&4 H9 `mRK iSYFn6Y-j<`FbsG^'!UmJJ~hEnUmh1<[4J1׸hv0|S1^d7sQ%I!K߿̬/[kȂFZp-+oaP זIIg~}gwnk0ns :ԏQB`i)$R+6D Dx  m. m `CbՆ\13g%}'48~B'-Hypxiv9 5:]\*0c0"䱁K CV»og+|i,!4YV`*-5҈(čEPaq,DAn,[^ G$ fhN>aԵ"vl\Qc,Mh c,JfͰ2gH|F#IFLi0P+~D.%C gm"Oy26,4z,LCL$7"1 },^~GcQeo!Qĵ=Hl=j ˨U! o,0odF(HGťŗѢь1)uܓNw“Ad1>O\f2)'(#'Cjҏ4v,oBW$ uO R8$7ŊŬտ7EE-6ʞ,'r1O.5L]uTЄq*`Eu|Q<6DA[a"KVi+ZDː E$˶ #9ʴbPL[z /Bڕ.WhM) H$3Ñj2-1ȨEpIMkЦI!ᭅ ?BNw[JB19.w%X$YÂz/ %) uQ. 3vե{CZ i |@B  BZnqɲRq DfRzGJ")#"Q1If5ϺVˠ귌%Y`Bj5B'=R{XJOzCRPs(BK~zL pr]D)(-` 2V$4EH  ~q$0v x"KR"3woKF 5W": ) Bf`&s]C5i}MLg2 hA q < _:f%2SɖZљ%u3[ s9d-rX]Ae*3M*p\IAd@+؅ qH hcUA&m'wIe}l5w's&t1U{.KC;h[bp ;ݦ `|ЃDBA&ĆseQ!b6&5!ҐȼD J .K"ɨZEdOɏdד8!\@^$y\V4vSI3g ffCDl{7\덗뷳db_X={kIgq 0[a xKb3i2DzK&yp%n7l0zd[FF ;:_Ă3>|_p  5  5D1d~6WulArEA U!DS=EbDuHkXsiCohg"!_$3@|p ) lI RHAr#TDu[CNBny"#0ӅVYZ].Od2bjˡ{`w p )pT@ @p`=c0>+b~o"dVoUP=DC1BM4zD6ӣ(O'%]&RhLdwuJ8J`G =+  0p>l%qn,R"YV9m%EaD5G$H-bA4U]&s5V2CPwb T@?'i 8@w -Qf0E,Hy0E5vci4!a0GL2hI6sRdQ%7b { G  -p =P= xfU~Ϙ IzwK+˳{unSZA2K"Jv DE"%O؎- 3wq0cX+ DppP  ·RpfH/Ypsfb-9?!_Q*ĕ$"[AH8y Jkdm#zWtE@x= l  É@@50Pi{3݁+6"d2705Tc$wc}xօ*YaqqSq_l P@J H=i{(^@ЃgPvGZw']8&fBrw/JeBs6&ulkS9lf(P߰ x:x@ 5 { - P(aOQ#QF'_t #q\4W[gzp"})gb`>D>{ D'JPO@xߡyՈ8"gʅv:7C\d?!w*sX]2:X$$3J,6(& =` 0 @퐥W=5O`zspq_e%6UwF"q862CK#[mgYТCTG߀@b@=  x`98Y 3pL;f?7R5[O6Ưd n]s{ =3=fOAJ_F" `N@(x$ y @y͖չ*e@1Cn9d/b7J#w,yq KKS^JL_b&@T˅t˳WdIp ep ҰlPT@p  ! P=zWnE^[hA3Q5;TeJA9 #T*Ņ,B0@@^^xǨ*sl@xp 5yp;`Ǖa/w[ h4wuiJp%@4$Ut+i\^P8pp @0=!Dp>pf{ n+5`0춼p )Vžwjym3ma"Ww>$20[޷a)D"ɦHpx x >POl= 09< ʔy@IWyOg/H@/xib"XalġJz4؛j=GE3$#B/rI ^  HИW(ҰPPx=0 ?GgĮe2]B^牴;'-g99M[E,X2l 3>w`+UEҵT롦m^F0 E @t g-hќt{M. 3Op acqT~%-^AO Qɣ7nV(*xVW!4"/;w @=`@^i= jk ୅-p@8  j-` dbE8ϛV~DuS`W lQ$!)-bYVjmYijz7m K01@ 0>0 :<ݻ0= epOZ z4 l@E) . +no VF vOL{I*7ޙM Ǝ*&JpW0P ߐ퀔~-YjGlGąl>8P$pP9X| np*4SjS"pu9V$0 F"P͂7]ańQ7@(P QP S4t @( 0i唬 el.ξ  5p ^ @T ;pY27ݳsL0eb豌H.X"Ǝ5u]hyP ] W  ZЍ`nkΖ0ܺpު p p^ՙ~ x`:kU1h:D[3M b"Mo掗᱂A; $@p'`P P @Φvl-qaf>GA堩 j |2F&DĜ4iR yYH&%k1ӦMmM|d† "^"FR&,% -d({L*SlJ_KsaEJQlʒ"qƍ̅'}pMdky1D ^ ½=Г;'(#蚴 ;څH%| SڲY.ɡ:w٦IWi,iԨTG:T6WN5L/_GD~yhì%jWT.& Y!z`H 0̇.8@|(Mॎ z"%b9j{ɥ 1$z n谢5j؀** In+$Z%i8ÖoSj8%TVY%8yA11,vv0vA"q 8Y0 )kzE"f;\bɶ=ls51H"II'})Hj*,JQ,z0hGznVPfn Vaq X q@/|.ȀP0gY!i yV'xV HzX8mq>%'Z $nU۰nj\aVbd5YIŊ2;\ vQxY4R&o$@fq"!zmsR0"q}XIK-.;IL%x‡2%NeaٸkIX1n)$-@p8+*ֈ'U26],bs|8*hf bŇFU#V!H(%KjA1Y@2*ƱžP!|n8 6L\\rlc;]YHq6xc|9( Ɣ^3ǔ$2~G;4qH(TADQxAЄka3nLi"  Lz1-Кav0 L !aש^wbⰱx$1U𦓜'6A$$I)rM^ @  zp2( uPڡ uP o.K$!/*|#Y'f#]kڰ7I(E1MT#bN1ATBʑ`~I)NGUHviHYMTc)F0)H>Z3T"AX+zC_"''X l@ D1 @M+7C\|Bi6Qf?dȉ'}@V)@#SU[(Cݷ0h=cr DcoUQ!r y#qXBtQ%:@$aHp@xXv=D@ M^ )Ϲ!Eq%Qp2>h $&>TIp"+[+ b;ba[,@?0FBюs t4 ;"α 6poC iullK1<}¿h`<H` A}+2Qa\h,Z5ՎJ\6b2kP+FI R3`S!؀v!z"$  \U`E6 !D*>tS 9 309w%1xSE3V[ 8s3{`k2Ɋ!G#"cis\a(s"\X66 hXVD2|(Aha )~L@'pP kHsPy ikв8w@!2cxyx<2iJ?7pcC(RJ.LjR+J+ &%c;pHF* Hh=X ՠ𸃐[hxђ#p(L:Xk qk/!tɉ\(R,?iGy9#'ʚh Ri4b16St#b8v ;M i9,&chh?H8 AάHp xA+]`ʬʘ;6cRH-z ceU!+rx1mU%7H͕c40]R7B(HH%H%BLj:Xt0]8Hah ёCZ&HɅj^ I 6Z` z,| )8`'In8uPV -- 5.%P4XTX6 SZ|`p0]lMk 20}`6مr@*'8tKc H؄ x, q8p~{ȽȬb=pl *;@+?cʇ3 b*ʶ+JpFЄv  Q`0A0L뼅FpŌ=H8xRݙZ :7]K&dU,!9†(C߬J+f9 }5%2J *8u6]HdP!8@~(t$}\p b2u] ʄJ}LBx6P"hZ6W^`Sa Ne ~7f s؁x l=r:<`[u} TV; Lh!Hޅ>x(@((j%@Pw S-+Z¶jB6 h#ee0E8 ѢVV*hF8'XEIJhٷ͈H$#k(eGܝÅ,̍]7(vw8: zCXBX9XxɘgbZU--4- lb60cpp# (`(*) t tZ2 Xq*f 0GX kصk\@B$gplwP@YP5вggUvM-SL.A%ʰ8`6"98('pbfh`\ |>RP iIvHL!`Bop `Ҕkp1Q) *pXf'OFY~BCM tPypTՀ=8Ѹ,+Xp,l](B]8?(x8l_\ A<h(QU?0O$p~>^e$#R e9gHaXSpckXؚHLpX8KpcO28>Y$s* w2@AǪ̈́lH+I@9O޳[n%Ma_M؆m +( L, ] h/kHhF8+Ixx,yb38@ [uh>bMYҟz?/祃.M|0HCn/hFMv|EX[ =yohU eJ kXǢn Ec|Ӥ_1`&W!TFMzX nH3to5rK$kH ^Юq. :rߓ O"0@ Q Jl1 *x $DwH@\HGkHG4jkm6aCYRgͲanm[Dm㲑KZj%Ťoڴs2aSH;P R$؁®E߁5\qmd2^AOГ'T٣ Q:e6Iv%8*l"kD] ".-#\0j\mz;vl`n۲cW{p^RFpNK<@+8-pN N#\i ԰H}EHAn8-1o8=G=SC/&!$pA0 c'uVyb~gx\wExW~|#9 b0$2L>"&JqD t 5c>@ ] 8S7 ‘TzA&֮=rXš f`f7|@%;hB.0ts?p 0$8 /@D\áTQĴJ0G4*$__@IG񐷁@2]М$PC%~KG4{I!yL f;=' VMٖY߯h' 8XA2.x7sDp /T1YH&d*o tJm=`%PՑ|ḭrb"{ /;POT=[KZ Z+KO؃4Q#&DЋH=h-`M4SȄ.p8\5L a)495ix!У|.XI RT≺ \'4bzYrр„֩;+xv P P (`n؃0΁fx&yiGB8Np *V #`/*PCDG؞"TbLs#4KV"18) @5RS`!h9梛-kY&=,J -ױ Q|^wLf0ESwxºc ( :c",8B$?i% *Ld)~ Ԡb=fDRM k\7-ASl؃%a' Gh LtB$ >|`S3E$&Jw|F-Pa) dg$¬CYc+1P!VD/@P"`DI>`1TM,6̀XrcE x(1z̠ =8BQ. BG"2JPE !bp-p.@t$WR\u1s;ur2 P$f!e| ԶX&mEձ@B9QvBr@d/d ^0 kX>xDP0 T GpF^>"!e@52]JR \ c9OY3Y hsc\I4H t` FZ *(lAc<@]/pG R +@"_Fp׸"k(F;@GbJJ$DK!9|6D#;D?1pg3I(y,@A 2Єzs-*@T* W08 E %K9 S,p-=ds ]*8\/ [Ԡfl /2Rmna,3_`8-̀4608 .!"R7Q#1zNt@ t浡yOsʉ[wР,v̼Dc(#PWYissr1:p.qτm W^X 98@P(I9 \-%çF8ç I \qQ(9,1H>Fw|(4D 4GX@, 7uS_.{| 9;  X,C<0l (CQ(76%|6d*ā 8Ѐ>5V#4eUCHH S$šqP>ë XL;0Ibу0\N0U*r=N }XY 2TQ$<ΉD@<2А*!C<4lDrD/iTLr >9 49zR⛵|ta$Z>(B7TC|7XH#B$l@@=|&JR\40(A8\ѝŔ(</\R`tG8w78G]JRXK@DS!d^B&d˨&ǛbGޥ^dX[<01 S$Ѓă-T=A&i=($,Az~+ ;,Bd0I< if)!FFFrF$Pt@LFv4I8iH>*-J\X P.̽TN%آH2 y (@v9D"p8-*$'A==|A sԛAt9@||T0=ƫq9lo4D47R1P!gR(m 11B*hC2$,,D"ԃAdP`/ qD$i2E7$@U@)͏K3F,ŁPCAZn6qNGNу) B B,CP#>t+\l%@lB!&G;1ED+I]nd@i9Kw$/ )^NoÂ\C,DSaBlSVw[$qβse 55'$Pc?EX~4/tJ0@H i[mt\M  kCH9ܯ6”5` o7܀ͶTWDǙ)X= 0BD P # l 4DB "kd2Y6a;6+_9f;H6B9-t?/İnid&+nZb6 ~dZI "9c9Hz΁xFঙ2g# a`y" Q 61'qi{iL|0D隳XyLA@ &0:[Xh@!p#/z B[n1D sBǥ$zW48.P#͢"H`6 lQ8=R' !xq ^]\(58.A؋rE^``SE.EC'hzЃKA_Q!FG$Ģk8( V " VFr,`c0 \Q+K,PrR.Ƶ@1A > QE L BZQ@zf| .p D$F$2q1=k.P$ \ ~!%*qE$:dz\$# X+8 Nx#pe00G]\KYӖGkT6h+A) ӈ!ʨ93$>|/7$` ?i z@-/xTcH豕ҒhDMm$A8O *TM측–NByE@tr -$C]Xaf\ "W` " b0.q v8F $ (4#1ધsp+h. Hf=]$6A2]@0ŋK^< O[W!$E+$pr`ˀ0θ2x(ǁa 7U8f;\v8B5{C^DM@ohtmrT$zTBk`6G+Zv = >vS|„jO¥P|=2j^ኦq5d5 B?d &ʠ`@ X' %$Ç1" A5 ah#s#JL  9#A1E5V8kKfw9ؑ. haO+#WU Nwa'24Y@|f i|#4(*2t!E zي >8>'ԿiD j7L5P: xS-/e $ 2`ЪF;م4L1'D)2zh@Nj9e$j=R4ʂʄ0h "@C0;PHI V!P0P$E8 4%6t#>Z?FSTR]$c%›wQo~3>V H d c .| (0c¸Qv2.K B=]aq 5N0Z,ZP *Fd0|xn-AصTx$b.va/# 09zN>H|#CˆaݶY!'Xir5\Z}Lnj)ʋ#.ш$`Vȃ+BV|l>؀Ȏs# "XDc#A,j ,>POb*vHL&$8惞DBa>%xœAZ$B> jj,N.k$X`H@ pA pa!,F vxSC| GpI#n.:8'#m ^ G"2\ &-c 8,j.  2 !D   t J|^1 `aA`8! T 6qܠ&Bf'&2GBAخOʠ B:.ةcI% RRZpq6,`-ZN`,@11xz@<@\!n`XX `@van8hjE1F$dǨM'ʀb6!Bj xbL,quƅB w$ִRgk<*`״L!zDd!܁a eRK(v74:E6qh`%bZ$*/`w > h=$@.j0jGkA Ɓ hz@5Hڡ8!jx`,u2 $$nhnL*3f䦺-ASjߚ<k3!`w&] \*)Q$P`f aj$-@! A Ha$xJn`¬#@`:&'@DFLQLƪD%  va4db V*es 8)Zq/K1`! "!V-i@ `6΁'u~p4i4L!)p#4e#(S%<e@ & AC(`{`66@́*1B67xpOG@D\@\@aL2z0 u@ pa FU$"J |2m$l(`=S,(<У^` .@Ρ!j$2 f/ 0irsg+]h>X  j؉"$h8!  ܡHB~z@zAqefW$`c8(Vd5`8 Wv  Gʔ`aTTB %cvr7f1Ws7Nuz` :A-yt"\| (oc!R ġ x!H.Fz7 e%\NA`(n3f&#G$aea=}#$S2gV+X0jM p@ AP@@ jhd >F@a*&p$U" sL!EB,@2f$,`= x,AQs2s).Nbxa6VA تaa \ a=6Xxav䄜+ROZ@6a}GHBĂ2Aa*7z$4VUĤz&rwNwb rcAA`$!#a 6 A hTNI^Ip&qJt${*avV" -B 6Z%f`>ec+$4W36d7}qzh A\pLa t>a5,b}GC)B&mp*QieT84 aG9&l9$ GfNc c_'NLC҄/d^qV4 6c.* `A\@\j£ SQTŁqA)bmlB@ $aWE"b=Sgn;va? !E>2j``aj .ASȚ$a@"}0z<a YVa6*Jo$f*Uz2Ȟp%"g|x$*L h`3?hT`$n=hM%~eA+nA!i@jA !v nicD): 9Q$x&)h {2(PL%K)9DpAp DGvc!}Y%-pCqh `>,A l"a2|  `a"UXZܡGZcLʨ'78ʌWv'u,!zg=a \& >x@xf3Khҵ@`@`j! !asd;Q M,V'r~VGoV/܃)=tځ"ԅ и #:|Dv 5Dd1bxdӱ>|$Fo2iH̃bœ=LZo7%*k\ nѼyT#҂k @vMBTླྀbUwȠzą1=K9LJ)ASbcʶG]Юѣw-Ү IMI/PzTm;`@$G H$IXtB=vfŷoݡ $*CVYJHBEB&RUV9gW} kQ+ 5f{!җ([ 5$_ @=@aDQ=M+@s>_А |ѰD MԐj8Fґ )HE4Q~T = 5H0x2 (D3C}9 ‹1O/$ Hp8D G|PI=Eq 7DK]c.5I+&wM&(!dуhgIBc3PlJ=cbZGHlq /%1H FB /.eEhٝ vP״cLM]J*ZGeT8n@C\r2u.&.q 8'A,t8= $A9@*AE!1G2(qu8ݨ4L # >8a qH4C"'sVhyI rDd 8W\iP5\ g_E`e56eG|4{q;Q6X EA "1ذC Vhq]c q]#es50 @70)BKUJ R {  TD W8$a  A&=b/M`? 8[O1pC%0حCiE0B<: dFP>:!oY>2!GS!%\FH@#5P=!Z"x@ʣa@&$ TCE16688Ȝu>\}rD`** T D.@*(.9 12a!p cX@|` |"0-` p!t#> HԺFkqH Qa4Vq )HRpC1@,> ϔfp*r A 6vb},DڸKf  kB l`N_ E+p"A|lDD  L?kG'AjѢ]pt/FjQ Lb1`D.u @X($h =़ ՀJ ٠д% =+еcADhND|Ur%@A#Ѓr> :@ 5;]@@7эb)m9Lavn\Wb0,eG7D=&ʃ[3~;I`5(þ CY7Hrd4U!UF50 !V<h0 SA:P#zP *"ҌNnq@n* Kj1@Gu+*SVl^  @Gv;kUT<s:Œ$tF\,# ɨ(xf;\װXV98<H@<9c~2 d\ WBRqZL+=詄XH TAxYSm`ƐGH[AY"C<nm5`hQw XЎ@8>sJvi#ʠݎ/,EG]3:,!Ap 1,ȚVT, EX;%\@"~\@q2V`~YAPd`j&@_Dlh8"fSL :ߘpA|HCH)bH!@h28 o@k D `@ ;+B5HS~ j _fHhg63/ >]E5r0S h>AaGh 0HP8h@P 30@R6e=@qs `v.u P5Sox 4q@i YYQ0 p.Oz*pmsAl@$|p&cA=)s 0 h (C-&2\v8 KbʀŰ0 5`=R r y1[3sqz Jpu Ơu#35x!^Djd5WQ Q87Rb uMC'U~/ 6MHNwNL \r@j? $5D` ` O @7s Α_@@RbPFF>]( #@ < ذZ*@lG: ѣ%S Q/ %0` guux 0/q`Qq$@9;@G{` @D q PIop `J00PuT0B> A!thlA^t Ȗ1Bt ߠ7m O @ 3 @/PfMA(E &p O#4D4 9wNVB`AeG`G3t?L lT8 +>H3u PA \T 64 guga4}R)RS,s PO0MؑbU \t9L 632$pt`@f% @cE)e :R B7|Q'# 5 i̠p$:Q7 ȐY/ /'p'!DU6@ .hx2)y \9G P =P t  z  ? !? 8 `u0B)W"55u8 'CB5moe>Nt V Q0 01Н wM "]‰UU p@b?:Y4`  ?  sXe= J"j;`T x"z 5 <_ ܰ pO'u6a.<\J@@O*Po/l 9sJN)@$!+̈ p 4Bk^3a@P P*0+0 ހ5kk0|p`pl p |2O`   7@Ny * JB5@4P6[ e8w JW aഀHlQI5 TQd !V =WW! @?PqqssZ0P ~6p^0'\/ * 8` Abtn g Pٶ  >#$;]>0p尮z bV MU 0" ҃аREP Z;ڏ ' C (!3O(R"W9K>$@lmWW-0 L GJY.#u0PۜQQP5 @&QYj< iꑢiҗ!Bv5)  i?(RʘNP3:WR I) O5*+ 2 I>Ѐa -lJk(A65ApCX|B$ @Q^~ܱ* T) @]2&`$!rE1b ${ t`Y T_Z# leyuPf P*-L҉ ڀH@.'TxQ Zl_Ya 0 _ *JYҼ§j0P t0'ЄQ|18 9bvpoQp E#P2pBU ztB@^5t\]@ 5>~z,AfAgo0B|K!` O  AE o|%EL& W@#  *p<@4A`J  }{ >l(NQ0a# /mg ԑ (H&plD s ]FDqwsq@A` 5p$Ћx*i p|V O a ! >M3Ru Mk]w+^P} xl· gȁv '4`eV hP[D 'ta3xEi1QA*@N pfk! t2g` > mB"g~:0߀ -c1Q{C` .]{Z.0ulAl4|m5+TUUNQd p_A0g@l;z )@Q5g#tT^,#U"=OTqntI$(ftO >46R Tw/*)p$CED3p ~@N ݄%h4^cv:ST޽. PLBtHmBep N5$Q W1PMh44Q 3 @,zq*hO 7  rU gCE`Mp\\Opmn  ^`.F'QYZ ؠ , @rcAx t 6qXL@+"8Ǎɽыd`]3vߦ 5<٥ .XT]!“zkŽ^5vr%嘨tI@05hT$]*I  %-] ӧ^(`%mLpnWDl9+ t]7̎'Þ !.THA3VÕJBQ: " ,>%>*JL.kru Ai)# 3?ޔ 07 {ɁJA@#2UTVI$J|иZ ժ#wWpUF%I=[BD P,"c |ـ#"1ڻ6bkx¤jm^!x ੍\Rxr,p|b!x|h(]PeUPQ唍%8֊ xX1C(fcPB ēĘ,`z0jhH:Hc5D1|'{I_zÑ&u:.f@¨H.$›q>( j0.DeZ^xnYhh؃wMz5QEǘ!hŁ$f&Ah]6!/1؅ vA`ćđWR8]"Ǟ\̱%&N)UB5be C Pk(Vٖ"2"#Uĝe5@ C_2=VC D' evQfH`d]|@D*28`q|fT>yW^[yۡ*&2)(hiM6`+`8-P R4,@ @ ɸ'}D9¹bT p;ƤdcPq(]#DX!RD8e7a.2EӲZ!Hc5C= eA-&-eA( Ù)"TS^Xl؃cNmN-#P,ΆIɀ@n$W &+)+Wyx1S;Fl`Z F"@B.<hP\HAVQ )ZÎ!~Y6\8 LlSOxt%h@BW9`KA/DAexu'SF|C8>f=lLp* D~G|d`c|n; R8 ȉNJ1_/Ktʑ&//``uV$J ޢBi(!%\4Y Pe z E 5cMۅzaMd ;dt[]4z1>iI/~3L7;yO 'lh[{-o*)Hy::b>QFo `)SQ]܀X49M( DWL -1[p/|QSyZF2ɚǡ)މ0X}5adPgqLAM;ZWc8hP7B1ᕿ q3 G(4&s `\_'Q(q:\xB6%FX 9YX`X tIv+a 9e] ڀu ƕh(džBNTaWGy2a*oh7VP^MЗAEeB ^$qڕS8ER  ;^3LV8zNia7**Ew N&6n`PZc t"1& /~ #c2VX" a:蔘/H <`Iih8 Ӓ -ۦjm*%2b! &Ax>*|@ hkn`Y(pG↋1?P?x?`4%$#_0 cP * aKr&xR`D+ OI$rr* **&6d[^x ÑY@x.G QO(PsOxcG&?0_Bb1aX/7B@3]j٫ňP3 ~c':] m $Û5s ";” 6h3KFR<pH @T;]A*ଷR(P;㓭KJ(?&CH1 qyq`\8YHGSXy+N[ aXrx9Kb52r\ ҉Dd ^pi:HT 'xA&P}50> B00*Ew` `Y|I=A!"bPȀۅhCā h WІ Oq3'$0S_S,]߄y5nP0HQ7ѕ:H A(7p X x O] e]VfmVg}VhViVjVkVlVi ;ocaml-book-1.0/en/html/book-ora096.html0000644000000000000000000001067307453055400014500 0ustar Dependency Analysis Previous Contents Next

Dependency Analysis

Dependency analysis of a set of implementation and interface files that make up an Objective CAML application pursues a double end. The first is to get a global view of the interdependencies between modules. The second is to use this information in order to recompile only the absolutely necessary files after modifications of certain files.

The ocamldep command takes a set of .ml and .mli files and outputs the dependencies between files in Makefile1 format.

These dependencies originate from global declarations in other modules, either by using dot.notation (e.g. M1.f) or by opening a module (e.g. open M1).

Suppose the following files exist:

dp.ml :

let print_vect v =
for i = 0 to Array.length v do
Printf.printf "%f " v.(i)
done;
print_newline();;


and d1.ml :

let init n e =
let v = Array.create 4 3.14 in
Dp.print_vect v;
v;;


Given the name of these files, the ocamldep command will output the following dependencies:
$ ocamldep dp.ml d1.ml array.ml array.mli printf.ml printf.mli
dp.cmo: array.cmi printf.cmi 
dp.cmx: array.cmx printf.cmx 
d1.cmo: array.cmi dp.cmo 
d1.cmx: array.cmx dp.cmx 
array.cmo: array.cmi 
array.cmx: array.cmi 
printf.cmo: printf.cmi 
printf.cmx: printf.cmi 
The dependencies are determined for both the bytecode and the native compiler. The output is to be read in the following manner: production of the file dp.cmo depends on the files array.cmi and printf.cmi. Files with the extension .cmi depend on files with the same name and extension .mli. And the same holds by analogy for .ml files with .cmo and .cmx files.

The object files of the distribution do not show up in the dependency lists. In fact, if ocamldep does not find the files array.ml and printf.ml in the current directory, it will find them in the library directory of the installation and produce the following output:
$ ocamldep dp.ml d1.ml
d1.cmo: dp.cmo 
d1.cmx: dp.cmx 
To give new file search paths to the ocamldep command, the -I directory option is used, which adds a directory to the list of include directories.


Previous Contents Next ocaml-book-1.0/en/html/book-ora168.html0000644000000000000000000011613707453055400014502 0ustar Communication Between Processes Previous Contents Next

Communication Between Processes

The use of processes in application development allows you to delegate work. Nevertheless, these jobs may not be independent and it may be necessary for the processes to communicate with each other.

We introduce two methods of communication between processes: communication pipes and signals. This chapter does not discuss all possibilities of process communication. It is only a first approach to the applications developed in chapters 19 and 20.

Communication Pipes

It is possible for processes to communicate directly between each other in a file oriented style.

Pipes are something like virtual files from which it is possible to read and to write with the input / output functions read and write. They are of limited size, the exact limit depending from the system. They behave like queues: the first input is also the first output. Whenever data is read from a pipe, it is also removed from it.

This queue behavior is realized by the association of two descriptors with a pipe: one corresponding to the end of the pipe where new entries are written and one for the end where they are read. A pipe is created by the function:

# Unix.pipe ;;
- : unit -> Unix.file_descr * Unix.file_descr = <fun>
The first component of the resulting pair is the exit of the pipe used for reading. The second is the entry of the pipe used for writing. All processes knowing them can close the descriptors.

Reading from a pipe is blocking, unless all processes knowing its input descriptor (and therefore able to write to it) have closed it; in the latter case, the function read returns 0. If a process tries to write to a full pipe, it is suspended until another process has done a read operation. If a process tries to write to a pipe while no other process is available to read from it (all having closed their output descriptors), the process trying to write receives the signal sigpipe, which, if not indicated otherwise, leads to its termination.

The following example shows a use of pipes in which grandchildren tell their process number to their grandparents.
let output, input = Unix.pipe();;

let write_pid input =
try
let m = "(" ^ (string_of_int (Unix.getpid ())) ^ ")"
in ignore (Unix.write input m 0 (String.length m)) ;
Unix.close input
with
Unix.Unix_error(n,f,arg) ->
Printf.printf "%s(%s) : %s\n" f arg (Unix.error_message n) ;;

match Unix.fork () with
0 -> for i=0 to 5 do
match Unix.fork() with
0 -> write_pid input ; exit 0
| _ -> ()
done ;
Unix.close input
| _ -> Unix.close input;
let s = ref "" and buff = String.create 5
in while true do
match Unix.read output buff 0 5 with
0 -> Printf.printf "My grandchildren are %s\n" !s ; exit 0
| n -> s := !s ^ (String.sub buff 0 n) ^ "."
done ;;


We obtain the trace:
My grandchildren are (1067.3).(1067.4).(1067.8).(1067.7).(1067.6).(1067.5).


We have introduced points between each part of the sequence read. This way it is possible to read from the trace the succession of contents of the pipe. Note how the reading is desynchronized: whenever an entry is made, even a partial one, it is consumed.

Named pipes.
Some Unix systems support named pipes, which look as if they were normal files. It is possible then to communicate between two processes without a descendence relation using the name of the pipe. The following function allows you to create such a pipe.

# Unix.mkfifo ;;
- : string -> Unix.file_perm -> unit = <fun>


The file descriptors necessary to use the pipe are obtained by openfile, as for usual files, but their behavior is that of pipes. In particular, the command lseek can not be used, since we have waiting lines.

Warning


mkfifo is not implemented for Windows.


Communication Channels

The Unix module provides a high level function allowing you to start a program associating with it input or output channels of the calling program:

# Unix.open_process ;;
- : string -> in_channel * out_channel = <fun>
The argument is the name of the program, or more precisely the calling path of the program, as we would write it to a command line interpreter. The string may contain arguments for the program to execute. The two output values are file descriptors associated with the standard input / output of the started program. It will be executed in parallel with the calling program.

Warning


The program started by open_process is executed via a call to the Unix command line interpreter /bin/sh.
The use of that function is therefore only possible for systems that have this interpreter.


We can end the execution of a program started by open_process by using:

# Unix.close_process ;;
- : in_channel * out_channel -> Unix.process_status = <fun>
The argument is the pair of channels associated with a process we want to close. The return value is the execution status of the process whose termination we wait.

There are variants of that functions, opening and closing only one input or output channel:

# Unix.open_process_in ;;
- : string -> in_channel = <fun>
# Unix.close_process_in ;;
- : in_channel -> Unix.process_status = <fun>
# Unix.open_process_out ;;
- : string -> out_channel = <fun>
# Unix.close_process_out ;;
- : out_channel -> Unix.process_status = <fun>


Here is a nice small example for the use of open_process: we start ocaml from ocaml!

# let n_print_string s = print_string s ; print_string "(* <-- *)" ;;
val n_print_string : string -> unit = <fun>
# let p () =
let oc_in, oc_out = Unix.open_process "/usr/local/bin/ocaml"
in n_print_string (input_line oc_in) ; print_newline() ;
n_print_string (input_line oc_in) ; print_newline() ;
print_char (input_char oc_in) ;
print_char (input_char oc_in) ;
flush stdout ;
let s = input_line stdin
in output_string oc_out s ;
output_string oc_out "#quit\n" ;
flush oc_out ;
let r = String.create 250 in
let n = input oc_in r 0 250
in n_print_string (String.sub r 0 n) ;
print_string "Thank you for your visit\n" ;
flush stdout ;
Unix.close_process (oc_in, oc_out) ;;
val p : unit -> Unix.process_status = <fun>
The call of the function p starts a toplevel of Objective CAML. We note that it is version 2.03 which is in directory /usr/local/bin. The first four read operations allow us to get the header, which is shown by toplevel. The line let x = 1.2 +. 5.6;; is read from the keybard, then sent to oc_out (the output channel bound to the standard input of the new process). This one evaluates the passed Objective CAML expression and writes the result to the standard output which is bound to the input channel oc_in. This result is read and written to the output by the function input. Also the string "Thank you for your visit" is written to the output. We send the command #quit;; to exit the new process.
# p();;
        Objective Caml version 2.03

# let x = 1.2 +. 5.6;;
val x : float = 6.8
Thank you for your visit
- : Unix.process_status = Unix.WSIGNALED 13
# 

Signals under Unix

One possibility to communicate with a process is to send it a signal. A signal may be received at any moment during the execution of a program. Reception of a signal causes a logical interruption. The execution of a program is interrupted to treat the received signal. Then the execution continues at the point of interruption. The number of signals is quite restricted (32 under Linux). The information carried by a signal is quite rudimentary: it is only the identity (the number) of the signal. The processes have a predefined reaction to each signal. However, the reactions can be redefined for most of the signals.

The data and functions to handle signals are distributed between the modules Sys and Unix. The module Sys contains signals conforming to the POSIX norm (described in [Ste92]) as well as some functions to handle signals. The module Unix defines the function kill to send a signal. The use of signals under Windows is restricted to sigint.

A signal may have several sources: the keyboard, an illegal attempt to access memory, etc. A process may send a signal to another by calling the function

# Unix.kill ;;
- : int -> int -> unit = <fun>
Its first parameter is the PID of the receiver. The second is the signal which we want to send.

Handling Signals

There are three categories of reactions associated with a signal. For each category there is a constructor of type signal_behavior:
  • Signal_default: the default behavior defined by the system. In most of the cases this is the termination of the process, with or without the creation of a file describing the process state (core file).
  • Signal_ignore: the signal is ignored.
  • Signal_handle: the behavior is redefined by an Objective CAML function of type int -> unit which is passed as an argument to the constructor. For the modified handling of the signal, the number of the signal is passed to the handling function.
On reception of a signal, the execution of the receiving process is diverted to the function handling the signal. The function allowing you to redefine the behavior associated with a signal is provided by the module Sys:

# Sys.set_signal;;
- : int -> Sys.signal_behavior -> unit = <fun>
The first argument is the signal to redefine. The second is the associated behavior.

The module Sys provides another modification function to handle signals:

# Sys.signal ;;
- : int -> Sys.signal_behavior -> Sys.signal_behavior = <fun>


It behaves like set_signal, except that it returns in addition the value associated with the signal before the modification. So we can write a function returning the behavioral value associated with a signal. This can be done even without changing this value:

# let signal_behavior s =
let b = Sys.signal s Sys.Signal_default
in Sys.set_signal s b ; b ;;
val signal_behavior : int -> Sys.signal_behavior = <fun>
# signal_behavior Sys.sigint;;
- : Sys.signal_behavior = Sys.Signal_handle <fun>


However, the behavior associated with some signals can not be changed. Therefore our function can not be used for all signals:

# signal_behavior Sys.sigkill ;;
Uncaught exception: Sys_error("Invalid argument")


Some Signals

We illustrate the use of some essential signals.

sigint.
This signal is generally associated with the key combination CTRL-C. In the following small example we modify the reaction to this signal so that the receiving process is not interrupted until the third occurence of the signal.

We create the following file ctrlc.ml:
let sigint_handle =
let n = ref 0
in function _ -> incr n ;
match !n with
1 -> print_string "You just pushed CTRL-C\n"
| 2 -> print_string "You pushed CTRL-C a second time\n"
| 3 -> print_string "If you insist ...\n" ; exit 1
| _ -> () ;;
Sys.set_signal Sys.sigint (Sys.Signal_handle sigint_handle) ;;
match Unix.fork () with
0 -> while true do () done
| pid -> Unix.sleep 1 ; Unix.kill pid Sys.sigint ;
Unix.sleep 1 ; Unix.kill pid Sys.sigint ;
Unix.sleep 1 ; Unix.kill pid Sys.sigint ;;


This program simulates the push of the key combination CTRL-C by sending the signal sigint. We obtain the following execution trace:
$ ocamlc -i -o ctrlc ctrlc.ml
val sigint_handle : int -> unit
$ ctrlc
You just pushed CTRL-C
You pushed CTRL-C a second time
If you insist ...
sigalrm.
Another frequently used signal is sigalrm, which is associated with the system clock. It can be sent by the function

# Unix.alarm ;;
- : int -> int = <fun>
The argument specifies the number of seconds to wait before the sending of the signal sigalrm. The return value indicates the number of remaining seconds before the sending of a second signal, or if there is no alarm set.

We use this function and the associated signal to define the function timeout, which starts the execution of another function and interrupts it if neccessary, when the indicated time is elapsed. More precisely, the function timeout takes as arguments a function f, the argument arg expected by f, the duration (time) of the ``timeout'' and the value (default_value) to be returned when the duration time has elapsed.

A timeout is handled as follows:
  1. We modify the behavior associated with the signal sigalrm so that a Timeout exception is thrown.
  2. We take care to remember the behavior associated originally with sigalrm, so that it can be restored.
  3. We start the clock.
  4. We distinguish two cases:
    1. If everything goes well, we restore the original state of sigalrm and return the value of the calculation.
    2. If not, we restore sigalrm, and if the duration has elapsed, we return the default value.
Here are the corresponding definitions and a small example:

# exception Timeout ;;
exception Timeout
# let sigalrm_handler = Sys.Signal_handle (fun _ -> raise Timeout) ;;
val sigalrm_handler : Sys.signal_behavior = Sys.Signal_handle <fun>
# let timeout f arg time default_value =
let old_behavior = Sys.signal Sys.sigalrm sigalrm_handler in
let reset_sigalrm () = Sys.set_signal Sys.sigalrm old_behavior
in ignore (Unix.alarm time) ;
try let res = f arg in reset_sigalrm () ; res
with exc -> reset_sigalrm () ;
if exc=Timeout then default_value else raise exc ;;
val timeout : ('a -> 'b) -> 'a -> int -> 'b -> 'b = <fun>
# let iterate n = for i = 1 to n do () done ; n ;;
val iterate : int -> int = <fun>

Printf.printf "1st execution : %d\n" (timeout iterate 10 1 (-1));
Printf.printf "2nd execution : %d\n" (timeout iterate 100000000 1 (-1)) ;;
1st execution : 10
2nd execution : -1
- : unit = ()
sigusr1 and sigusr2.
These two signals are provided only for the programer. They are not used by the operating system.

In this example, reception of the signal sigusr1 by the child triggers the output of the content of variable i.
let i = ref 0  ;;
let write_i s = Printf.printf "signal received (%d) -- i=%d\n" s !i ;
flush stdout ;;
Sys.set_signal Sys.sigusr1 (Sys.Signal_handle write_i) ;;

match Unix.fork () with
0 -> while true do incr i done
| pid -> Unix.sleep 0 ; Unix.kill pid Sys.sigusr1 ;
Unix.sleep 3 ; Unix.kill pid Sys.sigusr1 ;
Unix.sleep 1 ; Unix.kill pid Sys.sigkill



Here is the trace of a program execution:
signal received (10) -- i=0
signal received (10) -- i=167722808


When we examine the trace, we can see that after having executed the code associated with signal sigusr1 the first time, the child process continues to execute the loop and to increment i.

sigchld.
This signal is sent to a parent on termination of a process. We will use it to make a parent more attentive to the evolution of its children. Here's how:
  1. We define a function handling the signal sigchld. It handles all terminated children on reception of this signal5 and terminates the parent when he does not have any more children (exception Unix_error). In order not to block the parent if not all his children are dead, we use waitpid instead of wait.
  2. The main program, after having redefined the reaction associated with sigchld, loops to create five children. After this, the parent does something else (loop while true) until his children have terminated.
let rec sigchld_handle s =
try let pid, _ = Unix.waitpid [Unix.WNOHANG] 0
in if pid <> 0
then ( Printf.printf "%d is dead and buried at signal %d\n" pid s ;
flush stdout ;
sigchld_handle s )
with Unix.Unix_error(_, "waitpid", _) -> exit 0 ;;

let i = ref 0
in Sys.set_signal Sys.sigchld (Sys.Signal_handle sigchld_handle) ;
while true do
match Unix.fork() with
0 -> let pid = Unix.getpid ()
in Printf.printf "Creation of %d\n" pid ; flush stdout ;
Unix.sleep (Random.int (5+ !i)) ;
Printf.printf "Termination of %d\n" pid ; flush stdout ;
exit 0
| _ -> incr i ; if !i = 5 then while true do () done
done ;;


We obtain the trace:
Creation of 10658
Creation of 10659
Creation of 10662
Creation of 10661
Creation of 10660
Termination of 10662
10662 is dead and buried at signal 17
Termination of 10658
10658 is dead and buried at signal 17
Termination of 10660
Termination of 10659
10660 is dead and buried at signal 17
10659 is dead and buried at signal 17
Termination of 10661
10661 is dead and buried at signal 17







Previous Contents Next ocaml-book-1.0/en/html/book-ora202.html0000644000000000000000000003144307453055401014464 0ustar Similar functional languages Previous Contents Next

Similar functional languages

There are several languages similar to Objective CAML, whether through the functional aspect, or through typing. Objective CAML is descended from the ML family, and thus it has cousins of which the closest are across the Atlantic and across the channel in the lineage of SML (Standard ML). The Lisp family, and in particular the Scheme language, differs from ML mainly by its dynamic typing. Two lazy languages, Miranda and Haskell, take up or extend ML's typing in the framework of delayed evaluation. Two functional languages, Erlang and SCOL, developed by the Ericsson and Cryo-Networks corporations respectively, are directed towards communication.

ML family

The ML family comprises two main branches: Caml (Categorical Abstract Machine Language) and its derivatives Caml-Light and Objective CAML, SML (Standard ML) and its descendants SML/NJ and mosml. Caml, the ancestor, was developed between 1986 and 1990 by INRIA's FORMEL project in collaboration with University Paris 7 and the cole Normale Suprieure. Its implementation was based on Le_Lisp's runtime. It integrated within the language the definition of grammars and pretty-printers, which allowed communication of values between the language described and Caml. Its type system was more restrictive for mutable values, insofar as it did not allow such values to be polymorphic. Its first descendant, Caml-Light, no longer used the CAM machine, but instead used Zinc for its implementation. The name was nevertheless retained to show its ancestry. It contributed a more lightweight implementation while optimizing the allocation of closures and using an optimizing GC as a precursor to the actual GC. This streamlining allowed it to be used on the PC's of the time. The various Caml-Light versions evolved towards actual typing of imperative features and were enriched with numerous libraries. The following offshoot, Caml Special Light or CSL, introduced parameterized modules and a native-code compiler. Finally the baby is actually Objective CAML which mainly adds the object-oriented extension to CSL. Since there has never been a complete specification of the Caml languages, these various changes have been able to take place in complete freedom.

The SML approach has been the opposite. The formal specification [MTH97] was given before the first implementation. It is difficult to read, and a second book gives a commentary ([MT91]) on it. This method, specification then implementation, has allowed the development of several implementations, of which the best-known is SML/NJ (Standard ML of New Jersey) from Lucent (ex-AT&T). Since its origin, SML has integrated parameterized modules. Its initial type system was different from that of Caml for imperative features, introducing a level of weakness for type variables. The differences between the two languages are detailed in [CKL96]. These differences are being effaced with time. The two families have the same type system for the functional and imperative core, Objective CAML now has parameterized modules. SML has also undergone changes, bringing it closer to Objective CAML, such as for record types. If the two languages don't merge, this mainly derives from their separate development. It is to be noted that there is a commercial development environment for SML, MLWorks, from Harlequin:

Link


http://www.harlequin.com/products/
An SML implementation, mosml, based on Caml-Light's runtime, has also been implemented.

Scheme

The Scheme language (1975) is a dialect of the Lisp language (1960). It has been standardized (IEEE Std 1178-1990). It is a functional language with strict evaluation, equipped with imperative features, dynamically typed. Its syntax is regular and particular about the use of parentheses. The principal data structure is the dotted pair (equivalent to an ML pair) with which possibly heterogeneous lists are constructed. The main loop of a Scheme toplevel is written (print (eval (read))). The read function reads standard input and constructs a Scheme expression. The eval function evaluates the constructed expression and the print function prints the result. Scheme has a very useful macro-expansion system which, in association with the eval function, permits the easy construction of language extensions. It not only supports interrupting computation (exceptions) but also resuming computation thanks to continuations. A continuation corresponds to a point of computation. The special form call_cc launches a computation with the possibility of resuming this one at the level of the current continuation, that is to say of returning to this computation. There are many Scheme implementations. It is even used as a macro language for the GIMP image manipulation software. Scheme is an excellent experimental laboratory for the implementation of new sequential or parallel programming concepts (thanks to continuations).

Languages with delayed evaluation

In contrast with ML or Lisp, languages with delayed evaluation do not compute the parameters of function calls when they are passed, but when evaluation of the body of the function requires it. There is a ``lazy'' version of ML called Lazy ML but the main representatives of this language family are Miranda and Haskell.

Miranda

Miranda([Tur85]) is a pure functional language. That is to say, without side effects. A Miranda program is a sequence of equations defining functions and data structures.

Link


http://www.engin.umd.umich.edu/CIS/course.des/cis400/miranda/miranda.html


For example the fib function is defined in this way:
 
 fib a = 1, a=0 
       = 1, a=1 
       = fib(a-1) + fib(a-2), a>1
Equations are chosen either through guards (conditional expressions) as above, or by pattern-matching as in the example below:
fib 0 = 1
fib 1 = 1
fib a = fib(a-1)+ fib(a-2)
These two methods can be mixed.

Functions are higher order and can be partially evaluated. Evaluation is lazy, no subexpression is computed until the moment when its value becomes necessary. Thus, Miranda lists are naturally streams.

Miranda has a concise syntax for infinite structures (lists, sets): [1..] represents the list of all the natural numbers. The list of values of the Fibonacci function is written briefly: fibs = [a | (a,b) <- (1,1),(b,a+b)..]. Since values are only computed when used, the declaration of fibs costs nothing.

Miranda is strongly typed, using a Hindley-Milner type system. Its type discipline is essentially the same as ML's. It accepts the definition of data by the user.

Miranda is the archetype of pure lazy functional languages.

Haskell

The main Haskell language website contains reports of the definition of the language and its libraries, as well as its main implementations.

Link


http://www.haskell.org


Several books are dedicated to functional programming in Haskell, one of the most recent is [Tho99].

This is a language which incorporates almost all of the new concepts of functional languages. It is pure (without side effects), lazy (not strict), equipped with an ad hoc polymorphism (for overloading) as well as parametric polymorphism la ML.

Ad hoc polymorphism
This system is different from the polymorphism seen up to now. In ML a polymorphic function disregards its polymorphic arguments. The treatment is identical for all types. In Haskell it is the opposite. A polymorphic function may have a different behavior according to the type of its polymorphic arguments. This allows function overloading.

The basic idea is to define type classes which group together sets of overloaded functions. A class declaration defines a new class and the operations which it permits. A (class) instance declaration indicates that a certain type is an instance of some class. It includes the definition of the overloaded operations of this class for this type.

For example the Num class has the following declaration:
class Num a where
  (+)    :: a -> a -> a
  negate :: a -> a
Now an instance Int of the class Num can be declared in this way:
instance Num Int where
  x + y    =  addInt x y
  negate x = negateInt x
And the instance Float:
instance Num Float where
  x + y    =  addFloat x y
  negate x = negateFloat x
The application of negate Num will have a different behavior if the argument is an instance of Int or Float.

The other advantage of classes derives from inheritance between classes. The descendant class recovers the functions declared by its ancestor. Its instances can modify their behavior.

Other characteristics
The other characteristics of the Haskell language are mainly the following:
  • a purely functional I/O system using monads;
  • arrays are built lazily;
  • views permit different representations of a single data type.
In fact it contains just about all the high-strung features born of research in the functional language domain. This is its advantage and its disadvantage.

Communication languages

ERLANG

ERLANG is a dynamically typed functional language for concurrent programming. It was developed by the Ericsson corporation in the context of telecommunications applications. It is now open source. The main site for accessing the language is the following:

Link


http://www.erlang.org
It was conceived so that the creation of processes and their communication might be easy. Communications take place by message passing and they can be submitted with delays. It is easy to define protocols via ports. Each process possesses its own definition dictionary. Error management uses an exception mechanism and signals can propagate among processes. Numerous telephony applications have been developed in Erlang, yielding non-negligible savings of development time.

SCOL

The SCOL language is a communication language for constructing 3D worlds. It was developed by the Cryo Networks corporation:

Link


http://www.cryo-networks.com
Its core is close to that of Caml: it is functional, statically typed, parametrically polymorphic with type inference. It is ``multimedia'' thanks to its API's for sound, 2D, and 3D. The 3D engine is very efficient. SCOL's originality comes from communication between virtual machines by means of channels. A channel is an (environment, network link) pair. The link is a (TCP or UDP) socket.

SCOL's originality lies in having resolved simply the problem of securing downloaded code: only the text of programs circulates on the network. The receiving machine types the passed program, then executes it, guaranteeing that the code produced does indeed come from an official compiler. To implement such a solution, without sacrificing speed of transmission and reception, the choice of a statically typed functional language was imposed by the conciseness of source code which it supports.


Previous Contents Next ocaml-book-1.0/en/html/book-ora039.html0000644000000000000000000006631107453055377014512 0ustar Mixing Styles Previous Contents Next

Mixing Styles

As we have mentioned, a language offering both functional and imperative characteristics allows the programmer to choose the more appropriate style for each part of the implementation of an algorithm. One can indeed use both aspects in the same function. This is what we will now illustrate.

Closures and Side Effects

The convention, when a function causes a side effect, is to treat it as a procedure and to return the value (), of type unit. Nevertheless, in some cases, it can be useful to cause the side effect within a function that returns a useful value. We have already used this mixture of the styles in the function permute_pivot of quicksort.

The next example is a symbol generator that creates a new symbol each time that it is called. It simply uses a counter that is incremented at every call.

# let c = ref 0;;
val c : int ref = {contents=0}
# let reset_symb = function () -> c:=0 ;;
val reset_symb : unit -> unit = <fun>
# let new_symb = function s -> c:=!c+1 ; s^(string_of_int !c) ;;
val new_symb : string -> string = <fun>
# new_symb "VAR" ;;
- : string = "VAR1"
# new_symb "VAR" ;;
- : string = "VAR2"
# reset_symb () ;;
- : unit = ()
# new_symb "WAR" ;;
- : string = "WAR1"
# new_symb "WAR" ;;
- : string = "WAR2"


The reference c may be hidden from the rest of the program by writing:

# let (reset_s , new_s) =
let c = ref 0
in let f1 () = c := 0
and f2 s = c := !c+1 ; s^(string_of_int !c)
in (f1,f2) ;;
val reset_s : unit -> unit = <fun>
val new_s : string -> string = <fun>


This declaration creates a pair of functions that share the variable c, which is local to this declaration. Using these two functions produces the same behavior as the previous definitions.

# new_s "VAR";;
- : string = "VAR1"
# new_s "VAR";;
- : string = "VAR2"
# reset_s();;
- : unit = ()
# new_s "WAR";;
- : string = "WAR1"
# new_s "WAR";;
- : string = "WAR2"


This example permits us to illustrate the way that closures are represented. A closure may be considered as a pair containing the code (that is, the function part) as one component and the local envoronment containing the values of the free variables of the function. Figure 4.1 shows the memory representation of the closures reset_s and new_s.




Figure 4.1: Memory representation of closures.


These two closures share the same environment, containing the value of c. When either one modifies the reference c, it modifies the contents of an area of memory that is shared with the other closure.

Physical Modifications and Exceptions

Exceptions make it possible to escape from situations in which the computation cannot proceed. In this case, an exception handler allows the calculation to continue, knowing that one branch has failed. The problem with side effects comes from the state of the modifiable data when the exception was raised. One cannot be sure of this state if there have been physical modifications in the branch of the calculation that has failed.

Let us define the increment function (++) analogous to the operator in C:

# let (++) x = x:=!x+1; x;;
val ++ : int ref -> int ref = <fun>
The following example shows a little computation where division by zero occurs together with

# let x = ref 2;;
val x : int ref = {contents=2}
(* 1 *)
# !((++) x) * (1/0) ;;
Uncaught exception: Division_by_zero
# x;;
- : int ref = {contents=2}
(* 2 *)
# (1/0) * !((++) x) ;;
Uncaught exception: Division_by_zero
# x;;
- : int ref = {contents=3}
The variable x is not modified during the computation of the expression in (*1*), while it is modified in the computation of (*2*). Unless one saves the initial values, the form try .. with .. must not have a with .. part that depends on modifiable variables implicated in the expression that raised the exception.

Modifiable Functional Data Structures

In functional programming a program (in particular, a function expression) may also serve as a data object that may be manipulated, and one way to see this is to write association lists in the form of function expressions. In fact, one may view association lists of type ('a * 'b) list as partial functions taking a key chosen from the set 'a and returning a value in the set of associated values 'b. Each association list is then a function of type 'a -> 'b.

The empty list is the everywhere undefined function, which one simulates by raising an exception:

# let nil_assoc = function x -> raise Not_found ;;
val nil_assoc : 'a -> 'b = <fun>


We next write the function add_assoc which adds an element to a list, meaning that it extends the function for a new entry:

# let add_assoc (k,v) l = function x -> if x = k then v else l x ;;
val add_assoc : 'a * 'b -> ('a -> 'b) -> 'a -> 'b = <fun>
# let l = add_assoc ('1', 1) (add_assoc ('2', 2) nil_assoc) ;;
val l : char -> int = <fun>
# l '2' ;;
- : int = 2
# l 'x' ;;
Uncaught exception: Not_found


We may now re-write the function mem_assoc:

# let mem_assoc k l = try (l k) ; true with Not_found -> false ;;
val mem_assoc : 'a -> ('a -> 'b) -> bool = <fun>
# mem_assoc '2' l ;;
- : bool = true
# mem_assoc 'x' l ;;
- : bool = false


By contrast, writing a function to remove an element from a list is not trivial, because one no longer has access to the values captured by the closures. To accomplish the same purpose we mask the former value by raising the exception Not_found.

# let rem_assoc k l = function x -> if x=k then raise Not_found else l x ;;
val rem_assoc : 'a -> ('a -> 'b) -> 'a -> 'b = <fun>
# let l = rem_assoc '2' l ;;
val l : char -> int = <fun>
# l '2' ;;
Uncaught exception: Not_found


Clearly, one may also create references and work by side effect on such values. However, one must take some care.

# let add_assoc_again (k,v) l = l := (function x -> if x=k then v else !l x) ;;
val add_assoc_again : 'a * 'b -> ('a -> 'b) ref -> unit = <fun>


The resulting value for l is a function that points at itself and therefore loops. This annoying side effect is due to the fact that the dereferencing !l is within the scope of the closure function x ->. The value of !l is not evaluated during compilation, but at run-time. At that time, l points to the value that has already been modified by add_assoc. We must therefore correct our definition using the closure created by our original definition of add_assoc:

# let add_assoc_again (k, v) l = l := add_assoc (k, v) !l ;;
val add_assoc_again : 'a * 'b -> ('a -> 'b) ref -> unit = <fun>
# let l = ref nil_assoc ;;
val l : ('_a -> '_b) ref = {contents=<fun>}
# add_assoc_again ('1',1) l ;;
- : unit = ()
# add_assoc_again ('2',2) l ;;
- : unit = ()
# !l '1' ;;
- : int = 1
# !l 'x' ;;
Uncaught exception: Not_found


Lazy Modifiable Data Structures

Combining imperative characteristics with a functional language produces good tools for implementing computer languages. In this subsection, we will illustrate this idea by implementing data structures with deferred evaluation. A data structure of this kind is not completely evaluated. Its evaluation progresses according to the use made of it.

Deferred evaluation, which is often used in purely functional languages, is simulated using function values, possibly modifiable. There are at least two purposes for manipulating incompletely evaluated data structures: first, so as to calculate only what is effectively needed in the computation; and second, to be able to work with potentially infinite data structures.

We define the type vm, whose members contain either an already calculated value (constructor Imm) or else a value to be calculated (constructor Deferred):

# type 'a v =
Imm of 'a
| Deferred of (unit -> 'a);;
# type 'a vm = {mutable c : 'a v };;


A computation is deferred by encapsulating it in a closure. The evaluation function for deferred values must return the value if it has already been calculated, and otherwise, if the value is not already calculated, it must evaluate it and then store the result.

# let eval e = match e.c with
Imm a -> a
| Deferred f -> let u = f () in e.c <- Imm u ; u ;;
val eval : 'a vm -> 'a = <fun>


The operations of deferring evaluation and activating it are also called freezing and thawing a value.

We could also write the conditional control structure in the form of a function:

# let if_deferred c e1 e2 =
if eval c then eval e1 else eval e2;;
val if_deferred : bool vm -> 'a vm -> 'a vm -> 'a = <fun>


Here is how to use it in a recursive function such as factorial:

# let rec facr n =
if_deferred
{c=Deferred(fun () -> n = 0)}
{c=Deferred(fun () -> 1)}
{c=Deferred(fun () -> n*(facr(n-1)))};;
val facr : int -> int = <fun>
# facr 5;;
- : int = 120


The classic form of if can not be written in the form of a function. In fact, if we define a function if_function this way:

# let if_function c e1 e2 = if c then e1 else e2;;
val if_function : bool -> 'a -> 'a -> 'a = <fun>


then the three arguments of if_function are evaluated at the time they are passed to the function. So the function fact loops, because the recursive call fact(n-1) is always evaluated, even when n has the value 0.

# let rec fact n = if_function (n=0) 1 (n*fact(n-1)) ;;
val fact : int -> int = <fun>
# fact 5 ;;
Stack overflow during evaluation (looping recursion?).


Module Lazy

The implementation difficulty for frozen values is due to the conflict between the eager evaluation strategy of Objective CAML and the need to leave expressions unevaluated. Our attempt to redefine the conditional illustrated this. More generally, it is impossible to write a function that freezes a value in producing an object of type vm:

# let freeze e = { c = Deferred (fun () -> e) };;
val freeze : 'a -> 'a vm = <fun>
When this function is applied to arguments, the Objective CAML evaluation strategy evaluates the expression e passed as argument before constructing the closure fun () -> e. The next example shows this:

# freeze (print_string "trace"; print_newline(); 4*5);;
trace
- : int vm = {c=Deferred <fun>}


This is why the following syntactic form was introduced.

Syntax


lazy expr


Warning


This form is a language extension that may evolve in future versions.


When the keyword lazy is applied to an expression, it constructs a value of a type declared in the module Lazy:

# let x = lazy (print_string "Hello"; 3*4) ;;
val x : int Lazy.status ref = {contents=Lazy.Delayed <fun>}


The expression (print_string "Hello") has not been evaluated, because no message has been printed. The function force of module Lazy allows one to force evaluation:

# Lazy.force x ;;
Hello- : int = 12
Now the value x has altered:

# x ;;
- : int Lazy.t = {contents=Lazy.Value 12}
It has become the value of the expression that had been frozen, namely 12.

For another call to the function force, it's enough to return the value already calculated:

# Lazy.force x ;;
- : int = 12
The string "Hello" is no longer prefixed.

``Infinite'' Data Structures

The second reason to defer evaluation is to be able to construct potentially infinite data structures such as the set of natural numbers. Because it might take a long time to construct them all, the idea here is to compute only the first one and to know how to pass to the next element.

We define a generic data structure 'a enum which will allow us to enumerate the elements of a set.

# type 'a enum = { mutable i : 'a; f :'a -> 'a } ;;
type 'a enum = { mutable i: 'a; f: 'a -> 'a }
# let next e = let x = e.i in e.i <- (e.f e.i) ; x ;;
val next : 'a enum -> 'a = <fun>


Now we can get the set of natural numbers by instantiating the fields of this structure:

# let nat = { i=0; f=fun x -> x + 1 };;
val nat : int enum = {i=0; f=<fun>}
# next nat;;
- : int = 0
# next nat;;
- : int = 1
# next nat;;
- : int = 2
Another example gives the elements of the Fibonnacci sequence, which has the definition:


u0 = 1
u1 = 1
un+2 = un + un+1
The function to compute the successor must take account of the current value, (un-1), but also of the preceding one (un-2). For this, we use the state c in the following closure:

# let fib = let fx = let c = ref 0 in fun v -> let r = !c + v in c:=v ; r
in { i=1 ; f=fx } ;;
val fib : int enum = {i=1; f=<fun>}
# for i=0 to 10 do print_int (next fib); print_string " " done ;;
1 1 2 3 5 8 13 21 34 55 89 - : unit = ()



Previous Contents Next ocaml-book-1.0/en/html/book-ora147.html0000644000000000000000000015537307453055400014504 0ustar Exercises Previous Contents Next

Exercises

Stacks as Objects

Let us reconsider the stacks example, this time in object oriented style.
  1. Define a class intstack using Objective CAML's lists, implementing methods push, pop, top and size.

    # exception EmptyStack

    class intstack () =
    object
    val p = ref ([] : int list)
    method emstack i = p := i:: !p
    method push i = p := i :: !p
    method pop () = if !p = [] then raise EmptyStack else p := List.tl !p
    method top () = if !p = [] then raise EmptyStack else List.hd !p
    method size () = List.length !p
    end ;;
    exception EmptyStack
    class intstack :
    unit ->
    object
    val p : int list ref
    method emstack : int -> unit
    method pop : unit -> unit
    method push : int -> unit
    method size : unit -> int
    method top : unit -> int
    end


  2. Create an instance containing 3 and 4 as stack elements.

    # let p = new intstack () ;;
    val p : intstack = <obj>
    # p#push 3 ;;
    - : unit = ()
    # p#push 4 ;;
    - : unit = ()


  3. Define a new class stack containing elements answering the method
    print : unit -> unit.

    # class stack () =
    object
    val p = ref ([] : <print : unit -> unit> list)
    method push i = p := i:: !p
    method pop () = if !p = [] then raise EmptyStack else p := List.tl !p
    method top () = if !p = [] then raise EmptyStack else List.hd !p
    method size () = List.length !p
    end ;;
    class stack :
    unit ->
    object
    val p : < print : unit -> unit > list ref
    method pop : unit -> unit
    method push : < print : unit -> unit > -> unit
    method size : unit -> int
    method top : unit -> < print : unit -> unit >
    end


  4. Define a parameterized class ['a] stack, using the same methods.

    # class ['a] pstack () =
    object
    val p = ref ([] : 'a list)
    method push i = p := i:: !p
    method pop () = if !p = [] then raise EmptyStack else p := List.tl !p
    method top () = if !p = [] then raise EmptyStack else (List.hd !p)
    method size () = List.length !p
    end ;;
    class ['a] pstack :
    unit ->
    object
    val p : 'a list ref
    method pop : unit -> unit
    method push : 'a -> unit
    method size : unit -> int
    method top : unit -> 'a
    end


  5. Compare the different classes of stacks.

Delayed Binding

This exercise illustrates how delayed binding can be used in a setting other than subtyping.

Given the program below:
  1. Draw the relations between classes.

  2. Draw the different messages.

  3. Assuming you are in character mode without echo, what does the program display?

exception CrLf;;
class chain_read (m) =
object (self)
val msg = m
val mutable res = ""

method char_read =
let c = input_char stdin in
if (c != '\n') then begin
output_char stdout c; flush stdout
end;
String.make 1 c

method private chain_read_aux =
while true do
let s = self#char_read in
if s = "\n" then raise CrLf
else res <- res ^ s;
done

method private chain_read_aux2 =
let s = self#lire_char in
if s = "\n" then raise CrLf
else begin res <- res ^ s; self#chain_read_aux2 end

method chain_read =
try
self#chain_read_aux
with End_of_file -> ()
| CrLf -> ()

method input = res <- ""; print_string msg; flush stdout;
self#chain_read

method get = res
end;;

class mdp_read (m) =
object (self)
inherit chain_read m
method char_read = let c = input_char stdin in
if (c != '\n') then begin
output_char stdout '*'; flush stdout
end;

let s = " " in s.[0] <- c; s
end;;

let login = new chain_read("Login : ");;
let passwd = new mdp_read("Passwd : ");;
login#input;;
passwd#input;;
print_string (login#get);;print_newline();;
print_string (passwd#get);;print_newline();;


Abstract Classes and an Expression Evaluator

This exercise illustrates code factorization with abstract classes.

All constructed arithmetic expressions are instances of a subclass of the abstract class expr_ar.
  1. Define an abstract class expr_ar for arithmetic expressions with two abstract methods: eval of type float, and print of type unit, which respectively evaluates and displays an arithmetic expression.

    # class virtual expr_ar () =
    object
    method virtual eval : unit -> float
    method virtual print : unit -> unit
    end ;;
    class virtual expr_ar :
    unit ->
    object
    method virtual eval : unit -> float
    method virtual print : unit -> unit
    end


  2. Define a concrete class constant, a subclass of expr_ar.

    # class constant x =
    object
    inherit expr_ar ()
    val c = x
    method eval () = c
    method print () = print_float c
    end ;;
    class constant :
    float ->
    object
    val c : float
    method eval : unit -> float
    method print : unit -> unit
    end

    (* autre solution : *)

    # class const x =
    object
    inherit expr_ar ()
    method eval () = x
    method print () = print_float x
    end ;;
    class const :
    float -> object method eval : unit -> float method print : unit -> unit end


  3. Define an abstract subclass bin_op of expr_ar implementing methods eval and print using two new abstract methods oper, of type (float * float) -> float (used by eval) and symbol of type string (used by print).

    # class virtual bin_op g d =
    object (this)
    inherit expr_ar ()
    val fg = g
    val fd = d
    method virtual symbol : string
    method virtual oper : float * float -> float
    method eval () =
    let x = fg#eval()
    and y = fd#eval() in
    this#oper(x,y)
    method print () =
    fg#print () ;
    print_string (this#symbol) ;
    fd#print ()
    end ;;
    class virtual bin_op :
    (< eval : unit -> float; print : unit -> 'b; .. > as 'a) ->
    (< eval : unit -> float; print : unit -> unit; .. > as 'c) ->
    object
    val fd : 'c
    val fg : 'a
    method eval : unit -> float
    method virtual oper : float * float -> float
    method print : unit -> unit
    method virtual symbol : string
    end


  4. Define concrete classes add and mul as subclasses of bin_op that implement the methods oper and symbol.

    # class add x y =
    object
    inherit bin_op x y
    method symbol = "+"
    method oper(x,y) = x +. y
    end ;;
    class add :
    (< eval : unit -> float; print : unit -> 'b; .. > as 'a) ->
    (< eval : unit -> float; print : unit -> unit; .. > as 'c) ->
    object
    val fd : 'c
    val fg : 'a
    method eval : unit -> float
    method oper : float * float -> float
    method print : unit -> unit
    method symbol : string
    end

    # class mul x y =
    object
    inherit bin_op x y
    method symbol = "*"
    method oper(x,y) = x *. y
    end ;;
    class mul :
    (< eval : unit -> float; print : unit -> 'b; .. > as 'a) ->
    (< eval : unit -> float; print : unit -> unit; .. > as 'c) ->
    object
    val fd : 'c
    val fg : 'a
    method eval : unit -> float
    method oper : float * float -> float
    method print : unit -> unit
    method symbol : string
    end


  5. Draw the inheritance tree.

  6. Write a function that takes a sequence of Genlex.token, and constructs an object of type expr_ar.

    # open Genlex ;;
    # exception Found of expr_ar ;;
    exception Found of expr_ar

    # let rec create accu l =
    let r = match Stream.next l with
    Float f -> new constant f
    | Int i -> ( new constant (float i) :> expr_ar)
    | Kwd k ->
    let v1 = accu#top() in accu#pop();
    let v2 = accu#top() in accu#pop();
    ( match k with
    "+" -> ( new add v2 v1 :> expr_ar)
    | "*" -> ( new mul v2 v1 :> expr_ar)
    | ";" -> raise (Found (accu#top()))
    | _ -> failwith "aux : bad keyword" )
    | _ -> failwith "aux : bad case"
    in
    create (accu#push (r :> expr_ar); accu) l ;;
    val create :
    < pop : unit -> 'a; push : expr_ar -> 'b; top : unit -> expr_ar; .. > ->
    Genlex.token Stream.t -> 'c = <fun>

    # let gl = Genlex.make_lexer ["+"; "*"; ";"] ;;
    val gl : char Stream.t -> Genlex.token Stream.t = <fun>

    # let run () =
    let s = Stream.of_channel stdin in
    create (new pstack ()) (gl s) ;;
    val run : unit -> 'a = <fun>


  7. Test this program by reading the standard input using the generic lexical analyzer Genlex. You can enter the expressions in post-fix form.

The Game of Life and Objects.

Define the following two classes:
  • cell : for the cells of the world, with the method isAlive : unit -> bool
  • world : with an array of cell, and the messages:
    display : unit -> unit
    nextGen : unit -> unit
    setCell : int * int -> cell -> unit
    getCell : int * int -> cell
    
  1. Write the class cell.

    # class cell a =
    object
    val mutable v = (a : bool)
    method isAlive = v
    end ;;
    class cell : bool -> object val mutable v : bool method isAlive : bool end


  2. Write an abstract class absWorld that implements the abstract methods display, getCell and setCell. Leave the method nextGen abstract.

    # class virtual absWorld n m =
    object(self)
    val mutable tcell = Array.create_matrix n m (new cell false)
    val maxx = n
    val maxy = m
    val mutable gen = 0
    method private draw(c) =
    if c#isAlive then print_string "*"
    else print_string "."
    method display() =
    for i = 0 to (maxx-1) do
    for j=0 to (maxy -1) do
    print_string " " ;
    self#draw(tcell.(i).(j))
    done ;
    print_newline()
    done
    method getCell(i,j) = tcell.(i).(j)
    method setCell(i,j,c) = tcell.(i).(j) <- c
    method getCells = tcell
    end ;;
    class virtual absWorld :
    int ->
    int ->
    object
    val mutable gen : int
    val maxx : int
    val maxy : int
    val mutable tcell : cell array array
    method display : unit -> unit
    method private draw : cell -> unit
    method getCell : int * int -> cell
    method getCells : cell array array
    method setCell : int * int * cell -> unit
    end


  3. Write the class world, a subclass of absWorld, that implements the method nextGen according to the growth rules.

    # class world n m =
    object(self)
    inherit absWorld n m
    method neighbors(x,y) =
    let r = ref 0 in
    for i=x-1 to x+1 do
    let k = (i+maxx) mod maxx in
    for j=y-1 to y+1 do
    let l = (j + maxy) mod maxy in
    if tcell.(k).(l)#isAlive then incr r
    done
    done;
    if tcell.(x).(y)#isAlive then decr r ;
    !r

    method nextGen() =
    let w2 = new world maxx maxy in
    for i=0 to maxx-1 do
    for j=0 to maxy -1 do
    let n = self#neighbors(i,j) in
    if tcell.(i).(j)#isAlive
    then (if (n = 2) || (n = 3) then w2#setCell(i,j,new cell true))
    else (if n = 3 then w2#setCell(i,j,new cell true))
    done
    done ;
    tcell <- w2#getCells ;
    gen <- gen + 1
    end ;;
    class world :
    int ->
    int ->
    object
    val mutable gen : int
    val maxx : int
    val maxy : int
    val mutable tcell : cell array array
    method display : unit -> unit
    method private draw : cell -> unit
    method getCell : int * int -> cell
    method getCells : cell array array
    method neighbors : int * int -> int
    method nextGen : unit -> unit
    method setCell : int * int * cell -> unit
    end


  4. Write the main program which creates an empty world, adds some cells, and then enters an interactive loop that iterates displaying the world, waiting for an interaction and computing the next generation.

    # exception The_end;;
    exception The_end

    # let main () =
    let a = 10 and b = 12 in
    let w = new world a b in
    w#setCell(4,4,new cell true) ;
    w#setCell(4,5,new cell true) ;
    w#setCell(4,6,new cell true) ;
    try
    while true do
    w#display() ;
    if ((read_line()) = "F") then raise The_end else w#nextGen()
    done
    with The_end -> () ;;
    val main : unit -> unit = <fun>

Previous Contents Next ocaml-book-1.0/en/html/book-ora190.html0000644000000000000000000000315207453055401014466 0ustar Summary Previous Contents Next

Summary

This chapter presented the new possibilities offered by distributed programming. Communication between programs is accomplished with the fundamental mechanism of sockets, used by low-level Internet protocols. The action models used by clients and servers are asymmetric. Communication between clients and servers use some notion of protocol, most often using plain text. Functional programming and object-oriented programming allow us to easily build distributed applications. The client-server model lends itself to different software architectures, with two or three tiers, according to the distribution of tasks between them.


Previous Contents Next ocaml-book-1.0/en/html/book-ora061.html0000644000000000000000000001442607453055400014470 0ustar Presentation of part II Previous Contents Next

Presentation of part II

We describe the set of elements of the environment included in the language distribution. There one finds different compilers, numerous libraries, program analysis tools, lexical and syntactic analysis tools, and an interface with the C language.

Objective CAML is a compiled language offering two types of code generation:
  1. bytecode to be executed by a virtual machine;
  2. native code to be executed directly by a microprocessor.
The Objective CAML toplevel uses bytecode to execute the phrases submitted to it. It constitutes the primary development aid, offering the possibility of rapid typing, compilation and testing of function definitions. Moreover, it offers a trace mechanism visualizing parameter values and return values of functions.

The other usual development tools are supplied by the distribution as well: file dependency computation, debugging and profiling. The debugger allows one to execute programs step-by-step, use breakpoints and inspect values. The profiling tool gives measurements of the number of calls or the amount of time spent in a particular function or a particular part of the code. These two tools are only available for Unix platforms.

The richness of a language derives from its core but also from the libraries, sets of reusable programs, which come with it. Objective CAML is no exception to the rule. We have already portrayed to a large extent the graphical library that comes with the distribution. There are many others which we will describe. Libraries bring new functionality to the language, but they are not without drawbacks. In particular, they can present some difficulty vis-a-vis the type discipline.

However rich a language's set of libraries may be, it will always be necessary that it be able to communicate with another language. The Objective CAML distribution includes an interface with the C language allowing Objective CAML to call C functions or be called by them. The difficulty of understanding and implementing this interface lies in the fact that the memory models of Objective CAML and C are different. The essential reason for this difference is that an Objective CAML program includes a garbage collection mechanism.

C as well as Objective CAML allow dynamic memory allocation, and thus fine control over space according to the needs of a program. This only makes sense if unused space can be reclaimed for other use during the course of execution. Garbage collection frees the programmer from responsibility for managing deallocation, a frequent source of execution errors. This feature constitutes one of the safety elements of the Objective CAML language.

However, this mechanism has an impact on the representation of data. Also, knowledge of the guiding principles of memory management is indispensable in order to use communication between the Objective CAML world and the C world correctly.

Chapter 7 presents the basic elements of the Objective CAML system: virtual machine, compilers, and execution library. It describes the language's different compilation modes and compares their portability and efficiency.

Chapter 8 gives a bird's-eye view of the set of predefined types, functions, and exceptions that come with the system distribution. It does not do away with the need to read the reference manual ([LRVD99]) which describes these libraries very well. On the contrary it focuses on the new functionalities supplied by some of them. In particular we may mention output formatting, persistence of values and interfacing with the operating system.

Chapter 9 presents different garbage collection methods in order to then describe the mechanism used by Objective CAML.

Chapter 10 presents debugging tools for Objective CAML programs. Although still somewhat frustrating in some respects, these tools quite often allow one to understand why a program does not work.

Chapter 11 describes the language's different approaches to lexical and syntactic analysis problems: a regular expression library, the ocamlex and ocamlyacc tools, but also the use of streams.

Chapter 12 describes the interface with the C language. It is no longer possible for a language to be completely isolated from other languages. This interface lets an Objective CAML program call a C function, while passing it values from the Objective CAML world, and vice-versa. The main difficulty with this interface stems from the memory model. For this reason it is recommended that you read the 9 chapter beforehand.

Chapter 13 covers two applications: an improved graphics library based on a hierarchical model of graphical components inspired by the JAVA AWT2; and a classic program to find least-cost paths in a graph using our new graphical interface as well as a cache memory mechanism.




Previous Contents Next ocaml-book-1.0/en/html/book-ora045.html0000644000000000000000000000336307453055377014505 0ustar Chapter overview Previous Contents Next

Chapter overview

The first section explains how to make use of this library on different systems. The second section introduces the basic notions of graphics programming: reference point, plotting, filling, colors, bitmaps. The third section illustrates these concepts by describing and implementing functions for creating and drawing ``boxes.'' The fourth section demonstrates the animation of graphical objects and their interaction with the background of the screen or other animated objects. The fifth section presents event-driven programming, in other terms the skeleton of all graphical interfaces. Finally, the last section uses the library Graphics to construct a graphical interface for a calculator (see page ??).


Previous Contents Next ocaml-book-1.0/en/html/book-ora027.gif0000644000000000000000000000376307452056111014274 0ustar GIF89aUUUU999rrr!,Uh0I8ͻ`di)lp,t|NG,HpL:ШTMجUt8贚x\ԧECe$}{S}H} d8 ,T K$ž Ģ%T Aт6Tɿͺ @7ςD*#*gd(a*| &j$f DAY_Goaʉp$rݳsV4hWBd ,`CUMM!.WK#+DA,Lұx-)wNlq(Wm,;qZ$ IJ=%(@MU'Nr860;X˖6esҜG{}EtЮc{Rj͛Ϳ-ď+_~!硙KNسkνËOӫ_Ͼg~$ΰ_0`_ *`22@]6h0asƅW0~U⊼t",xO*0d!V 9`bAȂt@Kj@"&pH/8kf̰H"X'az"dM6ѤiJEIN"=Iˆw8x6~!V^5 H2Vt$iE@,%Y~3eꉈ4*dFTЖ&V85 @e6L ])Y%!\A3udDe$FI~ *sV;#Cs=D}W֪Sm;=VH=?B/gͤT*:$Tύ&MȔKeÓK3\R(Ы+.b2*UL3\p[^З*UqPts{5 8[T$,fB%lI8X;2M:1D4ŅN4pk{5泞ܥqh6=)J!0.yfx OyʈaCy ~:ߦ7ŬLye(۱l~>^{x Wz-{?ߞ}Kogw/o觯>Է:/~ ApcED&-4M[0 %']PRP/ͮYU% "gvpcx:,PG k{)A*lưL"sIde)JTwB 'P{Ll6dG&5UO1nEa @uSΊ@KPgz%hCM!SVdŠ_`Ԥf3NyCE*WآEBȤhByW`8M XqW5g! Y&K&T.^'H!E7P孅,YnMe*Gdb-g"i5Gn{U+)dT?fT@,(*%¥ڶ]=#Z\wnحCq̳BAgo+.> RJ Lc<u:;pq0. S^03 s8ŒtV'i9މGl;x9)6ꋏ3͘vKyT1-!!F~jP_;ocaml-book-1.0/en/html/book-ora122.html0000644000000000000000000000413307453055401014461 0ustar Notes
1
Objective CAML's sum types are discriminated unions. Refer to chapter 2, page ?? for a full description.
2
Linking is performed differently for the bytecode compiler and the native-code compiler.
3
Recall that a function such as fst, of type 'a * 'b -> 'a, does not have two arguments, but only one that happens to be a pair; on the other hand, a function of type int -> int -> int has two arguments.
4
Under Unix, this directory is /usr/local/lib/ocaml by default, or sometimes /usr/lib/ocaml. Under Windows, the default location is C:\OCAML\LIB, or the value of the environment variable CAMLLIB, if set.
5
More precisely, by their ISO Latin-1 code, which is an 8-bit character encoding extending ASCII with accented letters and signs for Western languages. Objective CAML does not yet handle wider internationalized character sets such as Unicode.
6
Here, the tag bit is the least significant bit.
ocaml-book-1.0/en/html/book-ora013.html0000644000000000000000000001052007453055377014471 0ustar Introduction Previous Contents Next

Introduction

The first functional language, Lisp, appeared at the end of the 1950's. That is, at the same time as Fortran, the first representative of the imperative languages. These two languages still exist, although both have evolved greatly. They are used widely for numerical programming (in the case of Fortran) and symbolic applications in the case of Lisp. Interest in functional programming arises from the great ease of writing programs and specifying the values which they manipulate. A program is a function applied to its arguments. It computes a result which is returned (when the computation terminates) as the output of the program. In this way it becomes easy to combine programs: the output of one program becomes an input argument to another, in the sense of function composition.

Functional programming is based on a simple computation model with three constructions: variables, function definitions, and applications of a function to an argument. This model is called the l-calculus and it was introduced by Alonzo Church in 1932, thus before the first computer. It was created to offer a general theoretical model of the notion of computability. In the l-calculus, all functions are values which can be manipulated. They can be used as arguments to other functions, or returned as the result of a call to another function. The theory of l-calculus asserts that everything which is computable (i.e., programmable) can be written in this formalism. Its syntax is too limited to make its use as a programming language practical, so primitive values (such as integers or character strings), operations on these primitive values, control structures, and declarations which allow the naming of values or functions and, in particular, recursive functions, have all been added to the l-calculus to make it more palatable.

There are several classifications of functional languages. For our part, we will distinguish them according to two characteristics which seem to us most salient:
  • Without side effects (pure) or with side effects (impure): a pure functional language is a language in which there is no change of state. There everything is simply a computation and the way it is carried out is unimportant. Impure functional languages, such as Lisp or ML, integrate imperative traits such as change of state. They permit the writing of algorithms in a style closer to languages like Fortran, where the order of evaluation of expressions is significant.

  • Dynamically typed or statically typed: typing permits verification of whether an argument passed to a function is indeed of the type of the function's formal parameter. This verification can be made during program execution. In that case this verification is called dynamic typing. If type errors occur the program will halt in a consistent state. This is the case in the language Lisp. This verification can also be done before program execution, that is, at compilation time. This a priori verification is called static typing. Having been carried out once and for all, it won't slow down program execution. This is the case in the ML language and its dialects such as Objective CAML. Only correctly typed programs, i.e., those accepted by the type verifier, will be able to be compiled and then executed.

Previous Contents Next ocaml-book-1.0/en/html/book-ora011.html0000644000000000000000000000161207453055401014455 0ustar Notes
1
``Free software'' is not to be confused with ``freeware''. ``Freeware'' is software which costs nothing, whereas ``free software'' is software whose source is also freely available. In the present case, all the programs used cost nothing and their source is available.
ocaml-book-1.0/en/html/book-ora046.html0000644000000000000000000000446207453055377014507 0ustar Using the Graphics Module Previous Contents Next

Using the Graphics Module

Utilization of the library Graphics differs depending on the system and the compilation mode used. We will not cover applications other than usable under the interactive toplevel of Objective CAML. Under the Windows and MacOS systems the interactive working environment already preloads this library. To make it available under Unix, it is necessary to create a new toplevel. This depends on the location of the X11 library. If this library is placed in one of the usual search paths for C language libraries, the command line is the following:
ocamlmktop -custom -o mytoplevel graphics.cma -cclib -lX11
It generates a new executablemytoplevel into which the library Graphics is integrated. Starting the executable works as follows:
./mytoplevel
If, however, as under Linux, the library X11 is placed in another directory, this has to be indicated to the command ocamlmktop:
ocamlmktop -custom -o mytoplevel graphics.cma -cclib \ 
           -L/usr/X11/lib -cclib -lX11
In this example, the file libX11.a is searched in the directory /usr/X11/lib.

A complete description of the command ocamlmktop can be found in chapter 7.


Previous Contents Next ocaml-book-1.0/en/html/book-ora154.html0000644000000000000000000006476307453055400014504 0ustar Extending Components Previous Contents Next

Extending Components

We call a collection of data and methods on the data a component. In the functional/modular model, a component consists of the definition of a type and some functions which manipulate the type. Similarly a component in the object model consists of a hierarchy of classes, inheriting from one (single) class and therefore having all of its behaviors. The problem of the extensibility of components consists of wanting on the one hand to extend the behaviors and on the other to extend the data operated on, and all this without modifying the initial program sources. For example a component image can be either a rectangle or a circle which one can draw or move.

  rectangle circle group
draw X X  
move X X  
grow      

We might wish to extend the image component with the method grow and create groups of images. The behavior of the two models differs depending on the direction of the extension: data or methods. First we define, in each model, the common part of the image component, and then we try to extend it.

In the Functional Model

We define the type image as a variant type which contains two cases. The methods take a parameter of type image and carry out the required action.

# type image = Rect of float | Circle of float ;;
# let draw = function Rect r -> ... | Circle c -> ... ;;
# let move = ... ;;


Afterwards, we could encapsulate these global declarations in a simple module.

Extension of Methods

The extension of the methods depends on the representation of the type image in the module. If this type is abstract, it is no longer possible to extend the methods. In the case where the type remains concrete, it is easy to add a grow function which changes the scale of an image by choosing a rectangle or a circle by pattern matching.

Extension of Data Types

The extension of data types cannot be achieved with the type image. In fact Objective CAML types are not extensible, except in the case of the type exn which represents exceptions. It is not possible to extend data while keeping the same type, therefore it is necessary to define a new type n_image in the following way:
type n_image = I of image | G of n_image * n_image;;
Thus we should redefine the methods for this new type, simulating a kind of inheritance. This becomes complex when there are many extensions.

In the Object Model

We define the classes rectangle and circle, subclasses of the abstract class image which has two abstract methods, draw and move.

# class virtual image () =
object(self:'a)
method virtual draw : unit -> unit
method virtual move : float * float -> unit
end;;
# class rectangle x y w h =
object
inherit image ()
val mutable x = x
val mutable y = y
val mutable w = w
val mutable h = h
method draw () = Printf.printf "R: (%f,%f) [%f,%f]" x y w h
method move (dx,dy) = x <- x +. dx; y <- y +. dy
end;;
# class circle x y r =
object
val mutable x = x
val mutable y = y
val mutable r = r
method draw () = Printf.printf "C: (%f,%f) [%f]" x y r
method move (dx, dy) = x <- x +. dx; y <- y +. dy
end;;


The following program constructs a list of images and displays it.

# let r = new rectangle 1. 1. 3. 4.;;
val r : rectangle = <obj>
# let c = new circle 1. 1. 4.;;
val c : circle = <obj>
# let l = [ (r :> image); (c :> image)];;
val l : image list = [<obj>; <obj>]
# List.iter (fun x -> x#draw(); print_newline()) l;;
R: (1.000000,1.000000) [3.000000,4.000000]
C: (1.000000,1.000000) [4.000000]
- : unit = ()


Extension of Data Types

The data are easily extended by adding a subclass of the class image in the following way.

# class group i1 i2 =
object
val i1 = (i1:#image)
val i2 = (i2:#image)
method draw () = i1#draw(); print_newline (); i2#draw()
method move p = i1#move p; i2#move p
end;;


We notice now that the ``type'' image becomes recursive because the class group depends outside inheritance on the class image.

# let g = new group (r:>image) (c:>image);;
val g : group = <obj>
# g#draw();;
R: (1.000000,1.000000) [3.000000,4.000000]
C: (1.000000,1.000000) [4.000000]- : unit = ()


Extension of Methods

We define an abstract subclass of image which contains a new method.

# class virtual e_image () =
object
inherit image ()
method virtual surface : unit -> float
end;;


We can define classes e_rectangle and e_circle which inherit from e_image and from rectangle and circle respectively. We can then work on extended image to use this new method. There is a remaining difficulty with the class group. This contains two fields of type image, so even when inheriting from the class e_image it will not be possible to send the grow message to the image fields. It is thus possible to extend the methods, except in the case of subclasses corresponding to recursive types.

Extension of Data and Methods

To implement extension in both ways, it is necessary to define recursive types in the for of a parameterized class. We redefine the class group.

# class ['a] group i1 i2 =
object
val i1 = (i1:'a)
val i2 = (i2:'a)
method draw () = i1#draw(); i2#draw()
method move p = i1#move p; i2#move p
end;;


We then carry on the same principle for the class e_image.

# class virtual ext_image () =
object
inherit image ()
method virtual surface : unit -> float
end;;
# class ext_rectangle x y w h =
object
inherit ext_image ()
inherit rectangle x y w h
method surface () = w *. h
end;;
# class ext_circle x y r=
object
inherit ext_image ()
inherit circle x y r
method surface () = 3.14 *. r *.r
end;;


The extension of the class group thus becomes

# class ['a] ext_group ei1 ei2 =
object
inherit image()
inherit ['a] group ei1 ei2
method surface () = ei1#surface() +. ei2#surface ()
end;;


We get the following program which constructs a list le of the type ext_image.

# let er = new ext_rectangle 1. 1. 2. 4. ;;
val er : ext_rectangle = <obj>
# let ec = new ext_circle 1. 1. 8.;;
val ec : ext_circle = <obj>
# let eg = new ext_group er ec;;
val eg : ext_rectangle ext_group = <obj>
# let le = [ (er:>ext_image); (ec :> ext_image); (eg :> ext_image)];;
val le : ext_image list = [<obj>; <obj>; <obj>]
# List.map (fun x -> x#surface()) le;;
- : float list = [8; 200.96; 208.96]


Generalization

To generalize the extension of the methods it is preferable to integrate some functions in a method handler and to construct a parameterized class with the return type of the method. For this we define the following class:

# class virtual ['a] get_image (f: 'b -> unit -> 'a) =
object(self:'b)
inherit image ()
method handler () = f(self) ()
end;;


The following classes then possess an additional functional parameter for the construction of their instances.

# class ['a] get_rectangle f x y w h =
object(self:'b)
inherit ['a] get_image f
inherit rectangle x y w h
method get = (x,y,w,h)
end;;
# class ['a] get_circle f x y r=
object(self:'b)
inherit ['a] get_image f
inherit circle x y r
method get = (x,y,r)
end;;


The extension of the class group thus takes two type parameters:

# class ['a,'c] get_group f eti1 eti2 =
object
inherit ['a] get_image f
inherit ['c] group eti1 eti2
method get = (i1,i2)
end;;


We get the program which extends the method of the instance of get_image.

# let etr = new get_rectangle
(fun r () -> let (x,y,w,h) = r#get in w *. h) 1. 1. 2. 4. ;;
val etr : float get_rectangle = <obj>
# let etc = new get_circle
(fun c () -> let (x,y,r) = c#get in 3.14 *. r *. r) 1. 1. 8.;;
val etc : float get_circle = <obj>
# let etg = new get_group
(fun g () -> let (i1,i2) = g#get in i1#handler() +. i2#handler())
(etr :> float get_image) (etc :> float get_image);;
val etg : (float, float get_image) get_group = <obj>
# let gel = [ (etr :> float get_image) ; (etc :> float get_image) ;
(etg :> float get_image) ];;
val gel : float get_image list = [<obj>; <obj>; <obj>]
# List.map (fun x -> x#handler()) gel;;
- : float list = [8; 200.96; 208.96]


The extension of data and methods is easier in the object model when it is combined with the functional model.


Previous Contents Next ocaml-book-1.0/en/html/book-ora078.gif0000644000000000000000000000512707452056112014277 0ustar GIF89aUUU!,ڋ޼扦ʶ L L*'ť J/*ڮ nT䲙xNן1 7rm#}WHXUt5x٥( X9et)9Y*1:k!{k{T L 8LL,̜a Mp\=x}ˍM]M^m^(d̾~=:ȏgشM$tVbQyX(f`|X5HrT l2R5 mN@V$ש6H1lёIu-ՔJV\Fz[T^a(,j\rC(h櫪P,J%_J|XRS}ٸ,b!pND؉f(AW/ Kg9"gޜa%)qdz@y)g`wgǾ[.h#"6xkU>Dw#} j)cu% V݁W[: cUń!HQ'xbL>Йv[D!mO Vi# 6gZ[` gq 4 cCH(e @I]4$ߒ!jIlUiA$+4`"]&(e+fx2x[\rf'XO%c3tm1 ԍ,@`jo(M: z@(VY~"J zyh I)녶v]eDkف67-G+R8f t *`퍖w6hgi݅j "߶]ء{Fn*;1iX6> 0 moƉq@d,$,34c,!dP@K&t6uZQϋJ\5s`KkCT8=4y^ly/3훂38C7ҡ& hF더:mBw9jFnF&= _.N_ӭ31C.99zkҔ%/w48Ԑ}!{l;3* yTݱl۞0hH$JLPpL/^=Rэ!)t #8O1 uCf#E߈[jxV6a|xB!>"\=4}#Pd\נ%^1NW-+HLbHH.1Ye5~zhG{XA@E#!wH*$ux"]1Ѣ$,?:qaL8:|!(a"Jr\\SϐLM`/Z:Җe3Г"/ KSœb@|)h K"&0LPcd6[BDll 47R1<' Y9<qM^'IzӚ IVҝ4Lh dC)jƃbT%#C@ߘ 鷊QhZ7Z83Zi*%LJ2SSq{;(mG"ΑMA}UMIVb5F^gd<~ Z%5RB)Qգ]\RNF+_MW4pNTj-(6JXu3~,KYZ:jd}<.Td Zub_(S1fuAlk vjnVpo9J[sDgQڹ\&G+pL]һڵx@4e$PZ*4o{8r/j[7]|/`oc^7D ~߃#U T۝pkm~d;)ɋ#}M &N^䑍 ce70 됑]'X2UZS;$KMǥܷF&\+eҗ73g{-م5t\5;3 A;sS??Y\$ [D#V=z-Jc:t<[N 5Ii [ԋE$]- zִo\^5C~43{ ;˦pl<;v̴[^ QAC9=BjKaҨ *`n0YbZM`IneQbdviETWr6Y=q 5N%A5OJCD (ڎ)gkG>&(֦`8PF0-onCSG'D c8zLsǥ#SА,GI/޼Ȥǫq\33k1ߖ,WfĦ1a2ܮg*L!.E`kmG>g-Y9qD {-'h gj\7Xx尦!vjѬP=ISTAS/sLY^si|v>pm]Wc3?3Qd?}2_֢ O~_ph)r Ȁ {bx;ocaml-book-1.0/en/html/book-ora115.html0000644000000000000000000011340407453055400014464 0ustar Exploring Objective CAML values from C Previous Contents Next

Exploring Objective CAML values from C

The machine representation of Objective CAML values differs from that of C values, even for fundamental types such as integers. This is because the Objective CAML garbage collector needs to record additional information in values. Since Objective CAML values are represented uniformly, their representations all belong to the same C type, named (unsurprisingly) value.

When Objective CAML calls a C function, passing it one or several arguments, those arguments must be decoded before using them in the C function. Similarly, the result of this C function must be encoded before being returned to Objective CAML.

These conversions (decoding and encoding) are performed by a number of macros and C functions provided by the Objective CAML runtime system. These macros and functions are declared in the include files listed in figure 12.3. These include files are part of the Objective CAML installation, and can be found in the directory where Objective CAML libraries are installed4


   
caml/mlvalues.h definition of the value type and basic value conversion macros.
caml/alloc.h functions for allocating Objective CAML values.
caml/memory.h macros for interfacing with the Objective CAML garbage collector.

Figure 12.3: Include files for the C interface.


Classification of Objective CAML representations

An Objective CAML representation, that is, a C datum of type value, is one of:
  • an immediate value (represented as an integer);
  • a pointer into the Objective CAML heap;
  • a pointer pointing outside the Objective CAML heap.
The Objective CAML heap is the memory area that is managed by the Objective CAML garbage collector. C code can also allocate and manipulate data structures in its own memory space, and communicate pointers to these data structures to Objective CAML.

Figure 12.4 shows the macros for classifying representations and converting between C integers and their Objective CAML representation.

   
Is_long(v) is v an Objective CAML integer?
Is_block(v) is v an Objective CAML pointer?
   
Long_val(v) extract the integer contained in v, as a C "long"
Int_val(v) extract the integer contained in v, as a C "int"
Bool_val(v) extract the boolean contained in v (0 if false, non-zero if true)

Figure 12.4: Classification of representations and conversion of immediate values.


Note that C offers several integer types of varying sizes (short, int, long, etc), while Objective CAML has only one integer type, int.

Accessing immediate values

All Objective CAML immediate values are represented as integers:
  • integers are represented by their value;
  • characters are represented by their ASCII code5;
  • constant constructors are represented by an integer corresponding to their position in the datatype declaration: the nth constant constructor of a datatype is represented by the integer n-1.
The following program defines a C function inspect that inspects the representation of its argument:
#include <stdio.h>
#include <caml/mlvalues.h>
value inspect (value v)
{
if (Is_long(v))
printf ("v is an integer (%ld) : %ld", (long) v, Long_val(v));
else if (Is_block(v))
printf ("v is a pointer");
else
printf ("v is neither an integer nor a pointer (???)");
printf(" ");
fflush(stdout) ;
return v ;
}
The function inspect tests whether its argument is an Objective CAML integer. If so, it prints the integer twice, first viewed as a C long integer (without conversion), then converted by the Long_val macro, which extracts the actual integer represented in the argument.

On the following example, we see that the machine representation of integers in Objective CAML differs from that of C:

# external inspect : 'a -> 'a = "inspect" ;;
external inspect : 'a -> 'a = "inspect"
# inspect 123 ;;
v is an integer (247) : 123 - : int = 123
# inspect max_int;;
v is an integer (2147483647) : 1073741823 - : int = 1073741823
We can also inspect values of other predefined types, such as char and bool:

# inspect 'A' ;;
v is an integer (131) : 65 - : char = 'A'
# inspect true ;;
v is an integer (3) : 1 - : bool = true
# inspect false ;;
v is an integer (1) : 0 - : bool = false
# inspect [] ;;
v is an integer (1) : 0 - : '_a list = []


Consider the Objective CAML type foo defined thus:

# type foo = C1 | C2 of int | C3 | C4 ;;


The inspect function shows that constant constructors and non-constant constructors of this type are represented differently:

# inspect C1 ;;
v is an integer (1) : 0 - : foo = C1
# inspect C4 ;;
v is an integer (5) : 2 - : foo = C4
# inspect (C2 1) ;;
v is a pointer - : foo = C2 1


When the function inspect detects an immediate value, it prints first the ``physical'' representation of this value (i.e. the representation viewed as a word-sized C integer of C type long); then it prints the ``logical'' contents of this value (i.e. the Objective CAML integer it represents, as returned by the decoding macro Long_val). The examples above show that the ``physical'' and the ``logical'' contents differ. This difference is due to the tag bit6 used by the garbage collector to distinguish immediate values from pointers (see chapter 9, page ??).

Representation of structured values

Non-immediate Objective CAML values are said to be structured values. Those values are allocated in the Objective CAML heap and represented as a pointer to the corresponding memory block. All memory blocks contain a header word indicating the kind of the block as well as its size expressed in machine words. Figure 12.5 shows the structure of a block for a 32-bit machine.


Figure 12.5: Structure of an Objective CAML heap block.


The two ``color'' bits are used by the garbage collector for walking the memory graph (see chapter 9, page ??). The ``tag'' field, or ``tag'' for short, contains the kind of the block. The ``size'' field contains the size of the block, in words, excluding the header. The macros listed in figure 12.6 return the tag and size of a block.

   
Wosize_val(v) return the size of the block v (header excluded)
Tag_val(v) return the tag of the block v

Figure 12.6: Accessing header information in memory blocks.


The tag of a memory block can take the values listed in figure 12.7.

from 0 to No_scan_tag-1 an array of Objective CAML value representations
Closure_tag a function closure
String_tag a character string
Double_tag a double-precision float
Double_array_tag an array of float
Abstract_tag an abstract data type
Final_tag an abstract data type equipped with a finalization function

Figure 12.7: Tags of memory blocks.


Depending on the block tag, different macros are used to access the contents of the blocks. These macros are described in figure 12.8. When the tag is less than No_scan_tag, the heap block is structured as an array of Objective CAML value representations. Each element of the array is called a ``field'' of the memory block. In accordance with C and Objective CAML conventions, the first field is at index 0, and the last field is at index Wosize_val(v) - 1.

   
Field(v,n) return the nth field of v.
Code_val(v) return the code pointer for a closure.
string_length(v) return the length of a string.
Byte(v,n) return the n th character of a string, with C type char.
Byte_u(v,n) same, but result has C type unsigned char.
String_val(v) return the contents of a string with C type (char *).
Double_val(v) return the float contained in v.
Double_field(v,n) return the n th float contained in the float array v.

Figure 12.8: Accessing the content of a memory block.


As we did earlier for immediate values, we now define a function to inspect memory blocks. The C function print_block takes an Objective CAML value representation, tests whether it is an immediate value or a memory block, and in the latter case prints the kind and contents of the block. It is called from the wrapper function inspect_block, which can be called from Objective CAML.

#include <stdio.h>
#include <caml/mlvalues.h>

void margin (int n)
{ while (n-- > 0) printf("."); return; }

void print_block (value v,int m)
{
int size, i;
margin(m);
if (Is_long(v))
{ printf("immediate value (%d)\n", Long_val(v)); return; };
printf ("memory block: size=%d - ", size=Wosize_val(v));
switch (Tag_val(v))
{
case Closure_tag :
printf("closure with %d free variables\n", size-1);
margin(m+4); printf("code pointer: %p\n",Code_val(v)) ;
for (i=1;i<size;i++) print_block(Field(v,i), m+4);
break;
case String_tag :
printf("string: %s (%s)\n", String_val(v),(char *) v);
break;
case Double_tag:
printf("float: %g\n", Double_val(v));
break;
case Double_array_tag :
printf ("float array: ");
for (i=0;i<size/Double_wosize;i++) printf(" %g", Double_field(v,i));
printf("\n");
break;
case Abstract_tag : printf("abstract type\n"); break;
case Final_tag : printf("abstract finalized type\n"); break;
default:
if (Tag_val(v)>=No_scan_tag) { printf("unknown tag"); break; };
printf("structured block (tag=%d):\n",Tag_val(v));
for (i=0;i<size;i++) print_block(Field(v,i),m+4);
}
return ;
}

value inspect_block (value v)
{ print_block(v,4); fflush(stdout); return v; }

Each possible tag for a block corresponds to a case of the switch construct. In the case of a block containing an array of Objective CAML values, we recursively call print_block on each field of the array. We then redefine the inspect function:

# external inspect : 'a -> 'a = "inspect_block" ;;
external inspect : 'a -> 'a = "inspect_block"
We can now explore the representations of Objective CAML structured values. We must be careful not to apply inspect_block to a cyclic value, since the recursive traversal of the value would then loop indefinitely.

Arrays, tuples, and records

Arrays and tuples are represented by structured blocks. The nth field of the block contains the representation of the nth element of the array or tuple.

# inspect [| 1; 2; 3 |] ;;
....memory block: size=3 - structured block (tag=0):
........immediate value (1)
........immediate value (2)
........immediate value (3)
- : int array = [|1; 2; 3|]
# inspect ( 10 , true , () ) ;;
....memory block: size=3 - structured block (tag=0):
........immediate value (10)
........immediate value (1)
........immediate value (0)
- : int * bool * unit = 10, true, ()


Records are also represented as structured blocks. The values of the record fields appear in the order given at record declaration time. Mutable fields and immutable fields are represented identically.

# type foo = { fld1: int ; mutable fld2: int } ;;
type foo = { fld1: int; mutable fld2: int }
# inspect { fld1=10 ; fld2=20 } ;;
....memory block: size=2 - structured block (tag=0):
........immediate value (10)
........immediate value (20)
- : foo = {fld1=10; fld2=20}


Warning


Nothing prevents a C function from physically modifying an immutable record field. It is the programmers' responsibility to make sure that their C functions do not introduce inconsistencies in Objective CAML data structures.


Sum types

We previously saw that constant constructors are represented like integers. A non-constant constructor is represented by a block containing the constructor's arguments, with a tag identifying the constructor. The tag associated with a non-constant constructor represents its position in the type declaration: the first non-constant constructor has tag 0, the second one has tag 1, and so on.

# type foo = C1 of int * int * int | C2 of int | C3 | C4 of int * int ;;
type foo = | C1 of int * int * int | C2 of int | C3 | C4 of int * int
# inspect (C1 (1,2,3)) ;;
....memory block: size=3 - structured block (tag=0):
........immediate value (1)
........immediate value (2)
........immediate value (3)
- : foo = C1 (1, 2, 3)
# inspect (C4 (1,2)) ;;
....memory block: size=2 - structured block (tag=2):
........immediate value (1)
........immediate value (2)
- : foo = C4 (1, 2)


Note


The type list is a sum type whose declaration is:
type 'a list = [] | :: of 'a * 'a list. This type has only one non-constant constructor (::). Thus, a non-empty list is represented by a memory block with tag 0.


Character strings

Characters inside strings occupy one byte each. Thus, the memory block representing a string uses one word per group of four characters (on a 32-bit machine) or eight characters (on a 64-bit machine).

Warning


Objective CAML strings can contain the null character whose ASCII code is 0. In C, the null character represents the end of a string, and cannot appear inside a string.


#include <stdio.h>
#include <caml/mlvalues.h>

value explore_string (value v)
{
char *s;
int i,size;
s = (char *) v;
size = Wosize_val(v) * sizeof(value);
for (i=0;i<size;i++)
{
int p = (unsigned int) s[i] ;
if ((p>31) && (p<128)) printf("%c",s[i]); else printf("(#%u)",p);
}
printf("\n");
fflush(stdout);
return v;
}

The length and position of last character of an Objective CAML string are determined not by looking for a terminating null character, as in C, but by combining the size of the memory block that contains the string with the last byte of the last word of this block, which indicates the number of unused bytes in the last word. The following examples clarify the role played by this last byte.

# external explore : string -> string = "explore_string" ;;
external explore : string -> string = "explore_string"
# ignore(explore "");
ignore(explore "a");
ignore(explore "ab");
ignore(explore "abc");
ignore(explore "abcd");
ignore(explore "abcd\000") ;;
(#0)(#0)(#0)(#3)
a(#0)(#0)(#2)
ab(#0)(#1)
abc(#0)
abcd(#0)(#0)(#0)(#3)
abcd(#0)(#0)(#0)(#2)
- : unit = ()
In the last two examples ("abcd" and "abcd\000"), the strings are of length 4 and 5 respectively. This explains why the last byte takes two different values, although the other bytes of the string representations are identical.

Floats and float arrays

Objective CAML offers only one type (float) of floating-point numbers. This type corresponds to 64-bit, double-precision floating point numbers in C (type double). Values of type float are heap-allocated and represented by a memory block of size 2 words (on a 32-bit machine) or 1 word (on a 64-bit machine).

# inspect 1.5 ;;
....memory block: size=2 - float: 1.5
- : float = 1.5
# inspect 0.0;;
....memory block: size=2 - float: 0
- : float = 0


Arrays of floats are represented specially to reduce their memory occupancy: the floats contained in the array are stored consecutively in the memory block, rather than having each float heap-allocated separately. Therefore, float arrays possess a specific tag and specific access macros.

# inspect [| 1.5 ; 2.5 ; 3.5 |] ;;
....memory block: size=6 - float array: 1.5 2.5 3.5
- : float array = [|1.5; 2.5; 3.5|]
This optimized representation encourages the use of Objective CAML for numerical computations that manipulate many float arrays: operations on array elements are much more efficient than if each float was heap-allocated separately.

Warning


When allocating an Objective CAML float array from C, the size of the block should be the number of array elements multiplied by Double_wosize. The Double_wosize macro represents the number of words occupied by a double-precision float (2 words on a 32-bit machine, but only 1 word on a 64-bit machine).


With the exception of float arrays, floating-point numbers contained in other data structures are always treated as a structured, heap-allocated value. The following example shows the representation of a list of floats.

# inspect [ 3.14; 1.2; 7.6];;
....memory block: size=2 - structured block (tag=0):
........memory block: size=2 - float: 3.14
........memory block: size=2 - structured block (tag=0):
............memory block: size=2 - float: 1.2
............memory block: size=2 - structured block (tag=0):
................memory block: size=2 - float: 7.6
................immediate value (0)
- : float list = [3.14; 1.2; 7.6]
The list is viewed as a block with size 2, containing its head and its tail. The head of the list is a float, which is also a block of size 2.

Closures

A function value is represented by the code to be executed when the function is applied, and by its environment (see chapter 2, page ??). There are two ways to build a function value: either by explicit abstraction (as in fun x -> x+1) or by partial application of a curried function (as in (fun x -> fun y -> x+y) 1).

The environment of a closure can contain three kinds of variables: those declared globally, those declared locally, and the function parameters already instantiated by a partial application. The implementation treats those three kinds differently. Global variables are stored in a global environment that is not explicitly part of any closure. Local variables and instantiated parameters can appear in closures, as we now illustrate.

A closure with an empty environment is simply a memory block containing a pointer to the code of the function:

# let f = fun x y z -> x+y+z ;;
val f : int -> int -> int -> int = <fun>
# inspect f ;;
....memory block: size=1 - closure with 0 free variables
........code pointer: 0x807308c
- : int -> int -> int -> int = <fun>
Functions with free local variables are represented by closures with non-empty environments. Here, the closure contains both a pointer to the code of the function, and the values of its free local variables.

# let g = let x = 1 and y = 2 in fun z -> x+y+z ;;
val g : int -> int = <fun>
# inspect g ;;
....memory block: size=3 - closure with 2 free variables
........code pointer: 0x8086450
........immediate value (1)
........immediate value (2)
- : int -> int = <fun>


The Objective CAML virtual machine treats partial applications of functions specially for better performance. A partial application of an abstraction is represented by a closure containing a value for each of the instantiated parameters, plus a pointer to the closure for the initial abstraction.

# let a1 = f 1 ;;
val a1 : int -> int -> int = <fun>
# inspect (a1) ;;
....memory block: size=3 - closure with 2 free variables
........code pointer: 0x8073088
........memory block: size=1 - closure with 0 free variables
............code pointer: 0x807308c
........immediate value (1)
- : int -> int -> int = <fun>
# let a2 = a1 2 ;;
val a2 : int -> int = <fun>
# inspect (a2) ;;
....memory block: size=4 - closure with 3 free variables
........code pointer: 0x8073088
........memory block: size=1 - closure with 0 free variables
............code pointer: 0x807308c
........immediate value (1)
........immediate value (2)
- : int -> int = <fun>
Figure 12.9 depicts the result of the inspection above.


Figure 12.9: Closure representation.


The function f has no free variables, hence the environment part of its closure is empty. The code pointer for a function with several arguments points to the code that should be called when all arguments are provided. In the case of f, this is the code corresponding to x+y+z. Partial applications of this function result in intermediate closures that point to a shared code (it is the same code pointer for a1 and a2). The role of this code is to accumulate the arguments and detect when all arguments have been provided. If so, it pushes all arguments and calls the actual code for the function body; if not, it creates a new closure. For instance, the application of a1 to 2 fails to provide all arguments to the function f (the last argument is still missing), hence a closure is created containing the first two arguments, 1 and 2. Notice that the closures resulting from partial applications always contain, in the first environment slot, a pointer to the original closure. The original closure will be called when all arguments have been gathered.

Mixing local declarations and partial applications results in the following representation:

# let g x = let y=2 in fun z -> x+y+z ;;
val g : int -> int -> int = <fun>
# let a1 = g 1 ;;
val a1 : int -> int = <fun>
# inspect a1 ;;
....memory block: size=3 - closure with 2 free variables
........code pointer: 0x8086548
........immediate value (1)
........immediate value (2)
- : int -> int = <fun>


Abstract types

Values of an abstract type are represented like those of its implementation type. Actually, type information is used only during type-checking and compilation. During execution, the types are not needed -- only the memory representation (tag bits on values, size and tag fields on memory blocks) needs to be communicated to the garbage collector.

For instance, a value of the abstract type 'a Stack.t is represented as a reference to a list, since the type 'a Stack.t is implemented as 'a list ref.

# let p = Stack.create();;
val p : '_a Stack.t = <abstr>
# Stack.push 3 p;;
- : unit = ()
# inspect p;;
....memory block: size=1 - structured block (tag=0):
........memory block: size=2 - structured block (tag=0):
............immediate value (3)
............immediate value (0)
- : int Stack.t = <abstr>
On the other hand, some abstract types are implemented by representations that cannot be expressed in Objective CAML. Typical examples include arrays of weak pointers and input-output channels. Often, values of those abstract types are represented as memory blocks with tag Abstract_tag.

# let w = Weak.create 10;;
val w : '_a Weak.t = <abstr>
# Weak.set w 0 (Some p);;
- : unit = ()
# inspect w;;
....memory block: size=11 - abstract type
- : int Stack.t Weak.t = <abstr>
Sometimes, a finalization function is attached to those values. Finalization functions are C functions which are called by the garbage collector just before the value is collected. They are very useful to free external resources, such as an input-output buffer, just before the memory block referring to those resources disappears. For instance, inspection of the ``standard output'' channel reveals that the type out_channel is represented by abstract memory blocks with a finalization function:

# inspect (stdout) ;;
....memory block: size=2 - abstract finalized type
- : out_channel = <abstr>



Previous Contents Next ocaml-book-1.0/en/html/book-ora085.gif0000644000000000000000000002506707452056112014302 0ustar GIF89av  df;B3TR;TN;dYD,2$,.$,*$;=,;9,{}{,* TRD[YD[UD[][f3;5$dbL32,  323.;=33.$5;DJ;DF;TUDlrllnlf{ytdfdD$*$&$"d]LLN;LJ;,2,TDF3,.,DB3tvl$*${$"$39,35,",.32d   !C,vCREEE55 g#F xI!BArWbzUėjLUF3(S21KbDG\;vݔϟ A]갩ӆHJJ$2f*CP"@EcXKVۖmE6”w&5 ݼx|:_"-za姘Tx2g\ +7"pX"xCtcUcZ/f caΨژxϑ!*=)U}TJe,/ ;ld6&©stB;Er'm~L4箻O`>\@\ T-6^ uZ xM"`ףdx:v!*,iei2H RKos>q%BA$"ϸD|$9H:򒖴d%7INj̤'9P4*QyVd+?G:$e-mUr0q `6,cK8.ӌl9G$:uf1nz6͘LfBplfihӚ41(yS^X6Nfg?Ĥ2m409ЄRD?dꓟ4I~f@ωq;XOtb0Lge3j N~hM PkQ+(%ҞMFIS*UZUn\XTn"uD=# Z.Hԛ ԪfKO~,N3Ͳիa%+bzX6?]b;Y&Vulf!Xc$γ0.M]fWAوHu 2 wdh{=DHkp{47>@S ]x$=zjegzK0*,2 Q8V!]i-zrWb tә=#/wh/c" {HGv񮬪\<5@B9DYFyHJLٔNPL eGRyXZ\jYviYr.@fYejlhimr9tYnypiz|ٗ~!FaYeF#`f)m Uf7Yyٙ=gF:0u30gTYF<@&|~XUyn$ٛ9o<0U+Pl h[kUFTu )V'IIHJtL. wIHi쉞y !Г^hp pZ7XTٙQgfFi}׹TjTzRVU/TRSUFIY%I+:':/2Ze\]vh9|O&W*WeeAЇ֙$QBN8STV|֘=С'EXiUě/ʞ(ٙ-i-Z'ʢ(z1jj:e:vj\QV4Iyi_{OO$R!Rx]IjH}ؘEFZZHsuUyYJmRR*gT&ZkJn*ٞlzjIw:YeFH~juWVoVIU8X薅IVOζlHPFEZDgt+zaZu:lpIh{ZjFgVv+ [Zj:WFQ=vAGUѨFP P"";ypY01iY\yU N(XFJ@B &utVFWGyh{dxD&!Z\۵^`b;d[f{hjje渍)Xb|۷~;[;`9d\ 83cA``Cbd>{;[{~N& a[bd&`PE גjKn {śM(vSCj2|x 5}݆r GxIpELɍbپ5h%;t1I{h_v'T;G59'9g\ܻIؑ X G irMLky)ܴTX.lR{L9; :;\@<(hCi\}ƕPRd\fGօ{g$ gic|Er~[6Hz,ĻDxLjPA!<{|Ɏ۽+]N͋i,*mĕ͑&؟Mm,3ny[,INy?܂^F,~yՕ޼{巍͹͒L)MҎ>n軴 ݜN5WKwH.( ߪAfFF`ξb ʞ՞d,6bc%d4aϮcb+$VM֎[辺kIVaVaLۨ~]ÎWKJ=?]f^&Y kO򦮼g\^ĒNu^H᭎ .ꔞCn}ԛz=~0:?H<٬tfL;@^Mk1mk\Y]/‰SGo>}.^k\lU+B/FܒlΜt݉*XClh]w[֚v_=+ٷn׍~̆]V>ͭ?aOw\=L{LU抯ό` u٭>]_]鼬/yEܻ,މYv,HpRxv/CJrۘÒ4P"X$XE88XȨ)99HyIYi :hș: کzZ*zkX;4Tک;,98hi ڊ,I] -n[~M-Y\\O?h%G,agKt(@Mg-5n߀#;LSKhJj^j1~7#,MB! 2p"g2QJ@$=uk˱V[$szչs RMB,HbûH#67l\k8sd!.0H(/;8+<3ljTLl\dMG]9,f%8%ٰXlOƆkW-cEFF2ֽ|>̻d5ȯ^]-q~}։MVoCf>{~BS_i m7gޠu=p$`0lӈ&ړ ?%ePBI9WE2:zHQ}8Zf88}F䄿ys`MLMM#@ܥCrᘐ!E4_Le"-ȡ 果EXI}_*>*ڏ.*ICv[ Cuh!nmwܐg~r'Nz']rxelxe7 idzYBIw zx:NDW{]'Wf<(J~ nKnߪ~T,4]grz΁xafwoqz50w :ݧY>,1iL&%pk鹈Zjт-T)1Fr`x!:n.ʺ RH{HG(}Dp 6@~-dY{&]Z';,0x.opTSrVԔf{Se[YN,z\AL\wGY~uX` F ֬aJxՒ| -f#1X}LiH~,dՕR\Kw߲Ί(T7mxy"_XI)K oy1ڿw9Vi!QlĀ ꆍo@nxG:l 6BpA>x&шD.`".u1b$öh0@`]PS/jWc3|Ygd)^ WoS7866.IKEҭ~*e%S(9omEKz/({c78!a QwYݭviRە's;VPL#4ycGYZup>Kk^7Aob:Hv}e7åhڑR>w)icEI_"|{wW*D{/m KmEU'R#یV?t4=ذ{/!,峐6*ݙ>79i4_Mb8Տl͆ =׹ɭܽnյLY[W2rWNIw;)[]´$(:z4!TrԻ؇p֠ xO^w|*xiN h{;Ůyxڠ$c˲r/*w]ݸqK8}NdyhX(BKճs_}wgXw%E.GH7k}8{(GegD8XsUhxȈ8x>x5?i}-ÊfI2yJu> uJΧ4#6X4yՌ|W ^{6nu~HӨP2ȑ{QXZ'h7tDgg44i Y^җG} U$%tE)yy@t8:9#iw~ wx7'8h- Ŕvk=ɕbuYNÇQzDB)ۙr'H"g6C M4GٞHߘmS3OYaxJ>9z<)mٜ((I؊I#>:$i=ʤMOrCRlU͆f:mƥUZ\ʥ[cڥ`l^[bl&qJVsjqڥƧ}bJgzfVfZJ&mxJ{p l*z7 uʪڪJZJʪKzPꫪǫ&J R"T2j YjJ::՚ڭJr  YP=%0X0`j ) YJ URAʰ^;^ڪ+Z7FCŸ J KcŮ=Xٰ=00V+p:)%PY YpV b5)JjY^:WkT;˭F'nYyBY X 6{`?;0jb%*!.;+RcJKK*]_m'%s麮mT tY`%+වUPN; B[ǫH+ %g{ [:tȑ%DFZbѳƺ 4PpQ; + ȫj䠻pJ;z \qgf{†h[tH ~j rK@ڬ9 Pe``ʽ\˰{۰Z{831tCUH'ySy L| N":ڭV˰S˫C^̾c LablTk Library Previous Contents Next

LablTk Library

The interface to Tcl/Tk was integrated in the distribution of Objective CAML 3.04, and is available for Unix and Windows. The installation provides one new command: labltk, which launches a toplevel interactive loop integrating the LablTk library.

The LablTk library defines a large number of modules, and heavily uses the language extensions of Objective CAML 3.04. A detailed presentation of this module falls outside the scope of this appendix, and we invite the interested reader to refer to the documentation of Objective CAML 3.04.

There is also an interface with Gtk, written in class-based style, but it is not yet part of the Objective CAML distribution. It should be compatible with Unix and Windows.


Previous Contents Next ocaml-book-1.0/en/html/book-ora215.html0000644000000000000000000005774307453055401014503 0ustar Index of concepts Contents

Index of concepts

  • abstract
    • class, 15
    • method, 15
  • abstract machine, 7
  • abstract type, 14
  • abstraction, 2
  • affectation, 3
  • aggregation, 15
  • allocation
    • dynamic, 9
    • static, 9
  • application, 2
    • of a function, 2
    • partial, 2
  • arity, 2
  • array, 3
  • BNF, see grammar
  • big numbers, 8
  • binding
    • delayed, 15
    • static, 2
  • boolean, 2
  • boucle d'interaction, 7
    • construction, 7
    • options, 7
  • bytecode, 7
    • dynamic loading, 8
    • the compiler, see compiler
  • bytecode, see bytecode
  • C (the langage), 12
  • callback, 12
  • cartesian product, 2
  • character, 2
  • character string, 2
  • character strings, 3
  • class, 15
    • abstract, 15
    • inheritance, 15
    • instance, 15
    • interface, 15
    • local declaration, 15
    • multiple inheritance, 15
    • open type, 15
    • parameterized, 15
    • type, 15
    • variable, 15
    • virtual, 15
  • class instance, 15
  • client-server, 20
  • closure, 2, 2
    • from C, 12
    • representation, 12
  • command line, 8
    • parsing of, 8
  • communication channel, 3, 18
  • communication pipes, 18
    • named, 18
  • compilateur, 7
  • compilation, 7
    • portabilit, 7
    • unit, 7
  • compilation unit, 7
  • compiler
    • debug, 10
    • bytecode, 7, 7
    • command, 7
    • linking, 7
    • native, 7
    • profiling, 10
  • concurrency, 19, 19
  • conditional, 2
  • conflict, see parsing
  • conservative, 9
  • constraint, 14
  • constructor, 2, 2
    • constant, 2
    • functional, 2
  • critical section, 19
  • cyclic
    • type, A
  • debugger, see debugging
  • debugging, 10, 10
  • declaration
    • of an exception, 2
    • external, 12
    • of function, 2
    • global, 2
    • local, 2
    • by matching, 2
    • of an operator, 2
    • recursive, 2
    • simultaneous global, 2
    • simultaneous local, 2
    • of type, 2
      • scope, 2
    • of value, 2
  • destructor, 9
  • digest, see fingerprint
  • dot notation, 8
  • dynamic loading, 8
  • environment, 2, 12
  • evaluation
    • deferred, 4
    • retarde, 4
    • order of evaluation, 3
  • vnement, 5
  • exception, 2
    • declaration, 2
    • handling, 2
    • printing, 8
    • raising, 2
    • with C, 12
  • execution stack, 10
  • expression
    • functional, 2
    • rational, 11
    • regular, 11
  • external declaration, see declaration
  • famine, 19
  • file
    • extension
      • .ml, 7
      • .mli, 7
      • .mll, 11
      • .mly, 11
    • interface, 7
  • finalization, 12
  • fingerprint, 8
  • floating-point number, 2
  • floats
    • arrays of, 12
    • representation, 12
  • function, 2
    • for finalization, 12
    • higher order, 2
    • mutually recursive, 2
    • partial, 2
    • polymorphic, 2
      • trace, 10
    • recursive, 2
      • trace, 10
    • registration with C, 12
    • tail recursive, 4
  • function call, see application
  • functional, 2
  • functor, 14
  • GC, 9, 9, Mark& Sweep9
    • , conservative, 9
    • , generational, 9
    • from C, 12
    • incremental, 9
    • major, 9
    • minor, 9
    • Stop&Copy, 9
  • garbage collection, see GC, 9
  • garbage collector, see GC
  • grammar, 11
    • contextual, 11
    • definition, 11
    • rule, 11
  • Has-a, 15
  • hashing, 8
  • heap, 9
  • Is-a, 15
  • identifier, 11
  • inclusion
    • polymorphism, 15
  • inheritance, 15
  • inlining, 7
  • input-output, 8
    • with C, 12
  • installing Objective CAML, 1
    • Linux, 1
    • online HTML help, 1
    • Unix, 1
    • Windows, 1
  • installingObjective CAML
    • MacOS, 1
  • instance
    • class, 15
    • classe, 15
    • variable, 15
  • integer, 2
  • interface, 14, 15
    • graphical, 13
    • with C, 12, 12
    • with the system, 8
  • interoperability, 12
  • intput-output, 8
  • label, B
  • lazy (data), 4
  • lexical analysis, 11
    • ocamllex, 11
    • stream, 11
  • lexical unit, 11
    • ocamlyacc, 11
  • library, 8
    • preloaded, 8
    • standard, 8
  • linearization, 8
  • linking, 7
    • with C, 12
  • list, 2
    • association, 8
    • matching, 2
  • Mark&Sweep, 9
  • mmoire
    • rcupration
      • explicite, 9
      • implicite, 9
  • matching, 2
    • exhaustive, 2
    • destructive, 4
  • memory
    • allocation, 9
    • automatic
      • deallocation, 9
    • cache, 9
    • deallocation, 9, 9
    • dynamic, 9
    • explicit deallocation, 9
    • management, 9
    • reclamation, 9
    • static, 9
  • message sending, 15
  • method, 15
    • abstract, 15
    • virtual, 15
  • modifiable, 3
  • module, 14
    • constraint, 14
    • dependency, 10
    • local definition, 14
    • opening, 8
    • parameterized, 14
    • sub-module, 14
  • mutual exclusion, 19
  • OCamlBrowser, B
  • object, 15
    • copy, 15
    • creation, 15
  • operator
    • associativity, 11
    • declaration, 2
    • precedence, 11
  • optional (argument), B
  • pair, 2
  • parsing
    • bottom-up, 11
    • conflict, 11, 11
      • shift-reduce, 11
    • ocamlyacc, 11
    • stream, 11, 11
    • top-down, 11
  • pattern
    • character interval, 2
    • combining, 2
    • guard, 2
    • matching, 2
    • naming, 2
    • wildcard, 2, 2
  • pattern matching, see matching
  • persistence, 8
  • persistent
    • value, 8
  • pointer, 3
    • weak, 9
  • polymorphism, 2
    • inclusion, 15
  • portabilit, 7
  • process, 18
  • processus
  • producteur-consommateur, 19
  • production rule, see grammar
  • profiling, 10
  • protocol, 20
    • http, 20
  • reader-writer, 19
  • record, 2
    • mutable field, 3
  • reference, 3
  • root, 9
  • Sequence, 3
  • Stop&Copy, 9
  • scope of a variable, 2
  • self, 15
  • semaphore, 19
  • session, 7
  • sharing, 8
  • signal, 18
  • signature, 14
    • constraint, 14
  • socket, 20
  • stack, 9
  • standalone executable, 7
  • stream, 4
    • lexical analysis, 11
    • parsing, 11, 11
  • strict (language), 4
  • string, see character string
  • strings
    • representation, 12
  • structure, 14
  • subtyping-typage, 15
  • super, 15
  • synchronization, 19
  • syntax analysis, 11
  • Toplevel loop
    • directives, 7
  • tag bits, 9
  • this, 15
  • thread, see processus lger
  • toplevel, see boucle d'interaction
  • trace, 10
  • tree, 2
  • tuple, 2
  • type
    • abstract, 14
    • constraint, 2, 14, 15, 15
    • constructor, 2
    • declaration, 2
    • enumerated, 2
    • function, 2
    • functional, 2
    • mutually recursive, 2
    • object, 15
    • open, 15, 15
    • parameterized, 2, 2, 2
    • product, 2
    • record, 2
    • recursive, 2
    • sum, 2, 2
    • sum types
      • representation, 12
    • union, 2
  • UML, 15
  • value
    • atomic, 3
    • construction, 9
    • declaration, 2
    • exploration, 12
    • function, 12
    • global declaration, 2
    • immediate, 12
    • inspection, 10
    • local declaration, 2
    • persistent
      • type, 8
    • representation, 12
      • in C, 12
    • sharing, 3
    • structured, 3, 12
  • variable
    • bound, 2
    • free, 2, 2
    • type, 2
    • weak type, 3
  • variants
    • polymorphic, B
  • vector, see array
  • virtual
    • class, 15
    • method, 15
  • Zinc, 7
    • interpreter, 7
  • zombie, 18

Contents ocaml-book-1.0/en/html/book-ora214.html0000644000000000000000000002551307453055401014470 0ustar Bibliography Previous Contents

Bibliography

[AC96]
Mara-Virginia Aponte and Giuseppe Castagna. Programmation modulaire avec surcharge et liaison tardive. In Journes Francophones des Langages Applicatifs. INRIA, January 1996.

[AHU83]
Alfred Aho, John Hopcroft, and Jeffrey Ullman. Data Structures and Algorithms. Addison-Wesley, 1983.

[And91]
G. Andrews. Concurrent Programming : Principles and practices. Benjamin Cumming, 1991.

[Ari90]
Ben Ari. Principles of Concurrent and Distributed Programming. Prentice Hall, second edition, 1990.

[ASS96]
Harold Abelson, Gerald Jay Sussman, and Julie Sussman. Structure and Interpretation of Computer Programs. MIT Press, second edition, 1996.

[ASU86]
Alfred V. Aho, Ravi Sethi, and Jeffrey D. Ullman. Compilers: principles, techniques, and tools. Addison-Wesley, 1986.

[BLH00]
Olivier Ballereau, Frdric Loulergue, and Gatan Hains. High level BSP programming: BSML and BSlambda. In Stephen Gilmore, editor, Trends in Functional Programming, volume 2. Intellect, 2000.

[CC92]
Emmanuel Chailloux and Guy Cousineau. Programming images in ML. In Proceedings of the ACM SIGPLAN Workshop on ML and its Applications, 1992.

[CCM87]
Guy Cousineau, Pierre-Louis Curien, and Michel Mauny. The Categorical Abstract Machine. Science of Computer Programming, 8:173--202, 1987.

[CDM98]
Rmy Card, ric Dumas, and Franck Mvel. The Linux Kernel Book. Wiley, John & Sons, 1998.

[CKL96]
Emmanuel Chailloux, Laurent Kirsch, and Stphane Lucas. Caml2sml, un outil d'aide la traduction de Caml vers Sml. In Journes Francophones des Langages Applicatifs. INRIA, January 1996.

[CL99]
Sylvain Conchon and Fabrice Le Fessant. JoCaml: mobile agents for Objective-Caml. In International Symposium on Agent Systems and Applications, 1999.

[CM98]
Guy Cousineau and Michel Mauny. The functional approach to programming. Cambridge University Press, 1998.

[CP95]
Paul Caspi and Marc Pouzet. A functional extension to LUSTRE. In 8th International Symposium on Languages for Intensional Programming, Sydney, May 1995. World Scientific.

[CS94]
Emmanuel Chailloux and Ascnder Surez. mlPicTeX, a picture environment for LaTeX. In Proceedings of the ACM SIGPLAN Workshop on ML and its Applications, 1994.

[DDLP98]
Marco Danelutto, Roberto Di Cosmo, Xavier Leroy, and Susanna Pelagatti. Parallel functional programming with skeletons: the ocamlp3l experiment. In ML Workshop. ACM SIGPLAN, 1998.

[DEMN98]
Roland Ducournau, Jrme Euzenat, Grald Masini, and Amedeo Napoli, editors. Langages et modles objets: tat et perspectives de la recherche. INRIA, 1998.

[Eng98]
Emmanuel Engel. Extensions sres et praticables du systme de types de ML en prsence d'un langage de modules et de traits impratifs. PhD thesis, Universit Paris-Sud, Orsay, France, mai 1998.

[FC95]
Christian Foisy and Emmanuel Chailloux. Caml Flight: a Portable SPMD Extension of ML for Distributed Memory Multiprocessors. In Conference on High Performance Functional Computing, April 1995.

[FF98]
Robert B. Findler and Matthew Flatt. Modular Object-Oriented Programming with Units and Mixins. In International Conference on Functional Programming. ACM, 1998.

[FW00]
Jun Furuse and Pierre Weis. Entres/Sorties de valeurs en Caml. In JFLA'2000 : Journes Francophones des Langages Applicatifs, Mont Saint-Michel, January 2000. INRIA.

[GHJV95]
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns. Addison-Wesley, 1995.

[HF+96]
Pieter Hartel, Marc Feeley, et al. Benchmarking implementations of functional languages with ``Pseudoknot'', a float-intensive benchmark. Journal of Functional Programming, 6(4), 1996.

[HS94]
Samuel P. Harbison and Guy L. Steele. C: A reference manual. Prentice-Hall, fourth edition, 1994.

[Hui97]
Christian Huitema. IPv6 -- The New Internet Protocol. Prentice Hall, 1997.

[Jon98]
Richard Jones. Garbage Collection: Algorithms for Automatic Dynamic Memory Management. John Wiley & Sons, 1998.

[Ler90]
Xavier Leroy. The ZINC experiment: an economical implementation of the ML language. Technical report 117, INRIA, 1990.

[Ler92]
Xavier Leroy. Programmation du systme Unix en Caml Light. Technical report 147, INRIA, 1992.

[LMB92]
John R. Levine, Tony Mason, and Doug Brown. Lex & Yacc. O'Reilly, second edition, 1992.

[LRVD99]
Xavier Leroy, Didier Rmy, Jrme Vouillon, and Damien Doligez. The objective caml system release 2.04. Technical report, INRIA, December 1999.

[MdR92]
Michel Mauny and Daniel de Rauglaudre. Parser in ML. Research report 1659, INRIA, avril 1992.

[MNC+91]
Grald Masini, Amedeo Napoli, Dominique Colnet, Daniel Lonard, and Karl Tombre. Object-Oriented Languages. Academic Press, New York, 1991.

[MT91]
Robin Milner and Mads Tofte. Commentary on Standard ML. MIT Press, 1991.

[MTH97]
Robin Milner, Mads Tofte, and Robert Harper. The Definition of Standard ML (revised). MIT Press, 1997.

[Rep99]
John Reppy. Concurrent Programming in ML. Cambridge University Press, 1999.

[Rob89]
Eric S. Robert. Implementing exceptions in C. Technical Report SRC-40, Digital Equipment, 1989.

[Rou96]
Franois Rouaix. A Web navigator with applets in Caml. In Proceedings of the 5th International World Wide Web Conference, in Computer Networks and Telecommunications Networking, volume 28, pages 1365--1371. Elsevier, May 1996.

[RV98]
Didier Rmy and Jrme Vouillon. Objective ML: An effective object-oriented extension to ML. Theory And Practice of Object Systems, 4(1):27--50, 1998. A preliminary version appeared in the proceedings of the 24th ACM Conference on Principles of Programming Languages, 1997.

[Sed88]
Robert Sedgewick. Algorithms. Addison-Wesley, second edition, 1988.

[Ste92]
W. Richard Stevens. Advanced Programming in the UNIX Environment. Addison-Wesley, 1992.

[Tho99]
Simon Thompson. Haskell: The Craft of Functional Programming. Addison Wesley, seconde edition, 1999.

[Tur85]
David A. Turner. Miranda: A non-strict functional language with polymorphic types. In J. Jouannaud, editor, Proceedings International Conference on Functional Programming Languages and Computer Architecture, volume 201 of Lecture Notes in Computer Science, pages 1--16, New York, NY, September 1985. Springer-Verlag.

[Wil92]
Paul. R. Wilson. Uniprocessor garbage collection techniques. In International Workshop on Memory Management, number 637 in LNCS, pages 1--42. Springer-Verlag, 1992.

[Wri95]
Andrew K. Wright. Simple imperative polymorphism. Lisp and Symbolic Computation, 8(4):343--356, 1995.

Previous Contents ocaml-book-1.0/en/html/book-ora100.html0000644000000000000000000000420107453055400014450 0ustar Summary Previous Contents Next

Summary

This chapter presented the different programming support tools that come with the Objective CAML distribution.

The first tool performs a static analysis in order to determine the dependencies of a set of compilation units. This information can then be put in a Makefile, allowing for separate compilation (if you alter one source file in a program, you only have to compile that file, and the files that have dependencies to it, rather than the entire program).

Other tools give information about the execution of a program. The interactive toplevel offers a trace of the execution; but, as we have seen, polymorphism imposes quite heavy restrictions on the observable values. In fact, only the global declarations of monomorphic values are visible, which nevertheless includes the arguments of monomorphic functions and permits tracing of recursive functions.

The last tools are those in the tradition of development under Unix, namely a debugger and a profiler. With the first, you can execute a program step by step to examine it's operation, and the second gives information about its performance. Both are usable only under Unix.


Previous Contents Next ocaml-book-1.0/en/html/book-ora065.html0000644000000000000000000002337707453055400014501 0ustar Steps of Compilation Previous Contents Next

Steps of Compilation

An executable file is obtained by translating and linking as described in figure 7.1.


  Source program  
preprocessing  
  Source program  
compiling  
  Assembly program  
assembling  
  Machine instructions  
linking  
  Executable code  

Figure 7.1: Steps in the production of an executable.


To start off, preprocessing replaces certain pieces of text by other text according to a system of macros. Next, compilation translates the source program into assembly instructions, which are then converted to machine instructions. Finally, the linking process establishes a connection to the operating system for primitives. This includes adding the runtime library, which mainly consists of memory management routines.



The Objective CAML Compilers

The code generation phases of the Objective CAML compiler are detailed in figure 7.2. The internal representation of the code generated by the compiler is called an intermediate language (IL).


  Sequence of characters  
lexical analysis  
  Sequence of lexical elements  
parsing  
  Syntax tree  
semantic analysis  
  Annotated syntax tree  
generation of intermediate code  
  Sequence of IL  
optimization of intermediate code  
  Sequence of IL  
generation of pseudo code  
  Assembly program  

Figure 7.2: Compilation stages.


The lexical analysis stage transforms a sequence of characters to a sequence of lexical elements. These lexical entities correspond principally to integers, floating point numbers, characters, strings of characters and identifiers. The message Illegal character might be generated by this analysis.

The parsing stage constructs a syntax tree and verifies that the sequence of lexical elements is correct with respect to the grammar of the language. The message Syntax error indicates that the phrase analyzed does not follow the grammar of the language.

The semantic analysis stage traverses the syntax tree, checking another aspect of program correctness. The analysis consists principally of type inference, which if successful, produces the most general type of an expression or declaration. Type error messages may occur during this phase. This stage also detects whether any members of a sequence are not of type unit. Other warnings may result, including pattern matching analysis (e.g pattern matching is not exhaustive, part of pattern matching will not be used).

Generation and the optimization of intermediate code does not produce errors or warning messages.

The final step in the compilation process is the generation of a program binary. Details differ from compiler to compiler.

Description of the Bytecode Compiler

The Objective CAML virtual machine is called Zinc (``Zinc Is Not Caml''). Originally created by Xavier Leroy, Zinc is described in ([Ler90]). Zinc's name was chosen to indicate its difference from the first implementation of Caml on the virtual machine CAM (Categorical Abstract Machine, see [CCM87]).

Figure 7.3 depicts the bytecode compiler. The first part of this figure shows the Zinc machine interpreter, linked to the runtime library. The second part corresponds to the Objective CAML bytecode compiler which produces instructions for the Zinc machine. The third part contains the set of libraries that come with the compiler. They will be described in Chapter 8.



Figure 7.3: Virtual machine.


Standard compiler graphical notation is used for describing the components in figure 7.3. A simple box represents a file written in the language indicated in the box. A double box represents the interpretation of a language by a program written in another language. A triple box indicates that a source language is compiled to a machine language by using a compiler written in a third language. Figure 7.4 gives the legend of each box.




Figure 7.4: Graphical notation for interpreters and compilers.


The legend of figure 7.3 is as follows:

  • BC : Zinc bytecode;
  • C : C code;
  • .o : object code
  •  : micro-processor;
  • OC (v1 or v2) : Objective CAML code.

Note


The majority of the Objective CAML compiler is written in Objective CAML. The second part of figure 7.3 shows how to pass from version v1 of a compiler to version v2.



Previous Contents Next ocaml-book-1.0/en/html/book-ora045.gif0000644000000000000000000000460107452056111014264 0ustar GIF89aUUU!,ڋ޼H扦ʶ rJԌ7bш ̦ \B)j aܮ_иN5gg۽޼z}=G4H8Qx8)Ypbi@YR)))*i⪤ JzՊ{ [QdBIdFdJ.dN>y# heVcc5T^eSvE^I Q}efq2DmI'D,[Y}`jᕠ> ȢQ'(X^)zQNhY,*&e*J+yjrj^ l¶_u^죱>dZK-؊m:,ު:nG ٭"icϮ Jxnۯ[r¯(0ÁGOLq 1( Q ~ln$#kwl&׊R0ĎP!7r,T3LD9[JMGtB 0--"A5 "u2[_^BZgg.p%7oمvXwBQW#ݍͲلC~ UwTJY|9V%cL ]x.9΢n9%[eð>x#:HTGf7_㇫޵_Nס}YsI}RzG:Dcxɢ6pNuЊ̍ϧϽ!)z|צ\hSG G40dE1+,PmY @_[ RC6}'L#9dV s82a 8C{"Լ!iq\b 8!:c[V3ʪ^fTHs#UGyq6ID 阯BHIdH Er$DW|d%LZrTFIX٢VYX.r 7$Yw)Omjj%0HPg\1 **@`|!5?܁fcI3Az{T"#KhʅNHAh9G]ކ|%T?)pH) E *:ivk$JNưo  e 3 W`#mvSa1V(!Ud- 2Y@P^VH;nT*zD+#L:H$[VuaZ]k4H50̯5>""kGrumlc8V&̉2f#Ϫuf3).ԫ-i ZmUeٚZiRmm;֒mmqRkaKHQNefkn9e9ַFGYuj@mun?kbw*\l!𜍘C5oAg{^עPEIBF,q*Cg7eHr u&=8Rݰ~Վ6L4#oO{q;Z[0'+8n0ow ]A"3}o|J1J"ڈy1%x꯿61d˱T>"s'O@| yYZ⇜CFs'g4"՞h]Y4qnC13ғsa@V |;`S BZsqR/q/`\8{ j:yFYNͶ]f n]Mn_+ m7>vC|bzd'b M Th1#갇Qx(n4_xq>|]",J;ocaml-book-1.0/en/html/book-ora010.html0000644000000000000000000000406407453055377014474 0ustar Testing the installation Previous Contents Next

Testing the installation

Once installation of the Objective CAML development environment is done, it is necessary to test it, mainly to verify the search paths for executables and libraries. The simplest way is to launch the interactive toplevel of the system and write the first little program that follows:
String.concat "/" ["a"; "path"; "here"] ;;
This expression concatenates several character strings, inserting the ``/'' character between each word. The notation String.concat indicates use of the function concat from the String. If the library search path is not correct, the system will print an error. It will be noted that the system indicates that the computation returns a character string and prints the result.

The documentation of this function String.concat can be found in the online reference manual by following the links ``The standard library'' then ``Module String: string operations''.

To exit the interactive toplevel, the user must type the directive ``#quit ;;''.




Previous Contents Next ocaml-book-1.0/en/html/book-ora085.html0000644000000000000000000002210107453055400014463 0ustar Allocation and Deallocation of Memory Previous Contents Next

Allocation and Deallocation of Memory

Most languages permit dynamic memory allocation, among them C, Pascal, Lisp, ML, SmallTalk, C++, Java, ADA.

Explicit Allocation

We distinguish two types of allocation:
  • a simple allocation reserving a block of memory of a certain size without concern of its contents;
  • an allocation combining the reservation of space with its initialization.
The first case is illustrated by the function new in Pascal or malloc in C. These return a pointer to a memory block (i.e. its address), through which the value stored in memory can be read or modified. The second case corresponds to the construction of values in Objective CAML, Lisp, or in object-oriented languages. Class instances in object-oriented languages are constructed by combining new with the invocation of a constructor for the class, which usually expects a number of parameters. In functional languages, constructor functions are called in places where a structural value (tuple, list, record, vector, or closure) is defined.



Let's examine an example of value construction in Objective CAML. The representation of values in memory is illustrated in Figure 9.1.


Figure 9.1: Memory representation of values.



# let u = let l = ['c'; 'a'; 'm'] in List.tl l ;;
val u : char list = ['a'; 'm']
# let v = let r = ( ['z'] , u )
in match r with p -> (fst p) @ (snd p) ;;
val v : char list = ['z'; 'a'; 'm']
A list element is represented by a tuple of two words, the first containing a character and the second containing a pointer to the next element of the list. The actual runtime representation differs slightly and is described in the chapter on interfacing with C (see page ??).

The first definition constructs a value named l by allocating a cell (constructor ::) for each element of the list ['c';'a';'m']. The global declaration u corresponds to the tail of l. This establishes a sharing relationship between l and u, i.e. between the argument and the result of the function call to List.tl.

Only the declaration u is known after the evaluation of this first statement.

The second statement constructs a list with only one element, then a pair called r containing this list and the list u. This pair is pattern matched and renamed p by the matching. Next, the first element of p is concatenated with its second element, which creates a value ['z';'a';'m'] tied to the global identifier v. Notice that the result of snd (the list ['a';'m']) is shared with its argument p whereas the result of fst (the character 'z') is copied.

In each case memory allocation is explicit, meaning that it is requested by the programmer (by a language command or instruction).

Note


Allocated memory stores information on the size of the object allocated in order to be able to free it later.


Explicit Reclamation

Languages with explicit memory reclamation possess a freeing operator (free in C or dispose in Pascal) that take the address (a pointer) of the region to deallocate. Using the information stored at allocation time, the program frees this region and may re-use it later.

Dynamic allocation is generally used to manipulate data structures that evolve, such as lists, trees etc.. Freeing the space occupied by such data is not done in one fell swoop, but instead requires a function to traverse the data. We call such functions destructors.

Although correctly defining destructors is not too difficult, their use is quite delicate. In fact, in order to free the space occupied by a structure, it is necessary to traverse the structure's pointers and apply the language's freeing operator. Leaving the responsibility of freeing memory to the programmer has the advantage that the latter is sure of the actions taken. However, incorrect use of these operators can cause an error during the execution of the program. The principal dangers of explicit memory reclamation are:
  • dangling pointers: a memory region has been freed while there are still pointers pointing at it. If the region is reused, access to the region by these pointers risks being incoherent.
  • Inaccessible memory regions (a memory ``leak''): a memory region is still allocated, but no longer referenced by any pointer. There is no longer any possibility of freeing the region. There is a clear loss of memory.
The entire difficulty with explicit memory reclamation is that of knowing the lifetime of the set of values of a program.

Implicit Reclamation

Languages with implicit memory reclamation do not possess memory-freeing operators. It is not possible for the programmer to free an allocated value. Instead, an automatic reclamation mechanism is engaged when a value is no longer referenced, or at the time of an allocation failure, that is to say, when the heap is full.

An automatic memory reclamation algorithm is in some ways a global destructor. This characteristic makes its design and implementation more difficult than that of a destructor dedicated to a particular data structure. But, once this difficulty is overcome, the memory reclamation function obtained greatly enhances the safety of memory management. In particular, the risk of dangling pointers disappears.

Furthermore, an automatic memory reclamation mechanism may bring good properties to the heap:
  • compaction: all the recovered memory belongs to a single block, thereby avoiding fragmentation of the heap, and allowing allocation of objects of the size of the free space on the heap;
  • localization: the different parts of the same value are close to one another from the point of view of memory address, permitting them to remain in the same memory pages during use, and thereby avoiding their erasure from cache memory.
Design choices for a garbage collector must take certain criteria and constraints into account:
  • reclamation factor: what percentage of unused memory is available?
  • memory fragmentation: can one allocate a block the size of the free memory?
  • the slowness of allocation and collection;
  • what freedom do we have regarding the representation of values?
In practice, the safety criterion remains primordial, and garbage collectors find a compromise among the other constraints.


Previous Contents Next ocaml-book-1.0/en/html/book-ora123.html0000644000000000000000000000541207453055400014462 0ustar Introduction Previous Contents Next

Introduction

This chapter presents two applications which seek to illustrate the use of the many different programming concepts presented previously in Part III.

The first application builds a library of graphic components, Awi (Application Window Interface). Next the library will be applied in a simple Francs to Euros converter. The components library reacts to user input by calling event handlers. Although this is a simple application algorithmically, it shows the benefits of using closures to structure the communication between components. Indeed the various event handlers share certain values via their environment. To appreciate the construction of Awi it is necessary to know the base library Graphics (see chapter 5, page ??).

The second application is a search for a least cost path in a directed graph. It uses Dijkstra's algorithm which calculates all the least cost paths from a source node to all the other nodes connected to this source. A cache mechanism implemented using a table of weak pointers (see page ??) is used to speed the search. The GC can free the elements of this table at any time but they can be recalculated as necessary. The graph visualization uses the simple button component of the Awi library for selecting the origin and destination nodes of the path sought. We then compare the efficiency of running the algorithm both with and without the cache. To facilitate timing measurements between the two versions a file with the description of the graph and the origin and destination nodes is passed as an argument to the search algorithm. Finally, a small graphical interface will be added to the search program.




Previous Contents Next ocaml-book-1.0/en/html/book-ora018.html0000644000000000000000000001235007453055377014501 0ustar Polymorphism and return values of functions Previous Contents Next

Polymorphism and return values of functions

Objective CAML's parametric polymorphism permits the definition of functions whose return type is completely unspecified. For example:

# let id x = x ;;
val id : 'a -> 'a = <fun>


However, the return type depends on the type of the argument. Thus, when the function id is applied to an argument, the type inference mechanism knows how to instantiate the type variable 'a. So for each particular use, the type of id can be determined.

If this were not so, it would no longer make sense to use strong static typing, entrusted with ensuring execution safety. Indeed, a function of completely unspecified type such as 'a -> 'b would allow any type conversion whatsoever, which would inevitably lead to a run-time error since the physical representations of values of different types are not the same.

Apparent contradiction

However, it is possible in the Objective CAML language to define a function whose return type contains a type variable which does not appear in the types of its arguments. We will consider several such examples and see why such a possibility is not contradictory to strong static typing.

Here is a first example:

# let f x = [] ;;
val f : 'a -> 'b list = <fun>


This function lets us construct a polymorphic value from anything at all:

# f () ;;
- : '_a list = []
# f "anything at all" ;;
- : '_a list = []


Nevertheless, the value obtained isn't entirely unspecified: we're dealing with a list. So it can't be used just anywhere.

Here are three examples whose type is the dreaded 'a -> 'b:

# let rec f1 x = f1 x ;;
val f1 : 'a -> 'b = <fun>
# let f2 x = failwith "anything at all" ;;
val f2 : 'a -> 'b = <fun>
# let f3 x = List.hd [] ;;
val f3 : 'a -> 'b = <fun>


These functions are not, in fact, dangerous vis-a-vis execution safety, since it isn't possible to use them to construct a value: the first one loops forever, the latter two raise an exception which interrupts the computation.

Similarly, it is in order to prevent functions of type 'a -> 'b from being defined that new exception constructors are forbidden from having arguments whose type contains a variable.

Indeed, if one could declare a polymorphic exception Poly_exn of type 'a -> exn, one could then write the function:

let f = function
0 -> raise (Poly_exn false)
| n -> n+1 ;;


The function f being of type int -> int and Poly_exn being of type 'a -> exn, one could then define:

let g n = try f n with Poly_exn x -> x+1 ;;
This function is equally well-typed (since the argument of Poly_exn may be arbitrary) and now, evaluation of (g 0) would end up in an attempt to add an integer and a boolean!




Previous Contents Next ocaml-book-1.0/en/html/book-ora030.gif0000644000000000000000000000417407452056111014263 0ustar GIF89a7 UUU!,7 ڋޜHBB LCk/ D"L*#srݜϪUAҎYwSL>øi~.n|8hGX!`x8 *:JZjz { ;kW;kڻ{* Ez\lʬ|4MjM]=- #Y>sީ^î/Fo/V/?+5\!')Ȅb)pc)!͐@I}*QJ-a\VK0wԹ 0@wjJ1*Ut~VaJ5Yz 6رd˚=6ڬں} 7ܹnн7/\zK0! ́Ã7~lqĞ"q|R)s@o β闙[oAѝ#&z6o~tiự]<7Z;՛9nvΠVt,05n{6_ _{`,&R}eI_278}ˍUM4B[&h}h%ޑHh8aDqG$b%9A"1 @޹]"*ҋI> e ȂBf@n !-ؤ1b3([ezFaebXu~ g~Ics ^WdlbqY垠Qiz4܆i<ꠖ2Nxh|&(}-x+[x%!:C-0c%W*wi>,F,Jއ͛kb6;׺)nݼm꩛+//6!NjQ.6 ?L7CV_qoqIhgjݤ3j功>nꔯ:,{_w4*^33o[ϻ߰|E?9ꢁ&ؽZ P\,l f gC*"Pܝs 4.@<4H_yAQ8хe05 Mڂ S}M!Fu-i4x#@ XЪظB2 JfC"2id Y~$ 'HTsH>nKdT qKR J}2;(s%黥,D\R4%(Lq{<'DGPЕLd$ KSگu<%7ſNEКӥ9ݙ/1k̐Uqdg0zy37mL6f{jsDD㽈TZ#4~WBOci\TϣiMm7B)+NS#w*#*1H?uR3ԥNfD;DTʫ(x*/ϦU Z Jr`uni5bZVHDjm5GʍͪmR]յf\5Na=դ SOcfz JlR&J5xVLXq6k`WZõhIM\x`uв ,[^bY;:wč.tZ.l3Zd6e59mn+ K_c/+_ (`yn8E <"WWy" W+D$#_̤C'$NP7#Vi WSwc61%{8(LѪВ\N=x ʢ;͔\r &+ʸLyd.,UEjN'p'WDU&{fqUͦ!}ϧd#96/:C 0ABgСXsO]3g*ѧD)ou+RrF\[RsW懛k Oz(;ocaml-book-1.0/en/html/book-ora056.gif0000644000000000000000000001220207452056112014263 0ustar GIF89a?[}}}{{{yyywwwuuuqqqWWW333tttrrrnnnllljjj2P2!R,?[R*am)`(_&^%]$\#["!Y XWVUrH nXȰᔇŋ 㠣" Cl@2 0$IYɲ%0c I悛8sɳ@ ѣH"XʴSJիj꣫ׯŠۣٳҪUˣ۷ʝݻݫ߿La8+^)oHL9$PܠeK(]\dkIc~J}~x*1w. u뇳gg̽{bʔVnMHau]u! XY' -/lfRxq@a@<l@(dTDVeH dXLP2'sTJgeuXfv\~d`9&ze54nj xu¬ p[8km[["4ꨔJje^Xm*Y} j,侱F/xis+'εPcιć#}Jg8{~S'ӭ~,X`:>p``8׻^e9kg;vH]L>1&?~o{ /v mxvO^Mw}y||%;E< zn~z=M/wᬧ}m{ܯӖ}TOD&~ g A~~ҏ/uKwm}a?>!o~S߼w'_E^ߏ|Οwen'kd~%'vQmjGVɇdjHj!8d t%hI6Bv(sm28JvȀ7fʇx~GwHSzw:qWȄϖsօw]X`&rFfhqfs؅vUHPpn6LÆmb_X|f(bR8vkg4Hlhh(g{hkg+VO6Ė{b؊Xkg8vuƋLVٖW(dhh˸dgV|xeHhxj88(hѨ֌j֍M6kXf8g(|^ǎxz(VO6lgI֎Xe )g eِhIihYlyh h"kIe'"8,f2Yfe5 V֒Aj7w9Ye@zVVv.JigLٔ(Q攋xojZ 操HjCF jjfɕk6[pك&fwrIft f|q7i@}I8Kfg׈gcdi}g"|7v6H֙Ti9suyqM'sƚ,ٛiҘøsrݦKvD7si9z(iWƹɩ&l&29dy׉)fIsit)eidȹr镖 a7r3i3ɟ,%Z/B hrY99 eɇĩZpʡ|j9D f院*jz6 w4hN?ڣl雊i70*fZ6ǤP&fO ~)iVfjuiCNX}٥^Zfg%JlJOk:lG)@HqxDWڧ~kHifGp訏:ie㈨i:jyZJVYijGʩZVXM֩։|zke9j7wijwd:)ʓڪdګd鸝6JM8 _Ik dy*hwjd({*JIۜG|wyzh&|{"{ {ֱ췟'z{ꉤ-"#K55;h*{wʳ5X Y kh(}{}}GV{횈ƵzɖzGfˮm[iK8in[g]y{*g'92{8 [E{|k븞-Y˷6ˬ+t)[KшQ\ aڂ'jSY+7JgTj~뤻 H ȺV溯Kfțۤ`y;NjHi۽;ěBx9 K˽mʻu X ZGy+:{[o{\Fq׿ ڳ(+|Јh缳6Blkdˊ̿?x\1x5lwyycw*@m8wizyd>jU晶[ir6)`;,x5 Chapter Overview Previous Contents Next

Chapter Overview

This chapter presents dynamic memory allocation strategies and garbage collection algorithms, in particular the one used by Objective CAML which is a combination of the presented algorithms. The first section provides background on different classes of memory and their characteristics. The second section describes memory allocation and compares implicit and explicit deallocation. The third section presents the major GC algorithms. The fourth section details Objective CAML's algorithm. The fifth section uses the Gc module to control the heap. The sixth section introduces the use of weak pointers from the Weak module to implement caches.


Previous Contents Next ocaml-book-1.0/en/html/book-ora143.html0000644000000000000000000015602207453055400014470 0ustar Types and Genericity Previous Contents Next

Types and Genericity

Besides the ability to model a problem using aggregation and inheritance relations, object-oriented programming is interesting because it provides the ability to reuse or modify the behavior of classes. However, extending an Objective CAML class must preserve the static typing properties of the language.

With abstract classes, you can factorize code and group their subclasses into one ``communication protocol''. An abstract class fixes the names and types of messages that may be received by instances of child classes. This technique will be better appreciated in connection with multiple inheritance.

The notion of an open object type (or simply an open type) that specifies the required methods allows code to work with instances using generic functions. But you may need to make the type constraints precise; this will be necessary for parameterized classes, which provide the genericity of parameterized polymorphism in the context of classes. With this latter object layer feature, Objective CAML can really be generic.

Abstract Classes and Methods

In abstract classes, some methods are declared without a body. Such methods are called abstract. It is illegal to instantiate an abstract class; new cannot be used. The keyword virtual is used to indicate that a class or method is abstract.

Syntax


class virtual name = object ...end
A class must be declared abstract as soon as one of its methods is abstract. A method is declared abstract by providing only the method type.

Syntax


method virtual name : type


When a subclass of an abstract class redefines all of the abstract methods of its parent, then it may become concrete; otherwise it also has to be declared abstract.

As an example, suppose we want to construct a set of displayable objects, all with a method print that will display the object's contents translated into a character string. All such objects need a method to_string. We define class printable. The string may vary according to the nature of the objects that we consider; therefore method to_string is abstract in the declaration of printable and consequently the class is also abstract.

# class virtual printable () =
object(self)
method virtual to_string : unit -> string
method print () = print_string (self#to_string())
end ;;
class virtual printable :
unit ->
object
method print : unit -> unit
method virtual to_string : unit -> string
end
We note that the abstractness of the class and of its method to_string is made clear in the type we obtain.

From this class, let us try to define the class hierarchy of figure 15.4.



Figure 15.4: Relations between classes of displayable objects.


It is easy to redefine the classes point, colored_point and picture by adding to their declarations a line inherit printable () that provides them with a method print through inheritance.



# let p = new point (1,1) in p#print() ;;
( 1, 1)- : unit = ()
# let pc = new colored_point (2,2) "blue" in pc#print() ;;
( 2, 2) with color blue- : unit = ()
# let t = new picture 3 in t#add (new point (1,1)) ;
t#add (new point (3,2)) ;
t#add (new point (1,4)) ;
t#print() ;;
[ ( 1, 1) ( 3, 2) ( 1, 4)]- : unit = ()


Subclass rectangle below inherits from printable, and defines method to_string. Instance variables llc (resp. urc) mean the lower left corner point (resp. upper right corner point) in the rectangle.

# class rectangle (p1,p2) =
object
inherit printable ()
val llc = (p1 : point)
val urc = (p2 : point)
method to_string () = "[" ^ llc#to_string() ^ "," ^ urc#to_string() ^ "]"
end ;;
class rectangle :
point * point ->
object
val llc : point
val urc : point
method print : unit -> unit
method to_string : unit -> string
end


Class rectangle inherits from the abstract class printable, and thus receives method print. It has two instance variables of type point: the lower left corner (llc) and upper right corner. Method to_string sends the message to_string to its point instance variables llc and urc.


# let r = new rectangle (new point (2,3), new point (4,5));;
val r : rectangle = <obj>
# r#print();;
[( 2, 3),( 4, 5)]- : unit = ()


Classes, Types, and Objects

You may remember that the type of an object is determined by the type of its methods. For instance, the type point, inferred during the declaration of class point, is an abbreviation for type:
  point =
    < distance : unit -> float; get_x : int; get_y : int;
      moveto : int * int -> unit; rmoveto : int * int -> unit;
      to_string : unit -> string  >
This is a closed type; that is, all methods and associated types are fixed. No additional methods and types are allowed. Upon a class declaration, the mechanism of type inference computes the closed type associated with class.

Open Types

Since sending a message to an object is part of the language, you may define a function that sends a message to an object whose type is still undefined.

# let f x = x#get_x ;;
val f : < get_x : 'a; .. > -> 'a = <fun>


The type inferred for the argument of f is an object type, since a message is sent to x, but this object type is open. In function f, parameter x must have at least a method get_x. Since the result of sending this message is not used within function f, its type has to be as general as possible (i.e. a variable of type 'a). So type inference allows the function f to be used with any object having a method get_x. The double points (..) at the end of the type < get_x : 'a; .. > indicate that the type of x is open.


# f (new point(2,3)) ;;
- : int = 2
# f (new colored_point(2,3) "emerald") ;;
- : int = 2
# class c () =
object
method get_x = "I have a method get_x"
end ;;
class c : unit -> object method get_x : string end
# f (new c ()) ;;
- : string = "I have a method get_x"


Type inference for classes may generate open types, particularly for initial values in instance construction. The following example constructs a class couple, whose initial values a and b have a method to_string.

# class couple (a,b) =
object
val p0 = a
val p1 = b
method to_string() = p0#to_string() ^ p1#to_string()
method copy () = new couple (p0,p1)
end ;;
class couple :
(< to_string : unit -> string; .. > as 'a) *
(< to_string : unit -> string; .. > as 'b) ->
object
val p0 : 'a
val p1 : 'b
method copy : unit -> couple
method to_string : unit -> string
end
The types of both a and b are open types, with method to_string. We note that these two types are considered to be different. They are marked ``as 'a'' and ``as 'b'', respectively. Variables of types 'a and 'b are constrained by the generated type.

We use the sharp symbol to indicate the open type built from a closed type obj_type:

Syntax


#obj_type
The type obtained contains all of the methods of type obj_type and terminates with a double point.

Type Constraints.

In the chapter on functional programming (see page ??), we showed how an expression can be constrained to have a type more precise than what is produced by inference. Object types (open or closed) can be used to enhance such constraints. One may want to open a priori the type of a defined object, in order to apply it to a forthcoming method. We can use an open object constraint:

Syntax


(name:#type)
Which allows us to write:

# let g (x : #point) = x#message;;
val g :
< distance : unit -> float; get_x : int; get_y : int; message : 'a;
moveto : int * int -> unit; print : unit -> unit;
rmoveto : int * int -> unit; to_string : unit -> string; .. > ->
'a = <fun>
The type constraint with #point forces x to have at least all of the methods of point, and sending message ``message'' adds a method to the type of parameter x.

Just as in the rest of the language, the object extension of Objective CAML provides static typing through inference. When this mechanism does not have enough information to determine the type of an expression, a type variable is assigned. We have just seen that this process is also valid for typing objects; however, it sometimes leads to ambiguous situations which the user must resolve by explicitly giving type information.

# class a_point p0 =
object
val p = p0
method to_string() = p#to_string()
end ;;
Characters 6-89:
Some type variables are unbound in this type:
class a_point :
(< to_string : unit -> 'b; .. > as 'a) ->
object val p : 'a method to_string : unit -> 'b end
The method to_string has type unit -> 'a where 'a is unbound


We resolve this ambiguity by saying that parameter p0 has type #point.

# class a_point (p0 : #point) =
object
val p = p0
method to_string() = p#to_string()
end ;;
class a_point :
(#point as 'a) -> object val p : 'a method to_string : unit -> string end


In order to set type constraints in several places in a class declaration, the following syntax is used:

Syntax


constraint type1 = type2
The above example can be written specifying that parameter p0 has type 'a, then putting a type constraint upon variable 'a.

# class a_point (p0 : 'a) =
object
constraint 'a = #point
val p = p0
method to_string() = p#to_string()
end ;;
class a_point :
(#point as 'a) -> object val p : 'a method to_string : unit -> string end


Several type constraints can be given in a class declaration.

Warning


An open type cannot appear as the type of a method.


This strong restriction exists because an open type contains an uninstantiated type variable coming from the rest of the type. Since one cannot have a free variable type in a type declaration, a method containing such a type is rejected by type inference.

# class b_point p0 =
object
inherit a_point p0
method get = p
end ;;
Characters 6-77:
Some type variables are unbound in this type:
class b_point :
(#point as 'a) ->
object val p : 'a method get : 'a method to_string : unit -> string end
The method get has type #point where .. is unbound


In fact, due to the constraint ``constraint 'a = #point'', the type of get is the open type #point. The latter contains a free variable type noted by a double point (..), which is not allowed.

Inheritance and the Type of self

There exists an exception to the prohibition of a type variable in the type of methods: a variable may stand for the type of the object itself (self). Consider a method testing the equality between two points.

# class point_eq (x,y) =
object (self : 'a)
inherit point (x,y)
method eq (p:'a) = (self#get_x = p#get_x) && (self#get_y = p#get_y)
end ;;
class point_eq :
int * int ->
object ('a)
val mutable x : int
val mutable y : int
method distance : unit -> float
method eq : 'a -> bool
method get_x : int
method get_y : int
method moveto : int * int -> unit
method print : unit -> unit
method rmoveto : int * int -> unit
method to_string : unit -> string
end
The type of method eq is 'a -> bool, but the type variable stands for the type of the instance at construction time.

You can inherit from the class point_eq and redefine the method eq, whose type is still parameterized by the instance type.

# class colored_point_eq (xc,yc) c =
object (self : 'a)
inherit point_eq (xc,yc) as super
val c = (c:string)
method get_c = c
method eq (pc : 'a) = (self#get_x = pc#get_x) && (self#get_y = pc#get_y)
&& (self#get_c = pc#get_c)
end ;;
class colored_point_eq :
int * int ->
string ->
object ('a)
val c : string
val mutable x : int
val mutable y : int
method distance : unit -> float
method eq : 'a -> bool
method get_c : string
method get_x : int
method get_y : int
method moveto : int * int -> unit
method print : unit -> unit
method rmoveto : int * int -> unit
method to_string : unit -> string
end


The method eq from class colored_point_eq still has type 'a -> bool; but now the variable 'a stands for the type of an instance of class colored_point_eq. The definition of eq in class colored_point_eq masks the inherited one. Methods containing the type of the instance in their type are called binary methods. They will cause some limitations in the subtyping relation described in page ??.

Multiple Inheritance

With multiple inheritance, you can inherit data fields and methods from several classes. When there are identical names for fields or methods, only the last declaration is kept, according to the order of inheritance declarations. Nevertheless, it is possible to reference a method of one of the parent classes by associating different names with the inherited classes. This is not true for instance variables: if an inherited class masks an instance variable of a previously inherited class, the latter is no longer directly accessible. The various inherited classes do not need to have an inheritance relation. Multiple inheritance is of interest because it increases class reuse.

Let us define the abstract class geometric_object, which declares two virtual methods compute_area and compute_peri for computing the area and perimeter.

# class virtual geometric_object () =
object
method virtual compute_area : unit -> float
method virtual compute_peri : unit -> float
end;;


Then we redefine class rectangle as follows:

# class rectangle_1 ((p1,p2) :'a) =
object
constraint 'a = point * point
inherit printable ()
inherit geometric_object ()
val llc = p1
val urc = p2
method to_string () =
"["^llc#to_string()^","^urc#to_string()^"]"
method compute_area() =
float ( abs(urc#get_x - llc#get_x) * abs(urc#get_y - llc#get_y))
method compute_peri() =
float ( (abs(urc#get_x - llc#get_x) + abs(urc#get_y - llc#get_y)) * 2)
end;;
class rectangle_1 :
point * point ->
object
val llc : point
val urc : point
method compute_area : unit -> float
method compute_peri : unit -> float
method print : unit -> unit
method to_string : unit -> string
end


This implementation of classes respects the inheritance graph of figure 15.5.



Figure 15.5: Multiple inheritance.


In order to avoid rewriting the methods of class rectangle, we may directly inherit from rectangle, as shown in figure 15.6.



Figure 15.6: Multiple inheritance (continued).


In such a case, only the abstract methods of the abstract class geometric_object must be defined in rectangle_2.

# class rectangle_2 (p2 :'a) =
object
constraint 'a = point * point
inherit rectangle p2
inherit geometric_object ()
method compute_area() =
float ( abs(urc#get_x - llc#get_x) * abs(urc#get_y - llc#get_y))
method compute_peri() =
float ( (abs(urc#get_x - llc#get_x) + abs(urc#get_y - llc#get_y)) * 2)
end;;


Continuing in the same vein, the hierarchies printable and geometric_object could have been defined separately, until it became useful to have a class with both behaviors. Figure 15.7 displays the relations defined in this way.



Figure 15.7: Multiple inheritance (end).


If we assume that classes printable_rect and geometric_rect define instance variables for the corners of a rectangle, we get class rectangle_3 with four points (two per corner).

class rectangle_3 (p1,p2) =
inherit printable_rect (p1,p2) as super_print
inherit geometric_rect (p1,p2) as super_geo
end;;
In the case where methods of the same type exist in both classes ..._rect, then only the last one is visible. However, by naming parent classes (super_...), it is always possible to invoke a method from either parent.

Multiple inheritance allows factoring of the code by integrating methods already written from various parent classes to build new entities. The price paid is the size of constructed objects, which are bigger than necessary due to duplicated fields, or inherited fields useless for a given application. Furthermore, when there is duplication (as in our last example), communication between these fields must be established manually (update, etc.). In the last example for class rectangle_3, we obtain instance variables of classes printable_rect and geometric_rect. If one of these classes has a method which modifies these variables (such as a scaling factor), then it is necessary to propagate these modifications to variables inherited from the other class. Such a heavy communication between inherited instance variables often signals a poor modeling of the given problem.

Parameterized Classes

Parameterized classes let Objective CAML's parameterized polymorphism be used in classes. As with the type declarations of Objective CAML, class declarations can be parameterized with type variables. This provides new opportunities for genericity and code reuse. Parameterized classes are integrated with ML-like typing when type inference produces parameterized types.

The syntax differs slightly from the declaration of parameterized types; type parameters are between brackets.

Syntax


class ['a, 'b, ...] name = object ...end
The Objective CAML type is noted as usual: ('a,'b,...) name.

For instance, if a class pair is required, a naive solution would be to set:

# class pair x0 y0 =
object
val x = x0
val y = y0
method fst = x
method snd = y
end ;;
Characters 6-106:
Some type variables are unbound in this type:
class pair :
'a ->
'b -> object val x : 'a val y : 'b method fst : 'a method snd : 'b end
The method fst has type 'a where 'a is unbound


One again gets the typing error mentioned when class a_point was defined (page ??). The error message says that type variable 'a, assigned to parameter x0 (and therefore to x and fst), is not bound.

As in the case of parameterized types, it is necessary to parameterize class pair with two type variables, and force the type of construction parameters x0 and y0 to obtain a correct typing:

# class ['a,'b] pair (x0:'a) (y0:'b) =
object
val x = x0
val y = y0
method fst = x
method snd = y
end ;;
class ['a, 'b] pair :
'a ->
'b -> object val x : 'a val y : 'b method fst : 'a method snd : 'b end
Type inference displays a class interface parameterized by variables of type 'a and 'b.

When a value of a parameterized class is constructed, type parameters are instantiated with the types of the construction parameters:

# let p = new pair 2 'X';;
val p : (int, char) pair = <obj>
# p#fst;;
- : int = 2
# let q = new pair 3.12 true;;
val q : (float, bool) pair = <obj>
# q#snd;;
- : bool = true


Note


In class declarations, type parameters are shown between brackets, but in types, they are shown between parentheses.


Inheritance of Parameterized Classes

When inheriting from a parameterized class, one has to indicate the parameters of the class. Let us define a class acc_pair that inherits from ('a,'b) pair; we add two methods for accessing the fields, get1 and get2,

# class ['a,'b] acc_pair (x0 : 'a) (y0 : 'b) =
object
inherit ['a,'b] pair x0 y0
method get1 z = if x = z then y else raise Not_found
method get2 z = if y = z then x else raise Not_found
end;;
class ['a, 'b] acc_pair :
'a ->
'b ->
object
val x : 'a
val y : 'b
method fst : 'a
method get1 : 'a -> 'b
method get2 : 'b -> 'a
method snd : 'b
end
# let p = new acc_pair 3 true;;
val p : (int, bool) acc_pair = <obj>
# p#get1 3;;
- : bool = true


We can make the type parameters of the inherited parameterized class more precise, e.g. for a pair of points.

# class point_pair (p1,p2) =
object
inherit [point,point] pair p1 p2
end;;
class point_pair :
point * point ->
object
val x : point
val y : point
method fst : point
method snd : point
end


Class point_pair no longer needs type parameters, since parameters 'a and 'b are completely determined.

To build pairs of displayable objects (i.e. having a method print), we reuse the abstract class printable (see page ??), then we define the class printable_pair which inherits from pair.

# class printable_pair x0 y0 =
object
inherit [printable, printable] acc_pair x0 y0
method print () = x#print(); y#print ()
end;;


This implementation allows us to construct pairs of instances of printable, but it cannot be used for objects of another class with a method print.

We could try to open type printable used as a type parameter for acc_pair:

# class printable_pair (x0 ) (y0 ) =
object
inherit [ #printable, #printable ] acc_pair x0 y0
method print () = x#print(); y#print ()
end;;
Characters 6-149:
Some type variables are unbound in this type:
class printable_pair :
(#printable as 'a) ->
(#printable as 'b) ->
object
val x : 'a
val y : 'b
method fst : 'a
method get1 : 'a -> 'b
method get2 : 'b -> 'a
method print : unit -> unit
method snd : 'b
end
The method fst has type #printable where .. is unbound
This first attempt fails because methods fst and snd contain an open type.

So we shall keep the type parameters of the class, while constraining them to the open type #printable.

# class ['a,'b] printable_pair (x0 ) (y0 ) =
object
constraint 'a = #printable
constraint 'b = #printable
inherit ['a,'b] acc_pair x0 y0
method print () = x#print(); y#print ()
end;;
class ['a, 'b] printable_pair :
'a ->
'b ->
object
constraint 'a = #printable
constraint 'b = #printable
val x : 'a
val y : 'b
method fst : 'a
method get1 : 'a -> 'b
method get2 : 'b -> 'a
method print : unit -> unit
method snd : 'b
end


Then we construct a displayable pair containing a point and a colored point.

# let pp = new printable_pair
(new point (1,2)) (new colored_point (3,4) "green");;
val pp : (point, colored_point) printable_pair = <obj>
# pp#print();;
( 1, 2)( 3, 4) with color green- : unit = ()


Parameterized Classes and Typing

From the point of view of types, a parameterized class is a parameterized type. A value of such a type can contain weak type variables.

# let r = new pair [] [];;
val r : ('_a list, '_b list) pair = <obj>
# r#fst;;
- : '_a list = []
# r#fst = [1;2];;
- : bool = false
# r;;
- : (int list, '_a list) pair = <obj>


A parameterized class can also be viewed as a closed object type; therefore nothing prevents us from also using it as an open type with the sharp notation.

# let compare_nothing ( x : ('a, 'a) #pair) =
if x#fst = x#fst then x#mess else x#mess2;;
val compare_nothing :
< fst : 'a; mess : 'b; mess2 : 'b; snd : 'a; .. > -> 'b = <fun>


This lets us construct parameterized types that contain weak type variables that are also open object types.

# let prettytype x ( y : ('a, 'a) #pair) = if x = y#fst then y else y;;
val prettytype : 'a -> (('a, 'a) #pair as 'b) -> 'b = <fun>


If this function is applied to one parameter, we get a closure, whose type variables are weak. An open type, such as #pair, still contains uninstantiated parts, represented by the double point (..). In this respect, an open type is a partially known type parameter. Upon weakening such a type after a partial application, the displayer specifies that the type variable representing this open type has been weakened. Then the notation is _#pair.


# let g = prettytype 3;;
val g : ((int, int) _#pair as 'a) -> 'a = <fun>


Now, if function g is applied to a pair, its weak type is modified.

# g (new acc_pair 2 3);;
- : (int, int) acc_pair = <obj>
# g;;
- : (int, int) acc_pair -> (int, int) acc_pair = <fun>


Then we can no longer use g on simple pairs.

# g (new pair 1 1);;
Characters 4-16:
This expression has type (int, int) pair = < fst : int; snd : int >
but is here used with type
(int, int) acc_pair =
< fst : int; get1 : int -> int; get2 : int -> int; snd : int >
Only the second object type has a method get1


Finally, since parameters of the parameterized class can also get weakened, we obtain the following example.

# let h = prettytype [];;
val h : (('_b list, '_b list) _#pair as 'a) -> 'a = <fun>
# let h2 = h (new pair [] [1;2]);;
val h2 : (int list, int list) pair = <obj>
# h;;
- : (int list, int list) pair -> (int list, int list) pair = <fun>


The type of the parameter of h is no longer open. The following application cannot be typed because the argument is not of type pair.

# h (new acc_pair [] [4;5]);;
Characters 4-25:
This expression has type
('a list, int list) acc_pair =
< fst : 'a list; get1 : 'a list -> int list; get2 : int list -> 'a list;
snd : int list >
but is here used with type
(int list, int list) pair = < fst : int list; snd : int list >
Only the first object type has a method get1


Note


Parameterized classes of Objective CAML are absolutely necessary as soon as one has methods whose type includes a type variable different from the type of self.



Previous Contents Next ocaml-book-1.0/en/html/book-ora007.html0000644000000000000000000000402707453055377014501 0ustar Description of the CD-ROM Previous Contents Next

Description of the CD-ROM

The CD-ROM is provided as a hierarchy of files. At the root can be found the file index.html which presents the CD-ROM, as well as the five subdirectories below:
  • book: root of the HTML version of the book along with the solutions to the exercises;
  • apps: applications described in the book;
  • exercises: independent solutions to the proposed exercises;
  • distrib: set of distributions provided by Inria, as described in the next section;
  • tools: set of tools for development in Objective CAML;
  • docs: online documentation of the distribution and the tools.
To read the CD-ROM, start by opening the file index.html in the root using your browser of choice. To access directly the hypertext version of the book, open the file book/index.html. This file hierarchy, updated in accordance with readers' remarks, can be found posted on the editor's site:

Link


http://www.oreilly.fr



Previous Contents Next ocaml-book-1.0/en/html/book-ora052.html0000644000000000000000000023750007453055377014505 0ustar Exercises Previous Contents Next

Exercises

Polar coordinates

Coordinates as used in the library Graphics are Cartesian. There a line segment is represented by its starting point (x0,y0) and its end point (x1,y1). It can be useful to use polar coordinates instead. Here a line segment is described by its point of origin (x0,y0), a length (radius) (r) and an angle (a). The relation between Cartesian and Polar coordinates is defined by the following equations:


x1 = x0 + r * cos(a)
y1 = y0 + r * sin(a)
The following type defines the polar coordinates of a line segment:

# type seg_pol = {x:float; y:float; r:float; a:float};;
type seg_pol = { x: float; y: float; r: float; a: float }


  1. Write the function to_cart that converts polar coordinates to Cartesian ones.

    # let to_cart p =
    (p.x,p.y),(p.x +. p.r *. (cos p.a), p.y +. p.r *. (sin p.a)) ;;
    val to_cart : seg_pol -> (float * float) * (float * float) = <fun>


  2. Write the function draw_seg which displays a line segment defined by polar coordinates in the reference point of Graphics.

    # let draw_seg p =
    let (x1,y1),(x2,y2) = to_cart p in
    Graphics.moveto (int_of_float x1) (int_of_float y1);
    Graphics.lineto (int_of_float x2) (int_of_float y2) ;;
    val draw_seg : seg_pol -> unit = <fun>


  3. One of the motivations behind polar coordinates is to be able to easily apply transformations to line segments. A translation only modifies the point of origin, a rotation only affects the angle field and modifying the scale only changes the length field. Generally, one can represent a transformation as a triple of floats: the first represents the translation (we do not consider the case of translating the second point of the line segment here), the second the rotation and the third the scaling factor. Define the function app_trans which takes a line segment in polar coordinates and a triple of transformations and returns the new segment.

    # let app_trans seg ( da, dr, dxy ) =
    let _,(x1,y1) = to_cart {seg with r = seg.r *. dxy} in
    {x=x1; y=y1; a=seg.a +. da; r=seg.r *. dr} ;;
    val app_trans : seg_pol -> float * float * float -> seg_pol = <fun>


  4. One can construct recursive drawings by iterating transformations. Write the function draw_r which takes as arguments a line segment s, a number of iterations n, a list of transformations and displays all the segments resulting from the transformations on s iterated up to n.

    # let rec draw_r s n l =
    if n = 0 then ()
    else
    begin
    draw_seg s;
    List.iter (fun t -> draw_r (app_trans s t) (n-1) l) l
    end ;;
    val draw_r : seg_pol -> int -> (float * float * float) list -> unit = <fun>


  5. Verify that the following program does produce the images in figure 5.10.

    let pi = 3.1415927 ;;
    let s = {x=100.; y= 0.; a= pi /. 2.; r = 100.} ;;
    draw_r s 6 [ (-.pi/.2.),0.6,1.; (pi/.2.), 0.6,1.0] ;;
    Graphics.clear_graph();;
    draw_r s 6 [(-.pi /. 6.), 0.6, 0.766;
    (-.pi /. 4.), 0.55, 0.333;
    (pi /. 3.), 0.4, 0.5 ] ;;

    # Graphics.close_graph();;
    - : unit = ()
    # Graphics.open_graph " 200x200";;
    - : unit = ()
    # let pi = 3.1415927 ;;
    val pi : float = 3.1415927
    # let s = {x=100.; y= 0.; a= pi /. 2.; r = 100.} ;;
    val s : seg_pol = {x=100; y=0; r=100; a=1.57079635}
    # draw_r s 6 [ (-.pi/.2.),0.6,1.; (pi/.2.), 0.6,1.0] ;;
    - : unit = ()
    # Graphics.clear_graph();;
    - : unit = ()
    # draw_r s 6 [(-.pi /. 6.), 0.6, 0.766;
    (-.pi /. 4.), 0.55, 0.333;
    (pi /. 3.), 0.4, 0.5 ] ;;
    - : unit = ()


Figure 5.10: Recursive drawings.


Bitmap editor

We will attempt to write a small bitmap editor (similar to the command bitmap in X-window). For this we represent a bitmap by its dimensions (width and height), the pixel size and a two-dimensional table of booleans.

  1. Define a type bitmap_state describing the information necessary for containing the values of the pixels, the size of the bitmap and the colors of displayed and erased points.


    # type bitmap_state =
    {w : int; h : int; fg : Graphics.color; bg : Graphics.color;
    pix : bool array array; s : int} ;;
    type bitmap_state =
    { w: int;
    h: int;
    fg: Graphics.color;
    bg: Graphics.color;
    pix: bool array array;
    s: int }


  2. Write a function for creating bitmaps (create_bitmap) and for displaying bitmaps (draw_bitmap) .

    # let create_bitmap x y f g s =
    let r = Array.make_matrix x y false in
    { w = x; h = y; fg = f; bg = g; pix = r; s = s} ;;
    val create_bitmap :
    int -> int -> Graphics.color -> Graphics.color -> int -> bitmap_state =
    <fun>

    # let draw_pix i j s c =
    Graphics.set_color c;
    Graphics.fill_rect (i*s+1) (j*s+1) (s-1) (s-1) ;;
    val draw_pix : int -> int -> int -> Graphics.color -> unit = <fun>

    # let draw_bitmap b =
    for i=0 to b.w-1 do
    for j=0 to b.h-1 do
    draw_pix i j b.s (if b.pix.(i).(j) then b.fg else b.bg)
    done
    done ;;
    val draw_bitmap : bitmap_state -> unit = <fun>


  3. Write the functions read_bitmap and write_bitmap which respectively read and write in a file passed as parameter following the ASCII format of X-window. If the file does not exist, the function for reading creates a new bitmap using the function create_bitmap.

    # let read_file filename =
    let ic = open_in filename in
    let rec aux () =
    try
    let line = (input_line ic) in
    line :: (aux ())
    with End_of_file -> close_in ic ; []
    in aux ();;
    val read_file : string -> string list = <fun>

    # let read_bitmap filename =
    let r = Array.of_list (read_file filename) in
    let h = Array.length r in
    let w = String.length r.(0) in
    let b = create_bitmap w h Graphics.black Graphics.white 10 in
    for j = 0 to h - 1 do
    for i = 0 to w - 1 do
    b.pix.(i).(j) <- ( r.(j).[i] = '#')
    done
    done;
    b ;;
    val read_bitmap : string -> bitmap_state = <fun>

    # let write_bitmap filename b =
    let oc = open_out filename in
    let f x = output_char oc (if x then '#' else '-') in
    Array.iter (fun x -> (Array.iter f x); output_char oc '\n') b.pix ;
    close_out oc ;;
    val write_bitmap : string -> bitmap_state -> unit = <fun>
    A displayed pixel is represented by the character #, the absence of a pixel by the character -. Each line of characters represents a line of the bitmap. One can test the program using the functions atobm and bmtoa of X-window, which convert between this ASCII format and the format of bitmaps created by the command bitmap. Here is an example.

    
     ###################-------------#######---------######
     ###################---------------###-------------##--
     ###-----###-----###---------------###-------------#---
     ##------###------##----------------###-----------##---
     #-------###-------#-----------------###---------##----
     #-------###-------#-----------------###--------##-----
     --------###--------------------------###-------#------
     --------###-------###############-----###----##-------
     --------###-------###---------###------###--##--------
     --------###-------###----------##-------###-#---------
     --------###-------###-----------#-------#####---------
     --------###-------###-----------#--------###----------
     --------###-------###--------------------####---------
     --------###-------###--------------------####---------
     --------###-------###------#-----------##---###-------
     --------###-------###------#----------##----###-------
     --------###-------##########----------#------###------
     --------###-------##########---------##-------###-----
     --------###-------###------#--------##--------###-----
     --------###-------###------#-------##----------###----
     --------###-------###--------------#------------###---
     ------#######-----###-----------#######--------#######
     ------------------###---------------------------------
     ------------------###-----------#---------------------
     ------------------###-----------#---------------------
     ------------------###----------##---------------------
     ------------------###---------###---------------------
     ------------------###############---------------------
    
  4. We reuse the skeleton for interactive loops on page ?? to construct the graphical interface of the editor. The human-computer interface is very simple. The bitmap is permanently displayed in the graphical window. A mouse click in one of the slots of the bitmap inverts its color. This change is reflected on the screen. Pressing the key 'S' saves the bitmap in a file. The key 'Q' terminates the program.
    • Write a function start of type bitmap_state -> unit -> unit which opens a graphical window and displays the bitmap passed as parameter.

      # exception End ;;
      exception End
      # let skel f_init f_end f_key f_mouse f_except =
      f_init ();
      try
      while true do
      try
      let s = Graphics.wait_next_event
      [Graphics.Button_down; Graphics.Key_pressed] in
      if s.Graphics.keypressed
      then f_key s.Graphics.key
      else if s.Graphics.button
      then f_mouse s.Graphics.mouse_x s.Graphics.mouse_y
      with
      End -> raise End
      | e -> f_except e
      done
      with
      End -> f_end () ;;
      val skel :
      (unit -> 'a) ->
      (unit -> unit) ->
      (char -> unit) -> (int -> int -> unit) -> (exn -> unit) -> unit = <fun>

      # let start b () =
      let sw = 1+b.w*b.s and sh = 1+b.h*b.s in
      Graphics.open_graph (" " ^ (string_of_int sw) ^ "x" ^ (string_of_int sh)) ;
      Graphics.set_color (Graphics.rgb 150 150 150) ;
      Graphics.fill_rect 0 0 sw sh ;
      draw_bitmap b ;;
      val start : bitmap_state -> unit -> unit = <fun>
    • Write a function stop that closes the graphical window and exits the program.

      # let stop () = Graphics.close_graph() ; exit 0 ;;
      val stop : unit -> 'a = <fun>
    • Write a function mouse of type bitmap_state -> int -> int -> unit which modifies the pixel state corresponding to the mouse click and displays the change.

      # let mouse b x y =
      let i,j = (x / b.s),(y/b.s) in
      if ( i < b.w ) && ( j < b.h) then
      begin
      b.pix.(i).(j) <- not b.pix.(i).(j) ;
      draw_pix i j b.s (if b.pix.(i).(j) then b.fg else b.bg)
      end ;;
      val mouse : bitmap_state -> int -> int -> unit = <fun>
    • Write a function key of type string -> bitmap_state -> char -> unit which takes as arguments the name of a file, a bitmap and the char of the pressed key and executes the associated actions: saving to a file for the key 'S' and raising of the exception End for the key 'Q'.

      # let key filename b c =
      match c with
      'q' | 'Q' -> raise End
      | 's' | 'S' -> write_bitmap filename b
      | _ -> () ;;
      val key : string -> bitmap_state -> char -> unit = <fun>

  5. Write a function go which takes the name of a file as parameter, loads the bitmap, displays it and starts the interactive loop.

    # let go name =
    let b = try
    read_bitmap name
    with
    _ -> create_bitmap 10 10 Graphics.black Graphics.white 10
    in skel (start b) stop (key name b) (mouse b) (fun e -> ()) ;;
    val go : string -> unit = <fun>

Earth worm

The earth worm is a small, longish organism of a certain size which grows over time while eating objects in a world. The earth worm moves constantly in one direction. The only actions allowing a player to control it are changes in direction. The earth worm vanishes if it touches a border of the world or if it passes over a part of its body. It is most often represented by a vector of coordinates with two principal indices: its head and its tail. A move will therefore be computed from the new coordinates of its head, will display it and erase the tail. A growth step only modifies its head without affecting the tail of the earth worm.
  1. Write the Objective CAML type or types for representing an earth worm and the world where it evolves. One can represent an earth worm by a queue of its coordinates.

    # type cell = Empty | Full ;;
    type cell = | Empty | Full

    # type world = { sx : int; sy : int; cells : cell array array } ;;
    type world = { sx: int; sy: int; cells: cell array array }

    # type worm = { mutable head : int; mutable queue : int; mutable size : int;
    mutable vx : int; mutable vy : int;
    pos : (int * int) array } ;;
    type worm =
    { mutable head: int;
    mutable queue: int;
    mutable size: int;
    mutable vx: int;
    mutable vy: int;
    pos: (int * int) array }

    # type game = { w : world; wo : worm; s_cell : int;
    fg : Graphics.color; bg : Graphics.color } ;;
    type game =
    { w: world;
    wo: worm;
    s_cell: int;
    fg: Graphics.color;
    bg: Graphics.color }


  2. Write a function for initialization and displaying an earth worm in a world.

    # let init_world sx sy =
    { sx = sx; sy = sy; cells = Array.create_matrix sx sy Empty } ;;
    val init_world : int -> int -> world = <fun>

    # let init_worm s s_max x =
    if s > x then failwith "init_worm"
    else
    begin
    let wo = { head = s-1; queue = 0; size = s; vx = 1; vy = 0;
    pos = Array.create s_max (0,0) } in
    let y = x / 2
    and rx = ref (x/2 - s/2) in
    for i=0 to s-1 do wo.pos.(i) <- (!rx,y) ; incr rx done ;
    wo
    end ;;
    val init_worm : int -> int -> int -> worm = <fun>

    # let init_game s s_max sx sy sc c1 c2 =
    let j = { w = init_world sx sy; wo = init_worm s s_max sx;
    s_cell = sc; fg = c1; bg = c2 } in
    for i=j.wo.head to j.wo.queue do
    let (x,y) = j.wo.pos.(i) in
    j.w.cells.(x).(y) <- Full
    done ;
    j ;;
    val init_game :
    int -> int -> int -> int -> int -> Graphics.color -> Graphics.color -> game =
    <fun>

    # let display_cell x y s c =
    Graphics.set_color c;
    Graphics.fill_rect (x*s) (y*s) s s ;;
    val display_cell : int -> int -> int -> Graphics.color -> unit = <fun>

    # let display_world game =
    let w = game.w in
    for i=0 to w.sx-1 do
    for j=0 to w.sy-1 do
    let col = if w.cells.(i).(j) = Full then game.fg else game.bg in
    display_cell i j game.s_cell col
    done
    done ;;
    val display_world : game -> unit = <fun>


  3. Modify the function skel of the skeleton of the program which causes an action at each execution of the interactive loop, parameterized by a function. The treatment of keyboard events must not block.

    (************** interaction **********)

    # let tempo ti =
    for i = 0 to ti do ignore (i * ti * ti ) done ;;
    val tempo : int -> unit = <fun>

    # exception End;;
    exception End
    # let skel f_init f_end f_key f_mouse f_except f_run =
    f_init ();
    try
    while true do
    try
    tempo 200000 ;
    if Graphics.key_pressed() then f_key (Graphics.read_key()) ;
    f_run ()
    with
    End -> raise End
    | e -> f_except e
    done
    with
    End -> f_end () ;;
    val skel :
    (unit -> 'a) ->
    (unit -> unit) ->
    (char -> unit) -> 'b -> (exn -> 'c) -> (unit -> 'c) -> unit = <fun>


  4. Write a function run which advances the earth worm in the game. This function raises the exception Victory (if the worm reaches a certain size) and Loss if it hits a full slot or a border of the world.

    # exception Loss;;
    exception Loss
    # exception Victory;;
    exception Victory

    # let run game temps itemps () =
    incr temps;
    let wo = game.wo in
    let s = Array.length wo.pos in
    let sx,sy = wo.pos.(wo.head) in
    let nx,ny = sx+wo.vx,sy+wo.vy in
    if (nx < 0 ) || (nx >= game.w.sx) || (ny < 0) || (ny >= game.w.sy)
    then raise Loss
    else if game.w.cells.(nx).(ny) = Full then raise Loss
    else if wo.head = wo.queue then raise Victory
    else
    begin
    let nhead = (wo.head + 1) mod s in
    wo.head <- nhead ;
    wo.pos.(wo.head) <- (nx,ny);
    game.w.cells.(nx).(ny) <- Full ;
    display_cell nx ny game.s_cell game.fg ;
    if (!temps mod !itemps < (!itemps - 2)) then
    begin
    let qx,qy = wo.pos.(wo.queue) in
    game.w.cells.(qx).(qy) <- Empty ;
    display_cell qx qy game.s_cell game.bg ;
    wo.queue <- (wo.queue + 1) mod s
    end
    end ;;
    val run : game -> int ref -> int ref -> unit -> unit = <fun>


  5. Write a function for keyboard interaction which modifies the direction of the earth worm.

    # let key lact game c =
    match c with
    'q' | 'Q' -> raise End
    | 'p' | 'P' -> ignore (Graphics.read_key ())
    | '2' | '4' | '6' | '8' ->
    let dx,dy = List.assoc c lact in
    game.wo.vx <- dx ;
    game.wo.vy <- dy
    | _ -> () ;;
    val key : (char * (int * int)) list -> game -> char -> unit = <fun>


  6. Write the other utility functions for handling interaction and pass them to the new skeleton of the program.

    # let start game () =
    let sw = game.s_cell * game.w.sx and sh = game.s_cell * game.w.sy in
    let size = (string_of_int sw) ^ "x" ^ (string_of_int sh) in
    Graphics.open_graph (" " ^ size) ;
    Graphics.set_color (Graphics.rgb 150 150 150);
    Graphics.fill_rect 0 0 sw sh;
    display_world game;
    ignore (Graphics.read_key()) ;;
    val start : game -> unit -> unit = <fun>

    # let stop game () =
    ignore (Graphics.read_key());
    Graphics.close_graph() ;;
    val stop : 'a -> unit -> unit = <fun>

    # let mouse x y = () ;;
    val mouse : 'a -> 'b -> unit = <fun>

    # let except e = match e with
    Loss -> print_endline "LOSS"; raise End
    | Victory -> print_endline "VICTORY"; raise End
    | e -> raise e ;;
    val except : exn -> 'a = <fun>


  7. Write the initiating function which starts the application.

    # let la = [ ('2',(0,-1)) ; ('4',(-1,0)) ; ('6',(1,0)) ; ('8',(0,1)) ] ;;
    val la : (char * (int * int)) list =
    ['2', (0, -1); '4', (-1, 0); '6', (1, 0); '8', (0, ...)]

    # let go x y sc =
    let col = Graphics.rgb 150 150 150 in
    let game = init_game 5 (x*y /2) x y sc Graphics.black col in
    skel (start game) (stop game) (key la game) mouse except (run game (ref 0) (ref 100));;
    val go : int -> int -> int -> unit = <fun>

Previous Contents Next ocaml-book-1.0/en/html/book-ora134.html0000644000000000000000000012605107453055400014467 0ustar Exercises Previous Contents Next

Exercises

Association Lists

In this first simple exercise, we will implement a polymorphic abstract type for association lists, and present two different views of the implementation.

  1. Define a signature ALIST declaring an abstract type with two type parameters (one for the keys, the other for the associated values), a creation function, an add function, a lookup function, a membership test, and a deletion function.

    module type ALIST =
    sig
    type ('a,'b) t
    val create : unit -> ('a,'b) t
    val add : 'a -> 'b -> ('a,'b) t -> ('a,'b) t
    val get : 'a -> ('a,'b) t -> 'b
    val mem : 'a -> ('a,'b) t -> bool
    val rem : 'a -> ('a,'b) t -> ('a,'b) t
    end ;;
    Characters 142-148:
    Syntax error
    The interface should be functional, i.e. without in-place modifications of the abstract type.

  2. Define a module Alist implementing the signature ALIST

    # module Alist:ALIST =
    struct
    type ('a,'b) t = ('a*'b) list
    let create () = []
    let add k v al = (k,v)::al
    let get = List.assoc
    let mem = List.mem_assoc
    let rem = List.remove_assoc
    end ;;
    Characters 14-19:
    Unbound module type ALIST


  3. Define a signature ADM_ALIST for ``administrators'' of association lists. Administrators can only create association lists, and add or remove entries from a list.

    # module type ADM_ALIST =
    sig
    type ('a,'b) t
    val create : unit -> ('a,'b) t
    val add : 'a -> 'b -> ('a,'b) t -> ('a,'b) t
    val rem : 'a -> ('a,'b) t -> ('a,'b) t
    end ;;
    module type ADM_ALIST =
    sig
    type ('a, 'b) t
    val create : unit -> ('a, 'b) t
    val add : 'a -> 'b -> ('a, 'b) t -> ('a, 'b) t
    val rem : 'a -> ('a, 'b) t -> ('a, 'b) t
    end


  4. Define a signature USER_ALIST for ``users'' of association lists. Users can only perform lookups and membership tests.

    # module type USER_ALIST =
    sig
    type ('a,'b) t
    val get : 'a -> ('a,'b) t -> 'b
    val mem : 'a -> ('a,'b) t -> bool
    end ;;
    module type USER_ALIST =
    sig
    type ('a, 'b) t
    val get : 'a -> ('a, 'b) t -> 'b
    val mem : 'a -> ('a, 'b) t -> bool
    end


  5. Define two modules AdmAlist and UserAlist for administrators and for users. Keep in mind that users must be able to access lists created by administrators.

    # module AdmAlist = (Alist:ADM_ALIST with type ('a,'b) t = ('a,'b) Alist.t) ;;
    Characters 20-25:
    Unbound module Alist
    # module UserAlist = (Alist:USER_ALIST with type ('a,'b) t = ('a,'b) Alist.t) ;;
    Characters 20-25:
    Unbound module Alist

Parameterized Vectors

This exercise illustrates the genericity and code reuse abilities of parameterized modules. We will define a functor for manipulating two-dimensional vectors (pairs of (x,y) coordinates) that can be instantiated with different types for the coordinates.

Numbers have the following signature:

# module type NUMBER =
sig
type a
type t
val create : a -> t
val add : t -> t -> t
val string_of : t -> string
end ;;


  1. Define the functor FVector, parameterized by a module of signature NUMBER, and defining a type t of two-dimensional vectors over these numbers, a creation function, an addition function, and a conversion to strings.

    # module FVector (N:NUMBER) =
    struct
    type e = N.t
    type t = { mutable vx:e; mutable vy:e }
    let create x0 y0 = { vx=x0; vy=y0 }
    let add v1 v2= {vx = N.add v1.vx v2.vx; vy = N.add v1.vy v2.vy}
    let string_of v= "("^N.string_of v.vx^" , "^N.string_of v.vy^")"
    end ;;
    module FVector :
    functor(N : NUMBER) ->
    sig
    type e = N.t
    and t = { mutable vx: e; mutable vy: e }
    val create : e -> e -> t
    val add : t -> t -> t
    val string_of : t -> string
    end


  2. Define a signature VECTOR, without parameters, where the types of numbers and vectors are abstract.

    # module type VECTOR =
    sig
    type e
    type t
    val create : e -> e -> t
    val add :t -> t -> t
    val string_of : t -> string
    end ;;
    module type VECTOR =
    sig
    type e
    and t
    val create : e -> e -> t
    val add : t -> t -> t
    val string_of : t -> string
    end


  3. Define three structures Rational, Float et Complex implementing the signature NUMBER.

    # module Rational:NUMBER =
    struct
    type a = int*int
    type t = { p:int; q:int }
    let create (p0,q0) = { p=p0; q=q0 }
    let add r1 r2 = { p = r1.p*r2.q + r2.p*r1.q; q = r1.q*r2.q }
    let string_of r = (string_of_int r.p)^"/"^(string_of_int r.q)
    end ;;
    module Rational : NUMBER

    # module Float:NUMBER =
    struct
    type a = float
    type t = a
    let create x = x
    let add = (+.)
    let string_of = string_of_float
    end ;;
    module Float : NUMBER

    # module Complex:NUMBER =
    struct
    type a = float*float
    type t = { r:float; i:float }
    let create (r0, i0) = { r=r0; i=i0 }
    let add c1 c2 = { r = c1.r+.c2.r; i = c1.i+.c2.i }
    let string_of c =
    (string_of_float c.r)^"+"^(string_of_float c.r)^"*i"
    end ;;
    module Complex : NUMBER


  4. Use these structures to define (by functor application) three modules for vectors of rationals, reals and complex.

    # module RatVect:VECTOR = FVector(Rational) ;;
    module RatVect : VECTOR
    # module FloVect:VECTOR = FVector(Float) ;;
    module FloVect : VECTOR
    # module ComVect:VECTOR = FVector(Complex) ;;
    module ComVect : VECTOR

Lexical Trees

This exercise follows up on the lexical trees introduced in chapter 2, page ??. The goal is to define a generic module for handling lexical trees, parameterized by an abstract type of words.

  1. Define the signature WORD defining an abstract type alpha for letters of the alphabet, and another abstract type t for words on this alphabet. Declare also the empty word, the conversion from an alphabet letter to a one-letter word, the accessor to a letter of a word, the sub-word operation, the length of a word, and word concatenation.

    # module type WORD =
    sig
    type alpha
    type t
    val null : t
    val of_alpha : alpha -> t
    val get : t -> int -> alpha
    val sub : t -> int -> int -> t
    val length : t -> int
    val concat : t -> t -> t
    end ;;
    module type WORD =
    sig
    type alpha
    and t
    val null : t
    val of_alpha : alpha -> t
    val get : t -> int -> alpha
    val sub : t -> int -> int -> t
    val length : t -> int
    val concat : t -> t -> t
    end


  2. Define the functor LexTree, parameterized by a module implementing WORD, that defines (as a function of the types and operations over words) the type of lexical trees and functions exists, insert et select similar to those from chapter 2, page ??.

    # module LexTree (W:WORD) =
    struct
    type node = Letter of W.alpha * bool * t
    and t = node list

    let rec exist m d =
    let aux sm i n =
    match d with
    [] -> false
    | (Letter (a,b,l))::q when a = W.get sm i ->
    if n = 1 then b else exist (W.sub sm (i+1) (n-1)) l
    | (Letter (a,b,l))::q when a = W.get sm i ->
    exist sm q
    in aux m 0 (W.length m)

    let rec insert m d =
    let aux sm i n =
    if n = 0 then d
    else
    match d with
    [] ->
    let aux = insert (W.sub sm (i+1) (n-1)) [] in
    [ Letter (W.get sm i, n = 1, aux) ]
    | (Letter(a,b,l))::q when a = W.get sm i ->
    if n = 1 then (Letter(a,true,l))::q
    else Letter(a,b,add (W.sub sm (i+1) (n-1)) l)::q
    | (Letter(a,b,l))::q -> (Letter(a,b,l))::(ajoute sm q)
    in aux m 0 (W.length m)

    let rec select n d = match d with
    [] -> []
    | (Letter(a,b,l))::q when n=1 ->
    let f (Letter(a,b,_)) = if b then W.of_alpha a else W.null in
    List.filter ((<>) W.null) (List.map f d)
    | (Letter(a,b,l))::q ->
    let r = select (n-1) l and r2 = select n q in
    let pr = List.map (function s -> W.concat (W.of_alpha a) s) r in
    pr@r2
    end ;;
    Characters 161-407:
    Warning: this pattern-matching is not exhaustive.
    Here is an example of a value that is not matched:
    _::_
    Characters 853-854:
    This expression has type t = node list but is here used with type W.t dlist


  3. Define the module Chars implementing the WORD signature for the types alpha = char and t = string. Use it to obtain a module CharDict implementing dictionaries whose keys are character strings.

    # module Chars =
    struct
    type alpha = char
    type t = string
    let null = ""
    let of_alpha c = String.make 1 c
    let get s i =
    try s.[i]
    with Invalid_argument(_) -> raise (Invalid_argument "Chars.get")
    let sub s i1 i2 =
    try String.sub s i1 i2
    with Invalid_argument(_) -> raise (Invalid_argument "Chars.sub")
    let length = String.length
    let concat = (^)
    end ;;
    module Chars :
    sig
    type alpha = char
    and t = string
    val null : string
    val of_alpha : char -> string
    val get : string -> int -> char
    val sub : string -> int -> int -> string
    val length : string -> int
    val concat : string -> string -> string
    end

    # module CharsDic = LexTree(Chars) ;;
    Characters 19-26:
    Unbound module LexTree

Previous Contents Next ocaml-book-1.0/en/html/book-ora145.html0000644000000000000000000003437207453055400014475 0ustar Functional Style Previous Contents Next

Functional Style

Object-oriented programming usually has an imperative style. A message is sent to an object that physically modifies its internal state (i.e. its data fields). It is also possible to use a functional approach to object-oriented programming: sending a message returns a new object.

Object Copy

Objective CAML provides a special syntactic construct for returning a copy of an object self with some of the fields modified.

Syntax


{< name1=expr1;...; namen=exprn >}
This way we can define functional points where methods for relative moves have no side effect, but instead return a new point.

# class f_point p =
object
inherit point p
method f_rmoveto_x (dx) = {< x = x + dx >}
method f_rmoveto_y (dy) = {< y = y + dy >}
end ;;
class f_point :
int * int ->
object ('a)
val mutable x : int
val mutable y : int
method distance : unit -> float
method f_rmoveto_x : int -> 'a
method f_rmoveto_y : int -> 'a
method get_x : int
method get_y : int
method moveto : int * int -> unit
method print : unit -> unit
method rmoveto : int * int -> unit
method to_string : unit -> string
end


With the new methods, movement no longer modifies the receiving object; instead a new object is returned that reflects the movement.

# let p = new f_point (1,1) ;;
val p : f_point = <obj>
# print_string (p#to_string()) ;;
( 1, 1)- : unit = ()
# let q = p#f_rmoveto_x 2 ;;
val q : f_point = <obj>
# print_string (p#to_string()) ;;
( 1, 1)- : unit = ()
# print_string (q#to_string()) ;;
( 3, 1)- : unit = ()


Since these methods construct an object, it is possible to send a message directly to the result of the method f_rmoveto_x.

# print_string ((p#f_rmoveto_x 3)#to_string()) ;;
( 4, 1)- : unit = ()


The result type of the methods f_rmoveto_x and f_rmoveto_y is the type of the instance of the defined class, as shown by the 'a in the type of f_rmoveto_x.

# class f_colored_point (xc, yc) (c:string) =
object
inherit f_point(xc, yc)
val color = c
method get_c = color
end ;;
class f_colored_point :
int * int ->
string ->
object ('a)
val color : string
val mutable x : int
val mutable y : int
method distance : unit -> float
method f_rmoveto_x : int -> 'a
method f_rmoveto_y : int -> 'a
method get_c : string
method get_x : int
method get_y : int
method moveto : int * int -> unit
method print : unit -> unit
method rmoveto : int * int -> unit
method to_string : unit -> string
end


Sending f_rmoveto_x to an instance of f_colored_point returns a new instance of f_colored_point.

# let fpc = new f_colored_point (2,3) "blue" ;;
val fpc : f_colored_point = <obj>
# let fpc2 = fpc#f_rmoveto_x 4 ;;
val fpc2 : f_colored_point = <obj>
# fpc2#get_c;;
- : string = "blue"


One can also obtain a copy of an arbitrary object, using the the primitive copy from module Oo:

# Oo.copy ;;
- : (< .. > as 'a) -> 'a = <fun>
# let q = Oo.copy p ;;
val q : f_point = <obj>
# print_string (p#to_string()) ;;
( 1, 1)- : unit = ()
# print_string (q#to_string()) ;;
( 1, 1)- : unit = ()
# p#moveto(4,5) ;;
- : unit = ()
# print_string (p#to_string()) ;;
( 4, 5)- : unit = ()
# print_string (q#to_string()) ;;
( 1, 1)- : unit = ()


Example: a Class for Lists

A functional method may use the object itself, self, to compute the value to be returned. Let us illustrate this point by defining a simple hierarchy of classes for representing lists of integers.

First we define the abstract class, parameterized by the type of list elements.

# class virtual ['a] o_list () =
object
method virtual empty : unit -> bool
method virtual cons : 'a -> 'a o_list
method virtual head : 'a
method virtual tail : 'a o_list
end;;


We define the class of non empty lists.

# class ['a] o_cons (n ,l) =
object (self)
inherit ['a] o_list ()
val car = n
val cdr = l
method empty () = false
method cons x = new o_cons (x, (self : 'a #o_list :> 'a o_list))
method head = car
method tail = cdr
end;;
class ['a] o_cons :
'a * 'a o_list ->
object
val car : 'a
val cdr : 'a o_list
method cons : 'a -> 'a o_list
method empty : unit -> bool
method head : 'a
method tail : 'a o_list
end


We should note that method cons returns a new instance of 'a o_cons. To this effect, the type of self is constrained to 'a #o_list, then subtyped to 'a o_list. Without subtyping, we would obtain an open type ('a #o_list), which appears in the type of the methods, and is strictly forbidden (see page ??). Without the additional constraint, the type of self could not be a subtype of 'a o_list.

This way we obtain the expected type for method cons. So now we know the trick and we define the class of empty lists.

# exception EmptyList ;;
# class ['a] o_nil () =
object(self)
inherit ['a] o_list ()
method empty () = true
method cons x = new o_cons (x, (self : 'a #o_list :> 'a o_list))
method head = raise EmptyList
method tail = raise EmptyList
end ;;


First of all we build an instance of the empty list, and then a list of integers.

# let i = new o_nil ();;
val i : '_a o_nil = <obj>
# let l = new o_cons (3,i);;
val l : int o_list = <obj>
# l#head;;
- : int = 3
# l#tail#empty();;
- : bool = true
The last expression sends the message tail to the list containing the integer 3, which triggers the method tail from the class 'a o_cons. The message empty(), which returns true, is sent to this result. You can see that the method which has been executed is empty from the class 'a o_nil.


Previous Contents Next ocaml-book-1.0/en/html/book-ora191.html0000644000000000000000000000742707453055401014500 0ustar To Learn More Previous Contents Next

To Learn More

Communication between distant Objective CAML programs can be rich. Use of text protocols is greatly facilitated by utilities for syntactic analysis (see Chapter 11). The persistence mechanism offered by the Marshal library (see Chapter 8) allows sending complex data in its internal format including functional values if the two communicating programs are the same. The main deficiency of that mechanism is the inability to send instances of classes. One solution to that problem is to use an ORB (Object Request Broker) to transmit objects or invoke remote methods. This architecture already exists in many object-oriented languages in the form of the CORBA (Common ORB Architecture) standard. This standard from the OMG (Object Management Group), which debuted in 1990, allows the use of remote objects, and is independent of the implementation language used to create classes.

Link


http://www.omg.org
The two principal functions of CORBA are the ability to send objects to a remote program and, especially, the ability to use the same object at many locations in a network, in order to call methods which can modify its instance variables. Further, this standard is independent of the language used to implement these remote objects. To that end, an ORB furnishes a description language for interfaces called IDL (Interface Definition Language), in the manner of CAMLIDL for the interface between Objective CAML and C. For the moment, there is no ORB that works with Objective CAML, but it is possible to build one, since the IDL language is an abstraction of object-oriented languages with classes. To simplify, CORBA furnishes a software bus (IIOP) that allows transferring and addressing remote data.

The ability to reference the same object at many points in a network simulates distributed shared memory, which is not without problems for automatic garbage collection.

The ability to reference a remote object does not cause code to be transferred. One can only receive a copy of an instance of a class if the class exists on the server. For certain client-server applications, it may be necessary to use dynamic loading of code (such as in Java applets) and even to migrate processes along with their code. An interesting example of dynamic loading of remote code is the MMM browser built in Objective CAML by Franois Rouaix:

Link


http://caml.inria.fr/~rouaix/mmm/
This browser can be used conventionally to view Web pages, but can also load Objective CAML applets from a server and run them in a graphical window.






Previous Contents Next ocaml-book-1.0/en/html/book-ora044.html0000644000000000000000000000435107453055377014502 0ustar Introduction Previous Contents Next

Introduction

This chapter presents the Graphics library, which is included in the distribution of the Objective CAML-language. This library is designed in such a way that it works identically under the main graphical interfaces of the most commonly used operating systems: Windows, MacOS, Unix with X-Windows. Graphics permits the realization of drawings which may contain text and images, and it handles basic events like mouse clicks or pressed keys.

The model of programming graphics applied is the ``painter's model:'' the last touch of color erases the preceding one. This is an imperative model where the graphics window is a table of points which is physically modified by each graphics primitive. The interactions with the mouse and the keyboard are a model of event-driven programming: the primary function of the program is an infinite loop waiting for user interaction. An event starts execution of a special handler, which then returns to the main loop to wait for the next event.

Although the Graphics library is very simple, it is sufficient for introducing basic concepts of graphical interfaces, and it also contains basic elements for developing graphical interfaces that are rich and easy to use by the programmer.


Previous Contents Next ocaml-book-1.0/en/html/book-ora050.gif0000644000000000000000000000276407452056111014270 0ustar GIF89awww!,*޼Hʶ jL ! flJKЩbS-C1355r'9.='8WhH)9IYiyi5 *:JZ*iں*;K +PKy L\l:l =,] MMz.JY ~x==N/^oJâq  /d5tP!-EduQ+ -V4.SF($yPɓzEÏ1'sRHI,q ejISNn\3@L3qUT[VZk$Nztf՞cB6ώfOL%mN1 k^j 3`~'v1`ƔE!ͺM [q@D;ά1M?3m:PJo|.LkZMjBId;idJ.9$L> e8)%n%xUDYwZzI:d?IenM")Igc 'si |6 _Yi-h& ,~v"je7A UjhBrঊi#G*fzuz$ f蘩~Joc:J߳ޚ|ܙʬJޕbX—nykǂ;-ؒ%T-Jj,{>F-yp;άv lok\/:0{pR0k 1(Lp\嵦6l"L&n턝=f@Y 4эf:J'H]5O]Z+sKg"6? 7]Ms׍wͷ{ N5ژW6DG䔯cdn#p>24 >sȩɴ.9 53nj)c=Xf&uŻVa*ms3o 5 i߬,{&'<~̷>(`U%~s[ ]m~1hw|ln@<܆D!*=1"y HeձˈB2"< Fom;ţWv= y*g7m8KɸGBqdhHb2$'/ D%JiJ42dX%+Wr!-ki[R ܥz0%&1ac*"ʄ0)gBҜfi3T37&7`ps,9P;ocaml-book-1.0/en/html/book-ora019.gif0000644000000000000000000000150007452056111014260 0ustar GIF89a!,{޼oxb؈fLp-ݔC|}Pa<*el^~iCo_e#8Р6mcMUETtm!HG$&UBbʥ"yd͒:|3ѿ-*򨘟na*iáR ˇ5֭\I\jlؤTNaYg׶uیطpe.]vbJ| 7obË+nLrd/Wͅ'Mۛ۰G9Y|qWg@ZZ{r㥃orwߣ}?Z4_o;mG }5B~ӕVA.WSaN.ԃ$}!q.(c3Fa1Nx#\R)戝P= ފ;KBI*yɕhY]~ bz9fiehffjc)uVEN)U|Q~$ j苉ڄ>:*SF9KV"&]i|,jjv*r*h6f~v+:k_d +lK+*{,9J;-z+*U;ocaml-book-1.0/en/html/book-ora032.gif0000644000000000000000000000421607452056111014262 0ustar GIF89aUUU!,ڋ޼H% 皨Ğ4+ǤAMJu͆[Ss]ˮY;>Ǵ#͵ Oվoq'EhQx!H鐇H 890iz pj'  PZk+A7zۋ[;l[L+,iLi2KR<ӝ] -Xm΋^^<}&LoKYHpAns!$ hh$Ʊ.CrYȓ&d&e0d'&h,O.WBkGmDiSDJ2M/bt+W_<jY']Ѧ5HNsک[ ݝn݋_ >8z;~ ƜȔ+Gl9f`7{hoOFnLj8|,mԲg{xtmEvm8|zp[x3T;^1y *V:&`#>zr^{`6uL"|r|R I)\hzx~ ~[E݅2hQkއ 0 ;wD$0"|Jx*B^8H`8{`U=20$ @pevc ?͔MboBFJ)$ bJkإb)eh Ȥg y*^A+F@"zQvإ̀dЇiTԂ,,(jӕSm9˕ȥ1ȃV6 Tk-y`Ia*\.[F+B,G8bL*iy"FןǂW2/8uQ{*vC|JǺ&L1t0Crͽɬ8C<3j1fD_6Y77-bROMuV_ cMEsm%Kc=UHQ=Tuv?IvAm0vtmQ38<]GZᆯHU7֖WogSӟg}ӣlx٫:ᯯߖ#x㞑d޻ɿ;yR5 c-GG/Vg=L[_ ~ǟ~Qu?~e+̛s@%|Z*H(rX9e3[)ZƤrZ Eh &C \!Z9,0V8ҡaB b',bm I,!:qW*GvQ41BHDfQ]PT4L -pyHD ɬE.qR)D%4*}dcӨ0m%u o\YI倲5J,bT\CZInYYC 2`eK :L d0$eHY2v2&IjJm d3'pN,R&;Y4K<%T\t'1qOGO2=oMu f8>1 ]cCHn̠kY IX"4ݘ6YAib&MzCE%>(ctXf^!KB Xo% 'a n_CѲ@dҲ6EmjMK y6mu: 9^tKO[Эv pWjLDWi3{]fWM^.nG nck|]v\/}] `fu-5ޒB®މa7m Ռ4H,8.&ړ%h`b6uFbU.2wTV j92[dc|l7_`Dԅ] 1 [b o1ܔNıj JWDrWgUDurdKg\8*)! ]fN j ^G̹cC&gYU qQ(ןx<7 1{ʖJv & 1֧};`cOl";ocaml-book-1.0/en/html/book-ora062.html0000644000000000000000000000124107453055401014461 0ustar Notes
2
Abstract Windowing Toolkit
ocaml-book-1.0/en/html/book-ora042.html0000644000000000000000000000350507453055377014500 0ustar Summary Previous Contents Next

Summary

This chapter has compared the functional and imperative programming styles. They differ mainly in the control of execution (implicit in functional and explicit in imperative programming), and in the representation in memory of data (sharing or explicitly copied in the imperative case, irrelevant in the functional case). The implementation of algorithms must take account of these differences. The choice between the two styles leads in fact to mixing them. This mixture allows us to clarify the representation of closures, to optimize crucial parts of applications, and to create mutable functional data. Physical modification of values in the environment of a closure permits us to better understand what a functional value is. The mixture of the two styles gives powerful implementation tools. We used them to construct potentially infinite values.


Previous Contents Next ocaml-book-1.0/en/html/book-ora050.html0000644000000000000000000005625307453055377014507 0ustar Events Previous Contents Next

Events

The handling of events produced in the graphical window allows interaction between the user and the program. Graphics supports the treating of events like keystrokes, mouse clicks and movements of the mouse.

The programming style therefore changes the organization of the program. It becomes an infinite loop waiting for events. After handling each newly triggered event, the program returns to the infinite loop except for events that indicate program termination.

Types and functions for events

The main function for waiting for events is wait_next_event of type event list -> status.

The different events are given by the sum type event.

type event = Button_down | Button_up | Key_pressed | Mouse_motion | Poll;;
The four main values correspond to pressing and to releasing a mouse button, to movement of the mouse and to keystrokes. Waiting for an event is a blocking operation except if the constructor Poll is passed in the event list. This function returns a value of type status:

type status =
{ mouse_x : int;
mouse_y : int;
button : bool;
keypressed : bool;
key : char};;


This is a record containing the position of the mouse, a Boolean which indicates whether a mouse button is being pressed, another Boolean for the keyboard and a character which corresponds to the pressed key. The following functions exploit the data contained in the event record:
  • mouse_pos: unit -> int * int: returns the position of the mouse with respect to the window. If the mouse is placed elsewhere, the coordinates are outside the borders of the window.
  • button_down: unit -> bool: indicates pressing of a mouse button.
  • read_key: unit -> char: fetches a character typed on the keyboard; this operation blocks.
  • key_pressed: unit -> bool: indicates whether a key is being pressed on the keyboard; this operation does not block.
The handling of events supported by Graphics is indeed minimal for developing interactive interfaces. Nevertheless, the code is portable across various graphical systems like Windows, MacOS or X-Windows. This is the reason why this library does not take into account different mouse buttons. In fact, the Mac does not even possess more than one. Other events, such as exposing a window or changing its size are not accessible and are left to the control of the library.

Program skeleton

All programs implementing a graphical user interface make use of a potentially infinite loop waiting for user interaction. As soon as an action arrives, the program executes the job associated with this action. The following function possesses five parameters of functionals. The first two serve for starting and closing the application. The next two arguments handle keyboard and mouse events. The last one permits handling of exceptions that escape out of the different functions of the application. We assume that the events associated with terminating the application raise the exception End.

# exception End;;
exception End
# let skel f_init f_end f_key f_mouse f_except =
f_init ();
try
while true do
try
let s = Graphics.wait_next_event
[Graphics.Button_down; Graphics.Key_pressed]
in if s.Graphics.keypressed then f_key s.Graphics.key
else if s.Graphics.button
then f_mouse s.Graphics.mouse_x s.Graphics.mouse_y
with
End -> raise End
| e -> f_except e
done
with
End -> f_end ();;
val skel :
(unit -> 'a) ->
(unit -> unit) ->
(char -> unit) -> (int -> int -> unit) -> (exn -> unit) -> unit = <fun>


Here, we use the skeleton to implement a mini-editor. Touching a key displays the typed character. A mouse click changes the current point. The character '&' exits the program. The only difficulty in this program is line breaking. We assume as simplification that the height of characters does not exceed twelve pixels.

# let next_line () =
let (x,y) = Graphics.current_point()
in if y>12 then Graphics.moveto 0 (y-12)
else Graphics.moveto 0 y;;
val next_line : unit -> unit = <fun>
# let handle_char c = match c with
'&' -> raise End
| '\n' -> next_line ()
| '\r' -> next_line ()
| _ -> Graphics.draw_char c;;
val handle_char : char -> unit = <fun>
# let go () = skel
(fun () -> Graphics.clear_graph ();
Graphics.moveto 0 (Graphics.size_y() -12) )
(fun () -> Graphics.clear_graph())
handle_char
(fun x y -> Graphics.moveto x y)
(fun e -> ());;
val go : unit -> unit = <fun>
This program does not handle deletion of characters by pressing the key DEL.

Example: telecran

Telecran is a little drawing game for training coordination of movements. A point appears on a slate. This point can be moved in directions X and Y by using two control buttons for these axes without ever releasing the pencil. We try to simulate this behavior to illustrate the interaction between a program and a user. To do this we reuse the previously described skeleton. We will use certain keys of the keyboard to indicate movement along the axes.

We first define the type state, which is a record describing the size of the slate in terms of the number of positions in X and Y, the current position of the point and the scaling factor for visualization, the color of the trace, the background color and the color of the current point.

# type state = {maxx:int; maxy:int; mutable x : int; mutable y :int;
scale:int;
bc : Graphics.color;
fc: Graphics.color; pc : Graphics.color};;


The function draw_point displays a point given its coordinates, the scaling factor and its color.

# let draw_point x y s c =
Graphics.set_color c;
Graphics.fill_rect (s*x) (s*y) s s;;
val draw_point : int -> int -> int -> Graphics.color -> unit = <fun>


All these functions for initialization, handling of user interaction and exiting the program receive a parameter corresponding to the state. The first four functions are defined as follows:

# let t_init s () =
Graphics.open_graph (" " ^ (string_of_int (s.scale*s.maxx)) ^
"x" ^ (string_of_int (s.scale*s.maxy)));
Graphics.set_color s.bc;
Graphics.fill_rect 0 0 (s.scale*s.maxx+1) (s.scale*s.maxy+1);
draw_point s.x s.y s.scale s.pc;;
val t_init : state -> unit -> unit = <fun>
# let t_end s () =
Graphics.close_graph();
print_string "Good bye..."; print_newline();;
val t_end : 'a -> unit -> unit = <fun>
# let t_mouse s x y = ();;
val t_mouse : 'a -> 'b -> 'c -> unit = <fun>
# let t_except s ex = ();;
val t_except : 'a -> 'b -> unit = <fun>


The function t_init opens the graphical window and displays the current point, t_end closes this window and displays a message, t_mouse and t_except do not do anything. The program handles neither mouse events nor exceptions which may accidentally arise during program execution. The important function is the one for handling the keyboard t_key:

# let t_key s c =
draw_point s.x s.y s.scale s.fc;
(match c with
'8' -> if s.y < s.maxy then s.y <- s.y + 1;
| '2' -> if s.y > 0 then s.y <- s.y - 1
| '4' -> if s.x > 0 then s.x <- s.x - 1
| '6' -> if s.x < s.maxx then s.x <- s.x + 1
| 'c' -> Graphics.set_color s.bc;
Graphics.fill_rect 0 0 (s.scale*s.maxx+1) (s.scale*s.maxy+1);
Graphics.clear_graph()
| 'e' -> raise End
| _ -> ());
draw_point s.x s.y s.scale s.pc;;
val t_key : state -> char -> unit = <fun>


It displays the current point in the color of the trace. Depending on the character passed, it modifies, if possible, the coordinates of the current point (characters: '2', '4', '6', '8'), clears the screen (character: 'c') or raises the exception End (character: 'e'), then it displays the new current point. Other characters are ignored. The choice of characters for moving the cursor comes from the layout of the numeric keyboard: the chosen keys correspond to the indicated digits and to the direction arrows. It is therefore useful to activate the numeric keyboard for the ergonomics of the program.

We finally define a state and apply the skeleton function in the following way:

# let stel = {maxx=120; maxy=120; x=60; y=60;
scale=4; bc=Graphics.rgb 130 130 130;
fc=Graphics.black; pc=Graphics.red};;
val stel : state =
{maxx=120; maxy=120; x=60; y=60; scale=4; bc=8553090; fc=0; pc=16711680}
# let slate () =
skel (t_init stel) (t_end stel) (t_key stel)
(t_mouse stel) (t_except stel);;
val slate : unit -> unit = <fun>


Calling function slate displays the graphical window, then it waits for user interaction on the keyboard. Figure 5.8 shows a drawing created with this program.


Figure 5.8: Telecran.



Previous Contents Next ocaml-book-1.0/en/html/book-ora023.html0000644000000000000000000000575307453055401014472 0ustar Notes
1
In the interval [-230,230-1] on 32-bit machines and in the interval [-262,262-1] on 64-bit machines
2
The floating point number m 10n is represented with a 53-bit mantissa m and an exponent n in the interval [-1022,1023].
3
Not a Number
4
The List module is presented on page ??.
5
Fortunately since the number of types is only limited by machine memory
6
Some predefined functions do not obey this rule, in particular the structural equality function (=) which is polymorphic (its type is 'a -> 'a -> bool) but which explores the structure of its arguments to test their equality.
7
Translator's note: In Molire's play Le Bourgeois Gentilhomme (The Bourgeois Gentleman), the character Mr. Jourdain is amazed to discover that he has been speaking prose all his life. The play can be found at

Link


http://www.site-moliere.com/pieces/bourgeoi.htm
and

Link


http://moliere-in-english.com/bourgeois.html
gives an excerpt from an English translation, including this part.
8
Translator's note: The rules for French Tarot can be found, for example, at

Link


http://www.pagat.com/tarot/frtarot.html
9
Translator's note: Thanks to the new ``polymorphic variants'' feature of Objective CAML 3.00, some other sum types can now be extended as well
ocaml-book-1.0/en/html/book-ora206.html0000644000000000000000000001065707453055401014474 0ustar Texte Previous Contents Next

Texte

Although computer science has become an industrial activity, in many respects the success of a programming language is a subjective affair. If ``the heart has its reasons of which reason knows nothing,'' then Objective CAML is a reasonable choice for a lover of heart.

It is based on solid theoretical foundations, all while providing a wide spectrum of programming paradigms. If one adds the simplicity of interaction with the language which the toplevel supports, that makes it a language perfectly adapted for teaching.
  • Structured types and abstract types support approaching algorithmic problems and their complex data structures, all while abstracting away from problems of memory representation and allocation.
  • The functional theoretical model underlying the language supplies a precise introduction to the notions of evaluation and typing which, as a ``true programmer'', one owes it to oneself to be taught.
  • The various programming models can be approached independently of one another: from modular or object-oriented program structure to low-level systems programming, there are few areas where Objective CAML is not useful.
  • Its suitability for symbolic programming makes it an excellent support for theoretical courses such as compiling or artifical intelligence.
For these qualities, Objective CAML is often used as the basis of the introductory computer science curriculum as well as for advanced programming courses which make explicit the link between the language's high level of abstraction and its execution. Many teachers have been and remain seduced by the pedagogical advantages of Objective CAML and, by way of consequence, many computer scientists have been schooled in it.

One of the first causes for satisfaction in Objective CAML development is how comfortable it is to use. The compiler loads rapidly and its static type inference lets nothing escape. Other static analyses of the code give the programmer precious indices of anomalies if not errors: incomplete pattern-matching is signaled, partial application of a function in a sequence is detected, etc. To this first cause of satisfaction is added a second: the compiler very rapidly generates efficient code.

Compiler performance, conciseness of expression of functional programming, quality and diversity of libraries make Objective CAML a language perfectly adapted to the needs of ``disposable software''. But it would be diminishing it to restrict it to this single application domain. For these same reasons, Objective CAML is a precious tool for experimentation and application prototyping. Moreover, when the structuring mechanisms of modules and objects come to be added to the features already mentioned, the language opens the way to the conception and development of finished applications.

Finally, Objective CAML and its developer community form a milieu which reacts quickly to innovation in the area of programming. The free availability and the distribution of the source code of the language offer emerging concepts a terrain for experimentation.

Learning Objective CAML requires a certain effort from the programmer familiar with other languages. And this, as well as the object of study is in constant evolution. We hope that without masking the complexity of certain concepts, this book will facilitate this phase of learning and can thus accelerate the return on investment for the Objective CAML application developer.






Previous Contents Next ocaml-book-1.0/en/html/logocaml.gif0000644000000000000000000001241207452056113014120 0ustar GIF89aw`5m&C24w3113w4q4s3x3~12鲫I;q蓎I(<"7A > '< " -?JRc!Made with GIMP! ?,w`pH,Ȥrl:ШtJZجvzxL.zn|N~pf\XGIFEљTBa۲S]PRCYKOVD_gƝ#dX)^KCxژ\,lĎ?(z2="K|Jo4r̓[ϝq @2@fģz 'VIZQ췟d63R1;-0M v;%f2^7zPx.͂qʄZygF82KhnF>:ޡGbO ֪"قi(zW=!kG+PFjg sNqvR^]KZ(~{RV٪"+$J믁~Ժici&Loi(Ѷ#H-IUKTp ǰӱC*yWlsy3/p7#eaM, ^|UƳys϶LW]]OCm?)m9MRoT6cwg֬l,*"|O'veò+m8}D[i2wݲ-.{&'͙;V8|E`+W촋zŮ3N謹wK;āǝi|гZc['>M 50}ϳ춋odﳯqG.C~@{E> N=U $U88'd:ͣ` e<UB36yi8 ECΏ5T>(!Q *uH4"'=%jy`#b.b8mpD,xD¢-XB쬑m!(,: =.g`{HA}o4̘5Pj|d#)I.0w CEMB72Dt)I0EouemE%%!&]BA3&K}i ,1Cē#d#e)Mi$yG\R,`:37R5zy^gȁMȵ퓲5Zu_6WwNJֹed14`U]]Ůi+VOWD9?+?e=6  W(uO)xk~Q`(6q«wuq{XE6Nqjo{E oÿ1G:bṯQiR0gppsd wpXqAq` k/CdN..5|fש9OV̛} {68X4%]g;wwK7wcdqWSgo|ή:5mN>m0v?8%C=<=wrO]?:EI僯 Z<~Fg*W_V=W+>selp쓟g{k:x~?WȞ?SWaԖuyag+k7mKvHuv'v}w5xe{]恼GGt3*7g08W~i`{Ył׃䂕\AꐃƄMG(CR8dJ8MXhW5eb^hB(!(%'H"()6r5?i5wȆohi`-f:RgȇwsS< #nUX@}n؂?#4kȉ!㉇bĊ䊯-E(Ih2^'|8HH}(UA{ x PKX% Ȧ(SW~0h5?X{$jcU*HEm$jl& k=Zf$tH ƍ>n9j`rǁ6LV掘"En\Ē8Ɏ%uUBNE8. 2ِm;Eɒ:F0K{jƖCQxU) py(F5!Е^9 ԏ~@`i4bhuwƖ/hz ?au%i"i˜yxOp(x LtdْGB0J֚7 J zWrYYhy9v5)IXi rٓ?Iof癎y$IoنYyyT#gV1ɂțҩ)X'rxڛP(wܒᡟğy9->Iy"\#!$0 a!-J*zo"Pn'jC6&frPVn֎6@BV?FYEH%j 5"Rf"h3)! s T%VEdQ}l-R'tj4I:EcŤČ0Uڕ$uJ :Yg1<$2<(D|õX+TL+!MO,v 8qgJظMAe\˕K [\ j{n+{ul Bi1G¿v:̨ |4K+{0t]QN 򺵼CD54eƘp츊;M/5 wQ7VKR}̦Ss̐e\j@E0^/L=5Μ, )Růs 0J- dg:LJ@GMMzexɸ.\ 9]7|0jƑ*=DpdžqZJ;e+= LVcӺG:i[]5UsVsn o<'ݙQSW;zfggط9 )L FkTWБktm-C)|ěaԜ٠VڝM]o\<bIغMqiة=Ľ(*fܩΝ1$ݏ3mݥWj=a+}T=gRD&4]߷ h)m-And ޵';R~54^. =$^}(:"-> "(31N3#d2W2$Mb`J^@;Ni%Inw݆!8w?Ybh2 r4+f#\\,"nE Gx~w:ag}mW|iّn n"vC^=~:^ꨞ)>^~븞뺾>;ocaml-book-1.0/en/html/book-ora017.gif0000644000000000000000000000412207452056110014260 0ustar GIF89aZ !,Z ڋ޼kH扦ʶ L ĐL*̦37xJԪj:bܮ҂y)>v84?8HX"h889IY&Yyu):IzJcڪ+ ;kZ{++ ; ыbd䟳O'ß8I tyvVn)(B8F1NaUq(荈BHb.4{Ë6ވXD>UFN>YTeV$%tJ\e(#^yexj>i&EUfEo i}`_ ꃣ^|(Iu*]PJ5hzz*HdꦌfB뎯bRjiKdZIU캃Tzl48;3.*mԆ .䞛"K3ĸ)n鮯NBBpY?0 p;q*_^lC; ,2ds-j|)lC|̝]Ј,ҥ݉\/ɦo:TRX1-0օXײs;'d\+,6 &@wJ[w6L+nwg"!}]4,n^C3޸ŏsmȹG鰖n9Sgz~yسfC踓~:{b{*w>|g';co7箾~SiB'<9/ _RUO~k~@\8 KzK} (π Bv~1,E8J d_ !8=p(GBO0\ gX&t\A1>t"(*zWŵ ׻%>70bf½1Rai)H h6qh#D92Q{q\$GH.1#3IDj$!Iv\& @DQby2eHFSZ)mIGPsaTf"YKAҘD&&]YJfM5or,,0##Y< xs=O{`c> Ѐ t>nt mCK̭VE/ьV5 Ґ4XBUgj3T9/ GU1)UJxRԎ%NRճ)Js:Jzu^F)Run3ijR *URV*Voç :jV}gcLղе++\Ɋд«Inb\"ԣT,c5bW]-bX)PE>'KW0]6;]PTv9<,53K.m dVhH R4jUej4w8r ݅2΍u{F¶ŮwKm6y[Wo _讗uk_7.y[w.j" ,C0+*laT`8ä0yK"K X'^1%Zbb8€1a#c:^0{9B2l #9^2;'X1`+W2/Wbfl0g^%fE9ν6l.9[3+i9Ѝ30C/UPf4 hz`Ҕ/LkzӜF@;ocaml-book-1.0/en/html/book-ora103.html0000644000000000000000000000571707453055400014470 0ustar Introduction Previous Contents Next

Introduction

The development of lexical analysis and parsing tools has been an important area of research in computer science. This work has produced the lexer and parser generators lex and yacc whose worthy scions camllex and camlyacc are presented in this chapter. These two tools are the de-facto standard for implementing lexers and parsers, but there are other tools, like streams or the regular expression library str, that may be adequate for applications which do not need a powerful analysis.

The need for such tools is especially acute in the area of state-of-the-art programming languages, but other applications can profit from such tools: for example, database systems offering the possibility of issuing queries, or spreadsheets defining the contents of cells as the result of the evaluation of a formula. More modestly, it is common to use plain text files to store data; for example system configuration files or spreadsheet data. Even in such limited cases, processing the data involves some amount of lexical analysis and parsing.

In all of these examples the problem that lexical analysis and parsing must solve is that of transforming a linear character stream into a data item with a richer structure: a string of words, a record structure, the abstract syntax tree for a program, etc.

All languages have a set of vocabulary items (lexicon) and a grammar describing how such items may be combined to form larger items (syntax). For a computer or program to be able to correctly process a language, it must obey precise lexical and syntactic rules. A computer does not have the detailed semantic understanding required to resolve ambiguities in natural language. To work around the limitation, computer languages typically obey clearly stated rules without exceptions. The lexical and syntactic structure of such languages has received formal definitions that we briefly introduce in this chapter before introducing their uses.


Previous Contents Next ocaml-book-1.0/en/html/book-ora014.html0000644000000000000000000000366507453055377014506 0ustar Chapter outline Previous Contents Next

Chapter outline

This chapter presents the basic elements of the functional part of the Objective CAML language, namely its syntactic elements, its language of types and its exception mechanism. This will lead us to the development of a first example of a complete program.

The first section describes the core of the language, beginning with primitive values and the functions which manipulate them. We then go on to structured values and to function values. The basic control structures are introduced as well as local and global value declarations. The second section deals with type definitions for the construction of structured values and with pattern matching to access these structures. The third section compares the inferred type of functions and their domain of definition, which leads us to introduce the exception mechanism. The fourth section illustrates all these notions put together, by describing a simple application: a desktop calculator.


Previous Contents Next ocaml-book-1.0/en/html/book-ora114.html0000644000000000000000000004040607453055400014464 0ustar Communication between C and Objective CAML Previous Contents Next

Communication between C and Objective CAML

Communication between parts of a program written in C and in Objective CAML is accomplished by creating an executable (or a new toplevel interpreter) containing both parts. These parts can be separately compiled. It is therefore the responsibility of the linking phase2 to establish the connection between Objective CAML function names and C function names, and to create the final executable. To this end, the Objective CAML part of the program contains external declarations describing this connection.

Figure 12.1 shows a sample program composed of a C part and an Objective CAML part.


Figure 12.1: Communication between Objective CAML and C.


Each part comprises code (function definitions and toplevel expressions for Objective CAML) and a memory area for dynamic allocation. Calling the function f with three Objective CAML integer arguments triggers a call to the C function f_c. The body of the C function converts the three Objective CAML integers to C integers, computes their sum, and returns the result converted to an Objective CAML integer.

We now introduce the basic mechanisms for interfacing C with Objective CAML: external declarations, calling conventions for C functions invoked from Objective CAML, and linking options. Then, we show an example using input-output.

External declarations

External function declarations in Objective CAML associate a C function definition with an Objective CAML name, while giving the type of the latter.

The syntax is as follows:

Syntax


external caml_name : type = "C_name"
This declaration indicates that calling the function caml_name from Objective CAML code performs a call to the C function C_name with the given arguments. Thus, the example in figure 12.1 declares the function f as the Objective CAML equivalent of the C function f_c.

An external function can be declared in an interface (i.e., in an .mli file) either as an external or as a regular value:

Syntax


external caml_name : type = "C_name"
val caml_name : type

In the latter case, calls to the C function first go through the general function application mechanism of Objective CAML. This is slightly less efficient, but hides the implementation of the function as a C function.

Declaration of the C functions

C functions intended to be called from Objective CAML must have the same number of arguments as described in their external declarations. These arguments have type value, which is the C type for Objective CAML values. Since those values have uniform representations (9), a single C type suffices to encode all Objective CAML values. On page ??, we will present the facilities for encoding and decoding values, and illustrate them by a function that explores the representations of Objective CAML values.

The example in figure 12.1 respects the constraints mentioned above. The function f_c, associated with an Objective CAML function of type int -> int -> int -> int, is indeed a function with three parameters of type value returning a result of type value.

The Objective CAML bytecode interpreter evaluates calls to external functions differently, depending on the number of arguments3. If the number of arguments is less than or equal to five, the arguments are passed directly to the C function. If the number of arguments is greater than five, the C function's first parameter will get an array containing all of the arguments, and the C function's second parameter will get the number of arguments. These two cases must therefore be distinguished for external C functions that can be called from the bytecode interpreter. On the other hand, the Objective CAML native-code compiler always calls external functions by passing all the arguments directly, as function parameters.

External functions with more than five arguments

For external functions with more than five arguments, the programmer must provide two C functions: one for bytecode and the other for native-code. The syntax of external declarations allows the declaration of one Objective CAML function associated with two C functions:

Syntax


external caml_name : type = "C_name_bytecode" "C_name_native"
The function C_name_bytecode takes two parameters: an array of values of type value (i.e. a C pointer of type value*) and an integer giving the number of elements in this array.

Example

The following C program defines two functions for adding together six integers: plus_native, callable from native code, and plus_bytecode, callable from the bytecode compiler. The C code must include the file mlvalues.h containing the definitions of C types, Objective CAML values, and conversion macros.

#include <stdio.h>
#include <caml/mlvalues.h>

value plus_native (value x1,value x2,value x3,value x4,value x5,value x6)
{
printf("<< NATIVE PLUS >>\n") ; fflush(stdout) ;
return Val_long ( Long_val(x1) + Long_val(x2) + Long_val(x3)
+ Long_val(x4) + Long_val(x5) + Long_val(x6)) ;
}

value plus_bytecode (value * tab_val, int num_val)
{
int i;
long res;
printf("<< BYTECODED PLUS >> : ") ; fflush(stdout) ;
for (i=0,res=0;i<num_val;i++) res += Long_val(tab_val[i]) ;
return Val_long(res) ;
}

The following Objective CAML program exOCAML.ml calls these two C functions.

external plus : int -> int -> int -> int -> int -> int -> int
= "plus_bytecode" "plus_native" ;;
print_int (plus 1 2 3 4 5 6) ;;
print_newline () ;;


We now compile these programs with the two Objective CAML compilers and a C compiler that we call cc. We must give it the access path for the mlvalues.h include file.
$ cc -c -I/usr/local/lib/ocaml  exC.c 

$ ocamlc -custom exC.o exOCAML.ml -o ex_byte_code.exe 
$ ex_byte_code.exe
<< BYTECODED PLUS >> : 21 

$ ocamlopt exC.o exOCAML.ml -o ex_native.exe 
$ ex_native.exe 
<< NATIVE PLUS >> : 21 

Note


To avoid writing the C function twice (with the same body but different calling conventions), it suffices to implement the bytecode version as a call to the native-code version, as in the following sketch:
value prim_nat (value x1, ..., value xn) { ... }
value prim_bc (value *tbl, int n)
{ return prim_nat(tbl[0],tbl[1],...,tbl[n-1]) ; }


Linking with C

The linking phase creates an executable from C and Objective CAML files compiled with their respective compilers. The result of the native-code compiler is shown in figure 12.2.



Figure 12.2: Mixed-language executable.


The compilation of the C and Objective CAML sources generates machine code that is stored in the static allocation area of the program. The dynamic allocation area contains the execution stack (corresponding to the function calls in progress) and the heaps for C and Objective CAML.

Run-time libraries

The C functions that can be called from a program using only the standard Objective CAML library are contained in the execution library of the abstract machine (see figure 7.3 page ??). For such a program, there is no need to provide additional libraries at link-time. However, when using Objective CAML libraries such as Graphics, Num or Str, the programmer must explicitly provide the corresponding C libraries at link-time. This is the purpose of the -custom compiler option (see 7, page ??). Similarly, when we wish to call our C functions from Objective CAML, we must provide the object file containing those C functions at link-time. The following example illustrates this.

The three linking modes

The linking commands differ slightly between the native-code compiler, the bytecode compiler, and the construction of toplevel interactive loops. The compiler options relevant to these linking modes are described in chapter 7.

To illustrate these linking modes, we consider again the example in figure 12.1. Assume the Objective CAML source file is named progocaml.ml. It uses the external function f_c defined in the C file progC.c. In turn, the function f_c refers to a C library a_C_library.a. Once all these files are compiled separately, we link them together using the following commands:
  • bytecode:
    ocamlc -custom -o vbc.exe progC.o a_C_library.a progocaml.cmo
  • native code:
    ocamlopt progC.o -o vn.exe a_C_library.a progocaml.cmx
We obtain two executable files: vbc.exe for the bytecode version, and vn.exe for the native-code version.

Building an enriched abstract machine

Another possibility is to augment the run-time library of the abstract machine with new C functions callable from Objective CAML. This is achieved by the following commands:
ocamlc -make-runtime -o new_ocamlrun progC.o a_C_library.a
We can then build a bytecode executable vbcnam.exe targeted to the new abstract machine:
ocamlc -o vbcnam.exe -use-runtime new_ocamlrun progocaml.cmo
To run this bytecode executable, either give it as the first argument to the new abstract machine, as in new_ocaml vbcnam.exe , or run it directly as vbcnam.exe

Note


Linking in -custom mode scans the object files (.cmo) to build a table of all external functions mentioned. The bytecode required to use them is generated and added to the bytecode corresponding to the Objective CAML code.


Building a toplevel interactive loop

To be able to use an external function in the toplevel interactive loop, we must first build a new toplevel interpreter containing the C code for the function, as well as an Objective CAML file containing its declaration.

We assume that we have compiled the file progC.c containing the function f_c. We then build the toplevel loop ftop as follows:
ocamlmktop -custom -o ftop progC.o a_C_library.a ex.ml
The file ex.ml contains the external declaration for the function f. The new toplevel interpreter ftop then knows this function and contains the corresponding C code, as found in progC.o.

Mixing input-output in C and in Objective CAML

The input-output functions in C and in Objective CAML do not share their file buffers. Consider the following C program:

#include <stdio.h>
#include <caml/mlvalues.h>
value hello_world (value v)
{ printf("Hello World !!"); fflush(stdout); return v; }

Writes to standard output must be flushed explicitly (fflush) to guarantee that they will be printed in the intended order.


# external caml_hello_world : unit -> unit = "hello_world" ;;
external caml_hello_world : unit -> unit = "hello_world"
# print_string "<< " ;
caml_hello_world () ;
print_string " >>\n" ;
flush stdout ;;
Hello World !!<< >>
- : unit = ()


The outputs from C and from Objective CAML are not intermingled as expected, because each language buffers its outputs independently. To get the correct behavior, the Objective CAML part must be rewritten as follows:

# print_string "<< " ; flush stdout ;
caml_hello_world () ;
print_string " >>\n" ; flush stdout ;;
<< Hello World !! >>
- : unit = ()
By flushing the Objective CAML output buffer after each write, we ensure that the outputs from each language appear in the expected order.


Previous Contents Next ocaml-book-1.0/en/html/book-ora138.html0000644000000000000000000001011607453055400014465 0ustar Introduction Previous Contents Next

Introduction

As you may have guessed from the name, Objective CAML supports object-oriented programming. Unlike imperative programming, in which execution is driven by explicit sequencing of operations, or functional programming, where it is driven by the required computations, object-oriented programming can be thought of as data driven. Using objects introduces a new organization of programs into classes of related objects. A class groups together data and operations. The latter, also known as methods, define the possible behaviors of an object. A method is invoked by sending a message to an object. When an object receives a message, it performs the action or the computation corresponding to the method specified by the message. This is different from applying a function to arguments because a message (which contains the method name) is sent to an object. It is up to the object itself to determine the code that will actually be executed; such a delayed binding between name and code makes behavior more adaptable and code easier to reuse.

With object-oriented programming, relations are defined between classes. Classes also define how objects communicate through message parameters. Aggregation and inheritance relations between classes allow new kinds of application modeling. A class that inherits from another class includes all definitions from the parent's class. However, it may extend the set of data and methods and redefine inherited behaviors, provided typing constraints are respected. We will use a graphical notation1 to represent relations between classes.

Objective CAML's object extensions are integrated with the type system of the language: a class declaration defines a type with the same name as the class. Two kinds of polymorphism coexist. One of them is parametric polymorphism, which we have already seen with parameterized types: parameterized classes. The other one, known as inclusion polymorphism, uses the subtyping relation between objects and delayed binding. If the type of the class sc is a subtype of the class c then any object from sc may be used in place of an object from c. The subtype constraint must be stated explicitly. Inclusion polymorphism makes it possible to construct non-homogeneous lists where the type of each element is a subtype of a type common to all list elements. Since binding is delayed, sending the same message to all elements of such a list can activate different methods according to the sub-classes of the actual elements.

On the other hand, Objective CAML does not include the notion of method overloading, which would allow several definitions for one method name. Without this restriction, type inference might encounter ambiguous situations requiring additional information from the programmer.

It should be emphasized that Objective CAML is the only language with an object extension that provides both parameterized and inclusion polymorphism, while still being fully statically typed through type inference.


Previous Contents Next ocaml-book-1.0/en/html/book-ora004.gif0000644000000000000000000000174507452056110014264 0ustar GIF89aUUU!,ڋ޼gH扦ʶ xLCq}xS )ah\ %)tBO* ʰFՊc8xiP1QuhhA b#fIty) IJ0*@ ZZ)`p˚{;6l< L:y, |'E ΩH>/n(ؑg_WwO/ܾ~P9lE6BӠ8 V ^RHO~˗MFZs6gÙSfOX%}BݙtTZ #VQmkdшMlalպze۹0ɠٻyUׯ`{ >naČ}~82WȀ%[xG˧Fu̹sfC^\4ꥩS[ʤef۶ܵu]7gEb .\(ɃO޼uunޝ(k'oy杮^{g_}}'Q~Fx h',2蠅Fm(Ma3`5(R/,j*(chj<sK5njTDd:.y[#PR^YrIfffjfnUrI' P)񩔟*hp 1LJiZZX*462vE)Th$ @:xԹJiD:̠qkD ǖj*N1,mƶZ ,lm+>) 6&쯿KLt`<1W \r' R -A3G[f=s3p^n\niEtutvC/JkV_ʛ;ocaml-book-1.0/en/html/book-ora014.gif0000644000000000000000000000536107452056110014263 0ustar GIF89a𪪪ddd!,H0I8͙`(dihlp,(!`x<. #,G\:}4s>Ve2{r3B (:=ؒ\~xyJnHw{ac6x}MyQ2&me%w$o"o }ǜbRпµعݣאۤΞ͉0+:tKxݿx3> %.B bP}?r|dKKL)bH钞͝8{NLTFo7FVIc|iQK{ *\vkְ[}ZD:)WiƺXn$׬Zɲuv(ɦdڷٷ&ܘHZ.- x4f՚:/&hOCm~_lYJ+{ҿqwv8 a\yըrз[7UGVs/).fǏ=f}&儜xڱ JZ I`~Wnݡ`57"%$7`Mva(f)8)$218&vLWHiC*IUV%!%| f2f(c>v&=kΙv&Yfd*蠄jh[袅z裋& 餍JJa|pBjiJj,ꪬj *무R뤦ڨ :,**l&K&zEmw]8SdhpKٞkNe:0.(x[ W#񻜿^3"  >\!1k!+qXG<Ÿzr)0-bp 2rLF'e;tM,3/ 4&\5HlՂ|6G]u"xD)S=c]Zu n'bw 6:3uᆟ x|B2-杶Soؐooϝ3 ]Vw~u=6כ菫m:5~KbT]ar];gG(D3 y쬫PC(˗?>? z㿟û-n+\1z~#Al˞>yz w lPz$! їmSJ(AEd(^DŽ@-pu> cvdS"((0/{HEz4b 97%>IO0At/5NyZ%w)}NuL8HAOu$K! YG\A YԲ,fUd4,N^rXelERU'JVrQ^]R-/s+]dW0KV]<0)LfD-hIFD&Ħ4S#ng >ze{ۛBDo:19՜0"S!QM]gQ TRp^TuR2zT> X ¨uDթl=jjWsu"=hWբ5/bbXon\W﯎da ԷU._N إTN#Yڈ'ak{61!:k-HqkC⓷a=\uLeKՔY5+GJқ+歮ϘNʕ -{/ުkG:^IyR8GfL"4`D#wSHEMfdPH1eq9a 'XSe'5Y\Җ|dOb4)w@><Ǥq"LT9&R*3-˹e2G˞pI.nnmn_f m3F{źYz/yg6Wuz;5t{hZwЌu_BӘ~b;Q 8A\})}h:GGm-Q\=[^їvulUVv#=_:ƞR;βa{ζ]ldVх6q;wZ;7oZz.mo[Fw}|K{Ĺ]pNrr#8  <7'7?L|ċPᛳ\Mr?ST?K9YPqԏ d"[굊1N*Ky4:ԑ.uS}Eb>&."wp-3n7v9w<+|x}ܿw/'õ8)W:|7zx 6-U?5x>8 k|/mW3\+>}}}6;GA[?'O>j~o>Ͽ{7܇~ɧ~~~xxW~G}ݷ~(|療 |-&/r1s'&mG)` s3t58t&s`9s6؃Fa4CXbBǂZ,\+K+fvbdԄvTuXZ X^1Ѕ`8dxbXhl؆np;ocaml-book-1.0/en/html/book-ora017.html0000644000000000000000000003176107453055377014507 0ustar Typing, domain of definition, and exceptions Previous Contents Next

Typing, domain of definition, and exceptions

The inferred type of a function corresponds to a subset of its domain of definition. Just because a function takes a parameter of type int doesn't mean it will know how to compute a value for all integers passed as parameters. In general this problem is dealt with using Objective CAML's exception mechanism. Raising an exception results in a computational interruption which can be intercepted and handled by the program. For this to happen program execution must have registered an exception handler before the computation of the expression which raises this exception.

Partial functions and exceptions

The domain of definition of a function corresponds to the set of values on which the function carries out its computation. There are many mathematical functions which are partial; we might mention division or taking the natural log. This problem also arises for functions which manipulate more complex data structures. Indeed, what is the result of computing the first element of an empty list? In the same way, evaluation of the factorial function on a negative integer can lead to an infinite recursion.

Several exceptional situations may arise during execution of a program, for example an attempt to divide by zero. Trying to divide a number by zero will provoke at best a program halt, at worst an inconsistent machine state. The safety of a programming language comes from the guarantee that such a situation will not arise for these particular cases. Exceptions are a way of responding to them.

Division of 1 by 0 will cause a specific exception to be raised:

# 1/0;;
Uncaught exception: Division_by_zero
The message Uncaught exception: Division_by_zero indicates on the one hand that the Division_by_zero exception has been raised, and on the other hand that it has not been handled. This exception is among the core declarations of the language.

Often, the type of a function does not correspond to its domain of definition when a pattern-matching is not exhaustive, that is, when it does not match all the cases of a given expression. To prevent such an error, Objective CAML prints a message in such a case.

# let head l = match l with h::t -> h ;;
Characters 14-36:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]
val head : 'a list -> 'a = <fun>


If the programmer nevertheless keeps the incomplete definition, Objective CAML will use the exception mechanism in the case of an erroneous call to the partial function:

# head [] ;;
Uncaught exception: Match_failure("", 14, 36)


Finally, we have already met with another predefined exception: Failure. It takes an argument of type string. One can raise this exception using the function failwith. We can use it in this way to complete the definition of our head:

# let head = function
[] -> failwith "Empty list"
| h::t -> h;;
val head : 'a list -> 'a = <fun>
# head [] ;;
Uncaught exception: Failure("Empty list")


Definition of an exception

In Objective CAML, exceptions belong to a predefined type exn. This type is very special since it is an extensible sum type: the set of values of the type can be extended by declaring new constructors9. This detail lets users define their own exceptions by adding new constructors to the type exn.

The syntax of an exception declaration is as follows:

Syntax


exception Name ;;
or

Syntax


exception Name of t ;;
Here are some examples of exception declarations:

# exception MY_EXN;;
exception MY_EXN
# MY_EXN;;
- : exn = MY_EXN
# exception Depth of int;;
exception Depth of int
# Depth 4;;
- : exn = Depth(4)
Thus an exception is a full-fledged language value.

Warning


The names of exceptions are constructors. So they necessarily begin with a capital letter.

# exception lowercase ;;
Characters 11-20:
Syntax error


Warning


Exceptions are monomorphic: they do not have type parameters in the declaration of the type of their argument.

# exception Value of 'a ;;
Characters 20-22:
Unbound type parameter 'a
A polymorphic exception would permit the definition of functions with an arbitrary return type as we will see further on, page ??.

Raising an exception

The function raise is a primitive function of the language. It takes an exception as an argument and has a completely polymorphic return type.

# raise ;;
- : exn -> 'a = <fun>
# raise MY_EXN;;
Uncaught exception: MY_EXN
# 1+(raise MY_EXN);;
Uncaught exception: MY_EXN
# raise (Depth 4);;
Uncaught exception: Depth(4)
It is not possible to write the function raise in Objective CAML. It must be predefined.

Exception handling

The whole point of raising exceptions lies in the ability to handle them and to direct the sequence of computation according to the value of the exception raised. The order of evaluation of an expression thus becomes important for determining which exception is raised. We are leaving the purely functional context, and entering a domain where the order of evaluation of arguments can change the result of a computation, as will be discussed in the following chapter (see page ??).

The following syntactic construct, which computes the value of an expression, permits the handling of an exception raised during this computation:

Syntax


try expr with
| p1 -> expr1
:
| pn -> exprn

If the evaluation of expr does not raise any exception, then the result is that of the evaluation of expr. Otherwise, the value of the exception which was raised is pattern-matched; the value of the expression corresponding to the first matching pattern is returned. If none of the patterns corresponds to the value of the exception then the latter is propagated up to the next outer try-with entered during the execution of the program. Thus pattern matching an exception is always considered to be exhaustive. Implicitly, the last pattern is | e -> raise e. If no matching exception handler is found in the program, the system itself takes charge of intercepting the exception and terminates the program while printing an error message.

One must not confuse computing an exception (that is, a value of type exn) with raising an exception which causes computation to be interrupted. An exception being a value like others, it can be returned as the result of a function.

# let return x = Failure x ;;
val return : string -> exn = <fun>
# return "test" ;;
- : exn = Failure("test")
# let my_raise x = raise (Failure x) ;;
val my_raise : string -> 'a = <fun>
# my_raise "test" ;;
Uncaught exception: Failure("test")
We note that applying my_raise does not return any value while applying return returns one of type exn.

Computing with exceptions

Beyond their use for handling exceptional values, exceptions also support a specific programming style and can be the source of optimizations. The following example finds the product of all the elements of a list of integers. We use an exception to interrupt traversal of the list and return the value 0 when we encounter it.

# exception Found_zero ;;
exception Found_zero
# let rec mult_rec l = match l with
[] -> 1
| 0 :: _ -> raise Found_zero
| n :: x -> n * (mult_rec x) ;;
val mult_rec : int list -> int = <fun>
# let mult_list l =
try mult_rec l with Found_zero -> 0 ;;
val mult_list : int list -> int = <fun>
# mult_list [1;2;3;0;5;6] ;;
- : int = 0
So all the computations standing by, namely the multiplications by n which follow each of the recursive calls, are abandoned. After encountering raise, computation resumes from the pattern-matching under with.


Previous Contents Next ocaml-book-1.0/en/html/book-ora037.html0000644000000000000000000004242007453055377014503 0ustar Comparison between Functional and Imperative Previous Contents Next

Comparison between Functional and Imperative

Character strings (of Objective CAML type string) and linked lists (of Objective CAML type 'a list) will serve as examples to illustrate the differences between ``functional'' and ``imperative.''

The Functional Side

The function map (see page ??) is a classic ingredient in functional languages. In a purely functional style, it is written:

# let rec map f l = match l with
[] -> []
| h::q -> (f h) :: (map f q) ;;
val map : ('a -> 'b) -> 'a list -> 'b list = <fun>
It recursively constructs a list by applying f to the elements of the list given as argument, independently specifying its head (f h) and its tail (map f q). In particular, the program does not stipulate which of the two will be computed first.

Moreover, the physical representation of lists need not be known to the programmer to write such a function. In particular, problems of allocating and sharing data are managed implicitly by the system and not by the programmer. An example illustrating this follows:

# let example = [ "one" ; "two" ; "three" ] ;;
val example : string list = ["one"; "two"; "three"]
# let result = map (function x -> x) example ;;
val result : string list = ["one"; "two"; "three"]


The lists example and result contain equal values:

# example = result ;;
- : bool = true


These two values have exactly the same structure even though their representation in memory is different, as one learns by using the test for physical equality:

# example == result ;;
- : bool = false
# (List.tl example) == (List.tl result) ;;
- : bool = false


The Imperative Side

Let us continue the previous example, and modify a string in the list result.

# (List.hd result).[1] <- 's' ;;
- : unit = ()
# result ;;
- : string list = ["ose"; "two"; "three"]
# example ;;
- : string list = ["ose"; "two"; "three"]
Evidently, this operation has modified the list example. Hence, it is necessary to know the physical structure of the two lists being manipulated, as soon as we use imperative aspects of the language.

Let us now observe how the order of evaluating the arguments of a function can amount to a trap in an imperative program. We define a mutable list structure with primitive functions for creation, modification, and access:

# type 'a ilist = { mutable c : 'a list } ;;
type 'a ilist = { mutable c: 'a list }
# let icreate () = { c = [] }
let iempty l = (l.c = [])
let icons x y = y.c <- x::y.c ; y
let ihd x = List.hd x.c
let itl x = x.c <- List.tl x.c ; x ;;
val icreate : unit -> 'a ilist = <fun>
val iempty : 'a ilist -> bool = <fun>
val icons : 'a -> 'a ilist -> 'a ilist = <fun>
val ihd : 'a ilist -> 'a = <fun>
val itl : 'a ilist -> 'a ilist = <fun>
# let rec imap f l =
if iempty l then icreate()
else icons (f (ihd l)) (imap f (itl l)) ;;
val imap : ('a -> 'b) -> 'a ilist -> 'b ilist = <fun>


Despite having reproduced the general form of the map of the previous paragraph, with imap we get a distinctly different result:

# let example = icons "one" (icons "two" (icons "three" (icreate()))) ;;
val example : string ilist = {c=["one"; "two"; "three"]}
# imap (function x -> x) example ;;
Uncaught exception: Failure("hd")


What has happened? Just that the evaluation of (itl l) has taken place before the evaluation of (ihd l), so that on the last iteration of imap, the list referenced by l became the empty list before we examined its head. The list example is henceforth definitely empty even though we have not obtained any result:

# example ;;
- : string ilist = {c=[]}


The flaw in the function imap arises from a mixing of the genres that has not been controlled carefully enough. The choice of order of evaluation has been left to the system. We can reformulate the function imap, making explicit the order of evaluation, by using the syntactic construction let .. in ..

# let rec imap f l =
if iempty l then icreate()
else let h = ihd l in icons (f h) (imap f (itl l)) ;;
val imap : ('a -> 'b) -> 'a ilist -> 'b ilist = <fun>
# let example = icons "one" (icons "two" (icons "three" (icreate()))) ;;
val example : string ilist = {c=["one"; "two"; "three"]}
# imap (function x -> x) example ;;
- : string ilist = {c=["one"; "two"; "three"]}


However, the original list has still been lost:

# example ;;
- : string ilist = {c=[]}


Another way to make the order of evaluation explicit is to use the sequencing operator and a looping structure.

# let imap f l =
let l_res = icreate ()
in while not (iempty l) do
ignore (icons (f (ihd l)) l_res) ;
ignore (itl l)
done ;
{ l_res with c = List.rev l_res.c } ;;
val imap : ('a -> 'b) -> 'a ilist -> 'b ilist = <fun>
# let example = icons "one" (icons "two" (icons "three" (icreate()))) ;;
val example : string ilist = {c=["one"; "two"; "three"]}
# imap (function x -> x) example ;;
- : string ilist = {c=["one"; "two"; "three"]}
The presence of ignore emphasizes the fact that it is not the result of the functions that counts here, but their side effects on their argument. In addition, we had to put the elements of the result back in the right order (using the function List.rev).

Recursive or Iterative

People often mistakenly associate recursive with functional and iterative with imperative. A purely functional program cannot be iterative because the value of the condition of a loop never varies. By contrast, an imperative program may be recursive: the original version of the function imap is an example.

Calling a function conserves the values of its arguments during its computation. If it calls another function, the latter conserves its own arguments in addition. These values are conserved on the execution stack. When the call returns, these values are popped from the stack. The memory space available for the stack being bounded, it is possible to encounter the limit when using a recursive function with calls too deeply nested. In this case, Objective CAML raises the exception Stack_overflow.


# let rec succ n = if n = 0 then 1 else 1 + succ (n-1) ;;
val succ : int -> int = <fun>
# succ 100000 ;;
Stack overflow during evaluation (looping recursion?).


In the iterative version succ_iter, the stack space needed for a call does not depend on its argument.

# let succ_iter n =
let i = ref 0 in
for j=0 to n do incr i done ;
!i ;;
val succ_iter : int -> int = <fun>
# succ_iter 100000 ;;
- : int = 100001


The following recursive version has a priori the same depth of calls, yet it executes successfully with the same argument.

# let succ_tr n =
let rec succ_aux n accu =
if n = 0 then accu else succ_aux (n-1) (accu+1)
in
succ_aux 1 n ;;
val succ_tr : int -> int = <fun>
# succ_tr 100000 ;;
- : int = 100001


This function has a special form of recursive call, called tail recursion, in which the result of this call will be the result of the function without further computation. It is therefore unnecessary to have stored the values of the arguments to the function while computing the recursive call. When Objective CAML can observe that a call is tail recursive, it frees the arguments on the stack before making the recursive call. This optimization allows recursive functions that do not increase the size of the stack.

Many languages detect tail recursive calls, but it is indispensable in a functional language, where naturally many tail recursive calls are used.


Previous Contents Next ocaml-book-1.0/en/html/book-ora033.gif0000644000000000000000000000433007452056111014260 0ustar GIF89aUUU!,ڋ޼扒ʶ Mm6àL*i JTjܮ{{6}5G?GGX7hH p `B3P j(z&@`zeXj([V;p8Л6vkK,[*Y||flF- <-g]=+m``ynWn>Ί}L={}[WB0T-C- ,nb%K6=|9K4KɜV3MP7uϡ~ %t,IԩQNJJXU\Il5l`Śņ6ڵI` 7ܹtڽ7^j$FKf4k\ a&7cŋo\wfΡY tЙ:8 k"f{WەETTnܻG8Op.>eʗi~z䴹>'sj5խCt_'m3;?X_{|2> hхO4qIH. 1|2 SjHb%5 bcT(>Hd*x#J> A: eT(Vn9ee{}]# ėf֠BNbQ6iRfDci}y'oÊ~ vvB$( `lHyE=[>DJgYXbb gKz韺9*q?wĩ"PwFz̹* يтJj}5[}liz#Ķ֒٧{EhnV+X&SQ־u*0k0p Z7{+f쫱ox#q%#1'K@B2ZCFjmj rqsAO WݲR3-/6-)Wb 4$Wv\FrOmv)?=węX/x?ycSl=ߘ߬*Nfg9ٖN:kߠ3jݹVح~JNcΎp>~<Ѩm'o7I|w~i^}W^>}m־9ݧoŃ/_\w=Х[Ǽ/s?bs (: \Qu| d楒MЄ!p r89@#Sf@a k#m-t!DQ:pE&R'"b,JYќ MkGǶ qF!@Aq;l"ԬFG~ qc%H߸Bq@m+)Lte*UQ=MR62:jejA/Kqeg^5(ūXeJFsE;ׯ5F Ub{VV cxUc5 jy]J eSDiro %k\rp :U w\r<ѕ.ĨkŧNÖfCmf0a:F"C9#|kB_u7/~'_NDx+OuBpt_ʤFbcWf.$:aF8U qFLbDXb(5W KK0+cT;ͱcٟJD.r,!Wu`t DPRq$efR~u"0cN ]>3]5_FlLPRhD&F1Ti.eZϛVRiѣn/}IDsf5P\=I֨ʩ}Z7(kא6TM"ANf;{հ4&ܣ֊inw"jND(nYh7r8؛޷C\;ocaml-book-1.0/en/html/book-ora049.gif0000644000000000000000000000274707452056111014301 0ustar GIF89awww!,*޼Hʶ jL ! flJKЩbS-C1355r'9.='8WhH)9IYiyi5 *:JZ*iں*;K +PKy L\l:l =,] MMz.JY ~x==N/^oJâq  /d5tP!-EduQ+ -V4.SF($yPɓzEÏ1'sRHI,q ejISNn\3@L3qUT[VZk$Nztf՞cB6ώfOL%mN1 k^j 3`~'v1`ƔE!ͺM [q@D;ά1M?3m:PJo|.LkZMl1)7*i<,+ !@Ki m44H'LWSv,NWMX/ ߧ^ vb}>dvjgn mMwvz\!ww6 87mx߈-uKؔcg揲-zZl랜Q/lH#:(7{dxƚڌyssO,mK[8so}C{ٻO{_|:9{}>5;Nۓ/ȍ.qc+ 7gRIH:8od HAs˒3N+ ᪾wp$\ S03`='>op.Daw(}@,OW(K1!, y2ȸr7+5Yɖ7ƽ|aT^ScFt|1 ȷ2D" /wI "l$'O!)KɅS \ZJ)2N%-@["</{_iL 3i&3ϰg lljs|@;ocaml-book-1.0/en/html/book-ora043.html0000644000000000000000000000636207453055377014505 0ustar To Learn More Previous Contents Next

To Learn More

The principal consequences of adding imperative traits to a functional language are:
  • To determine the evaluation strategy (strict evaluation);
  • to add implementation constraints, especially for the GC (see Chapter 9);
  • For statically typed languages, to make their type system more complex;
  • To offer different styles of programming in the same language, permitting us to program in the style appropriate to the algorithm at hand, or possibly in a mixed style.
This last point is important in Objective CAML where we need the same parametric polymorphism for functions written in either style. For this, certain purely functional programs are no longer typable after the addition. Wright's article ([Wri95]) explains the difficulties of polymorphism in languages with imperative aspects. Objective CAML adopts the solution that he advocates. The classification of different kinds of polymorphism in the presence of physical modification is described well in the thesis of Emmanuel Engel ([Eng98]).

These consequences make the job of programming a bit harder, and learning the language a bit more difficult. But because the language is richer for this reason and above all offers the choice of style, the game is worth the candle. For example, strict evaluation is the rule, but it is possible to implement basic mechanisms for lazy evaluation, thanks to the mixture of the two styles. Most purely functional languages use a lazy evaluation style. Among languages close to ML, we would mention Miranda, LazyML, and Haskell. The first two are used at universities for teaching and research. By contrast, there are significant applications written in Haskell. The absence of controllable side effects necessitates an additional abstraction for input/output called monads. One can read works on Haskell (such as [Tho99]) to learn more about this subject. Streams are a good example of the mixture of functional and imperative styles. Their use in lexical and syntactic analysis is described in Chapter 11.




Previous Contents Next ocaml-book-1.0/en/html/book-ora043.gif0000644000000000000000000001114107452056111014257 0ustar GIF89arUUUrrr!,rH0I8ͻ`(dihp,tmx|pH,Hpl:,IZجEz`vL.z^|zI;~~'G/.xd}DsJì[q0ty ilE c.(H' G!-o+x+-FS@@ rF1\G(1NdHw `̖&{8も,v1h vN=4rEFeܺȓ_ Q"9y[{" mUTO=HkKj2#CM՟㻏gmp/ߥΎ˜ȄLh ݴ3۪BzNΪ0v {/N6[*G`Vop}=<-|ovsR:= _Yy!!-׀n1@ RY @~Wi 3wr4V=qH f~%H!hq05z(~hNiqTV,a7֟K1szFU1ءHd=#$n19uȵC(8dFdYTMTYAcoC&~INe:/1W#jJkhH| \qg-ZIjĥDif3YLnVE]}zSͪ#Y*gqɪ>[ϑ<-4+O%885֑AN8r)Ƃk K.[o oko>;_aޟM8-&Nϥ?adU>|];S!QpWQ9:FeeLIw;\m6*n``GC_1N䃟'z/j]э֕7oȮ?^|#*)Z1EI6D NLfguc@F(R1L| 5j]iO$bK %;3=4w  y1Ĉyf$;ڡ#(: Z?r2N"V(o!$P(#KґdO%I ,3:V#hrC lÛL8p9CNgs@g PX=ә~3`?JІp{AЅ"x uh*pQ(F?эHCi&=RR`z&%(Ndy@=LPn lx uD/%zu9Щ5*jѴ]UZ,S؆NX~IV=}^KPyעUUl͗'߲[a[t+Ψם}+_cqV;Mwf.kQNp ;'L [ΰ7kE0<#)89Pb.vWlʔ-A\w@L"HN&;bT'á^VykunSyy>Q' FLBOb7N{zt+RԨ6sE]QCZ4\a,7udj][׾b|^WHjd;VZiIO{h\{nRYnnW{v[δMoNû&w7t^jm)LŬMUfxzT3 b]{ ;Jb$EW4LX d";J+G$|mGx۞O:F񑣹8sɔwy=| 4UIu2c-7w]߻rwn~x}ۯ]M~ڕ嗍{> '|2V#zɧwn쟽D?{)^1F;Xc?~/X==c/}S_?˿?ڷT?02Gx ؀&S{g (k&ȁx!+G}(Ȃ Xx)8.5V:-(6Cx6胰Hȃ;hEHNH7ƄQRS؄\=vj[XZxThBb=2fqk؆nHu؅vHr\h|XjM(F}88x[Hz艦HȈ戟؊Hh`xwȋw%8(woҸLe9!p!K%ts@93@"[QXȌ|yDAk vv1p7(،H`1Fj8Tj*V i"y9RV #B+UV3+rICBtcD@5uY)T we0'&3'[+B$-rI#}H; ‘ɔ!WO KSSY#65J^y2&E-DEWI$; 2ws) IGiHZDE3[tx"2ÑIS\D5q9IɆ@u[H5YY)@+HBDǚ)u3; q߄9IG44.ud[y吪*4\Q4QCs=#4) JӁyظ&?rḺKU^8LX<:ĕՍAɎ:WZp1`'Di # oT5n)!# Dp y7_gI/j:) \e)`٤.5Uw*LN RxG1]ڣ2U`a(A_ze *fmj觥hz=Ƨ~j? wur {Jz.$u 4D ڧ;%u+jgj, l%4#ӓV:eDU9b$@r kS:k[)4 E|A;a2}ʨkLR瞩T55K$tD[ 4D-]#ʳڳXX:`׵\USOَcۨKzhl۶n+';_o+6ЈnvawRJ#[NjL&BTrϕA. 8JyO#xQ,띉k_HD{#Y4n=m˺+soufG)%xƫ纙G5ۼ,CF&OKF[=C^O\ڻӹs !sDfCþBA&C@f?_ ¿FkdHoT vKl9' €Bzd°L¼+µ +k̦;|O*,IS̙I\K{b3!lK7e' \]zlqs{uUT|EWi)-ڕSI4s*ǼImʗ:%q>F$2$__ܱٲ>)f^ Chapter Overview Previous Contents Next

Chapter Overview

This chapter presents the different ways to compile an Objective CAML program and compares their portability and efficiency. The first section explains the different steps of Objective CAML compilation. The second section describes the different types of compilation and the syntax for the production of executables. The third section shows how to construct standalone executables - programs which are independent of an installation of the Objective CAML system. Finally the fourth section compares the different types of compilation with respect to portability and efficiency of execution.


Previous Contents Next ocaml-book-1.0/en/html/book-ora057.html0000644000000000000000000013670007453055400014475 0ustar Database queries Previous Contents Next

Database queries

The implementation of a database, its interface, and its query language is a project far too ambitious for the scope of this book and for the Objective CAML knowledge of the reader at this point. However, restricting the problem and using the functional programming style at its best allows us to create an interesting tool for query processing. For instance, we show how to use iterators as well as partial application to formulate and execute queries. We also show the use of a data type encapsulating functional values.

For this application, we use as an example a database on the members of an association. It is presumed to be stored in the file association.dat.

Data format

Most database programs use a ``proprietary'' format to store the data they manipulate. However, it is usually possible to store the data as some text that has the following structure:
  • the database is a list of cards separated by carriage-returns;
  • each card is a list of fields separated by some given character, ':' in our case;
  • a field is a string which contains no carriage-return nor the character ':';
  • the first card is the list of the names associated with the fields, separated by the character '|'.
The association data file starts with:
Num|Lastname|Firstname|Address|Tel|Email|Pref|Date|Amount
0:Chailloux:Emmanuel:Universit P6:0144274427:ec@lip6.fr:email:25.12.1998:100.00
1:Manoury:Pascal:Laboratoire PPS::pm@lip6.fr:mail:03.03.1997:150.00
2:Pagano:Bruno:Cristal:0139633963::mail:25.12.1998:150.00
3:Baro:Sylvain::0144274427:baro@pps.fr:email:01.03.1999:50.00
The meaning of the fields is the following:
  • Num is the member number;
  • Lastname, Firstname, Address, Tel, and Email are obvious;
  • Pref indicates the means by which the member wishes to be contacted: by mail (mail), by email (email), or by phone (tel);
  • Date and Amount are the date and the amount of the last membership fee received, respectively.
We need to decide what represention the program should use internally for a database. We could use either a list of cards or an array of cards. On the one hand, a list has the nice property of being easily modified: adding and removing a card are simple operations. On the other hand, an array allows constant access time to any card. Since our goal is to work on all the cards and not on some of them, each query accesses all the cards. Thus a list is a good choice. The same issue arises concerning the cards themselves: should they be lists or arrays of strings? This time an array is a good choice, since the format of a card is fixed for the whole database. It not possible to add a new field. Since a query might access only a few fields, it is important for this access to be fast.

The most natural solution for a card would be to use an array indexed by the names of the fields. Since such a type is not available in Objective CAML, we can use an array (indexed by integers) and a function associating a field name with the array index corresponding to the field.

# type data_card = string array ;;
# type data_base = { card_index : string -> int ; data : data_card list } ;;


Access to the field named n of a card dc of the database db is implemented by the function:

# let field db n (dc : data_card) = dc.(db.card_index n) ;;
val field : data_base -> string -> data_card -> string = <fun>
The type of dc has been set to data_card to constrain the function field to only accept string arrays and not arrays of other types.

Here is a small example:

# let base_ex =
{ data = [ [|"Chailloux"; "Emmanuel"|] ; [|"Manoury"; "Pascal"|] ] ;
card_index = function "Lastname"->0 | "Firstname"->1
| _->raise Not_found } ;;
val base_ex : data_base =
{card_index=<fun>;
data=[[|"Chailloux"; "Emmanuel"|]; [|"Manoury"; "Pascal"|]]}
# List.map (field base_ex "Lastname") base_ex.data ;;
- : string list = ["Chailloux"; "Manoury"]


The expression field base_ex "Lastname" evaluates to a function which takes a card and returns the value of its "Lastname" field. The library function List.map applies the function to each card of the database base_ex, and returns the list of the results: a list of the "Lastname" fields of the database.

This example shows how we wish to use the functional style in our program. Here, the partial application of field allows us to define an access function for a given field, which we can use on any number of cards. This also shows us that the implementation of the field function is not very efficient, since although we are always accessing the same field, its index is computed for each access. The following implementation is better:

# let field base name =
let i = base.card_index name in fun (card : data_card) -> card.(i) ;;
val field : data_base -> string -> data_card -> string = <fun>
Here, after applying the function to two arguments, the index of the field is computed and is used for any subsequent application.

Reading a database from a file

As seen from Objective CAML, a file containing a database is just a list of lines. The first work that needs to be done is to read each line as a string, split it into smaller parts according to the separating character, and then extract the corresponding data as well as the field indexing function.

Tools for processing a line

We need a function split that splits a string at every occurrence of some separating character. This function uses the function suffix which returns the suffix of a string s after some position i. To do this, we use three predefined functions:
  • String.length returns the length of a string;
  • String.sub returns the substring of s starting at position i and of length l;
  • String.index_from computes the position of the first occurrence of character c in the string s, starting at position n.

# let suffix s i = try String.sub s i ((String.length s)-i)
with Invalid_argument("String.sub") -> "" ;;
val suffix : string -> int -> string = <fun>
# let split c s =
let rec split_from n =
try let p = String.index_from s n c
in (String.sub s n (p-n)) :: (split_from (p+1))
with Not_found -> [ suffix s n ]
in if s="" then [] else split_from 0 ;;
val split : char -> string -> string list = <fun>


The only remarkable characteristic in this implementation is the use of exceptions, specifically the exception Not_found.

Computing the data_base structure
There is no difficulty in creating an array of strings from a list of strings, since this is what the of_list function in the Array module does. It might seem more complicated to compute the index function from a list of field names, but the List module provides all the needed tools.

Starting from a list of strings, we need to code a function that associates each string with an index corresponding to its position in the list.

# let mk_index list_names =
let rec make_enum a b = if a > b then [] else a::(make_enum (a+1) b) in
let list_index = (make_enum 0 ((List.length list_names) - 1)) in
let assoc_index_name = List.combine list_names list_index in
function name -> List.assoc name assoc_index_name ;;
val mk_index : 'a list -> 'a -> int = <fun>
To create the association function between field names and indexes, we combine the list of indexes and the list of names to obtain a list of associations of the type string * int list. To look up the index associated with a name, we use the function assoc from the List library. The function mk_index returns a function that takes a name and calls assoc on this name and the previously built association list.

It is now possible to create a function that reads a file of the given format.

# let read_base filename =
let channel = open_in filename in
let split_line = split ':' in
let list_names = split '|' (input_line channel) in
let rec read_file () =
try
let data = Array.of_list (split_line (input_line channel )) in
data :: (read_file ())
with End_of_file -> close_in channel ; []
in
{ card_index = mk_index list_names ; data = read_file () } ;;
val read_base : string -> data_base = <fun>
The auxiliary function read_file reads records from the file, and works recursively on the input channel. The base case of the recursion corresponds to the end of the file, signaled by the End_of_file exception. In this case, the empty list is returned after closing the channel.

The association's file can now be loaded:

# let base_ex = read_base "association.dat" ;;
val base_ex : data_base =
{card_index=<fun>;
data=
[[|"0"; "Chailloux"; "Emmanuel"; "Universit\233 P6"; "0144274427";
"ec@lip6.fr"; "email"; "25.12.1998"; "100.00"|];
[|"1"; "Manoury"; "Pascal"; "Laboratoire PPS"; ...|]; ...]}


General principles for database processing

The effectiveness and difficulty of processing the data in a database is proportional to the power and complexity of the query language. Since we want to use Objective CAML as query language, there is no limit a priori on the requests we can express! However, we also want to provide some simple tools to manipulate cards and their data. This desire for simplicity requires us to limit the power of the Objective CAML language, through the use of general goals and principles for database processing.

The goal of database processing is to obtain a state of the database. Building such a state may be decomposed into three steps:
  1. selecting, according to some given criterion, a set of cards;
  2. processing each of the selected cards;
  3. processing all the data collected on the cards.
Figure 6.1 illustrates this decomposition.


Figure 6.1: Processing a request.


According to this decomposition, we need three functions of the following types:
  1. (data_card -> bool) -> data_card list -> data_card list
  2. (data_card -> 'a) -> data_card list -> 'a list
  3. ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b
Objective CAML provides us with three higher-order function, also known as iterators, introduced page ??, that satisfy our specification:

# List.find_all ;;
- : ('a -> bool) -> 'a list -> 'a list = <fun>
# List.map ;;
- : ('a -> 'b) -> 'a list -> 'b list = <fun>
# List.fold_right ;;
- : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b = <fun>
We will be able to use them to implement the three steps of building a state by choosing the functions they take as an argument.

For some special requests, we will also use:

# List.iter ;;
- : ('a -> unit) -> 'a list -> unit = <fun>
Indeed, if the required processing consists only of displaying some data, there is nothing to compute.

In the next paragraphs, we are going to see how to define functions expressing simple selection criteria, as well as simple queries. We conclude this section with a short example using these functions according to the principles stated above.

Selection criteria

Concretely, the boolean function corresponding to the selection criterion of a card is a boolean combination of properties of some or all of the fields of the card. Each field of a card, even though it is a string, can contain some information of another type: a float, a date, etc.

Selection criteria on a field

Selecting on some field is usually done using a function of the type data_base -> 'a -> string -> data_card -> bool. The 'a type parameter corresponds to the type of the information contained in the field. The string argument corresponds to the name of the field.

String fields
We define two simple tests on strings: equality with another string, and non-emptiness.

# let eq_sfield db s n dc = (s = (field db n dc)) ;;
val eq_sfield : data_base -> string -> string -> data_card -> bool = <fun>
# let nonempty_sfield db n dc = ("" <> (field db n dc)) ;;
val nonempty_sfield : data_base -> string -> data_card -> bool = <fun>


Float fields
To implement tests on data of type float, it is enough to translate the string representation of a decimal number into its float value. Here are some examples obtained from a generic function tst_ffield:

# let tst_ffield r db v n dc = r v (float_of_string (field db n dc)) ;;
val tst_ffield :
('a -> float -> 'b) -> data_base -> 'a -> string -> data_card -> 'b = <fun>
# let eq_ffield = tst_ffield (=) ;;
# let lt_ffield = tst_ffield (<) ;;
# let le_ffield = tst_ffield (<=) ;;
(* etc. *)
These three functions have type:

data_base -> float -> string -> data_card -> bool.

Dates
This kind of information is a little more complex to deal with, as it depends on the representation format of dates, and requires that we define date comparison.

We decide to represent dates in a card as a string with format dd.mm.yyyy. In order to be able to define additional comparisons, we also allow the replacement of the day, month or year part with the underscore character ('_'). Dates are compared according to the lexicographic order of lists of integers of the form [year; month; day]. To express queries such as: ``is before July 1998'', we use the date pattern: "_.07.1998". Comparing a date with a pattern is accomplished with the function tst_dfield which analyses the pattern to create the ad hoc comparison function. To define this generic test function on dates, we need a few auxiliary functions.

We first code two conversion functions from dates (ints_of_string) and date patterns (ints_of_dpat) to lists of ints. The character '_' of a pattern will be replaced by the integer 0:

# let split_date = split '.' ;;
val split_date : string -> string list = <fun>
# let ints_of_string d =
try match split_date d with
[d;m;y] -> [int_of_string y; int_of_string m; int_of_string d]
| _ -> failwith "Bad date format"
with Failure("int_of_string") -> failwith "Bad date format" ;;
val ints_of_string : string -> int list = <fun>

# let ints_of_dpat d =
let int_of_stringpat = function "_" -> 0 | s -> int_of_string s
in try match split_date d with
[d;m;y] -> [ int_of_stringpat y; int_of_stringpat m;
int_of_stringpat d ]
| _ -> failwith "Bad date format"
with Failure("int_of_string") -> failwith "Bad date pattern" ;;
val ints_of_dpat : string -> int list = <fun>


Given a relation r on integers, we now code the test function. It simply consists of implementing the lexicographic order, taking into account the particular case of 0:

# let rec app_dtst r d1 d2 = match d1, d2 with
[] , [] -> false
| (0::d1) , (_::d2) -> app_dtst r d1 d2
| (n1::d1) , (n2::d2) -> (r n1 n2) || ((n1 = n2) && (app_dtst r d1 d2))
| _, _ -> failwith "Bad date pattern or format" ;;
val app_dtst : (int -> int -> bool) -> int list -> int list -> bool = <fun>


We finally define the generic function tst_dfield which takes as arguments a relation r, a database db, a pattern dp, a field name nm, and a card dc. This function checks that the pattern and the field from the card satisfy the relation.

# let tst_dfield r db dp nm dc =
r (ints_of_dpat dp) (ints_of_string (field db nm dc)) ;;
val tst_dfield :
(int list -> int list -> 'a) ->
data_base -> string -> string -> data_card -> 'a = <fun>


We now apply it to three relations.

# let eq_dfield = tst_dfield (=) ;;
# let le_dfield = tst_dfield (<=) ;;
# let ge_dfield = tst_dfield (>=) ;;
These three functions have type:
data_base -> string -> string -> data_card -> bool.

Composing criteria

The tests we have defined above all take as first arguments a database, a value, and the name of a field. When we write a query, the value of these three arguments are known. For instance, when we work on the database base_ex, the test ``is before July 1998'' is written

# ge_dfield base_ex "_.07.1998" "Date" ;;
- : data_card -> bool = <fun>


Thus, we can consider a test as a function of type data_card -> bool. We want to obtain boolean combinations of the results of such functions applied to a given card. To this end, we implement the iterator:

# let fold_funs b c fs dc =
List.fold_right (fun f -> fun r -> c (f dc) r) fs b ;;
val fold_funs : 'a -> ('b -> 'a -> 'a) -> ('c -> 'b) list -> 'c -> 'a = <fun>
Where b is the base value, the function c is the boolean operator, fs is the list of test functions on a field, and dc is a card.

We can obtain the conjunction and the disjunction of a list of tests with:

# let and_fold fs = fold_funs true (&) fs ;;
val and_fold : ('a -> bool) list -> 'a -> bool = <fun>
# let or_fold fs = fold_funs false (or) fs ;;
val or_fold : ('a -> bool) list -> 'a -> bool = <fun>


We easily define the negation of a test:

# let not_fun f dc = not (f dc) ;;
val not_fun : ('a -> bool) -> 'a -> bool = <fun>


For instance, we can use these combinators to define a selection function for cards whose date field is included in a given range:

# let date_interval db d1 d2 =
and_fold [(le_dfield db d1 "Date"); (ge_dfield db d2 "Date")] ;;
val date_interval : data_base -> string -> string -> data_card -> bool =
<fun>


Processing and computation

It is difficult to guess how a card might be processed, or the data that would result from that processing. Nevertheless, we can consider two common cases: numerical computation and data formatting for printing. Let's take an example for each of these two cases.

Data formatting

In order to print, we wish to create a string containing the name of a member of the association, followed by some information.

We start with a function that reverses the splitting of a line using a given separating character:

# let format_list c =
let s = String.make 1 c in
List.fold_left (fun x y -> if x="" then y else x^s^y) "" ;;
val format_list : char -> string list -> string = <fun>


In order to build the list of fields we are interested in, we code the function extract that returns the fields associated with a given list of names in a given card:

# let extract db ns dc =
List.map (fun n -> field db n dc) ns ;;
val extract : data_base -> string list -> data_card -> string list = <fun>


We can now write the line formatting function:

# let format_line db ns dc =
(String.uppercase (field db "Lastname" dc))
^" "^(field db "Firstname" dc)
^"\t"^(format_list '\t' (extract db ns dc))
^"\n" ;;
val format_line : data_base -> string list -> data_card -> string = <fun>
The argument ns is the list of requested fields. In the resulting string, fields are separated by a tab ('\t') and the string is terminated with a newline character.

We display the list of last and first names of all members with:

# List.iter print_string (List.map (format_line base_ex []) base_ex.data) ;;
CHAILLOUX Emmanuel
MANOURY Pascal
PAGANO Bruno
BARO Sylvain
- : unit = ()


Numerical computation

We want to compute the total amount of received fees for a given set of cards. This is easily done by composing the extraction and conversion of the correct field with the addition. To get nicer code, we define an infix composition operator:

# let (++) f g x = g (f x) ;;
val ++ : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c = <fun>
We use this operator in the following definition:

# let total db dcs =
List.fold_right ((field db "Amount") ++ float_of_string ++ (+.)) dcs 0.0 ;;
val total : data_base -> data_card list -> float = <fun>
We can now apply it to the whole database:

# total base_ex base_ex.data ;;
- : float = 450


An example

To conclude, here is a small example of an application that uses the principles described in the paragraphs above.

We expect two kinds of queries on our database:
  • a query returning two lists, the elements of the first containing the name of a member followed by his mail address, the elements of the other containing the name of the member followed by his email address, according to his preferences.
  • another query returning the state of received fees for a given period of time. This state is composed of the list of last and first names, dates and amounts of the fees as well as the total amount of the received fees.

List of addresses

To create these lists, we first select the relevant cards according to the field "Pref", then we use the formatting function format_line:

# let mail_addresses db =
let dcs = List.find_all (eq_sfield db "mail" "Pref") db.data in
List.map (format_line db ["Mail"]) dcs ;;
val mail_addresses : data_base -> string list = <fun>

# let email_addresses db =
let dcs = List.find_all (eq_sfield db "email" "Pref") db.data in
List.map (format_line db ["Email"]) dcs ;;
val email_addresses : data_base -> string list = <fun>


State of received fees

Computing the state of the received fees uses the same technique: selection then processing. In this case however the processing part is twofold: line formatting followed by the computation of the total amount.

# let fees_state db d1 d2 =
let dcs = List.find_all (date_interval db d1 d2) db.data in
let ls = List.map (format_line db ["Date";"Amount"]) dcs in
let t = total db dcs in
ls, t ;;
val fees_state : data_base -> string -> string -> string list * float = <fun>
The result of this query is a tuple containing a list of strings with member information, and the total amount of received fees.

Main program

The main program is essentially an interactive loop that displays the result of queries asked by the user through a menu. We use here an imperative style, except for the display of the results which uses an iterator.

# let main() =
let db = read_base "association.dat" in
let finished = ref false in
while not !finished do
print_string" 1: List of mail addresses\n";
print_string" 2: List of email addresses\n";
print_string" 3: Received fees\n";
print_string" 0: Exit\n";
print_string"Your choice: ";
match read_int() with
0 -> finished := true
| 1 -> (List.iter print_string (mail_addresses db))
| 2 -> (List.iter print_string (email_addresses db))
| 3
-> (let d1 = print_string"Start date: "; read_line() in
let d2 = print_string"End date: "; read_line() in
let ls, t = fees_state db d1 d2 in
List.iter print_string ls;
print_string"Total: "; print_float t; print_newline())
| _ -> ()
done;
print_string"bye\n" ;;
val main : unit -> unit = <fun>


This example will be extended in chapter 21 with an interface using a web browser.

Further work

A natural extension of this example would consist of adding type information to every field of the database. This information would be used to define generic comparison operators with type data_base -> 'a -> string -> data_card -> bool where the name of the field (the third argument) would trigger the correct conversion and test functions.


Previous Contents Next ocaml-book-1.0/en/html/book-ora149.html0000644000000000000000000000674607453055400014505 0ustar To Learn More Previous Contents Next

To Learn More

There are a huge number of publications on object-oriented programming. Each language implements a different model.

A general introduction (still topical for the first part) is ``Langages Objets '' ([MNC+91]) which explains the object-oriented approach. A more specialized book, ``Langages et modles objets'' [DEMN98], gives the examples in this domain.

For modeling, the book ``Design patterns'' ([GHJV95]) gives a catalogue of design patterns that show how reusability is possible.

The reference site for the UML notation is Rational:

Link


http://www.rational.com/uml/resources


For functional languages with an object extension, we mention the ``Lisp'' objects, coming from the SMALLTALK world, and CLOS (meaning Common Lisp Object System), as well as a number of Scheme's implementing generic functions similar to those in CLOS.

Other proposals for object-oriented languages have been made for statically typed functional languages, such as Haskell, a pure functional language which has parameterized and ad hoc polymorphism for overloading.

The paper [RV98] presents the theoretical aspects of the object extension of Objective CAML.

To learn more on the static object typing in Objective CAML, you can look at several lectures available online.

Lectures by Mara-Virginia Aponte:

Link


http://tulipe.cnam.fr/personne/aponte/ocaml.html


A short presentation of objects by Didier Rmy:

Link


http://pauillac.inria.fr/~remy/objectdemo.html


Lectures by Didier Rmy at Magistre MMFAI:

Link


http://pauillac.inria.fr/~remy/classes/magistere/


Lectures by Roberto Di Cosmo at Magistre MMFAI:

Link


http://www.dmi.ens.fr/users/dicosmo/CourseNotes/OO/









Previous Contents Next ocaml-book-1.0/en/html/book-ora034.html0000644000000000000000000000146407453055401014467 0ustar Notes
1
A 32-bit word contains four characters coded as bytes
2
With appropriate read permissions, that is.
ocaml-book-1.0/en/html/book-ora183.html0000644000000000000000000000454307453055400014474 0ustar Introduction Previous Contents Next

Introduction

With distributed programming, you can build applications running on several machines that work together through a network to accomplish a task. The computation model described here is parallel programming with distributed memory. Local and remote programs communicate using a network protocol. The best-known and most widely-used of these is IP (Internet protocol) and its TCP and UDP layers. Beginning with these low-level layers, many services are built on the client-server model, where a server waits for requests from different clients, processes those requests, and sends responses. As an example, the HTTP protocol allows communication between Web browsers and Web servers. The distribution of tasks between clients and servers is suitable for many different software architectures.

The Objective CAML language offers, through its Unix library, various means of communication between programs. Sockets allow communication through the TCP/IP and UDP/IP protocols. This part of the Unix library has been ported to Windows. Because you can create ``heavyweight'' processes with Unix.fork as well as lightweight processes with Thread.create, you can create servers that accept many requests at once. Finally, an important point when creating a new service is the definition of a protocol appropriate to the application.


Previous Contents Next ocaml-book-1.0/en/html/book-ora034.gif0000644000000000000000000000471107452056111014264 0ustar GIF89aUUUrrr!,X0I8ͻ`(dihlj,tmx/PpH,ȇ+K:Шtz[Ԭv"1wL.pMnpǎ'wuGQI~  / ,N ~ |R~nŇ ^ҋ@-ѫ-fָɱ hx͡_Q-]TM1GRhm]33QPǏ`I;M\g˗0I23͛5Ύ= }УEFAO'DHiӫ?V\jB`UڪٳhӾ(:A۷pʝKݻxўRa?:68 Vnym6l"p:W60ܘCbBX޳L9eake{}QNXs׿"M^m:O ,>lrcrsFD ŋ3?yU1دZ|Pl} {l[V~zTH !g& tDŽX̆Da%ՆbtĄr"B ζ%^#Q-c ODX\Bj X8!JȔTh=L"FCW֙EdsxFg٧c"]q%:h{m)-Rڈ*| jꊘ ܦ^zZqHsV*M Z+*, {l*;˖*ұJmKΐlZD ]zaqІ+ '**c. לAt\;oVX2D&'0z WOb#)~J`9%!\ ?|~>3ڗay _/y>3 йMaEʓ˲ #A5QLH79;P t6P{ wEo5$~a_T _)Uzrw{y˙JnrfRw޺ }o-;>;LC^89ȃL8%O!Z3(]o觯UH;vYsxPտ}?~(AF],&ŀ XAjC7%LYk z#=`V Bڊa ÀîZҳ`h#"Q(jUl&D0yVZMX+brZ$Ǝ b4&RܒE<чG.X)FHh:}Tƪ Hױ$h5iI.u$9E14Z qrh9F oR$e,iBVCF"#1ے_^#nSĵf)<:^92X7t 7{9Ν3 㦶#KdRA8g/Z'TOѯk'JJz3ۡ&:g66 "D*̥$f.\ZF2{Dh%NԅK(Q6JD=hct)IÕTNE$"*NpP,U29cj*A:H5V]i &Uz3HI8c[:|FP K0 '֎_;eٯzӂ ´u.%WTs`,xZ䈚BkX43N&n:ڝGhdTjqk\TҍLzWӽ^_Q0*enxq;_W5}XrZ_:WPu+}zUhz'_ڗd˰7{EL,kģC>U ;'5d̳j 4ڽhxJ M%|W.x-ldOƮ_0[(F3}mrh&n;no֛v&}x>>G= MHtჀû"-|⺨4q~^'8B>菋$-l <% ]}y>9 s 3OD|O LЁl{;UKdd]p5-عu7]#ɐN!1HDPv_D^RLǞȁwy4~W$K}B‡eBO$;ocaml-book-1.0/en/html/book-ora072.gif0000644000000000000000000002302007452056112014261 0ustar GIF89at*}}}{{{yyywwwuuusssWWW333pppnnnllljjjĊ!!Q,tQ*ma)`(_&^%]$\#["!Y X (zXȰ!C+#Jءŋhq# CBɒR\r˗0ajI  㠧O"@BhH"´Ӧ J*UիX.ʵ+ `Ê ٳhӢM۷p"Kݻvw/6@?+^ǐL2e3kܣϞMz4ӨSk;b˞M6ms{w .$Q+WysQP<*ɗY }`Պ_6m_?{=L0#wlbڀfh%xk fۃۄF܅܆9 $ljp ,GbvXmywz@ސyח|G~ h 6`\Vᗺe(fqr%)"l0FƜtIՎM@)hDG"1ɘAiQ a v`ØPg~fm .)5Z+Ʈ f 'o+l\v kh' z2A*Qʙajq\jcaJbmꬲ檯/ ,pm\Q m @ #E찄"puбs9+r{Ek& +2 kiy+  {.%b!;/n"v\:+,mkj!%_@&{Ϭ .P'.'0N= tB] 2Aݜ&Rob][w/-ٴ;;Q v|̄U/F 8Ox/~f߇>&,BM4k z!Ɖ>:7~:걪N+r`G0ىv˝(:wi @º ,{ >}|=8A UBvV E#C '!sCˇ[rm7!]~8_ 5ЀC= -2 hL61 @:1BfN( `Ͱbx1Bp![#,@N0IIV]"boH$*kmIVTņi1b]HKލfK4s0<082if"@P$PN  x~l"FY7V*[WN\e-o9F]沗>c0~.l@:=h28uD jvi TȤt8]8Er\eJw3#=h3Sf? ОLP ѢFTHhJ:B |$I?3nP) XWt1L%X29 NCQԾ&}j Kv Mlb IJ౐, | (ᲘU6YHhGKҚHHjWֲlgKᶸͭnw"OK\FQr2:ХQVrP5$iZpL3bҀ|Kͯ~L턂;'L [ΰ7{ GL(NW0gL8αw@L"HN&;PL*[Xβ.{`L2hN6pL:xs>πMBЈNF;ѐ'MJ[ϊ7N{ӠG LԨNWVЦ~gMZ֪5w^׸5Mb؍6f;~Mj_:ζ>cqnuv[~MoX'η}7Npj O;<~'kS u/.kseC|-?ˆ4_|)9K|}^:FЇs3zL9ГoSP:͋kW:"¾k|SGN9{v]t.E{ݹnhp:U=so6W?}#|7?χ>?sw}?^9=G_׻O|ygGyxy|hu H~ QwHzXiv{i"z]|$Xy؁sOvwƁ68i1t]˱3KK; ytl`wR;MrM˴Xm;bx$8Nn+^;/ 6ej=ʳ\y~Hs禷 Ǔ7@utٸ>w;nkk빦ɊCgjxɾtkʝfql+rݛ{ w;6KJػɺ( gJO)ÍI[~Q), zOˮg鋝뫿Չ8|͆!_0KU|E|K v׋xW (|@kȻZ ;u jKkrܚ*Y˥Qt囼Vlۼn<y G6ez| eJbJfށLՀkءӦX,&9]wh}]qكwγڸ,oy[ܺ;ε n79X|f ky|̐L;䋺ɔ=>nVY|r͜<^M^x_܅vs{X.ۊ\kZl9ڰΝ«f|~y^ƹޝIڄ΢[Pj_l.z'ԛ.|<ؓ^挷#˭p_nNNs]٧&{~TjqfKv.n`.P䯖}皊~{?^/,OŌ gI~{쀮ε*-/_^n$^I].ǹ&>UFj5N"P`>N.mj{=\AC, WJK. ݃3N~{V񈵬K->j:튖 pOL|B?xǘΎVԷ+xՄl*߾;ߏӆkʿg[?k8ǝ0 za]ۦՀ %EXhx((ȨyiHy9ye * Pjz0J 0K[kk˚ 9K)˩I\xl< ]: {z L>.h>IM+}K 9եcwPFL0&{{Ŋ--|PRH \ȇFvGqŋ2r{3J* N'Iќ0yYTCW\Z𪮡Uj ԰d\5`RYnMiٴkݼ{_Mo߸y λxY:7)~H9ᇏ|,ަޱrݾv'4|(Kz&  ap@Q(n^B' 48*^=dȢF+;""c?fWi6.2!A#7EX =_Z`)P&$Qif^J&/82%eqD"|b & ii Mign類&㦿Xz}˜NBa4[R`Jnڢ=QԤzᩥh#eВ:+"J;.ˬq>bҦºy-E箺b-+uwn8Bf\"Lf>msܮ;/E&FQ2;,< ì: ,^L3mL[Xm6'|04tMN!1Ul(Kn%+R<^Ԏj6sY1 I"MmYݱa[Nl-Dm.8nxȈ)[r_vb>N8|zK]2ob7i༲Mvz+{56` _*j-Ch +ҖUoaM1EIFce&xRN2eE_Kә1*uh!6bY!L-FR7Ԧ>l@eIb#ht\RfR0J]Bi8U)kc"''iYj:@iA詗DS %L*b\WH}F*:})"jx|GOC,}wO~JVxlXXj@hz#^(a?B(=f({nlyՅ SvAs8]/eGvz8Wj {Hg\6!ĖxxaWۦgMH芭h(Šh$Hܶ7X:xP{kwtŒHkyHYh'؍öe?#H&}רmtS8uW.$ yF(Pw|혐(/ǃyx?88XnXXmrx`ȋbؒ1II)BA9(o"iXeJI[chm,ԔN9rPIWqEd9~qUwȀ_ ٘DCg@L8I` D|yB8ݥvg ?1xY3(6FiQv8Yx(nwv1C ko J񚨙|i)ɛP H)ljyhyZWs؜ ׹HKi~/ّ4W0Y2 IG♗hᒟ8(iwnsS1gm*II }h*(02jʡKJ:1ʩ!Xډĕlg+ʢ- f)h=: zQZp>JZzWK:%i9L*C:Y8NܩZ \ڥ̙,a1ZyZ:zs;ئn)r:t9չyڣiԩjZ ʨ 8J1pʩZ J/1ʪ IGJvZʠɫ ʩjJP *Jj׊٪ʭ *Jj犮骮ʮ *Jjʯ +K؀ Q +Kk˱ !+#[ &K+˲-/ 1+3K{5˳=? A+/[CK˴MO۱I[KUkWP;ocaml-book-1.0/en/html/book-ora101.html0000644000000000000000000000457107453055400014463 0ustar To Learn More Previous Contents Next

To Learn More

The results produced by the ocamldep command can be visualized in graphical form by the ocamldot utility, which can be found on the following page:

Link


http://www.cis.upenn.edu/~tjim/ocamldot/index.html
ocamldot makes use of an independent program (dot), also downloadable:

Link


http://www.research.att.com/sw/tools/graphviz/


Several generic Makefile templates for Objective CAML have been proposed to ease the burden of project management:

Link


http://caml.inria.fr/FAQ/Makefile_ocaml-eng.html


Link


http://www.ai.univie.ac.at/~markus/ocaml_sources
These integrate the output of ocamldep.

In [HF+96] a performance evaluation of about twenty implementations of functional languages, among them several ML implementations, can be found. The benchmark is an example of numerical calculations on large datastructures.








Previous Contents Next ocaml-book-1.0/en/html/book-ora107.html0000644000000000000000000007536607453055400014503 0ustar Basic Revisited Previous Contents Next

Basic Revisited

We now want to use ocamllex and ocamlyacc to replace function parse on page ?? for Basic by some functions generated from files specifying the lexicon and syntax of the language.

To do this, we may not re-use as-is the type of lexical units that we have defined. We will be forced to define a more precise type which permits us to distinguish between operators, commands and keywords.

We will also need to isolate the type declarations describing abstract syntax within a file basic_types.mli. This will contain the declaration of type sentences and all types needed by it.

File basic_parser.mly

Header
The file header imports the types needed for the abstract syntax as well as two auxiliary functions to convert from character strings to their equivalent in the abstract syntax.

%{
open Basic_types ;;

let phrase_of_cmd c =
match c with
"RUN" -> Run
| "LIST" -> List
| "END" -> End
| _ -> failwith "line : unexpected command"
;;

let bin_op_of_rel r =
match r with
"=" -> EQUAL
| "<" -> INF
| "<=" -> INFEQ
| ">" -> SUP
| ">=" -> SUPEQ
| "<>" -> DIFF
| _ -> failwith "line : unexpected relation symbol"
;;

%}


Declarations
contains three sections: lexeme declarations, their rule associativity and precedence declarations, and the declaration of the start symbol line which stands for the parsing of a command or program line.

Lexical units are the following:


%token <int> Lint
%token <string> Lident
%token <string> Lstring
%token <string> Lcmd
%token Lplus Lminus Lmult Ldiv Lmod
%token <string> Lrel
%token Land Lor Lneg
%token Lpar Rpar
%token <string> Lrem
%token Lrem Llet Lprint Linput Lif Lthen Lgoto
%token Lequal
%token Leol
Their names are self-explanatory and they are described in file basic_lexer.mll (see page ??).

Precedence rules between operators once again take the values assigned by functions priority_uop and priority_binop defined when first giving the grammar for our Basic (see page ??).


%right Lneg
%left Land Lor
%left Lequal Lrel
%left Lmod
%left Lplus Lminus
%left Lmult Ldiv
%nonassoc Lop
Symbol Lop will be used to process unary minus. It is not a terminal in the grammar, but a ``pseudo non-terminal'' which allows overloading of operators when two uses of an operator should not receive the same precedence depending on context. This is the case with the minus symbol (-). We will reconsider this point once we have specified the rules in the grammar.

Since the start symbol is line, the function generated will return the syntax tree for the parsed line.

 %start line
 %type <Basic_types.phrase> line
Grammar rules
are decomposed into three non-terminals: line for a line; inst for an instruction in the language; exp for expressions. The action associated with each rule simply builds the corresponding abstract syntax tree.

%%
line :
Lint inst Leol { Line {num=$1; inst=$2} }
| Lcmd Leol { phrase_of_cmd $1 }
;

inst :
Lrem { Rem $1 }
| Lgoto Lint { Goto $2 }
| Lprint exp { Print $2 }
| Linput Lident { Input $2 }
| Lif exp Lthen Lint { If ($2, $4) }
| Llet Lident Lequal exp { Let ($2, $4) }
;

exp :
Lint { ExpInt $1 }
| Lident { ExpVar $1 }
| Lstring { ExpStr $1 }
| Lneg exp { ExpUnr (NOT, $2) }
| exp Lplus exp { ExpBin ($1, PLUS, $3) }
| exp Lminus exp { ExpBin ($1, MINUS, $3) }
| exp Lmult exp { ExpBin ($1, MULT, $3) }
| exp Ldiv exp { ExpBin ($1, DIV, $3) }
| exp Lmod exp { ExpBin ($1, MOD, $3) }
| exp Lequal exp { ExpBin ($1, EQUAL, $3) }
| exp Lrel exp { ExpBin ($1, (bin_op_of_rel $2), $3) }
| exp Land exp { ExpBin ($1, AND, $3) }
| exp Lor exp { ExpBin ($1, OR, $3) }
| Lminus exp %prec Lop { ExpUnr(OPPOSITE, $2) }
| Lpar exp Rpar { $2 }
;
%%
These rules do not call for particular remarks except:
exp :
 ...
 | Lminus exp %prec Lop { ExpUnr(OPPOSITE, $2) }
It concerns the use of unary -. Keyword %prec that we find in it declares that this rule should receive the precedence of Lop (here the highest precedence).

File basic_lexer.mll

Lexical analysis only contains one rule, lexer, which corresponds closely to the old function lexer (see page ??). The semantic action associated with the recognition of each lexical unit is simply the emission of the related constructor. As the type of lexical units is declared in the syntax rule file, we have to include the file here. We add a simple auxiliary function that strips double quotation marks from character strings.

{
open Basic_parser ;;

let string_chars s = String.sub s 1 ((String.length s)-2) ;;
}

rule lexer = parse
[' ' '\t'] { lexer lexbuf }

| '\n' { Leol }

| '!' { Lneg }
| '&' { Land }
| '|' { Lor }
| '=' { Lequal }
| '%' { Lmod }
| '+' { Lplus }
| '-' { Lminus }
| '*' { Lmult }
| '/' { Ldiv }

| ['<' '>'] { Lrel (Lexing.lexeme lexbuf) }
| "<=" { Lrel (Lexing.lexeme lexbuf) }
| ">=" { Lrel (Lexing.lexeme lexbuf) }

| "REM" [^ '\n']* { Lrem (Lexing.lexeme lexbuf) }
| "LET" { Llet }
| "PRINT" { Lprint }
| "INPUT" { Linput }
| "IF" { Lif }
| "THEN" { Lthen }
| "GOTO" { Lgoto }

| "RUN" { Lcmd (Lexing.lexeme lexbuf) }
| "LIST" { Lcmd (Lexing.lexeme lexbuf) }
| "END" { Lcmd (Lexing.lexeme lexbuf) }

| ['0'-'9']+ { Lint (int_of_string (Lexing.lexeme lexbuf)) }
| ['A'-'z']+ { Lident (Lexing.lexeme lexbuf) }
| '"' [^ '"']* '"' { Lstring (string_chars (Lexing.lexeme lexbuf)) }


Note that we isolated symbol = which is used in both expressions and assignments.

Only two of these regular expressions need further remarks. The first concerns comment lines ("REM" [^ '\n']*). This rule recognizes keyword REM followed by an arbitrary number of characters other than '\n'. The second remark concerns character strings ('"' [^ '"']* '"') considered as sequences of characters different from " and contained between two ".

Compiling, Linking

The compilation of the lexer and parser must be carried out in a definite order. This is due to the mutual dependency between the declaration of lexemes. To compile our example, we must enter the following sequence of commands:
ocamlc -c basic_types.mli
ocamlyacc basic_parser.mly
ocamllex basic_lexer.mll
ocamlc -c basic_parser.mli
ocamlc -c basic_lexer.ml
ocamlc -c basic_parser.ml
Which will generate files basic_lexer.cmo and basic_parser.cmo which may be linked into an application.

We now have at our disposal all the material needed to reimplement the application.

We suppress all types and all functions in paragraphs ``lexical analysis'' (on page ??) and ``parsing'' ( on page ??) of our Basic application; in function one_command (on page ??), we replace expression

match parse (input_line stdin) with
with

match line lexer (Lexing.from_string ((input_line stdin)^"\n")) with


We need to remark that we must put back at the end of the line the character '\n' which function input_line had filtered out. This is necessary because the '\n' character indicates the end of a command line (Leol).






Previous Contents Next ocaml-book-1.0/en/html/book-ora086.gif0000644000000000000000000000454507452056112014301 0ustar GIF89a֖!,H0I8c`qR`lp,tmJm~pv& 0Y ШtJZ4)YRejaNzn|Ngh#.go)w|lxp)e&g~dzG+jkl(m<jb /i˓'K&;(}ԵΈҳğś,ʁLNJW4{܃C|жoQuQ@Qa5vҦMGQ泌fXLIӟ}Tehƴ8bYnk7匢l2FӆhVJ7CÙ;# =Wҵ>vu:.+~˸K`I0R"3kޜϠC67ӨS^ͺװc˞MɺsͻoطNM_μKN]6tn vSN~߽sO={7}֯=x6x\}e |._.!ZzWl R hr'ᅴ(ءx8("8#8!:cL6ڒ#[NVyVf9ZvI^bYf܊ p)tixzng砄j衈g)jFʚ ЈY5ǣvV-鄞 jVXuaaZ fG MV&),뮐`:맱I*l i޾ .^K*:kl0[]v1@nz/^ I CpSp,( O% k\%vmJ}k_.kL2kn~kJ)i3V3ʲ80&ZsA{2M,8]\1SL^!.vӞF릭.X!5Hۨ ph hˮ=lKʷ6#L.nCn6Yk9o]~-4 sNz~kԚ{z|sHO4ʾaӦ<Nc6dٻ}Āϝ7O:ۮ;p|l8???o߶я9sH6b 6g[ؖƢB4yȂ48B%jK =og?B g axX@=gCr 7HH<9"RE8\k(6 }ZdbX03cqXC5+fJ"pL[G-:φvc1S% CFj4+i;z #)OG߬q+ ̲%.w]ʒR#WwHF2-]7/W< 7v=@cx1'!GvRz`i|'= iπ¦=?Wτo46cѡ)srL4-o%5IkKġԨ1t56ijrӌO]EP u?  M&U5DfScVd88Nb-Hfqbe!YEq-[*ô6e)9 Wծm<*R T VUaW5qi캺j r" 9PdYڕjڼ ꬼWN_2DaFSJvir [v֘aI6CBQے@b6R<.%Im<-l4d6[zy˲l-L]/}DݥkDi9ᗊwop׃+8 jTf,[ػ5 0PJ`8αw6lzo"HNP-!˒ɰtLK)V%52L*~b>ˬ暦͟z3&9{c3γ c&5huLЈfuG+ X#hE:ȌNUui2eZn6 ƿti>/I7*Lej٨7 <-7Q[;³qM7Nuͮ:/sSZֱIrMiMkqV؆mek : S/^{`H1ްc 4;0N X9&1RּGɋurJX, J0$ڮCr@ <CE"Xה~De%q2NeZ"@'b_*EjC)ٴKӹDĐ ˫{.$ĐBt 0!ẆDȤ2dŁr?ʴnMiPL98c߷}vf&NpvԓCWIxe#p_I-rRnA+ *ꇹfVcj;=v,aoI({.Sx6SV]3eK=؁ vnćpMwvpJi2?w".M8A9'?19.Uo>c瘫(->~ǛCS.{(;nx. _r<0 G-[_xkD>>|~Ψ=Wϵ?ïOF?m  < fDI! -d" K0~ )f02L! kІ!w B,b | 5T"(C'(UEaqVl5E}Q}a<X2qZ#F 1#h;"0zD#?>~ qaH2[$#Gv/%+?b{ܤ:I܁2%)QgS.+[6R'5iڽ2%/Q_2t%1Kl \&L^qԬޢ9B?bYTcI pQ&7iN0sTg7Nts9giOz*2$$?&n&> :Ѐ"j..tBf1Nj8,Z Ӏ4t#42R$JE <:k6+hJt~Q?RhŊ*# ndT(bbmKIjzɉZ{Q6pzƃPk9>䀹|rO?zSr!Rf$qWŁS`v-pP_ыebV)L(zoZF]\̅o@(F!vMApp@Gl &#b6q#J|⋤D*WĢ>w'p:ўLcT*FH,F=D*%W)SZ'{+vhG$ΈF&Qmc!9E'\emk\^'FJ+@t ?INrp%W:Uj2jcmA4ԣ!6WF)kt#?:f< &&J҄RM",0M@/`A;l&ŀ*E)Lv;׹= x%@ ?Nا-7*>1hG['w6ryF#ZhNA@IPMz8JS8LdbbdU|1 ՒtH1L*.tbj S%Me^_EARbUOK- mV-vU^ڐ5g_̿ (<ȰV,+nl ,mJIJ4KV0.[D`_EI-V+zN]j2@V^}E qA_ EZX)1}ވ¡KMv!?VY&_ͽfE=7&u"Op|b!) %>QZɤ NI;ocaml-book-1.0/en/html/book-ora019.html0000644000000000000000000004056507453055377014513 0ustar Desktop Calculator Previous Contents Next

Desktop Calculator

To understand how a program is built in Objective CAML, it is necessary to develop one. The chosen example is a desktop calculator---that is, the simplest model, which only works on whole numbers and only carries out the four standard arithmetic operations.

To begin, we define the type key to represent the keys of a pocket calculator. The latter has fifteen keys, namely: one for each operation, one for each digit, and the = key.

# type key = Plus | Minus | Times | Div | Equals | Digit of int ;;
We note that the numeric keys are gathered under a single constructor Digit taking an integer argument. In fact, some values of type key don't actually represent a key. For example, (Digit 32) is a possible value of type key, but doesn't represent any of the calculator's keys.

So we write a function valid which verifies that its argument corresponds to a calculator key. The type of this function is key -> bool, that is, it takes a value of type key as argument and returns a value of type bool.

The first step is to define a function which verifies that an integer is included between 0 and 9. We declare this function under the name is_digit:

# let is_digit = function x -> (x>=0) && (x<=9) ;;
val is_digit : int -> bool = <fun>


We then define the function valid by pattern-matching over its argument of type key:

# let valid ky = match ky with
Digit n -> is_digit n
| _ -> true ;;
val valid : key -> bool = <fun>
The first pattern is applied when the argument of valid is a value made with the Digit constructor; in this case, the argument of Digit is tested by the function is_digit. The second pattern is applied to every other kind of value of type key. Recall that thanks to typing, the value being matched is necessarily of type key.

Before setting out to code the calculator mechanism, we will specify a model allowing us to describe from a formal point of view the reaction to the activation of one of the device's keys. We will consider a pocket calculator to have four registers in which are stored respectively the last computation done, the last key activated, the last operator activated, and the number printed on the screen. The set of these four registers is called the state of the calculator; it is modified by each keypress on the keypad. This modification is called a transition and the theory governing this kind of mechanism is that of automata. A state will be represented in our program by a record type:

# type state = {
lcd : int; (* last computation done *)
lka : key; (* last key activated *)
loa : key; (* last operator activated *)
vpr : int (* value printed *)
} ;;


Figure 2.6 gives an example of a sequence of transitions.

  state key
  (0,=,=,0) 3
(0,3,=,3) +
(3,+,+,3) 2
(3,2,+,2) 1
(3,1,+,21)
(24,*,*,24) 2
(24,2,*,2) =
(48,=,=,48)  

Figure 2.6: Transitions for 3+21*2= .


In what follows we need the function evaluate which takes two integers and a value of type key containing an operator and which returns the result of the operation corresponding to the key, applied to the integers. This function is defined by pattern-matching over its last argument, of type key:

# let evaluate x y ky = match ky with
Plus -> x + y
| Minus -> x - y
| Times -> x * y
| Div -> x / y
| Equals -> y
| Digit _ -> failwith "evaluate : no op";;
val evaluate : int -> int -> key -> int = <fun>


Now we give the definition of the transition function by enumerating all possible cases. We assume that the current state is the quadruplet (a,b,,d):
  • a key with digit x is pressed, then there are two cases to consider:
    • the last key pressed was also a digit. So it is a number which the user of the pocket calculator is in the midst of entering; consequently the digit x must be affixed to the printed value, i.e., replacing it with d10+x. The new state is:
      (a,(Digit x),,d10+x)

    • the last key pressed was not a digit. So it is the start of a new number which is being entered. The new state is:
      (a,(Digit x),,x)
  • a key with operator has been pressed, the second operand of the operation has thus been completely entered and the calculator has to deal with carrying out this operation. It is to this end that the last operation (here ) is stored. The new state is:
    ( d,,,a d)
To write the function transition, it suffices to translate the preceding definition word for word into Objective CAML: the definition by cases becomes a definition by pattern-matching over the key passed as an argument. The case of a key, which itself is made up of two cases, is handled by the local function digit_transition by pattern-matching over the last key activated.

# let transition st ky =
let digit_transition n = function
Digit _ -> { st with lka=ky; vpr=st.vpr*10+n }
| _ -> { st with lka=ky; vpr=n }
in
match ky with
Digit p -> digit_transition p st.lka
| _ -> let res = evaluate st.lcd st.vpr st.loa
in { lcd=res; lka=ky; loa=ky; vpr=res } ;;
val transition : state -> key -> state = <fun>
This function takes a state and a key and computes the new state.

We can now test this program on the previous example:

# let initial_state = { lcd=0; lka=Equals; loa=Equals; vpr=0 } ;;
val initial_state : state = {lcd=0; lka=Equals; loa=Equals; vpr=0}
# let state2 = transition initial_state (Digit 3) ;;
val state2 : state = {lcd=0; lka=Digit 3; loa=Equals; vpr=3}
# let state3 = transition state2 Plus ;;
val state3 : state = {lcd=3; lka=Plus; loa=Plus; vpr=3}
# let state4 = transition state3 (Digit 2) ;;
val state4 : state = {lcd=3; lka=Digit 2; loa=Plus; vpr=2}
# let state5 = transition state4 (Digit 1) ;;
val state5 : state = {lcd=3; lka=Digit 1; loa=Plus; vpr=21}
# let state6 = transition state5 Times ;;
val state6 : state = {lcd=24; lka=Times; loa=Times; vpr=24}
# let state7 = transition state6 (Digit 2) ;;
val state7 : state = {lcd=24; lka=Digit 2; loa=Times; vpr=2}
# let state8 = transition state7 Equals ;;
val state8 : state = {lcd=48; lka=Equals; loa=Equals; vpr=48}


This run can be written in a more concise way using a function applying a sequence of transitions corresponding to a list of keys passed as an argument.


# let transition_list st ls = List.fold_left transition st ls ;;
val transition_list : state -> key list -> state = <fun>
# let example = [ Digit 3; Plus; Digit 2; Digit 1; Times; Digit 2; Equals ]
in transition_list initial_state example ;;
- : state = {lcd=48; lka=Equals; loa=Equals; vpr=48}







Previous Contents Next ocaml-book-1.0/en/html/book-ora073.gif0000644000000000000000000002240407452056112014267 0ustar GIF89at*}}}{{{yyywwwuuusssWWW333pppnnnllljjjĊ!!Q,tQ*ma)`(_&^%]$\#["!Y X (zXȰ!C+#Jءŋhq# CBɒR\r˗0ajI  㠧O"@BhH"´Ӧ J*UիX.ʵ+ `Ê ٳhӢM۷p"Kݻvw/6@?+^ǐL2e3kܣϞMz4ӨSk;b˞M6ms{w .$Q+WysQP<*ɗY }`Պ_6m_?{=L0#wlbڀfh%xk fۃۄF܅܆9 $ljp ,GbvXmywz@ސyח|G~ h 6`\Vᗺe(fqr%)"l0FƜtIՎM@)hDG"1ɘAiQ a v`ØPg~fm .)5Z+Ʈ f 'o+l\v kh' z2A*Qʙajq\jcaJbmꬲ檯/ ,pm\Q m @ #E찄"puбs9+r{Ek& +2 kiy+  {.%b!;/n"v\:+,mkj!%_@&{Ϭ .P'.'0N= tB] 2Aݜ&Rob][w/-ٴ;;Q v|̄U/F 8Ox/~f߇>&,BM4k z!Ɖ>:7~:걪N+r`G0ىv˝(:wi @º ,{ >}|=8A UBvV E#C '!sCˇ[rm7!]~8_ 5ЀC= -2 hL61 @:1BfN( `Ͱbx1Bp![#,@N0IIV]"boH$*kmIVTņi1b]HKލfK4s0<082if"@P$PN  x~l"FY7V*[WN\e-o9F]沗>c0~.l@:=h28uD jvi TȤt8]8Er\eJw3#=h3Sf? ОLP ѢFTHhJ:B |$I?3nP) XWt1L%X29 NCQԾ&}j Kv Mlb IJ౐, | (ᲘU6YHhGKҚHHjWֲlgKᶸͭnw"OK\FQr2:ХQVrP5$iZpL3bҀ|Kͯ~L턂;'L [ΰ7{ GL(NW0gL8αw@L"HN&;PL*[Xβ.{`L2hN6pL:xs>πMBЈNF;ѐ'MJ[ϊ7N{ӠG LԨNWVЦ~gMZ֪5w^׸5Mb؍6f;~Mj_:ζ>cqnuv[~MoX'η}7Npj O;<~'kS u/.kseC|-?ˆ4_|)9K|}^:FЇs3zL9ГoSP:͋kW:"¾k|SGN9{v]t.E{ݹnhp:U=so6W?}#|7?χ>?sw}?^9=G_׻O|ygGyxy|hu H~ QwHzXiv{i"z]|$Xy؁sOvwƁ68i1t]!5V&ܽ9^-^Wm(g i4 7O!j-/-|NNFz`= @~XnyZ^S<^Ky=>Xdwj洜D2>u@jmW<)؀~ʂ~Ci:>&|֑'jG}МlyN拎ZmқzNx=" וծƹ֣=WCH=?^k-쥼5 $A-ێ՗6턮N{ G]h&|R^]Э~uȐ>A,ć;]mkNYGNbJixȊ&fz.ӊ,σ νmL?/k-n#κkθ/=M j%'_ǩϺ_)l?w hBY .r8OM+iް4HXhXȘx(yhYy9Ij9Z*y +;Kku[K 6L\ll+: L iX=8 ZofG('cirꧧШVD0t_n6^)Zff\NۮFׂ@J>pėl!O QŻYm,xpH4\S5u2u1ƨjsp;^'v%SchKMg#mjXvCuG=b A@ݵp`*N@i]ۅ^txrx).NwOTE&fWⵈ 鳘~ 걨미"D{=W^>:+;0w:X3ˢKyW~[/~ X@&0s4oD(ap`~U۠GB̈́_$+ hv*d"C0]9"8max qQ!J%pEa^,)\/2 ыڕ-2튙c"e1Y#֤ rU*c~jǹl(Kvp}a:Y&'#;ĕHJKlw9&OCdእ Notes
1
Makefile files are used by the make command for the maintenance of a set of programs or files to keep everything up to date after modifications to some of them.
ocaml-book-1.0/en/html/book-ora092.html0000644000000000000000000000571307453055400014473 0ustar To Learn More Previous Contents Next

To Learn More

Memory reclamation techniques have been studied for forty years---in fact, since the first implementations of the Lisp programming language. For this reason, the literature in this area is enormous.

A comprehensive reference is Jones' book [Jon98]. Paul Wilson's tutorial [Wil92] is an excellent introduction to the field, with many references. The following web pages also provide a good view of the state of the art in memory management.

Link


ftp://ftp.netcom.com/pub/hb/hbaker/home.html
is an introduction to sequential garbage collectors.

Link


http://www.cs.ukc.ac.uk/people/staff/rej/gc.html
contains the presentation of [Jon98] and includes a large searchable bibliography.

Link


http://www.cs.colorado.edu/~zorn/DSA.html
lists different tools for debugging garbage collection.

Link


http://reality.sgi.com/boehm_mti/
offers C source code for a conservative garbage collector for the C language. This garbage collector replaces the classical allocator malloc by a specialized version GC_malloc. Explicit recovery by free is replaced by a new version that no longer does anything.

Link


http://www.harlequin.com/mm/reference/links.html
maintains a list of links on this subject.

In chapter 12 on the interface between C and Objective CAML we come back to memory management.




Previous Contents Next ocaml-book-1.0/en/html/book-ora187.html0000644000000000000000000017251407453055400014504 0ustar Client-server Previous Contents Next

Client-server

Interprocess communication between processes on the same machine or on different machines through TCP/IP sockets is a mode of point-to-point asynchronous communication. The reliability of such transmissions is assured by the TCP protocol. It is nonetheless possible to simulate the broadcast to a group of processes through point-to-point communication to all receivers.

The roles of different processes communicating in an application are asymmetric, as a general rule. That description holds for client-server architectures. A server is a process (or several processes) accepting requests and trying to respond to them. The client, itself a process, sends a request to the server, hoping for a response.

Client-server Action Model

A server provides a service on a given port by waiting for connections from future clients. Figure 20.1 shows the sequence of principal tasks for a server and a client.



Figure 20.1: Model of a server and client


A client can connect to a service once the server is ready to accept connections (accept). In order to make a connection, the client must know the IP number of the server machine and the port number of the service. If the client does not know the IP number, it needs to request name/number resolution using the function gethostbyname. Once the connection is accepted by the server, each program can communicate via input-output channels over the sockets created at both ends.

Client-server Programming

The mechanics of client-server programming follows the model described in Figure 20.1. These tasks are always performed. For these tasks, we write generic functions parameterized by particular functions for a given server. As an example of such a program, we describe a server that accepts a connection from a client, waits on a socket until a line of text has been received, converting the line to CAPITALS, and sending back the converted text to the client.

Figure 20.2 shows the communication between the service and different clients1.



Figure 20.2: CAPITAL service and its clients


Certain tasks run on the same machine as the server, while others are found on remote machines.

We will see
  1. How to write the code for a ``generic server'' and instantiate it for our particular capitalization service.
  2. How to test the server, without writing the client, by using the telnet program.
  3. How to create two types of clients:
    • a sequential client, which waits for a response after sending a request;

    • a parallel client, which separates the send and receive tasks.
    Therefore, there are two processes for this client.

Code for the Server

A server may be divided into two parts: waiting for a connection and the following code to handle the connection.

A Generic Server

The generic server function establish_server described below takes as its first argument a function for the service (server_fun) that handles requests, and as its second argument, the address of the socket in the Internet domain that listens for requests. This function uses the auxiliary function domain_of, which extracts the domain of a socket from its address.

In fact, the function establish_server is made up of high-level functions from the Unix library. This function sets up a connection to a server.


# let establish_server server_fun sockaddr =
let domain = domain_of sockaddr in
let sock = Unix.socket domain Unix.SOCK_STREAM 0
in Unix.bind sock sockaddr ;
Unix.listen sock 3;
while true do
let (s, caller) = Unix.accept sock
in match Unix.fork() with
0 -> if Unix.fork() <> 0 then exit 0 ;
let inchan = Unix.in_channel_of_descr s
and outchan = Unix.out_channel_of_descr s
in server_fun inchan outchan ;
close_in inchan ;
close_out outchan ;
exit 0
| id -> Unix.close s; ignore(Unix.waitpid [] id)
done ;;
val establish_server :
(in_channel -> out_channel -> 'a) -> Unix.sockaddr -> unit = <fun>


To finish building a server with a standalone executable that takes a port number parameter, we write a function main_server which takes a parameter indicating a service. The function uses the command-line parameter as the port number of a service. The auxiliary function get_my_addr, returns the address of the local machine.

# let get_my_addr () =
(Unix.gethostbyname(Unix.gethostname())).Unix.h_addr_list.(0) ;;
val get_my_addr : unit -> Unix.inet_addr = <fun>

# let main_server serv_fun =
if Array.length Sys.argv < 2 then Printf.eprintf "usage : serv_up port\n"
else try
let port = int_of_string Sys.argv.(1) in
let my_address = get_my_addr()
in establish_server serv_fun (Unix.ADDR_INET(my_address, port))
with
Failure("int_of_string") ->
Printf.eprintf "serv_up : bad port number\n" ;;
val main_server : (in_channel -> out_channel -> 'a) -> unit = <fun>


Code for the Service

The general mechanism is now in place. To illustrate how it works, we need to define the service we're interested in. The service here converts strings to upper-case. It waits for a line of text over an input channel, converts it, then writes it on the output channel, flushing the output buffer.

# let uppercase_service ic oc =
try while true do
let s = input_line ic in
let r = String.uppercase s
in output_string oc (r^"\n") ; flush oc
done
with _ -> Printf.printf "End of text\n" ; flush stdout ; exit 0 ;;
val uppercase_service : in_channel -> out_channel -> unit = <fun>


In order to correctly recover from exceptions raised in the Unix library, we wrap the initial call to the service in an ad hoc function from the Unix library:

# let go_uppercase_service () =
Unix.handle_unix_error main_server uppercase_service ;;
val go_uppercase_service : unit -> unit = <fun>


Compilation and Testing of the Service

We group the functions in the file serv_up.ml, adding an actual call to the function go_uppercase_service. We compile this file, indicating that the Unix library is linked in
ocamlc -i -custom -o serv_up.exe unix.cma serv_up.ml -cclib -lunix
The transcript from this compilation (using the option -i) gives:
val establish_server :
  (in_channel -> out_channel -> 'a) -> Unix.sockaddr -> unit
val main_server : (in_channel -> out_channel -> 'a) -> unit
val uppercase_service : in_channel -> out_channel -> unit
val go_uppercase_service : unit -> unit
We launch the server by writing:
serv_up.exe 1400
The port chosen here is 1400. Now the machine where the server was launched will accept connections on this port.

Testing with telnet

We can now begin to test the server by using an existing client to send and receive lines of text. The telnet utility, which normally is a client of the telnetd service on port 23, and used to control a remote connection, can be diverted from this role by passing a machine name and a different port number. This utility exists on several operating systems. To test our server under Unix, we type:
$ telnet boulmich 1400 
Trying 132.227.89.6...
Connected to boulmich.ufr-info-p6.jussieu.fr.
Escape character is '^]'.
The IP address for boulmich is 132.227.89.6 and its complete name, which contains its domain name, is boulmich.ufr-info-p6.jussieu.fr. The text displayed by telnet indicates a successful connection to the server. The client waits for us to type on the keyboard, sending the characters to the server that we have launched on boulmich on port 1400. It waits for a response from the server and displays:
The little cat is dead.
THE LITTLE CAT IS DEAD.
We obtained the expected result.
WE OBTAINED THE EXPECTED result.
The phrases entered by the user are in lower-case and those sent by the server are in upper-case. This is exactly the role of this service, to perform this conversion.

To exit from the client, we need to close the window where it was run, by executing the kill command. This command will close the client's socket, causing the server's socket to close as well. When the server displays the message ``End of text,'' the process associated with the service terminates.

The Client Code

While the server is naturally parallel (we would like to handle a particular request while accepting others, up to some limit), the client may or may not be so, according to the nature of the application. Below we give two versions of the client. Beforehand, we present two functions that will be useful for writing these clients.

The function open_connection from the Unix library allows us to obtain a couple of input-output channels for a socket.

The following code is contained in the language distribution.

# let open_connection sockaddr =
let domain = domain_of sockaddr in
let sock = Unix.socket domain Unix.SOCK_STREAM 0
in try Unix.connect sock sockaddr ;
(Unix.in_channel_of_descr sock , Unix.out_channel_of_descr sock)
with exn -> Unix.close sock ; raise exn ;;
val open_connection : Unix.sockaddr -> in_channel * out_channel = <fun>


Similarly, the function shutdown_connection closes down a socket.

# let shutdown_connection inchan =
Unix.shutdown (Unix.descr_of_in_channel inchan) Unix.SHUTDOWN_SEND ;;
val shutdown_connection : in_channel -> unit = <fun>


A Sequential Client

From these functions, we can write the main function of a sequential client. This client takes as its argument a function for sending requests and receiving responses. This function analyzes the command line arguments to obtain connection parameters before actual processing.

# let main_client client_fun =
if Array.length Sys.argv < 3
then Printf.printf "usage : client server port\n"
else let server = Sys.argv.(1) in
let server_addr =
try Unix.inet_addr_of_string server
with Failure("inet_addr_of_string") ->
try (Unix.gethostbyname server).Unix.h_addr_list.(0)
with Not_found ->
Printf.eprintf "%s : Unknown server\n" server ;
exit 2
in try
let port = int_of_string (Sys.argv.(2)) in
let sockaddr = Unix.ADDR_INET(server_addr,port) in
let ic,oc = open_connection sockaddr
in client_fun ic oc ;
shutdown_connection ic
with Failure("int_of_string") -> Printf.eprintf "bad port number";
exit 2 ;;
val main_client : (in_channel -> out_channel -> 'a) -> unit = <fun>


All that is left is to write the function for client processing.

# let client_fun ic oc =
try
while true do
print_string "Request : " ;
flush stdout ;
output_string oc ((input_line stdin)^"\n") ;
flush oc ;
let r = input_line ic
in Printf.printf "Response : %s\n\n" r;
if r = "END" then ( shutdown_connection ic ; raise Exit) ;
done
with
Exit -> exit 0
| exn -> shutdown_connection ic ; raise exn ;;
val client_fun : in_channel -> out_channel -> unit = <fun>
The function client_fun enters an infinite loop which reads from the keyboard, sends a string to the server, gets back the transformed upper-case string, and displays it. If the string is "END", then the exception Exit is raised in order to exit the loop. If another exception is raised, typically if the server has shut down, the function ceases its calculations.

The client program thus becomes:

# let go_client () = main_client client_fun ;;
val go_client : unit -> unit = <fun>


We place all these functions in a file named client_seq.ml, adding a call to the function go_client. We compile the file with the following command line:
ocamlc -i -custom -o client_seq.exe unix.cma client_seq.ml -cclib -lunix
We run the client as follows:
$ client_seq.exe boulmich 1400 
Request : The little cat is dead.
Response: THE LITTLE CAT IS DEAD.

Request : We obtained the expected result.
Response: WE OBTAINED THE EXPECTED RESULT.

Request : End
Response: END

The Parallel Client with fork

The parallel client mentioned divides its tasks between two processes: one for sending, and the other for receiving. The processes share the same socket. The functions associated with each of the processes are passed to them as parameters.

Here is the modified program:

# let main_client client_parent_fun client_child_fun =
if Array.length Sys.argv < 3
then Printf.printf "usage : client server port\n"
else
let server = Sys.argv.(1) in
let server_addr =
try Unix.inet_addr_of_string server
with Failure("inet_addr_of_string")
-> try (Unix.gethostbyname server).Unix.h_addr_list.(0)
with Not_found ->
Printf.eprintf "%s : unknown server\n" server ;
exit 2
in try
let port = int_of_string (Sys.argv.(2)) in
let sockaddr = Unix.ADDR_INET(server_addr,port) in
let ic,oc = open_connection sockaddr
in match Unix.fork () with
0 -> if Unix.fork() = 0 then client_child_fun oc ;
exit 0
| id -> client_parent_fun ic ;
shutdown_connection ic ;
ignore (Unix.waitpid [] id)
with
Failure("int_of_string") -> Printf.eprintf "bad port number" ;
exit 2 ;;
val main_client : (in_channel -> 'a) -> (out_channel -> unit) -> unit = <fun>
The expected behavior of the parameters is: the (grand)child sends the request and the parent receives the response.

This architecture has the effect that if the child needs to send several requests, then the parent receives the responses to requests as each is processed. Consider again the preceding example for capitalizing strings, modifying the client side program. The client reads the text from one file, while writing the response to another file. For this we need a function that copies from one channel, ic, to another, oc, respecting our little protocol (that is, it recognizes the string "END").

# let copy_channels ic oc =
try while true do
let s = input_line ic
in if s = "END" then raise End_of_file
else (output_string oc (s^"\n"); flush oc)
done
with End_of_file -> () ;;
val copy_channels : in_channel -> out_channel -> unit = <fun>


We write the two functions for the child and parent using the parallel client model:

# let child_fun in_file out_sock =
copy_channels in_file out_sock ;
output_string out_sock ("FIN\n") ;
flush out_sock ;;
val child_fun : in_channel -> out_channel -> unit = <fun>
# let parent_fun out_file in_sock = copy_channels in_sock out_file ;;
val parent_fun : out_channel -> in_channel -> unit = <fun>


Now we can write the main client function. It must collect two extra command line parameters: the names of the input and output files.

# let go_client () =
if Array.length Sys.argv < 5
then Printf.eprintf "usage : client_par server port filein fileout\n"
else let in_file = open_in Sys.argv.(3)
and out_file = open_out Sys.argv.(4)
in main_client (parent_fun out_file) (child_fun in_file) ;
close_in in_file ;
close_out out_file ;;
val go_client : unit -> unit = <fun>


We gather all of our material into the file client_par.ml (making sure to include a call to go_client), and compile it. We create a file toto.txt containing the text to be converted:
The little cat is dead.
We obtained the expected result.
We can test the client by typing:
client_par.exe boulmich 1400 toto.txt result.txt
The file result.txt contains the text:
$ more result.txt
THE LITTLE CAT IS DEAD.
WE OBTAINED THE EXPECTED RESULT.
When the client finishes, the server always displays the message "End of text".

Client-server Programming with Lightweight Processes

The preceding presentation of code for a generic server and a parallel client created processes via the fork primitive in the Unix library. This works well under Unix; many Unix services are implemented by this technique. Unfortunately, the same cannot be said for Windows. For portability, it is preferable to write client-server code with lightweight processes, which were presented in Chapter 19. In this case, it becomes necessary to examine the interactions among different server processes.

Threads and the Unix Library

The simultaneous use of lightweight processes and the Unix library causes all active threads to block if a system call does not return immediately. In particular, reads on file descriptors, including those created by socket, are blocking.

To avoid this problem, the ThreadUnix library reimplements most of the input-output functions from the Unix library. The functions defined in that library will only block the thread which is actually making the system call. As a consequence, input and output is handled with the low-level functions read and write found in the ThreadUnix library.

For example, the standard function for reading a string of characters, input_line, is redefined in such a way that it does not block other threads while reading a line.

# let my_input_line fd =
let s = " " and r = ref ""
in while (ThreadUnix.read fd s 0 1 > 0) && s.[0] <> '\n' do r := !r ^s done ;
!r ;;
val my_input_line : Unix.file_descr -> string = <fun>


Classes for a Server with Threads

Now let us recycle the example of the CAPITALIZATION service, this time giving a version using lightweight processes. Shifting to threads poses no problem for our little application on either the server side or the client side, which start processes independently.

Earlier, we built a generic server parameterized over a service function. We were able to achieve this kind of abstraction by relying on the functional aspect of the Objective CAML language. Now we are about to use the object-oriented extensions to the language to show how objects allow us to achieve a comparable abstraction.

The server is organized into two classes: serv_socket and connection. The first of these handles the service startup, and the second, the service itself. We have introduced some print statements to trace the main stages of the service.

The serv_socket class.
has two instance variables: port, the port number for the service, and socket, the socket for listening. When constructing such an object, the initializer opens the service and creates this socket. The run method accepts connections and creates a new connection object for handling requests. The serv_socket uses the connection class described in the following paragraph. Usually, this class must be defined before the serv_socket class.


# class serv_socket p =
object (self)
val port = p
val mutable sock = ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0

initializer
let my_address = get_my_addr ()
in Unix.bind sock (Unix.ADDR_INET(my_address,port)) ;
Unix.listen sock 3

method private client_addr = function
Unix.ADDR_INET(host,_) -> Unix.string_of_inet_addr host
| _ -> "Unexpected client"

method run () =
while(true) do
let (sd,sa) = ThreadUnix.accept sock in
let connection = new connection(sd,sa)
in Printf.printf "TRACE.serv: new connection from %s\n\n"
(self#client_addr sa) ;
ignore (connection#start ())
done
end ;;
class serv_socket :
int ->
object
val port : int
val mutable sock : Unix.file_descr
method private client_addr : Unix.sockaddr -> string
method run : unit -> unit
end
It is possible to refine the server by inheriting from this class and redefining the run method.

The connection class.
The instance variables in this class, s_descr and s_addr, are initialized to the descriptor and the address of the socket created by accept. The methods are start, run, and stop. The start creates a thread calling the two other methods, and returns its thread identifier, which can be used by the calling instance of serv_socket. The run method contains the core functionality of the service. We have slightly modified the termination condition for the service: we exit on receipt of an empty string. The stop service just closes the socket descriptor for the service.

Each new connection has an associated number obtained by calling the auxiliary function gen_num when the instance is created.


# let gen_num = let c = ref 0 in (fun () -> incr c; !c) ;;
val gen_num : unit -> int = <fun>
# exception Done ;;
exception Done
# class connection (sd,sa) =
object (self)
val s_descr = sd
val s_addr = sa
val mutable number = 0
initializer
number <- gen_num();
Printf.printf "TRACE.connection : object %d created\n" number ;
print_newline()

method start () = Thread.create (fun x -> self#run x ; self#stop x) ()

method stop() =
Printf.printf "TRACE.connection : object finished %d\n" number ;
print_newline () ;
Unix.close s_descr

method run () =
try
while true do
let line = my_input_line s_descr
in if (line = "") or (line = "\013") then raise Done ;
let result = (String.uppercase line)^"\n"
in ignore (ThreadUnix.write s_descr result 0 (String.length result))
done
with
Done -> ()
| exn -> print_string (Printexc.to_string exn) ; print_newline()
end ;;
class connection :
Unix.file_descr * 'a ->
object
val mutable number : int
val s_addr : 'a
val s_descr : Unix.file_descr
method run : unit -> unit
method start : unit -> Thread.t
method stop : unit -> unit
end


Here again, by inheritance and redefinition of the run method, we can define a new service.

We can test this new version of the server by running the protect_serv function.

# let go_serv () = let s = new serv_socket 1400 in s#run () ;;
# let protect_serv () = Unix.handle_unix_error go_serv () ;;


Multi-tier Client-server Programming

Even though the client-server relation is asymmetric, nothing prevents a server from being the client of another service. In this way, we have a communication hierarchy. A typical client-server application might be the following:

  • a mail client presents a friendly user interface;
  • a word-processing program is run, followed by an interaction with the user;
  • the word-processing program accesses a database.
One of the goals of client-server applications is to alleviate the processing of centralized machines. Figure 20.3 shows two client-server architectures with three tiers.



Figure 20.3: Different client-server architectures


Each tier may run on a different machine. The user interface runs on the machine running the user mail application. The processing part is handled by a machine shared by a collection of users, which itself sends requests to a remote database server. With this application, a particular piece of data may be sent to the user mail application or to the database server.

Some Remarks on the Client-server Programs

In the preceding sections, we constructed servers for a simple CAPITALIZATION service. Each server used a different approach for its implementation. The first such server used the Unix fork mechanism. Once we built that server, it became possible to test it with the telnet client supplied with the Unix, Windows, and MacOS operating systems. Next, we built a simple first client. We were then able to test the client and server together. Clients may have tasks to manage between communications. For this purpose, we built the client_par.exe client, which separates reading from writing by using forks. A new kind of server was built using threads to clearly show the relative independence of the server and the client, and to bring input-output into this setting. This server was organized into two easily-reused classes. We note that both functional programming and object-oriented programming support the separation of ``mechanical,'' reusable code from code for specialized processing.


Previous Contents Next ocaml-book-1.0/en/html/book-ora069.html0000644000000000000000000034555407453055400014511 0ustar Exercises Previous Contents Next

Exercises

Creation of a Toplevel and Standalone Executable

Consider again the Basic interpreter. Modify it to make a new toplevel.
  1. Split the Basic application into 4 files, each with the extension .ml. The files will be organized like this: abstract syntax (syntax.ml), printing (pprint.ml), parsing (alexsynt.ml) and evaluation of instructions (eval.ml). The head of each file should contain the open statements to load the modules required for compilation. syntax.ml :

    type op_unr = OPPOSE | NON ;;

    type op_bin = PLUS | MINUS | MULT | DIV | MOD
    | EQUAL | INF | INFEQ | SUP | SUPEQ | DIFF
    | AND | OR ;;

    type expression =
    ExpInt of int
    | ExpVar of string
    | ExpStr of string
    | ExpUnr of op_unr * expression
    | ExpBin of expression * op_bin * expression ;;

    type instruction =
    Rem of string
    | Goto of int
    | Print of expression
    | Input of string
    | If of expression * int
    | Let of string * expression ;;

    type line = { num : int ; inst : instruction } ;;

    type program = line list ;;

    type phrase = Line of line | List | Run | End ;;

    let priority_ou = function NON -> 1 | OPPOSE -> 7
    let priority_ob = function
    MULT | DIV -> 6
    | PLUS | MINUS -> 5
    | MOD -> 4
    | EQUAL | INF | INFEQ | SUP | SUPEQ | DIFF -> 3
    | AND | OR -> 2 ;;

    let pp_opbin = function
    PLUS -> "+" | MULT -> "*" | MOD -> "%" | MINUS -> "-"
    | DIV -> "/" | EQUAL -> " = " | INF -> " < "
    | INFEQ -> " <= " | SUP -> " > "
    | SUPEQ -> " >= " | DIFF -> " <> " | AND -> " & " | OR -> " | "
    let pp_opunr = function OPPOSE -> "-" | NON -> "!" ;;
    pprint.ml :
    open Syntax;;

    let parenthesis x = "(" ^ x ^ ")";;

    let pp_expression =
    let rec ppg pr = function
    ExpInt n -> (string_of_int n)
    | ExpVar v -> v
    | ExpStr s -> "\"" ^ s ^ "\""
    | ExpUnr (op,e) ->
    let res = (pp_opunr op)^(ppg (priority_ou op) e)
    in if pr=0 then res else parenthesis res
    | ExpBin (e1,op,e2) ->
    let pr2 = priority_ob op
    in let res = (ppg pr2 e1)^(pp_opbin op)^(ppd pr2 e2)
    (* parenthesis if the priority is not higher *)
    in if pr2 >= pr then res else parenthesis res
    and ppd pr exp = match exp with

    (* the sub-trees could only be different *)
    (* in their binary operators *)
    ExpBin (e1,op,e2) ->
    let pr2 = priority_ob op
    in let res = (ppg pr2 e1)^(pp_opbin op)^(ppd pr2 e2)
    in if pr2 > pr then res else parenthesis res
    | _ -> ppg pr exp
    in ppg 0 ;;

    let pp_instruction = function
    Rem s -> "REM " ^ s
    | Goto n -> "GOTO " ^ (string_of_int n)
    | Print e -> "PRINT " ^ (pp_expression e)
    | Input v -> "INPUT " ^ v
    | If (e,n) -> "IF "^(pp_expression e)^" THEN "^(string_of_int n)
    | Let (v,e) -> "LET " ^ v ^ " = " ^ (pp_expression e) ;;

    let pp_line l = (string_of_int l.num) ^ " " ^ (pp_instruction l.inst) ;;
    alexsynt.ml :
    open Syntax;;

    type lexeme = Lint of int
    | Lident of string
    | Lsymbol of string
    | Lstring of string
    | Lfin ;;

    type str_lexer = {str:string; mutable position:int; size:int } ;;

    let init_lex s = { str=s; position=0 ; size=String.length s } ;;

    let advance cl = cl.position <- cl.position+1 ;;

    let advance_n cl n = cl.position <- cl.position+n ;;

    let extract pred cl =
    let st = cl.str and ct = cl.position in
    let rec ext n = if n<cl.size && (pred st.[n]) then ext (n+1) else n in
    let res = ext ct
    in cl.position <- res ; String.sub cl.str ct (res-ct) ;;

    let extract_int =
    let is_integer = function '0'..'9' -> true | _ -> false
    in function cl -> int_of_string (extract is_integer cl)
    let extract_ident =
    let is_alpha_num = function
    'a'..'z' | 'A'..'Z' | '0' .. '9' | '_' -> true
    | _ -> false
    in extract is_alpha_num ;;

    exception LexerError ;;

    let rec lexer cl =
    let lexer_char c = match c with
    ' '
    | '\t' -> advance cl ; lexer cl
    | 'a'..'z'
    | 'A'..'Z' -> Lident (extract_ident cl)
    | '0'..'9' -> Lint (extract_int cl)
    | '"' -> advance cl ;
    let res = Lstring (extract ((<>) '"') cl)
    in advance cl ; res
    | '+' | '-' | '*' | '/' | '%' | '&' | '|' | '!' | '=' | '(' | ')' ->
    advance cl; Lsymbol (String.make 1 c)
    | '<'
    | '>' -> advance cl;
    if cl.position >= cl.size then Lsymbol (String.make 1 c)
    else let cs = cl.str.[cl.position]
    in ( match (c,cs) with
    ('<','=') -> advance cl; Lsymbol "<="
    | ('>','=') -> advance cl; Lsymbol ">="
    | ('<','>') -> advance cl; Lsymbol "<>"
    | _ -> Lsymbol (String.make 1 c) )
    | _ -> raise LexerError
    in
    if cl.position >= cl.size then Lfin
    else lexer_char cl.str.[cl.position] ;;

    type exp_elem =
    Texp of expression (* expression *)
    | Tbin of op_bin (* binary operator *)
    | Tunr of op_unr (* unary operator *)
    | Tpg (* right parenthesis *) ;;

    exception ParseError ;;

    let symb_unr = function
    "!" -> NON | "-" -> OPPOSE | _ -> raise ParseError
    let symb_bin = function
    "+" -> PLUS | "-" -> MINUS | "*" -> MULT | "/" -> DIV | "%" -> MOD
    | "=" -> EQUAL | "<" -> INF | "<=" -> INFEQ | ">" -> SUP
    | ">=" -> SUPEQ | "<>" -> DIFF | "&" -> AND | "|" -> OR
    | _ -> raise ParseError
    let tsymb s = try Tbin (symb_bin s) with ParseError -> Tunr (symb_unr s) ;;

    let reduce pr = function
    (Texp e)::(Tunr op)::st when (priority_ou op) >= pr
    -> (Texp (ExpUnr (op,e)))::st
    | (Texp e1)::(Tbin op)::(Texp e2)::st when (priority_ob op) >= pr
    -> (Texp (ExpBin (e2,op,e1)))::st
    | _ -> raise ParseError ;;

    let rec pile_or_reduce lex stack = match lex , stack with
    Lint n , _ -> (Texp (ExpInt n))::stack
    | Lident v , _ -> (Texp (ExpVar v))::stack
    | Lstring s , _ -> (Texp (ExpStr s))::stack
    | Lsymbol "(" , _ -> Tpg::stack
    | Lsymbol ")" , (Texp e)::Tpg::st -> (Texp e)::st
    | Lsymbol ")" , _ -> pile_or_reduce lex (reduce 0 stack)
    | Lsymbol s , _
    -> let symbole =
    if s<>"-" then tsymb s
    (* resolve the ambiguity of the symbol ``-'' *)
    (* follow the stack (i.e last exp_elem pile) *)
    else match stack
    with (Texp _)::_ -> Tbin MINUS
    | _ -> Tunr OPPOSE
    in ( match symbole with
    Tunr op -> (Tunr op)::stack
    | Tbin op ->
    ( try pile_or_reduce lex (reduce (priority_ob op)
    stack )
    with ParseError -> (Tbin op)::stack )
    | _ -> raise ParseError )
    | _ , _ -> raise ParseError ;;

    let rec reduce_all = function
    | [] -> raise ParseError
    | [Texp x] -> x
    | st -> reduce_all (reduce 0 st) ;;

    let parse_exp fin cl =
    let p = ref 0
    in let rec parse_un stack =
    let l = ( p:=cl.position ; lexer cl)
    in if not (fin l) then parse_un (pile_or_reduce l stack)
    else ( cl.position <- !p ; reduce_all stack )
    in parse_un [] ;;

    let parse_inst cl = match lexer cl with
    Lident s -> ( match s with
    "REM" -> Rem (extract (fun _ -> true) cl)
    | "GOTO" -> Goto (match lexer cl with
    Lint p -> p
    | _ -> raise ParseError)
    | "INPUT" -> Input (match lexer cl with
    Lident v -> v
    | _ -> raise ParseError)
    | "PRINT" -> Print (parse_exp ((=) Lfin) cl)
    | "LET" ->
    let l2 = lexer cl and l3 = lexer cl
    in ( match l2 ,l3 with
    (Lident v,Lsymbol "=") -> Let (v,parse_exp ((=) Lfin) cl)
    | _ -> raise ParseError )
    | "IF" ->
    let test = parse_exp ((=) (Lident "THEN")) cl
    in ( match ignore (lexer cl) ; lexer cl with
    Lint n -> If (test,n)
    | _ -> raise ParseError )
    | _ -> raise ParseError )
    | _ -> raise ParseError ;;

    let parse str =
    let cl = init_lex str
    in match lexer cl with
    Lint n -> Line { num=n ; inst=parse_inst cl }
    | Lident "LIST" -> List
    | Lident "RUN" -> Run
    | Lident "END" -> End
    | _ -> raise ParseError ;;
    eval.ml :
    open Syntax;;
    open Pprint;;
    open Alexsynt;;

    type vl = Vint of int | Vstr of string | Vbool of bool ;;

    type environment = (string * vl) list ;;

    type state = { line:int ; prog:program ; env:environment } ;;

    exception RunError of int
    let runerr n = raise (RunError n) ;;

    let rec eval_exp n envt expr = match expr with
    ExpInt p -> Vint p
    | ExpVar v -> ( try List.assoc v envt with Not_found -> runerr n )
    | ExpUnr (OPPOSE,e) ->
    ( match eval_exp n envt e with
    Vint p -> Vint (-p)
    | _ -> runerr n )
    | ExpUnr (NON,e) ->
    ( match eval_exp n envt e with
    Vbool p -> Vbool (not p)
    | _ -> runerr n )
    | ExpStr s -> Vstr s
    | ExpBin (e1,op,e2)
    -> match eval_exp n envt e1 , op , eval_exp n envt e2 with
    Vint v1 , PLUS , Vint v2 -> Vint (v1 + v2)
    | Vint v1 , MINUS , Vint v2 -> Vint (v1 - v2)
    | Vint v1 , MULT , Vint v2 -> Vint (v1 * v2)
    | Vint v1 , DIV , Vint v2 when v2<>0 -> Vint (v1 / v2)
    | Vint v1 , MOD , Vint v2 when v2<>0 -> Vint (v1 mod v2)

    | Vint v1 , EQUAL , Vint v2 -> Vbool (v1 = v2)
    | Vint v1 , DIFF , Vint v2 -> Vbool (v1 <> v2)
    | Vint v1 , INF , Vint v2 -> Vbool (v1 < v2)
    | Vint v1 , SUP , Vint v2 -> Vbool (v1 > v2)
    | Vint v1 , INFEQ , Vint v2 -> Vbool (v1 <= v2)
    | Vint v1 , SUPEQ , Vint v2 -> Vbool (v1 >= v2)

    | Vbool v1 , AND , Vbool v2 -> Vbool (v1 && v2)
    | Vbool v1 , OR , Vbool v2 -> Vbool (v1 || v2)

    | Vstr v1 , PLUS , Vstr v2 -> Vstr (v1 ^ v2)
    | _ , _ , _ -> runerr n ;;

    let rec add v e env = match env with
    [] -> [v,e]
    | (w,f)::l -> if w=v then (v,e)::l else (w,f)::(add v e l) ;;

    let rec goto_line n prog = match prog with
    [] -> runerr n
    | l::ll -> if l.num = n then prog
    else if l.num<n then goto_line n ll
    else runerr n ;;

    let print_vl v = match v with
    Vint n -> print_int n
    | Vbool true -> print_string "true"
    | Vbool false -> print_string "false"
    | Vstr s -> print_string s ;;

    let eval_inst state =
    let lc, ns =
    match goto_line state.line state.prog with
    [] -> failwith "empty program"
    | lc::[] -> lc,-1
    | lc::ls::_ -> lc,ls.num
    in
    match lc.inst with
    Rem _ -> { state with line=ns }
    | Print e -> print_vl (eval_exp lc.num state.env e) ;
    print_newline () ;
    { state with line=ns }
    | Let(v,e) -> let ev = eval_exp lc.num state.env e
    in { state with line=ns; env=add v ev state.env }
    | Goto n -> { state with line=n }
    | Input v -> let x = try read_int ()
    with Failure "int_of_string" -> 0
    in { state with line=ns ; env=add v (Vint x) state.env }
    | If (t,n) -> match eval_exp lc.num state.env t with
    Vbool true -> { state with line=n }
    | Vbool false -> { state with line=ns }
    | _ -> runerr n ;;

    let rec run state =
    if state.line = -1 then state else run (eval_inst state) ;;

    let rec insert line p = match p with
    [] -> [line]
    | l::prog ->
    if l.num < line.num then l::(insert line prog)
    else if l.num=line.num then line::prog
    else line::l::prog ;;

    let print_prog state =
    let print_line x = print_string (pp_line x) ; print_newline ()
    in print_newline () ;
    List.iter print_line state.prog ;
    print_newline () ;;

    let premiere_line = function [] -> 0 | i::_ -> i.num ;;

    exception Fin
    let one_command state =
    print_string "> " ; flush stdout ;
    try
    match parse (input_line stdin) with
    Line l -> { state with prog=insert l state.prog }
    | List -> (print_prog state ; state )
    | Run -> run {state with line = premiere_line state.prog}
    | End -> raise Fin
    with
    LexerError -> print_string "Illegal character\n"; state
    | ParseError -> print_string "syntax error\n"; state
    | RunError n ->
    print_string "runtime error at line ";
    print_int n ;
    print_string "\n";
    state ;;

    let go () =
    try
    print_string "Mini-BASIC version 0.1\n\n";
    let rec loop state = loop (one_command state) in
    loop { line=0; prog=[]; env=[] }
    with Fin -> print_string "A bientt...\n";;





  2. Compile all files separately.
    $ ocamlc -c syntax.ml 
    $ ocamlc -c pprint.ml
    $ ocamlc -c alexsynt.ml
    $ ocamlc -c eval.ml
    


  3. Add a file mainbasic.ml which contains only the statement for calling the main function. mainbasic.ml :
    open Eval;;

    go ();;


  4. Create a new toplevel with the name topbasic, which starts the Basic interpreter. cration du toplevel :
    $ ocamlmktop -o topbasic syntax.cmo pprint.cmo alexsynt.cmo eval.cmo mainbasic.ml
    
    test du toplevel :
    $ topbasic
    Mini-BASIC version 0.1
    
    > 10 PRINT "DONNER UN NOMBRE"
    > 20 INPUT X
    > 30 PRINT X
    > LIST
    
    10  PRINT "DONNER UN NOMBRE"
    20  INPUT X
    30  PRINT X
    
    > RUN
    DONNER UN NOMBRE
    44
    44
    > END
    A bientt...
            Objective Caml version 2.04
    
    # 
    


  5. Create a standalone executable which runs the Basic interpreter. compilation et dition de liens :
    $ ocamlc -custom -o basic.exe syntax.cmo pprint.cmo alexsynt.cmo eval.cmo mainbasic.ml
    test de l'excutable autonome :
    $ basic.exe
    Mini-BASIC version 0.1
    
    > 10 PRINT "BONJOUR"
    > LIST
    
    10  PRINT "BONJOUR"
    
    > RUN
    BONJOUR
    > END
    A bientt...
    $
    

Comparison of Performance

Try to compare the performance of code produced by the bytecode compiler and by the native compiler. For this purpose, write an application for sorting lists and arrays.
  1. Write a polymorphic function for sorting lists. The order relation should be passed as an argument to the sort function. The sort algorithm can be selected by the reader. For example: bubble sort, or quick sort. Write this function as sort.ml. On utilise les fichiers sort.mli et sort.ml de la distribution qui dfinissent les fonctions list et array de tri d'une liste et d'un tableau.

    sort.mli :

    (***********************************************************************)
    (* *)
    (* Objective Caml *)
    (* *)
    (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
    (* *)
    (* Copyright 1996 Institut National de Recherche en Informatique et *)
    (* Automatique. Distributed only by permission. *)
    (* *)
    (***********************************************************************)

    (* $Id: sort.mli,v 1.1.1.1 2000/06/26 14:37:50 xleroy Exp $ *)

    (* Module [Sort]: sorting and merging lists *)

    val list : ('a -> 'a -> bool) -> 'a list -> 'a list
    (* Sort a list in increasing order according to an ordering predicate.
    The predicate should return [true] if its first argument is
    less than or equal to its second argument. *)

    val array : ('a -> 'a -> bool) -> 'a array -> unit
    (* Sort an array in increasing order according to an
    ordering predicate.
    The predicate should return [true] if its first argument is
    less than or equal to its second argument.
    The array is sorted in place. *)

    val merge : ('a -> 'a -> bool) -> 'a list -> 'a list -> 'a list
    (* Merge two lists according to the given predicate.
    Assuming the two argument lists are sorted according to the
    predicate, [merge] returns a sorted list containing the elements
    from the two lists. The behavior is undefined if the two
    argument lists were not sorted. *)


    sort.ml

    (***********************************************************************)
    (* *)
    (* Objective Caml *)
    (* *)
    (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
    (* *)
    (* Copyright 1996 Institut National de Recherche en Informatique et *)
    (* Automatique. Distributed only by permission. *)
    (* *)
    (***********************************************************************)

    (* $Id: sort.ml,v 1.1.1.1 2000/06/26 14:37:50 xleroy Exp $ *)

    (* Merging and sorting *)

    open Array

    let rec merge order l1 l2 =
    match l1 with
    [] -> l2
    | h1 :: t1 ->
    match l2 with
    [] -> l1
    | h2 :: t2 ->
    if order h1 h2
    then h1 :: merge order t1 l2
    else h2 :: merge order l1 t2

    let list order l =
    let rec initlist = function
    [] -> []
    | [e] -> [[e]]
    | e1::e2::rest ->
    (if order e1 e2 then [e1;e2] else [e2;e1]) :: initlist rest in
    let rec merge2 = function
    l1::l2::rest -> merge order l1 l2 :: merge2 rest
    | x -> x in
    let rec mergeall = function
    [] -> []
    | [l] -> l
    | llist -> mergeall (merge2 llist) in
    mergeall(initlist l)

    let swap arr i j =
    let tmp = unsafe_get arr i in
    unsafe_set arr i (unsafe_get arr j);
    unsafe_set arr j tmp

    let array order arr =
    let rec qsort lo hi =
    if hi <= lo then ()
    else if hi - lo < 5 then begin
    (* Use insertion sort *)
    for i = lo + 1 to hi do
    let val_i = unsafe_get arr i in
    if order val_i (unsafe_get arr (i - 1)) then begin
    unsafe_set arr i (unsafe_get arr (i - 1));
    let j = ref (i - 1) in
    while !j >= 1 && order val_i (unsafe_get arr (!j - 1)) do
    unsafe_set arr !j (unsafe_get arr (!j - 1));
    decr j
    done;
    unsafe_set arr !j val_i
    end
    done
    end else begin
    let mid = (lo + hi) lsr 1 in
    (* Select median value from among LO, MID, and HI *)
    let pivotpos =
    let vlo = unsafe_get arr lo
    and vhi = unsafe_get arr hi
    and vmid = unsafe_get arr mid in
    if order vlo vmid then
    if order vmid vhi then mid
    else if order vlo vhi then hi else lo
    else
    if order vhi vmid then mid
    else if order vhi vlo then hi else lo in
    swap arr pivotpos hi;
    let pivot = unsafe_get arr hi in
    let i = ref lo and j = ref hi in
    while !i < !j do
    while !i < hi && order (unsafe_get arr !i) pivot do incr i done;
    while !j > lo && order pivot (unsafe_get arr !j) do decr j done;
    if !i < !j then swap arr !i !j
    done;
    swap arr !i hi;
    (* Recurse on larger half first *)
    if (!i - 1) - lo >= hi - (!i + 1) then begin
    qsort lo (!i - 1); qsort (!i + 1) hi
    end else begin
    qsort (!i + 1) hi; qsort lo (!i - 1)
    end
    end in
    qsort 0 (Array.length arr - 1)


  2. Create the main function in the file trilist.ml, which uses the previous function and applies it to a list of integers by sorting it in increasing order, then in decreasing order. interval.ml :

    let interval order next a b = 
    let rec aux a =
    if not (order a b) then [a] else a :: aux (next a)
    in aux a;;




    trilist.ml :


    let main () =
    let il = Interval.interval (>) (fun x -> x -1) 50000 20
    and il2 = Interval.interval (<) (fun x -> x + 1) 20 50000 in
    Sort.list (<) il, Sort.list (>) il2;;

    main();;


  3. Create two standalone executables - one with the bytecode compiler, and another with the native compiler. Measure the execution time of these two programs. Choose lists of sufficient size to get a good idea of the time differences.
    1. code-octet (Unix) : trilbyte.exe

      ocamlc -custom -o trilbyte.exe sort.mli sort.ml interval.ml trilist.ml
      
    2. natif (Unix) : trilopt.exe
      ocamlopt -o trilopt.exe sort.mli sort.ml interval.ml trilist.ml
      
    Performances :
    trilbyte.exe trilopt.exe
    2,55 secondes (user) 1,67 secondes (user)

    Le rapport trilopt.exe / trilbyte.exe est de 2/3.


  4. Rewrite the sort program for arrays. Continue using an order function as argument. Perform the test on arrays filled in the same manner as for the lists. triarray.ml :



    let main () =
    let il = Array.of_list(Interval.interval (>) (fun x -> x -1) 50000 20)
    and il2 = Array.of_list(Interval.interval (<) (fun x -> x + 1) 20 50000) in
    Sort.array (<) il, Sort.array (>) il2;;

    main();;


    1. code-octet (Unix) : triabyte.exe
      ocamlc -custom -o triabyte.exe sort.mli sort.ml interval.ml triarray.ml
      
    2. natif (Unix) : triaopt.exe
      ocamlopt -o triaoptu.exe sort.mli sort.ml interval.ml triarray.ml
      
    Performances :
    triabyte.exe triaopt.exe
    515 s 106 s

    Le rapport triaopt.exe / triabyte.exe est de 1/5.


  5. What can we say about the results of these tests? Le compilateur natif apporte un gain de temps d'excution variable ( facteur 2/3 pour les liste et 1/5 pour les tableaux).

Previous Contents Next ocaml-book-1.0/en/html/book-ora038.gif0000644000000000000000000000642107452056111014270 0ustar GIF89aUUUrrr!,H0I8ͻ`(dih .s lpH#A1MeqJZlDlVNK.FkL1<+V< 8a::zw|2lGR,J`980a0K4 <߷˞fR A^hHa"q9dPŇA.֚A#{qF#CH9dg\P&>OΤ%&OF?Ee =[;IjiBG9QU*WujV86`oX56Sm=97 \Ivrw[r}}ٴpbk 6l'fDr!x&#5% QaڧmC׾m8D+?GxDW69_OeR[<hB^=ʛrqC'/z}C\2-爁`E c =%"T U}q! ^t8څ R,f]@`1cH-娆9 H2j85^-3a%b_pRbdM-d[7I# F&'VJW(6'}"ڹ[yvT$y:%p'8*餔Vj饘#*Jr]2Յ&%D&!0-Lnr^:gg2o|hTuإd#0j}9|F P.`}_""e>aڍL9PTJ!*jQUPa\;[!z$ufGZ2Եɭ'[QZUxL&Gոְ+WM0xW /U_ <Be /˫l g=aBl62RXF fhͬJU։]oa 65nGdiH.~\lt.]+ݶ.. FMB+\vmAŗQtEZW|PpWrCEr``8")|7uFi68X%qF^6XwcZ;.5{]وEcpŴ0FP:HVeˠsy)Ô?6ٽz}{q7p31Tg;9ζsYfZXrU ,Z"f4iJY6KO3KL'*tD3JM83K]#5yx qYS8TׯO8Fp7< 4`RH@yq}(; <kDI^j;"d9ZqV eD/ToPޣVK8 ,G}կr׊FG`{Ѿ}S[ɗN!s\KO«׿%~nNAl<ГաcLpăr>rD/F)ճ;}6=@JN:,>pݓN?o7f\⏯o1ܯwKypּ|p'zdp$D+7%%'${rq70Q!wU(`1T^a!#V8apaՅ7CM xV4hq(cH懚P{Tf bRH8fmvX:Hz||xjWx؊8`|艹(^pExW5hhwyǨ'GtVEϘf17}.ؘvnڸtX蘎BR׎ mȄx9wz_؏?R9IbFZ u( YX/29i%YX6-ȑ(qS'I)Jw 4"|$Kғ9 *KC3tr18Y}}M{<=bd3M22wa}$d>R'=qL##R The Module Language Previous Contents Next

The Module Language

The Objective CAML language features a sub-language for modules, which comes in addition to the core language that we have seen so far. In this module language, the interface of a module is called a signature and its implementation is called a structure. When there is no ambiguity, we will often use the word ``module'' to refer to a structure.



The syntax for declaring signatures and structures is as follows:

Syntax


module type NAME =
  sig
    interface declarations
  end

Syntax


module Name =
  struct
    implementation definitions
  end



Warning


The name of a module must start with an uppercase letter. There are no such case restrictions on names of signatures, but by convention we will use names in uppercase for signatures.


Signatures and structures do not need to be bound to names: we can also use anonymous signature and structure expressions, writing simply

Syntax


sig declarations end

Syntax


struct definitions end
We write signature and structure to refer to either names of signatures and structures, or anonymous signature and structure expressions.

Every structure possesses a default signature, computed by the type inference system, which reveals all the definitions contained in the structure, with their most general types. When defining a structure, we can also indicate the desired signature by adding a signature constraint (similar to the type constraints from the core language), using one of the following two syntactic forms:

Syntax


module Name : signature = structure

Syntax


module Name = (structure : signature)
When an explicit signature is provided, the system checks that all the components declared in the signature are defined in the structure structure, and that the types are consistent. In other terms, the system checks that the explicit signature provided is ``included in'', or implied by, the default signature. If so, Name is viewed in the remainder of the code with the signature ``signature'', and only the components declared in the signature are accessible to the clients of the module. (This is the same behavior we saw previously with interface files.)

Access to the components of a module is via the dot notation:

Syntax


Name1.name2


We say that the name name2 is qualified by the name Name1 of its defining module.

The module name and the dot can be omitted using a directive to open the module:

Syntax


open Name
In the scope of this directive, we can use short names name2 to refer to the components of the module Name. In case of name conflicts, opening a module hides previously defined entities with the same names, as in the case of identifier redefinitions.

Two Stack Modules

We continue the example of stacks by recasting it in the module language. The signature for a stack module is obtained by wrapping the declarations from the stack.mli file in a signature declaration:

# module type STACK =
sig
type 'a t
exception Empty
val create: unit -> 'a t
val push: 'a -> 'a t -> unit
val pop: 'a t -> 'a
val clear : 'a t -> unit
val length: 'a t -> int
val iter: ('a -> unit) -> 'a t -> unit
end ;;
module type STACK =
sig
type 'a t
exception Empty
val create : unit -> 'a t
val push : 'a -> 'a t -> unit
val pop : 'a t -> 'a
val clear : 'a t -> unit
val length : 'a t -> int
val iter : ('a -> unit) -> 'a t -> unit
end

A first implementation of stacks is obtained by reusing the Stack module from the standard library:

# module StandardStack = Stack ;;
module StandardStack :
sig
type 'a t = 'a Stack.t
exception Empty
val create : unit -> 'a t
val push : 'a -> 'a t -> unit
val pop : 'a t -> 'a
val clear : 'a t -> unit
val length : 'a t -> int
val iter : ('a -> unit) -> 'a t -> unit
end


We then define an alternate implementation based on arrays:

# module MyStack =
struct
type 'a t = { mutable sp : int; mutable c : 'a array }
exception Empty
let create () = { sp=0 ; c = [||] }
let clear s = s.sp <- 0; s.c <- [||]
let increase s x = s.c <- Array.append s.c (Array.create 5 x)
let push x s =
if s.sp >= Array.length s.c then increase s x;
s.c.(s.sp) <- x;
s.sp <- succ s.sp
let pop s =
if s.sp =0 then raise Empty
else (s.sp <- pred s.sp ; s.c.(s.sp))
let length s = s.sp
let iter f s = for i = pred s.sp downto 0 do f s.c.(i) done
end ;;
module MyStack :
sig
type 'a t = { mutable sp: int; mutable c: 'a array }
exception Empty
val create : unit -> 'a t
val clear : 'a t -> unit
val increase : 'a t -> 'a -> unit
val push : 'a -> 'a t -> unit
val pop : 'a t -> 'a
val length : 'a t -> int
val iter : ('a -> 'b) -> 'a t -> unit
end


These two modules implement the type t of stacks by different data types.

# StandardStack.create () ;;
- : '_a StandardStack.t = <abstr>
# MyStack.create () ;;
- : '_a MyStack.t = {MyStack.sp=0; MyStack.c=[||]}


To abstract over the type representation in Mystack, we add a signature constraint by the STACK signature.

# module MyStack = (MyStack : STACK) ;;
module MyStack : STACK
# MyStack.create() ;;
- : '_a MyStack.t = <abstr>


The two modules StandardStack and MyStack implement the same interface, that is, provide the same set of operations over stacks, but their t types are different. It is therefore impossible to apply operations from one module to values from the other module:

# let s = StandardStack.create() ;;
val s : '_a StandardStack.t = <abstr>
# MyStack.push 0 s ;;
Characters 15-16:
This expression has type 'a StandardStack.t = 'a Stack.t
but is here used with type int MyStack.t


Even if both modules implemented the t type by the same concrete type, constraining MyStack by the signature STACK suffices to abstract over the t type, rendering it incompatible with any other type in the system and preventing sharing of values and operations between the various stack modules.

# module S1 = ( MyStack : STACK ) ;;
module S1 : STACK
# module S2 = ( MyStack : STACK ) ;;
module S2 : STACK
# let s = S1.create () ;;
val s : '_a S1.t = <abstr>
# S2.push 0 s ;;
Characters 10-11:
This expression has type 'a S1.t but is here used with type int S2.t


The Objective CAML system compares abstract types by names. Here, the two types S1.t and S2.t are both abstract, and have different names, hence they are considered as incompatible. It is precisely this restriction that makes type abstraction effective, by preventing any access to the definition of the type being abstracted.

Modules and Information Hiding

This section shows additional examples of signature constraints hiding or abstracting definitions of structure components.

Hiding Type Implementations

Abstracting over a type ensures that the only way to construct values of this type is via the functions exported from its definition module. This can be used to restrict the values that can belong to this type. In the following example, we implement an abstract type of integers which, by construction, can never take the value 0.
 
# module Int_Star =
( struct
type t = int
exception Isnul
let of_int = function 0 -> raise Isnul | n -> n
let mult = ( * )
end
:
sig
type t
exception Isnul
val of_int : int -> t
val mult : t -> t -> t
end
) ;;
module Int_Star :
sig type t exception Isnul val of_int : int -> t val mult : t -> t -> t end


Hiding Values

We now define a symbol generator, similar to that of page ??, using a signature constraint to hide the state of the generator.

We first define the signature GENSYM exporting only two functions for generating symbols.

# module type GENSYM =
sig
val reset : unit -> unit
val next : string -> string
end ;;


We then implement this signature as follows:

# module Gensym : GENSYM =
struct
let c = ref 0
let reset () = c:=0
let next s = incr c ; s ^ (string_of_int !c)
end;;
module Gensym : GENSYM


The reference c holding the state of the generator Gensym is not accessible outside the two exported functions.

# Gensym.reset();;
- : unit = ()
# Gensym.next "T";;
- : string = "T1"
# Gensym.next "X";;
- : string = "X2"
# Gensym.reset();;
- : unit = ()
# Gensym.next "U";;
- : string = "U1"
# Gensym.c;;
Characters 0-8:
Unbound value Gensym.c


The definition of c is essentially local to the structure Gensym, since it is hidden by the associated signature. The signature constraint achieves more simply the same goal as the local definition of a reference in the definition of the two functions reset_s and new_s on page ??.

Multiple Views of a Module

The module language and its signature constraints support taking several views of a given structure. For instance, we can have a ``super-user interface'' for the module Gensym, allowing the symbol counter to be reset, and a ``normal user interface'' that permits only the generation of new symbols, but no other intervention on the counter. To implement the latter interface, it suffices to declare the signature:

# module type USER_GENSYM =
sig
val next : string -> string
end;;
module type USER_GENSYM = sig val next : string -> string end


We then implement it by a mere signature constraint.

# module UserGensym = (Gensym : USER_GENSYM) ;;
module UserGensym : USER_GENSYM
# UserGensym.next "U" ;;
- : string = "U2"
# UserGensym.reset() ;;
Characters 0-16:
Unbound value UserGensym.reset


The UserGensym module fully reuses the code of the Gensym module. In addition, both modules share the same counter:

# Gensym.next "U" ;;
- : string = "U3"
# Gensym.reset() ;;
- : unit = ()
# UserGensym.next "V" ;;
- : string = "V1"


Type Sharing between Modules

As we saw on page ??, abstract types with different names are incompatible. This can be problematic when we wish to share an abstract type between several modules. There are two ways to achieve this sharing: one is via a special sharing construct in the module language; the other one uses the lexical scoping of modules.

Sharing via Constraints

The following example illustrates the sharing issue. We define a module M providing an abstract type M.t. We then restrict M on two different signatures exporting different subsets of operations.

# module M =
(
struct
type t = int ref
let create() = ref 0
let add x = incr x
let get x = if !x>0 then (decr x; 1) else failwith "Empty"
end
:
sig
type t
val create : unit -> t
val add : t -> unit
val get : t -> int
end
) ;;

# module type S1 =
sig
type t
val create : unit -> t
val add : t -> unit
end ;;

# module type S2 =
sig
type t
val get : t -> int
end ;;
# module M1 = (M:S1) ;;
module M1 : S1
# module M2 = (M:S2) ;;
module M2 : S2


As written above, the types M1.t and M2.t are incompatible. However, we would like to say that both types are abstract but identical. To do this, Objective CAML offers special syntax to declare a type equality over an abstract type in a signature.

Syntax


NAME with type t1 = t2 and ...
This type constraint forces the type t1 declared in the signature NAME to be equal to the type t2.

Type constraints over all types exported by a sub-module can be declared in one operation with the syntax

Syntax


NAME with module Name1 = Name2


Using these type sharing constraints, we can declare that the two modules M1 and M2 define identical abstract types.

# module M1 = (M:S1 with type t = M.t) ;;
module M1 : sig type t = M.t val create : unit -> t val add : t -> unit end
# module M2 = (M:S2 with type t = M.t) ;;
module M2 : sig type t = M.t val get : t -> int end
# let x = M1.create() in M1.add x ; M2.get x ;;
- : int = 1


Sharing and Nested Modules

Another possibility for ensuring type sharing is to use nested modules. We define two sub-modules (M1 et M2) sharing an abstract type defined in the enclosing module M.

# module M =
( struct
type t = int ref
module M_hide =
struct
let create() = ref 0
let add x = incr x
let get x = if !x>0 then (decr x; 1) else failwith "Empty"
end
module M1 = M_hide
module M2 = M_hide
end
:
sig
type t
module M1 : sig val create : unit -> t val add : t -> unit end
module M2 : sig val get : t -> int end
end ) ;;
module M :
sig
type t
module M1 : sig val create : unit -> t val add : t -> unit end
module M2 : sig val get : t -> int end
end


As desired, values created by M1 can be operated upon by M2, while hiding the representation of these values.

# let x = M.M1.create() ;;
val x : M.t = <abstr>
# M.M1.add x ; M.M2.get x ;;
- : int = 1
This solution is heavier than the previous solution based on type sharing constraints: the functions from M1 and M2 can only be accessed via the enclosing module M.

Extending Simple Modules

Modules are closed entities, defined once and for all. In particular, once an abstract type is defined using the module language, it is impossible to add further operations on the abstract type that depend on the type representation without modifying the module definition itself. (Operations derived from existing operations can of course be added later, outside the module.) As an extreme example, if the module exports no creation function, clients of the module will never be able to create values of the abstract type!

Therefore, adding new operations that depend on the type representation requires editing the sources of the module and adding the desired operations in its signature and structure. Of course, we then get a different module, and clients need to be recompiled. However, if the modifications performed on the module signature did not affect the components of the original signature, the remainder of the program remains correct and does not need to be modified, just recompiled.


Previous Contents Next ocaml-book-1.0/en/html/book-ora135.html0000644000000000000000000000373207453055400014470 0ustar Summary Previous Contents Next

Summary

In this chapter, we introduced all the facilities that the Objective CAML module language offers, in particular parameterized modules.

As all module systems, it reflects the duality between interfaces and implementations, here presented as a duality between signatures and structures. Signatures allow hiding information about type, value or exception definitions.

By hiding type representation, we can make certain types abstract, ensuring that values of these types can only be manipulated through the operations provided in the module signature. We saw how to exploit this mechanism to facilitate sharing of values hidden in closures, and to offer multiple views of a given implementation. In the latter case, explicit type sharing annotations are sometimes necessary to achieve the desired behavior.

Parameterized modules, also called functors, go one step beyond and support code reuse through simple mechanisms similar to function abstraction and function application.


Previous Contents Next ocaml-book-1.0/en/html/book-ora197.html0000644000000000000000000000202007453055401014466 0ustar Notes
1
This header is generally displayed in the title bar of the browser window.
2
...taking care to update the URL according to your machine
3
Nothing prevents one from using GET for this, but that does not correspond to the standard.
ocaml-book-1.0/en/html/book-ora137.html0000644000000000000000000000132007453055401014462 0ustar Notes
1
Both files name.ml and Name.ml result in the same module name.
ocaml-book-1.0/en/html/book-ora160.html0000644000000000000000000103411707453055400014470 0ustar Two Player Games Previous Contents Next

Two Player Games

The application presented in this section pursues two objectives. On the one hand, it seeks to resolve problems related to the complexity in searching state spaces, as well as showing that Objective CAML provides useful tools for dealing with symbolic applications. On the other hand, it also explores the benefits of using parametric modules to define a generic scheme for constructing two player games, providing the ability to factor out one part of the search, and making it easy to customize components such as functions for evaluating or displaying a game position.

We first present the problem of games involving two players, then describe the minimax-ab algorithm which provides an efficient search of the tree of possible moves. We present a parametric model for two player games. Then, we apply these functors to implement two games: ``Connect Four'' (a vertical tic-tac-toe), and Stonehenge (a game that involves constructing ley-lines).

The Problem of Two Player Games

Games involving two players represent one of the classic applications of symbolic programming and provide a good example of problem solving for at least two reasons:

  • The large number of solutions to be analyzed to obtain the best possible move necessitates using methods other than brute force.

    For instance, in the game of chess, the number of possible moves typically is around 30, and a game often involves around 40 moves per player. This would require a search tree of around 3080 positions just to explore the complete tree for one player.

  • The quality of a solution is easily verifiable. In particular, it is possible to test the quality of a proposed solution from one program by comparing it to that of another.
First, assume that we are able to explore the total list of all possible moves, given, as a starting point, a specific legal game position. Such a program will require a function to generate legal moves based on a starting position, as well as a function to evaluate some ``score'' for each resulting position. The evaluation function must give a maximum score to a winning position, and a minimal score to a losing position. After picking an initial position, one may then construct a tree of all possible variations, where each node corresponds to a position, the adjacent siblings are obtained by having played a move and with leaves having positions indicating winning, losing, or null results. Once the tree is constructed, its exploration permits determining if there exists a route leading to victory, or a null position, failing that. The shortest path may then be chosen to attain the desired goal.

As the overall size of such a tree is generally too large for it to be fully represented, it is typically necessary to limit what portions of the tree are constructed. A first strategy is to limit the ``depth'' of the search, that is, the number of moves and responses that are to be evaluated. One thus reduces the breadth of the tree as well as its height. In such cases, leaf nodes will seldom be found until nearly the end of the game.

On the other hand, we may try to limit the number of moves selected for additional evaluation. For this, we try to avoid evaluating any but the most favorable moves, and start by examining the moves that appear to be the very best. This immediately eliminates entire branches of the tree. This leads to the minimax a b algorithm presented in the next subsection.

Minimax ab

We present the minimax search and describe a variant optimized using a b cuts. The implementation of this algorithm uses a parametric module, FAlphabeta along with a representation of the game and its evaluation function. We distinguish between the two players by naming them A and B.

Minimax

The minimax algorithm is a depth-first search algorithm with a limit on the depth to which search is done. It requires:

  • a function to generate legal moves based on a position, and
  • a function to evaluate a game position.
Starting with some initial game position, the algorithm explores the tree of all legal moves down to the requested depth. Scores associated with leaves of the tree are calculated using an evaluation function. A positive score indicates a good position for player A, while a negative score indicates a poor position for player A, and thus a good position for player B. For each player, the transition from one position to another is either maximized (for player A) or minimized (for player B). Each player tries to select his moves in a manner that will be most profitable for him. In searching for the best play for player A, a search of depth 1 tries to determine the immediate move that maximizes the score of the new position.



Figure 17.1: Maximizing search at a given location.


In figure 17.1, player A starts at position O, finds four legal moves, constructs these new configurations, and evaluates them. Based on these scores, the best position is P2, with a score of 8. This value is propagated to position O, indicating that this position provides a move to a new position, giving a score of 8 when the player moves to C2. The search of depth 1 is, as a general rule, insufficient, as it does not consider the possible response of an adversary. Such a shallow search results in programs that search greedily for immediate material gains (such as the prize of a queen, in chess) without perceiving that the pieces are protected or that the position is otherwise a losing one (such as a gambit of trading one's queen for a mate). A deeper exploration to depth 2 permits perceiving at least the simplest such countermoves.

Figure 17.2 displays a supplementary analysis of the tree that takes into consideration the possible responses of player B. This search considers B's best moves. For this, the minimax algorithm minimizes scores of depth 2.



Figure 17.2: Maximizing and minimizing in depth-2 search.


Move P2, which provided an immediate position score of 8, leads to a position with a score of -3. In effect, if B plays D5, then the score of Q5 will be -3. Based on this deeper examination, the move C1 limits the losses with a score of -1, and is thus the preferred move.

In most games, it is possible to try to confuse the adversary, making him play forced moves, trying to muddle the situation in the hope that he will make a mistake. A shallow search of depth 2 would be completely inadequate for this sort of tactic. These sorts of strategies are rarely able to be well exploited by a program because it has no particular vision as to the likely evolution of the positions towards the end of the game.

The difficulty of increased depth of search comes in the form of a combinatorial ``explosion.'' For example, with chess, the exploration of two additional levels adds a factor of around a thousand times more combinations (30 30). Thus, if one searches to a depth of 10, one obtains around 514 positions, which represents too much to search. For this reason, you must try to somehow trim the search tree.

One may note in figure 17.2 that it may be useless to search the branch P3 insofar as the score of this position at depth 1 is poorer than that found in branch P1. In addition the branch P4 does not need to be completely explored. Based on the calculation of Q7, one obtains a score inferior to that of P1, which has already been completely explored. The calculations for Q8 and Q9 cannot improve this situation even if their scores are better than Q7. In a minimizing mode, the poorest score is dropped. The player knows then that these branches provide no useful new options. The minimax variant a b uses this approach to decrease the number of branches that must be explored.

Minimax-ab

We call the a cut the lower limit of a maximizing node, and cut b the upper limit of a minimizing node. Figure 17.3 shows the cuts carried out in branches P3 and P4 based on knowing the lower limit -1 of P1.



Figure 17.3: Limit a to one level max-min.


As soon as the tree gets broader or deeper the number of cuts increases, thus indicating large subtrees.

A Parametric Module for a b Minimax

We want to produce a parametric module, FAlphabeta, implementing this algorithm, which will be generically reusable for all sorts of two player games. The parameters correspond, on the one hand, to all the information about the proceedings of moves in the game, and on the other hand, to the evaluation function.

Interfaces.
We declare two signatures: REPRESENTATION to represent plays; and EVAL to evaluate a position.

# module type REPRESENTATION = 
sig
type game
type move
val game_start : unit -> game
val legal_moves: bool -> game -> move list
val play: bool -> move -> game -> game
end ;;
module type REPRESENTATION =
sig
type game
and move
val game_start : unit -> game
val legal_moves : bool -> game -> move list
val play : bool -> move -> game -> game
end

# module type EVAL =
sig
type game
val evaluate: bool -> game -> int
val moreI : int
val lessI: int
val is_leaf: bool -> game -> bool
val is_stable: bool -> game -> bool
type state = G | P | N | C
val state_of : bool -> game -> state
end ;;
module type EVAL =
sig
type game
val evaluate : bool -> game -> int
val moreI : int
val lessI : int
val is_leaf : bool -> game -> bool
val is_stable : bool -> game -> bool
type state = | G | P | N | C
val state_of : bool -> game -> state
end


Types game and move represent abstract types. A player is represented by a boolean value. The function legal_moves takes a player and position, and returns the list of possible moves. The function play takes a player, a move, and a position, and returns a new position. The values moreI and lessI are the limits of the values returned by function evaluate. The predicate is_leaf verifies if a player in a given position can play. The predicate is_stable indicates whether the position for the player represents a stable position. The results of these functions influence the pursuit of the exploration of moves when one attains the specified depth.

The signature ALPHABETA corresponds to the signature resulting from the complete application of the parametric module that one wishes to use. These hide the different auxiliary functions that we use to implement the algorithm.

# module type ALPHABETA = sig 
type game
type move
val alphabeta : int -> bool -> game -> move
end ;;
module type ALPHABETA =
sig type game and move val alphabeta : int -> bool -> game -> move end


The function alphabeta takes as parameters the depth of the search, the player, and the game position, returning the next move.

We then define the functional signature FALPHABETA which must correspond to that of the implementation of the functor.

# module type FALPHABETA = functor (Rep : REPRESENTATION) 
-> functor (Eval : EVAL with type game = Rep.game)
-> ALPHABETA with type game = Rep.game
and type move = Rep.move ;;
module type FALPHABETA =
functor(Rep : REPRESENTATION) ->
functor
(Eval : sig
type game = Rep.game
val evaluate : bool -> game -> int
val moreI : int
val lessI : int
val is_leaf : bool -> game -> bool
val is_stable : bool -> game -> bool
type state = | G | P | N | C
val state_of : bool -> game -> state
end) ->
sig
type game = Rep.game
and move = Rep.move
val alphabeta : int -> bool -> game -> move
end


Implementation.
The parametric module FAlphabetaO makes explicit the partition of the type game between the two parameters Rep and Eval. This module has six functions and two exceptions. The player true searches to maximize the score while the player false seeks to minimize the score. The function maxmin_iter calculates the maximum of the best score for the branches based on a move of player true and the pruning parameter a.

The function maxmin takes four parameters: depth, which indicates the actual calculation depth, node, a game position, and a and b, the pruning parameters. If the node is a leaf of the tree or if the maximum depth is reached, the function will return its evaluation of the position. If this is not the case, the function applies maxmin_iter to all of the legal moves of player true, passing it the search function, diminishing the depth remaining (minmax). The latter searches to minimize the score resulting from the response of player false.

The movements are implemented using exceptions. If the move b is found in the iteration across the legal moves from the function maxmin, then it is returned immediately, the value being propagated using an exception. The functions minmax_iter and minmax provide the equivalents for the other player. The function search determines the move to play based on the best score found in the lists of scores and moves.

The principal function alphabeta of this module calculates the legal moves from a given position for the requested player, searches down to the requested depth, and returns the best move.

# module FAlphabetaO 
(Rep : REPRESENTATION) (Eval : EVAL with type game = Rep.game) =
struct
type game = Rep.game
type move = Rep.move
exception AlphaMovement of int
exception BetaMovement of int

let maxmin_iter node minmax_cur beta alpha cp =
let alpha_resu =
max alpha (minmax_cur (Rep.play true cp node) beta alpha)
in if alpha_resu >= beta then raise (BetaMovement alpha_resu)
else alpha_resu

let minmax_iter node maxmin_cur alpha beta cp =
let beta_resu =
min beta (maxmin_cur (Rep.play false cp node) alpha beta)
in if beta_resu <= alpha then raise (AlphaMovement beta_resu)
else beta_resu

let rec maxmin depth node alpha beta =
if (depth < 1 & Eval.is_stable true node)
or Eval.is_leaf true node
then Eval.evaluate true node
else
try let prev = maxmin_iter node (minmax (depth - 1)) beta
in List.fold_left prev alpha (Rep.legal_moves true node)
with BetaMovement a -> a

and minmax depth node beta alpha =
if (depth < 1 & Eval.is_stable false node)
or Eval.is_leaf false node
then Eval.evaluate false node
else
try let prev = minmax_iter node (maxmin (depth - 1)) alpha
in List.fold_left prev beta (Rep.legal_moves false node)
with AlphaMovement b -> b

let rec search a l1 l2 = match (l1,l2) with
(h1::q1, h2::q2) -> if a = h1 then h2 else search a q1 q2
| ([], []) -> failwith ("AB: "^(string_of_int a)^" not found")
| (_ , _) -> failwith "AB: length differs"

(* val alphabeta : int -> bool -> Rep.game -> Rep.move *)
let alphabeta depth player level =
let alpha = ref Eval.lessI and beta = ref Eval.moreI in
let l = ref [] in
let cpl = Rep.legal_moves player level in
let eval =
try
for i = 0 to (List.length cpl) - 1 do
if player then
let b = Rep.play player (List.nth cpl i) level in
let a = minmax (depth-1) b !beta !alpha
in l := a :: !l ;
alpha := max !alpha a ;
(if !alpha >= !beta then raise (BetaMovement !alpha))
else
let a = Rep.play player (List.nth cpl i) level in
let b = maxmin (depth-1) a !alpha !beta
in l := b :: !l ;
beta := min !beta b ;
(if !beta <= !alpha then raise (AlphaMovement !beta))
done ;
if player then !alpha else !beta
with
BetaMovement a -> a
| AlphaMovement b -> b
in
l := List.rev !l ;
search eval !l cpl
end ;;
module FAlphabetaO :
functor(Rep : REPRESENTATION) ->
functor
(Eval : sig
type game = Rep.game
val evaluate : bool -> game -> int
val moreI : int
val lessI : int
val is_leaf : bool -> game -> bool
val is_stable : bool -> game -> bool
type state = | G | P | N | C
val state_of : bool -> game -> state
end) ->
sig
type game = Rep.game
and move = Rep.move
exception AlphaMovement of int
exception BetaMovement of int
val maxmin_iter :
Rep.game ->
(Rep.game -> int -> int -> int) -> int -> int -> Rep.move -> int
val minmax_iter :
Rep.game ->
(Rep.game -> int -> int -> int) -> int -> int -> Rep.move -> int
val maxmin : int -> Eval.game -> int -> int -> int
val minmax : int -> Eval.game -> int -> int -> int
val search : int -> int list -> 'a list -> 'a
val alphabeta : int -> bool -> Rep.game -> Rep.move
end


We may close module FAlphabetaO by associating with it the following signature:


# module FAlphabeta = (FAlphabetaO : FALPHABETA) ;;
module FAlphabeta : FALPHABETA


This latter module may be used with many different game representations and functions to play different games.

Organization of a Game Program

The organization of a program for a two player game may be separated into a portion specific to the game in question as well as a portion applicable to all sorts of games. For this, we propose using several parametric modules parameterized by specific modules, permitting us to avoid the need to rewrite the common portions each time. Figure 17.4 shows the chosen organization.



Figure 17.4: Organization of a game application.


The modules with no highlighting correspond to the common parts of the application. These are the parametric modules. We see again the functor FAlphabeta. The modules with gray highlighting are the modules designed specifically for a given game. The three principal modules are the representation of the game (J_Repr), display of the game (J_Disp), and the evaluation function (J_Eval). The modules with rounded gray borders are obtained by applying the parametric modules to the simple modules specific to the game.

The module FAlphabeta has already been described. The two other common modules are FMain, containing the main loop, and FSkeleton, that manages the players.

Module FMain

Module FMain contains the main loop for execution of a game program. It is parameterized using the signature module SKELETON, describing the interaction with a player using the following definition:

# module type SKELETON = sig 
val home: unit -> unit
val init: unit -> ((unit -> unit) * (unit -> unit))
val again: unit -> bool
val exit: unit -> unit
val won: unit -> unit
val lost: unit -> unit
val nil: unit -> unit
exception Won
exception Lost
exception Nil
end ;;
module type SKELETON =
sig
val home : unit -> unit
val init : unit -> (unit -> unit) * (unit -> unit)
val again : unit -> bool
val exit : unit -> unit
val won : unit -> unit
val lost : unit -> unit
val nil : unit -> unit
exception Won
exception Lost
exception Nil
end



The function init constructs a pair of action functions for each player. The other functions control the interactions. Module FMain contains two functions: play_game which alternates between the players, and main which controls the main loop.

# module FMain (P : SKELETON) = 
struct
let play_game movements = while true do (fst movements) () ;
(snd movements) () done

let main () = let finished = ref false
in P.home ();
while not !finished do
( try play_game (P.init ())
with P.Won -> P.won ()
| P.Lost -> P.lost ()
| P.Nil -> P.nil () );
finished := not (P.again ())
done ;
P.exit ()
end ;;
module FMain :
functor(P : SKELETON) ->
sig
val play_game : (unit -> 'a) * (unit -> 'b) -> unit
val main : unit -> unit
end


Module FSkeleton

Parametric module FSkeleton controls the moves of each player according to the rules provided at the start of the section based on the nature of the players (automated or not) and the order of the players. It needs various parameters to represent the game, game states, the evaluation function, and the a b search as described in figure 17.4.

We start with the signature needed for game display.

# module type DISPLAY = sig 
type game
type move
val home: unit -> unit
val exit: unit -> unit
val won: unit -> unit
val lost: unit -> unit
val nil: unit -> unit
val init: unit -> unit
val position : bool -> move -> game -> game -> unit
val choice : bool -> game -> move
val q_player : unit -> bool
val q_begin : unit -> bool
val q_continue : unit -> bool
end ;;
module type DISPLAY =
sig
type game
and move
val home : unit -> unit
val exit : unit -> unit
val won : unit -> unit
val lost : unit -> unit
val nil : unit -> unit
val init : unit -> unit
val position : bool -> move -> game -> game -> unit
val choice : bool -> game -> move
val q_player : unit -> bool
val q_begin : unit -> bool
val q_continue : unit -> bool
end


It is worth noting that the representation of the game and of the moves must be shared by all the parametric modules, which constrain the types. The two principal functions are playH and playM, respectively controlling the move of a human player (using the function Disp.choice) and that of an automated player. The function init determines the nature of the players and the sorts of responses for Disp.q_player.

# module FSkeleton 
(Rep : REPRESENTATION)
(Disp : DISPLAY with type game = Rep.game and type move = Rep.move)
(Eval : EVAL with type game = Rep.game)
(Alpha : ALPHABETA with type game = Rep.game and type move = Rep.move) =
struct
let depth = ref 4
exception Won
exception Lost
exception Nil
let won = Disp.won
let lost = Disp.lost
let nil = Disp.nil
let again = Disp.q_continue
let play_game = ref (Rep.game_start())
let exit = Disp.exit
let home = Disp.home

let playH player () =
let choice = Disp.choice player !play_game in
let old_game = !play_game
in play_game := Rep.play player choice !play_game ;
Disp.position player choice old_game !play_game ;
match Eval.state_of player !play_game with
Eval.P -> raise Lost
| Eval.G -> raise Won
| Eval.N -> raise Nil
| _ -> ()

let playM player () =
let choice = Alpha.alphabeta !depth player !play_game in
let old_game = !play_game
in play_game := Rep.play player choice !play_game ;
Disp.position player choice old_game !play_game ;
match Eval.state_of player !play_game with
Eval.G -> raise Won
| Eval.P -> raise Lost
| Eval.N -> raise Nil
| _ -> ()

let init () =
let a = Disp.q_player () in
let b = Disp.q_player()
in play_game := Rep.game_start () ;
Disp.init () ;
match (a,b) with
true,true -> playM true, playM false
| true,false -> playM true, playH false
| false,true -> playH true, playM false
| false,false -> playH true, playH false
end ;;
module FSkeleton :
functor(Rep : REPRESENTATION) ->
functor
(Disp : sig
type game = Rep.game
and move = Rep.move
val home : unit -> unit
val exit : unit -> unit
val won : unit -> unit
val lost : unit -> unit
val nil : unit -> unit
val init : unit -> unit
val position : bool -> move -> game -> game -> unit
val choice : bool -> game -> move
val q_player : unit -> bool
val q_begin : unit -> bool
val q_continue : unit -> bool
end) ->
functor
(Eval : sig
type game = Rep.game
val evaluate : bool -> game -> int
val moreI : int
val lessI : int
val is_leaf : bool -> game -> bool
val is_stable : bool -> game -> bool
type state = | G | P | N | C
val state_of : bool -> game -> state
end) ->
functor
(Alpha : sig
type game = Rep.game
and move = Rep.move
val alphabeta : int -> bool -> game -> move
end) ->
sig
val depth : int ref
exception Won
exception Lost
exception Nil
val won : unit -> unit
val lost : unit -> unit
val nil : unit -> unit
val again : unit -> bool
val play_game : Disp.game ref
val exit : unit -> unit
val home : unit -> unit
val playH : bool -> unit -> unit
val playM : bool -> unit -> unit
val init : unit -> (unit -> unit) * (unit -> unit)
end


The independent parts of the game are thus implemented. One may then begin programming different sorts of games. This modular organization facilitates making modifications to the movement scheme or to the evaluation function for a game as we shall soon see.

Connect Four

We will next examine a simple game, a vertical tic-tac-toe, known as Connect Four. The game is represented by seven columns each consisting of six lines. In turn, a player places on a column a piece of his color, where it then falls down to the lowest free location in this column. If a column is completely filled, neither player is permitted to play there. The game ends when one of the players has built a line of four pieces in a row (horizontal, vertical, or diagonal), at which point this player has won, or when all the columns are filled with pieces, in which the outcome is a draw. Figure 17.5 shows a completed game.



Figure 17.5: An example of Connect Four.


Note the ``winning'' line of four gray pieces in a diagonal, going down and to the right.

Game Representation: module C4_rep.
We choose for this game a matrix-based representation. Each element of the matrix is either empty, or contains a player's piece. A move is numbered by the column. The legal moves are the columns in which the final (top) row is not filled.

# module C4_rep = struct
type cell = A | B | Empty
type game = cell array array
type move = int
let col = 7 and row = 6
let game_start () = Array.create_matrix row col Empty

let legal_moves b m =
let l = ref [] in
for c = 0 to col-1 do if m.(row-1).(c) = Empty then l := (c+1) :: !l done;
!l

let augment mat c =
let l = ref row
in while !l > 0 & mat.(!l-1).(c-1) = Empty do decr l done ; !l + 1

let player_gen cp m e =
let mj = Array.map Array.copy m
in mj.((augment mj cp)-1).(cp-1) <- e ; mj

let play b cp m = if b then player_gen cp m A else player_gen cp m B
end ;;
module C4_rep :
sig
type cell = | A | B | Empty
and game = cell array array
and move = int
val col : int
val row : int
val game_start : unit -> cell array array
val legal_moves : 'a -> cell array array -> int list
val augment : cell array array -> int -> int
val player_gen : int -> cell array array -> cell -> cell array array
val play : bool -> int -> cell array array -> cell array array
end


We may easily verify if this module accepts the constraints of the signature REPRESENTATION.


# module C4_rep_T = (C4_rep : REPRESENTATION) ;;
module C4_rep_T : REPRESENTATION


Game Display: Module C4_text.
Module C4_text describes a text-based interface for the game Connect Four that is compatible with the signature DISPLAY. It it is not particularly sophisticated, but, nonetheless, demonstrates how modules are assembled together.

# module C4_text = struct
open C4_rep
type game = C4_rep.game
type move = C4_rep.move

let print_game mat =
for l = row - 1 downto 0 do
for c = 0 to col - 1 do
match mat.(l).(c) with
A -> print_string "X "
| B -> print_string "O "
| Empty -> print_string ". "
done;
print_newline ()
done ;
print_newline ()

let home () = print_string "C4 ...\n"
let exit () = print_string "Bye for now ... \n"
let question s =
print_string s;
print_string " y/n ? " ;
read_line() = "y"
let q_begin () = question "Would you like to begin?"
let q_continue () = question "Play again?"
let q_player () = question "Is there to be a machine player ?"

let won ()= print_string "The first player won" ; print_newline ()
let lost () = print_string "The first player lost" ; print_newline ()
let nil () = print_string "Stalemate" ; print_newline ()

let init () =
print_string "X: 1st player O: 2nd player";
print_newline () ; print_newline () ;
print_game (game_start ()) ; print_newline()

let position b c aj j = print_game j

let is_move = function '1'..'7' -> true | _ -> false

exception Move of int
let rec choice player game =
print_string ("Choose player" ^ (if player then "1" else "2") ^ " : ") ;
let l = legal_moves player game
in try while true do
let i = read_line()
in ( if (String.length i > 0) && (is_move i.[0])
then let c = (int_of_char i.[0]) - (int_of_char '0')
in if List.mem c l then raise (Move c) );
print_string "Invalid move - try again"
done ;
List.hd l
with Move i -> i
| _ -> List.hd l
end ;;
module C4_text :
sig
type game = C4_rep.game
and move = C4_rep.move
val print_game : C4_rep.cell array array -> unit
val home : unit -> unit
val exit : unit -> unit
val question : string -> bool
val q_begin : unit -> bool
val q_continue : unit -> bool
val q_player : unit -> bool
val won : unit -> unit
val lost : unit -> unit
val nil : unit -> unit
val init : unit -> unit
val position : 'a -> 'b -> 'c -> C4_rep.cell array array -> unit
val is_move : char -> bool
exception Move of int
val choice : bool -> C4_rep.cell array array -> int
end


We may immediately verify that this conforms to the constraints of the signature DISPLAY


# module C4_text_T = (C4_text : DISPLAY) ;;
module C4_text_T : DISPLAY


Evaluation Function: module C4_eval.
The quality of a game player depends primarily on the position evaluation function. Module C4_eval defines evaluate, which evaluates the value of a position for the specified player. This function calls eval_bloc for the four compass directions as well as the diagonals. eval_bloc then calls eval_four to calculate the number of pieces in the requested line. Table value provides the value of a block containing 0, 1, 2, or 3 pieces of the same color. The exception Four is raised when 4 pieces are aligned.

# module C4_eval = struct open C4_rep type game = C4_rep.game 
let value =
Array.of_list [0; 2; 10; 50]
exception Four of int
exception Nil_Value
exception Arg_invalid
let lessI = -10000
let moreI = 10000
let eval_four m l_dep c_dep delta_l delta_c =
let n = ref 0 and e = ref Empty
and x = ref c_dep and y = ref l_dep
in try
for i = 1 to 4 do
if !y<0 or !y>=row or !x<0 or !x>=col then raise Arg_invalid ;
( match m.(!y).(!x) with
A -> if !e = B then raise Nil_Value ;
incr n ;
if !n = 4 then raise (Four moreI) ;
e := A
| B -> if !e = A then raise Nil_Value ;
incr n ;
if !n = 4 then raise (Four lessI);
e := B;
| Empty -> () ) ;
x := !x + delta_c ;
y := !y + delta_l
done ;
value.(!n) * (if !e=A then 1 else -1)
with
Nil_Value | Arg_invalid -> 0

let eval_bloc m e cmin cmax lmin lmax dx dy =
for c=cmin to cmax do for l=lmin to lmax do
e := !e + eval_four m l c dx dy
done done

let evaluate b m =
try let evaluation = ref 0
in (* evaluation of rows *)
eval_bloc m evaluation 0 (row-1) 0 (col-4) 0 1 ;
(* evaluation of columns *)
eval_bloc m evaluation 0 (col-1) 0 (row-4) 1 0 ;
(* diagonals coming from the first line (to the right) *)
eval_bloc m evaluation 0 (col-4) 0 (row-4) 1 1 ;
(* diagonals coming from the first line (to the left) *)
eval_bloc m evaluation 1 (row-4) 0 (col-4) 1 1 ;
(* diagonals coming from the last line (to the right) *)
eval_bloc m evaluation 3 (col-1) 0 (row-4) 1 (-1) ;
(* diagonals coming from the last line (to the left) *)
eval_bloc m evaluation 1 (row-4) 3 (col-1) 1 (-1) ;
!evaluation
with Four v -> v

let is_leaf b m = let v = evaluate b m
in v=moreI or v=lessI or legal_moves b m = []

let is_stable b j = true

type state = G | P | N | C

let state_of player m =
let v = evaluate player m
in if v = moreI then if player then G else P
else if v = lessI then if player then P else G
else if legal_moves player m = [] then N else C
end ;;
module C4_eval :
sig
type game = C4_rep.game
val value : int array
exception Four of int
exception Nil_Value
exception Arg_invalid
val lessI : int
val moreI : int
val eval_four :
C4_rep.cell array array -> int -> int -> int -> int -> int
val eval_bloc :
C4_rep.cell array array ->
int ref -> int -> int -> int -> int -> int -> int -> unit
val evaluate : 'a -> C4_rep.cell array array -> int
val is_leaf : 'a -> C4_rep.cell array array -> bool
val is_stable : 'a -> 'b -> bool
type state = | G | P | N | C
val state_of : bool -> C4_rep.cell array array -> state
end


Module C4_eval is compatible with the constraints of signature EVAL.


# module C4_eval_T = (C4_eval : EVAL) ;;
module C4_eval_T : EVAL


To play two evaluation functions against one another, it is necessary to modify evaluate to apply the proper evaluation function for each player.

Assembly of the modules
All the components needed to realize the game of Connect Four are now implemented. We only need assemble them together based on the schema of diagram 17.4. First, we construct C4_skeleton, which is the application of parameter module FSkeleton to modules C4_rep, C4_text, C4_eval and the result of the application of parametric module FAlphaBeta to C4_rep and C4_eval.


# module C4_skeleton =
FSkeleton (C4_rep) (C4_text) (C4_eval) (FAlphabeta (C4_rep) (C4_eval)) ;;
module C4_skeleton :
sig
val depth : int ref
exception Won
exception Lost
exception Nil
val won : unit -> unit
val lost : unit -> unit
val nil : unit -> unit
val again : unit -> bool
val play_game : C4_text.game ref
val exit : unit -> unit
val home : unit -> unit
val playH : bool -> unit -> unit
val playM : bool -> unit -> unit
val init : unit -> (unit -> unit) * (unit -> unit)
end


We then obtain the principal module C4_main by applying parametric module FMain on the result of the preceding application C4_skeleton


# module C4_main = FMain(C4_skeleton) ;;
module C4_main :
sig
val play_game : (unit -> 'a) * (unit -> 'b) -> unit
val main : unit -> unit
end


The game is initiated by the application of function C4_main.main on ().

Testing the Game.
Once the general game skeleton has been written, games may be played in various ways. Two human players may play against each other, with the program merely verifying the validity of the moves; a person may play against a programmed player; or programs may play against each other. While this last mode might not be interesting for the human, it does make it easy to run tests without having to wait for a person's responses. The following game demonstrates this scenario.

# C4_main.main () ;;
C4 ...
Is there to be a machine player ? y/n ? y
Is there to be a machine player ? y/n ? y
X: 1st player   O: 2nd player

. . . . . . . 
. . . . . . . 
. . . . . . . 
. . . . . . . 
. . . . . . . 
. . . . . . . 
Once the initial position is played, player 1 (controlled by the program) calculates its move which is then applied.

. . . . . . . 
. . . . . . . 
. . . . . . . 
. . . . . . . 
. . . . . . . 
. . . . . X . 
Player 2 (always controlled by the program) calculates its response and the game proceeds, until a game-ending move is found. In this example, player 1 wins the game based on the following final position:

. O O O . O . 
. X X X . X . 
X O O X . O . 
X X X O . X . 
X O O X X O . 
X O O O X X O 
Player 1 wins
Play again(y/n) ? n
Good-bye ... 
- : unit = ()
Graphical Interface.
To improve the enjoyment of the game, we define a graphical interface for the program, by defining a new module, C4_graph, compatible with the signature DISPLAY, which opens a graphical window, controlled by mouse clicks. The text of this module may be found in the subdirectory Applications on the CD-ROM (see page ??).

# module C4_graph = struct
open C4_rep
type game = C4_rep.game
type move = C4_rep.move
let r = 20 (* color of piece *)
let ec = 10 (* distance between pieces *)
let dec = 30 (* center of first piece *)
let cote = 2*r + ec (* height of a piece looked at like a checker *)
let htexte = 25 (* where to place text *)
let width = col * cote + ec (* width of the window *)
let height = row * cote + ec + htexte (* height of the window *)
let height_of_game = row * cote + ec (* height of game space *)
let hec = height_of_game + 7 (* line for messages *)
let lec = 3 (* columns for messages *)
let margin = 4 (* margin for buttons *)
let xb1 = width / 2 (* position x of button1 *)
let xb2 = xb1 + 30 (* position x of button2 *)
let yb = hec - margin (* position y of the buttons *)
let wb = 25 (* width of the buttons *)
let hb = 16 (* height of the buttons *)

(* val t2e : int -> int *)
(* Convert a matrix coordinate into a graphical coordinate *)
let t2e i = dec + (i-1)*cote

(* The Colors *)
let cN = Graphics.black (* trace *)
let cA = Graphics.red (* Human player *)
let cB = Graphics.yellow (* Machine player *)
let cF = Graphics.blue (* Game Background color *)
(* val draw_table : unit -> unit : Trace an empty table *)
let draw_table () =
Graphics.clear_graph();
Graphics.set_color cF;
Graphics.fill_rect 0 0 width height_of_game;
Graphics.set_color cN;
Graphics.moveto 0 height_of_game;
Graphics.lineto width height_of_game;
for l = 1 to row do
for c = 1 to col do
Graphics.draw_circle (t2e c) (t2e l) r
done
done

(* val draw_piece : int -> int -> Graphics.color -> unit *)
(* 'draw_piece l c co' draws a piece of color co at coordinates l c *)
let draw_piece l c col =
Graphics.set_color col;
Graphics.fill_circle (t2e c) (t2e l) (r+1)

(* val augment : Rep.item array array -> int -> Rep.move *)
(* 'augment m c' redoes the line or drops the piece for c in m *)
let augment mat c =
let l = ref row in
while !l > 0 & mat.(!l-1).(c-1) = Empty do
decr l
done;
!l

(* val conv : Graphics.status -> int *)
(* convert the region where player has clicked in controlling the game *)
let conv st =
(st.Graphics.mouse_x - 5) / 50 + 1

(* val wait_click : unit -> Graphics.status *)
(* wait for a mouse click *)
let wait_click () = Graphics.wait_next_event [Graphics.Button_down]

(* val choiceH : Rep.game -> Rep.move *)
(* give opportunity to the human player to choose a move *)
(* the function offers possible moves *)
let rec choice player game =
let c = ref 0 in
while not ( List.mem !c (legal_moves player game) ) do
c := conv ( wait_click() )
done;
!c
(* val home : unit -> unit : home screen *)
let home () =
Graphics.open_graph
(" " ^ (string_of_int width) ^ "x" ^ (string_of_int height) ^ "+50+50");
Graphics.moveto (height/2) (width/2);
Graphics.set_color cF;
Graphics.draw_string "C4";
Graphics.set_color cN;
Graphics.moveto 2 2;
Graphics.draw_string "by Romuald COEFFIER & Mathieu DESPIERRE";
ignore (wait_click ());
Graphics.clear_graph()

(* val end : unit -> unit , the end of the game *)
let exit () = Graphics.close_graph()

(* val draw_button : int -> int -> int -> int -> string -> unit *)
(* 'draw_button x y w h s' draws a rectangular button at coordinates *)
(* x,y with width w and height h and appearance s *)
let draw_button x y w h s =
Graphics.set_color cN;
Graphics.moveto x y;
Graphics.lineto x (y+h);
Graphics.lineto (x+w) (y+h);
Graphics.lineto (x+w) y;
Graphics.lineto x y;
Graphics.moveto (x+margin) (hec);
Graphics.draw_string s

(* val draw_message : string -> unit * position message s *)
let draw_message s =
Graphics.set_color cN;
Graphics.moveto lec hec;
Graphics.draw_string s

(* val erase_message : unit -> unit erase the starting position *)
let erase_message () =
Graphics.set_color Graphics.white;
Graphics.fill_rect 0 (height_of_game+1) width htexte

(* val question : string -> bool *)
(* 'question s' poses the question s, the response being obtained by *)
(* selecting one of two buttons, 'yes' (=true) and 'no' (=false) *)
let question s =
let rec attente () =
let e = wait_click () in
if (e.Graphics.mouse_y < (yb+hb)) & (e.Graphics.mouse_y > yb) then
if (e.Graphics.mouse_x > xb1) & (e.Graphics.mouse_x < (xb1+wb)) then
true
else
if (e.Graphics.mouse_x > xb2) & (e.Graphics.mouse_x < (xb2+wb)) then
false
else
attente()
else
attente () in
draw_message s;
draw_button xb1 yb wb hb "yes";
draw_button xb2 yb wb hb "no";
attente()

(* val q_begin : unit -> bool *)
(* Ask, using function 'question', if the player wishes to start *)
(* (yes=true) *)
let q_begin () =
let b = question "Would you like to begin ?" in
erase_message();
b

(* val q_continue : unit -> bool *)
(* Ask, using function 'question', if the player wishes to play again *)
(* (yes=true) *)
let q_continue () =
let b = question "Play again ?" in
erase_message();
b

let q_player () =
let b = question "Is there to be a machine player?" in
erase_message ();
b
(* val won : unit -> unit *)
(* val lost : unit -> unit *)
(* val nil : unit -> unit *)
(* Three functions for these three cases *)
let won () =
draw_message "I won :-)" ; ignore (wait_click ()) ; erase_message()
let lost () =
draw_message "You won :-("; ignore (wait_click ()) ; erase_message()
let nil () =
draw_message "Stalemate" ; ignore (wait_click ()) ; erase_message()

(* val init : unit -> unit *)
(* This is called at every start of the game for the position *)
let init = draw_table

let position b c aj nj =
if b then
draw_piece (augment nj c) c cA
else
draw_piece (augment nj c) c cB

(* val drawH : int -> Rep.item array array -> unit *)
(* Position when the human player chooses move cp in situation j *)
let drawH cp j = draw_piece (augment j cp) cp cA

(* val drawM : int -> cell array array -> unit*)
(* Position when the machine player chooses move cp in situation j *)
let drawM cp j = draw_piece (augment j cp) cp cB
end ;;
module C4_graph :
sig
type game = C4_rep.game
and move = C4_rep.move
val r : int
val ec : int
val dec : int
val cote : int
val htexte : int
val width : int
val height : int
val height_of_game : int
val hec : int
val lec : int
val margin : int
val xb1 : int
val xb2 : int
val yb : int
val wb : int
val hb : int
val t2e : int -> int
val cN : Graphics.color
val cA : Graphics.color
val cB : Graphics.color
val cF : Graphics.color
val draw_table : unit -> unit
val draw_piece : int -> int -> Graphics.color -> unit
val augment : C4_rep.cell array array -> int -> int
val conv : Graphics.status -> int
val wait_click : unit -> Graphics.status
val choice : 'a -> C4_rep.cell array array -> int
val home : unit -> unit
val exit : unit -> unit
val draw_button : int -> int -> int -> int -> string -> unit
val draw_message : string -> unit
val erase_message : unit -> unit
val question : string -> bool
val q_begin : unit -> bool
val q_continue : unit -> bool
val q_player : unit -> bool
val won : unit -> unit
val lost : unit -> unit
val nil : unit -> unit
val init : unit -> unit
val position : bool -> int -> 'a -> C4_rep.cell array array -> unit
val drawH : int -> C4_rep.cell array array -> unit
val drawM : int -> C4_rep.cell array array -> unit
end


We may also create a new skeleton (C4_skeletonG) which results from the application of parametric module FSkeleton.


# module C4_skeletonG =
FSkeleton (C4_rep) (C4_graph) (C4_eval) (FAlphabeta (C4_rep) (C4_eval)) ;;


Only the display parameter differs from the text version application of FSkeleton. We may thereby create a principal module for Connect Four with a graphical user interface.


# module C4_mainG = FMain(C4_skeletonG) ;;
module C4_mainG :
sig
val play_game : (unit -> 'a) * (unit -> 'b) -> unit
val main : unit -> unit
end


The evaluation of the expression C4_mainG.main() opens a graphical window as in figure 17.5 and controls the interaction with the user.

Stonehenge

Stonehenge, created by Reiner Knizia, is a game involving construction of ``ley-lines.'' The rules are simple to understand but our interest in the game lies in its high number of possible moves. The rules may be found at:

Link


http://www.cix.co.uk/~convivium/files/stonehen.htm


The initial game position is represented in figure 17.6.

Game Presentation

The purpose of the game is to win at least 8 ``ley-lines'' (clear lines) out of the 15 available. One gains a line by positioning pieces (or megaliths) on gray positions along a ley-line.



Figure 17.6: Initial position of Stonehenge.


In turn, each player places one of his 9 pieces, numbered from 1 to 6, on one of the 18 gray internal positions. They may not place a piece on a position that is already occupied. Each time a piece is placed, one or several ley-lines may be won or lost.

A ley-line is won by a player if the total of the values of his pieces on the line is greater than the total of the pieces for the other player. There may be empty spaces left if the opponent has no pieces left that would allow winning the line.

For example in figure 17.7, the black player starts by placing the piece of value 3, the red player his ``2'' piece, then the black player plays the ``6'' piece, winning a line.

Red then plays the ``4'' piece, also winning a ley-line. This line has not been completely filled, but red has won because there is no way for black to overcome red's score.

Note that the red player might just as well have played ``3'' rather than ``4,'' and still won the line. In effect, there is only one free case for this ley-line where the strongest black piece has a value of 5, and so black cannot beat red for this particular line.



Figure 17.7: Position after 4 moves.


In the case where the scores are equal across a full line, whoever placed the last piece without having beaten his adversary's score, loses the line. Figure 17.8 demonstrates such a situation.

The last red move is piece ``4''. On the full line where the ``4'' is placed, the scores are equal. Since red was the last player to have placed a piece, but did not beat his adversary, red loses the line, as indicated by a black block.

We may observe that the function play fills the role of arbitrating and accounting for these subtleties in the placement of lines.



Figure 17.8: Position after 6 moves.


There can never be a tie in this game. There are 15 lines, each of which will be accounted for at some point in the game, at which point one of the players will have won at least 8 lines.

Search Complexity

Before completely implementing a new game, it is important to estimate the number of legal moves between two moves in a game, as well as the number of possible moves for each side. These values may be used to estimate a reasonable maximum depth for the minimax-ab algorithm.

In the game Stonehenge, the number of moves for each side is initially based on the number of pieces for the two players, that is, 18. The number of possible moves diminishes as the game progresses. At the first move, the player has 6 different pieces and 18 positions free. At the second move, the second player has 6 different pieces, and 17 positions in which they may be placed (102 legal moves). Moving from a depth of 2 to 4 for the initial moves of the game results in the number of choices going from about 104 to about 108.

On the other hand, near the end of the game, in the final 8 moves, the complexity is greatly reduced. If we take a pessimistic calculation (where all pieces are different), we obtain about 23 million possibilities:

4*8 * 4*7 * 3*6 * 3*5 * 2*4 * 2*3 * 1*2 * 1*1 = 23224320

It might seem appropriate to calculate with a depth of around 2 for the initial set of moves. This may depend on the evaluation function, and on its ability to evaluate the positions at the start of the game, when there are few pieces in place. On the other hand, near the end of the game, the depth may readily be increased to around 4 or 6, but this would probably be too late a point to recover from a weak position.

Implementation

We jump straight into describing the game representation and arbitration so that we may concentrate on the evaluation function.

The implementation of this game follows the architecture used for Connect Four, described in figure 17.4. The two principal difficulties will be to follow the game rules for the placement of pieces, and the evaluation function, which must be able to evaluate positions as quickly as possible while remaining useful.

Game Representation.
There are four notable data structures in this game:
  • the pieces of the players (type piece),
  • the positions (type placement),
  • the 15 ley-lines,
  • the 18 locations where pieces may be placed.
We provide a unique number for each location:
             1---2
            / \ / \
           3---4---5
          / \ / \ / \
         6---7---8---9
        / \ / \ / \ / \
       10--11--12--13--14
        \ / \ / \ / \ /
         15--16--17--18 
Each location participates in 3 ley-lines. We also number each ley-line. This description may be found in the declaration of the list lines, which is converted to a vector (vector_l). A location is either empty, or contains a piece that has been placed, and the piece's possessor. We also store, for each location, the number of the lines that pass through it. This table is calculated by lines_per_case and is named num_line_per_case.

The game is represented by the vector of 18 cases, the vector of 15 ley-lines either won or not, and the lists of pieces left for the two players. The function game_start creates these four elements.

The calculation of a player's legal moves resolves into a Cartesian product of the pieces available against the free positions. Various utility functions allow counting the score of a player on a line, calculating the number of empty locations on a line, and verifying if a line has already been won. We only need to implement play which plays a move and decides which pieces to place. We write this function at the end of the listing of module Stone_rep.

# module Stone_rep = struct
type player = bool
type piece = P of int
let int_of_piece = function P x -> x
type placement = None | M of player
type case = Empty | Occup of player*piece
let value_on_case = function
Empty -> 0
| Occup (j, x) -> int_of_piece x

type game = J of case array * placement array * piece list * piece list
type move = int * piece

let lines = [
[0;1]; [2;3;4]; [5; 6; 7; 8;]; [9; 10; 11; 12; 13]; [14; 15; 16; 17];
[0; 2; 5; 9]; [1; 3; 6; 10; 14]; [4; 7; 11; 15]; [8; 12; 16]; [13; 17];
[9; 14]; [5; 10; 15]; [2; 6; 11; 16]; [0; 3; 7; 12; 17]; [1; 4; 8; 13] ]

let vector_l = Array.of_list lines

let lines_per_case v =
let t = Array.length v in
let r = Array.create 18 [||] in
for i = 0 to 17 do
let w = Array.create 3 0
and p = ref 0 in
for j=0 to t-1 do if List.mem i v.(j) then (w.(!p) <- j; incr p)
done;
r.(i) <- w
done;
r

let num_line_per_case = lines_per_case vector_l
let rec lines_of_i i l = List.filter (fun t -> List.mem i t) l

let lines_of_cases l =
let a = Array.create 18 l in
for i=0 to 17 do
a.(i) <- (lines_of_i i l)
done; a

let ldc = lines_of_cases lines

let game_start ()= let lp = [6; 5; 4; 3; 3; 2; 2; 1; 1] in
J ( Array.create 18 Empty, Array.create 15 None,
List.map (fun x -> P x) lp, List.map (fun x -> P x) lp )

let rec unicity l = match l with
[] -> []
| h::t -> if List.mem h t then unicity t else h:: (unicity t)

let legal_moves player (J (ca, m, r1, r2)) =
let r = if player then r1 else r2 in
if r = [] then []
else
let l = ref [] in
for i = 0 to 17 do
if value_on_case ca.(i) = 0 then l:= i:: !l
done;
let l2 = List.map (fun x->
List.map (fun y-> x,y) (List.rev(unicity r)) ) !l in
List.flatten l2
let copy_board p = Array.copy p

let carn_copy m = Array.copy m
let rec play_piece stone l = match l with
[] -> []
| x::q -> if x=stone then q
else x::(play_piece stone q)

let count_case player case = match case with
Empty -> 0
| Occup (j,p) -> if j = player then (int_of_piece p) else 0

let count_line player line pos =
List.fold_left (fun x y -> x + count_case player pos.(y)) 0 line

let rec count_max n = function
[] -> 0
| t::q ->
if (n>0) then
(int_of_piece t) + count_max (n-1) q
else
0

let rec nbr_cases_free ca l = match l with
[] -> 0
| t::q -> let c = ca.(t) in
match c with
Empty -> 1 + nbr_cases_free ca q
| _ -> nbr_cases_free ca q

let a_placement i ma =
match ma.(i) with
None -> false
| _ -> true

let which_placement i ma =
match ma.(i) with
None -> failwith "which_placement"
| M j -> j

let is_filled l ca = nbr_cases_free ca l = 0

(* function play : arbitrates the game *)
let play player move game =
let (c, i) = move in
let J (p, m, r1, r2) = game in
let nr1,nr2 = if player then play_piece i r1,r2
else r1, play_piece i r2 in
let np = copy_board p in
let nm = carn_copy m in
np.(c)<-Occup(player,i); (* on play le move *)
let lines_of_the_case = num_line_per_case.(c) in

(* calculation of the placements of the three lines *)
for k=0 to 2 do
let l = lines_of_the_case.(k) in
if not (a_placement l nm) then (
if is_filled vector_l.(l) np then (
let c1 = count_line player vector_l.(l) np
and c2 = count_line (not player) vector_l.(l) np in
if (c1 > c2) then nm.(l) <- M player
else ( if c2 > c1 then nm.(l) <- M (not player)
else nm.(l) <- M (not player ))))
done;

(* calculation of other placements *)
for k=0 to 14 do
if not (a_placement k nm ) then
if is_filled vector_l.(k) np then failwith "player"
else
let c1 = count_line player vector_l.(k) np
and c2 = count_line (not player) vector_l.(k) np in
let cases_free = nbr_cases_free np vector_l.(k) in
let max1 = count_max cases_free
(if player then nr1 else nr2)
and max2 = count_max cases_free
(if player then nr2 else nr1) in
if c1 >= c2 + max2 then nm.(k) <- M player
else if c2 >= c1 + max1 then nm.(k) <- M (not player)
done;
J(np,nm,nr1,nr2)
end ;;
module Stone_rep :
sig
type player = bool
and piece = | P of int
val int_of_piece : piece -> int
type placement = | None | M of player
and case = | Empty | Occup of player * piece
val value_on_case : case -> int
type game = | J of case array * placement array * piece list * piece list
and move = int * piece
val lines : int list list
val vector_l : int list array
val lines_per_case : int list array -> int array array
val num_line_per_case : int array array
val lines_of_i : 'a -> 'a list list -> 'a list list
val lines_of_cases : int list list -> int list list array
val ldc : int list list array
val game_start : unit -> game
val unicity : 'a list -> 'a list
val legal_moves : bool -> game -> (int * piece) list
val copy_board : 'a array -> 'a array
val carn_copy : 'a array -> 'a array
val play_piece : 'a -> 'a list -> 'a list
val count_case : player -> case -> int
val count_line : player -> int list -> case array -> int
val count_max : int -> piece list -> int
val nbr_cases_free : case array -> int list -> int
val a_placement : int -> placement array -> bool
val which_placement : int -> placement array -> player
val is_filled : int list -> case array -> bool
val play : player -> int * piece -> game -> game
end


The function play decomposes into three stages:
  1. Copying the game position and placing a move onto this position;

  2. Determination of the placement of a piece on one of the three lines of the case played;

  3. Treatment of the other ley-lines.
The second stage verifies that, of the three lines passing through the position of the move, none has already been won, and then checks if they are able to be won. In the latter case, it counts scores for each player and determines which strictly has the greatest score, and attributes the line to the appropriate player. In case of equality, the line goes to the most recent player's adversary. In effect, there are no lines with just one case. A filled line has at least two pieces. Thus if the player which just played has just matched the score of his adversary, he cannot expect to win the line which then goes to his adversary. If the line is not filled, it will be analyzed by ``stage 3.''

The third stage verifies for each line not yet attributed that it is not filled, and then checks if a player cannot be beaten by his opponent. In this case, the line is immediately given to the opponent. To perform this test, it is necessary to calculate the maximum total potential score of a player on the line (that is, by using his best pieces). If the line is still under dispute, nothing more is done.

Evaluation.
The evaluation function must remain simple due to the large number of cases to deal with near the beginning of the game. The idea is not to excessively simplify the game by immediately playing the strongest pieces which would then leave the remainder of the game open for the adversary to play his strong pieces.

We will use two criteria: the number of lines won and an estimate of the potential of future moves by calculating the value of the remaining pieces. We may use the following formula for player 1:

score   =   50 * (c1 - c2) + 10 * (pr1 - pr2)

where ci is the number of lines won, and pri is the sum of the pieces remaining for player i.

The formula returns a positive result if the differences between won lines (c1 - c2) and the potentials (pr1 - pr2) turn to the advantage of player 1. We may see thus that a placement of piece 6 is not appropriate unless it provides a win of at least 2 lines. The gain of one line provides 50, while using the ``6'' piece costs 10 6 points, so we would thus prefer to play ``1'' which results in the same score, namely a loss of 10 points.

# module Stone_eval = struct
open Stone_rep
type game = Stone_rep.game

exception Done of bool
let moreI = 1000 and lessI = -1000

let nbr_lines_won (J(ca, m,r1,r2)) =
let c1,c2 = ref 0,ref 0 in
for i=0 to 14 do
if a_placement i m then if which_placement i m then incr c1 else incr c2
done;
!c1,!c2

let rec nbr_points_remaining lig = match lig with
[] -> 0
| t::q -> (int_of_piece t) + nbr_points_remaining q

let evaluate player game =
let (J (ca,ma,r1,r2)) = game in
let c1,c2 = nbr_lines_won game in
let pr1,pr2 = nbr_points_remaining r1, nbr_points_remaining r2 in
match player with
true -> if c1 > 7 then moreI else 50 * (c1 - c2) + 10 * (pr1 - pr2)
| false -> if c2 > 7 then lessI else 50 * (c1 - c2) + 10 * (pr1 - pr2)

let is_leaf player game =
let v = evaluate player game in
v = moreI or v = lessI or legal_moves player game = []

let is_stable player game = true

type state = G | P | N | C
let state_of player m =
let v = evaluate player m in
if v = moreI then if player then G else P
else
if v = lessI
then if player then P else G
else
if legal_moves player m = [] then N else C
end;;
module Stone_eval :
sig
type game = Stone_rep.game
exception Done of bool
val moreI : int
val lessI : int
val nbr_lines_won : Stone_rep.game -> int * int
val nbr_points_remaining : Stone_rep.piece list -> int
val evaluate : bool -> Stone_rep.game -> int
val is_leaf : bool -> Stone_rep.game -> bool
val is_stable : 'a -> 'b -> bool
type state = | G | P | N | C
val state_of : bool -> Stone_rep.game -> state
end

# module Stone_graph = struct
open Stone_rep
type piece = Stone_rep.piece
type placement = Stone_rep.placement
type case = Stone_rep.case
type game = Stone_rep.game
type move = Stone_rep.move

(* brightness for a piece *)
let brightness = 20

(* the colors *)
let cBlack = Graphics.black
let cRed = Graphics.rgb 165 43 24
let cYellow = Graphics.yellow
let cGreen = Graphics.rgb 31 155 33 (*Graphics.green*)
let cWhite = Graphics.white
let cGray = Graphics.rgb 128 128 128
let cBlue = Graphics.rgb 196 139 25 (*Graphics.blue*)

(* width and height *)
let width = 600
let height = 500
(* the border at the top of the screen from which drawing begins *)
let top_offset = 30

(* height of foundaries *)
let bounds = 5

(* the size of the border on the left side of the virtual table *)
let virtual_table_xoffset = 145

(* left shift for the black pieces *)
let choice_black_offset = 40

(* left shift for the red pieces *)
let choice_red_offset = 560

(* height of a case for the virtual table *)
let virtual_case_size = 60

(* corresp : int*int -> int*int *)
(* establishes a correspondence between a location in the matrix *)
(* and a position on the virtual table servant for drawing *)
let corresp cp =
match cp with
0 -> (4,1)
| 1 -> (6,1)
| 2 -> (3,2)
| 3 -> (5,2)
| 4 -> (7,2)
| 5 -> (2,3)
| 6 -> (4,3)
| 7 -> (6,3)
| 8 -> (8,3)
| 9 -> (1,4)
| 10 -> (3,4)
| 11 -> (5,4)
| 12 -> (7,4)
| 13 -> (9,4)
| 14 -> (2,5)
| 15 -> (4,5)
| 16 -> (6,5)
| 17 -> (8,5)
| _ -> (0,0)

let corresp2 ((x,y) as cp) =
match cp with
(0,0) -> 0
| (0,1) -> 1
| (1,0) -> 2
| (1,1) -> 3
| (1,2) -> 4
| (2,0) -> 5
| (2,1) -> 6
| (2,2) -> 7
| (2,3) -> 8
| (3,0) -> 9
| (3,1) -> 10
| (3,2) -> 11
| (3,3) -> 12
| (3,4) -> 13
| (4,0) -> 14
| (4,1) -> 15
| (4,2) -> 16
| (4,3) -> 17
| (x,y) -> print_string "Err ";
print_int x;print_string " ";
print_int y; print_newline() ; 0

let col = 5
let lig = 5

(* draw_background : unit -> unit *)
(* draw the screen background *)
let draw_background () =
Graphics.clear_graph() ;
Graphics.set_color cBlue ;
Graphics.fill_rect bounds bounds width (height-top_offset)

(* draw_places : unit -> unit *)
(* draw the pieces at the start of the game *)
let draw_places () =
for l = 0 to 17 do
let cp = corresp l in
if cp <> (0,0) then
begin
Graphics.set_color cBlack ;
Graphics.draw_circle
((fst cp)*30 + virtual_table_xoffset)
(height - ((snd cp)*55 + 25)-50) (brightness+1) ;
Graphics.set_color cGray ;
Graphics.fill_circle
((fst cp)*30 + virtual_table_xoffset)
(height - ((snd cp)*55 + 25)-50) brightness
end

done

(* draw_force_lines : unit -> unit *)
(* draws ley-lines *)
let draw_force_lines () =
Graphics.set_color cYellow ;
let lst = [((2,1),(6,1)); ((1,2),(7,2)); ((0,3),(8,3));
((-1,4),(9,4)); ((0,5),(8,5)); ((5,0),(1,4));
((7,0),(2,5)); ((8,1),(4,5)); ((9,2),(6,5));
((10,3),(8, 5)); ((3,6),(1,4)); ((5,6),(2,3));
((7,6),(3,2)); ((9,6),(4,1)); ((10,5),(6,1))] in
let rec lines l =
match l with
[] -> ()
| h::t -> let deb = fst h and complete = snd h in
Graphics.moveto
((fst deb) * 30 + virtual_table_xoffset)
(height - ((snd deb) * 55 + 25) -50) ;
Graphics.lineto
((fst complete) * 30 + virtual_table_xoffset)
(height - ((snd complete) * 55 + 25) -50) ;
lines t
in lines lst

(* draw_final_places : unit -> unit *)
(* draws final cases for each ley-line *)
(* coordinates represent in the virtual array
used for positioning *)

let draw_final_places () =
let lst = [(2,1); (1,2); (0,3); (-1,4); (0,5); (3,6); (5,6);
(7,6); (9,6); (10,5); (10,3); (9,2); (8,1); (7,0);
(5,0)] in
let rec final l =
match l with
[] -> ()
| h::t -> Graphics.set_color cBlack ;
Graphics.draw_circle
((fst h)*30 + virtual_table_xoffset)
(height - ((snd h)*55 + 25)-50) (brightness+1) ;
Graphics.set_color cGreen ;
Graphics.fill_circle
((fst h)*30 + virtual_table_xoffset)
(height - ((snd h)*55 + 25)-50) brightness ;
final t
in final lst


(* draw_table : unit -> unit *)
(* draws the whole game *)
let draw_table () =
Graphics.set_color cYellow ;
draw_background () ;
Graphics.set_line_width 5 ;
draw_force_lines () ;
Graphics.set_line_width 2 ;
draw_places () ;
draw_final_places ();
Graphics.set_line_width 1

(* move -> couleur -> unit *)
let draw_piece player (n_case,P cp) = (* (n_caOccup(c,v),cp) col =*)
Graphics.set_color (if player then cBlack else cRed); (*col;*)
let co = corresp n_case in
let x = ((fst co)*30 + 145) and y = (height - ((snd co)*55 + 25)-50) in
Graphics.fill_circle x y brightness ;
Graphics.set_color cWhite ;
Graphics.moveto (x - 3) (y - 3) ;
let dummy = 5 in
Graphics.draw_string (string_of_int cp) (*;*)
(* print_string "---";print_int n_case; print_string " "; print_int cp ;print_newline() *)

(* conv : Graphics.status -> int *)
(* convert a mouse click into a position on a virtual table permitting *)
(* its drawing *)
let conv st =
let xx = st.Graphics.mouse_x and yy = st.Graphics.mouse_y in
let y = (yy+10)/virtual_case_size - 6 in
let dec =
if y = ((y/2)*2) then 60 else 40 in
let offset = match (-1*y) with
0 -> -2
| 1 -> -1
| 2 -> -1
| 3 -> 0
| 4 -> -1
| _ -> 12 in
let x = (xx+dec)/virtual_case_size - 3 + offset in
(-1*y, x)

(* line_number_to_aff : int -> int*int *)
(* convert a line number into a polition on the virtual table serving *)
(* for drawing *)
(* the coordinate returned corresponds to the final case for the line *)
let line_number_to_aff n =
match n with
0 -> (2,1)
| 1 -> (1,2)
| 2 -> (0,3)
| 3 -> (-1,4)
| 4 -> (0,5)
| 5 -> (5,0)
| 6 -> (7,0)
| 7 -> (8,1)
| 8 -> (9,2)
| 9 -> (10,3)
| 10 -> (3,6)
| 11 -> (5,6)
| 12 -> (7,6)
| 13 -> (9,6)
| 14 -> (10,5)
| _ -> failwith "line" (*raise Rep.Out_of_bounds*)

(* draw_lines_won : game -> unit *)
(* position a marker indicating the player which has taken the line *)
(* this is done for all lines *)
let drawb l i =
match l with
None -> failwith "draw"
| M j -> let pos = line_number_to_aff i in
(* print_string "''''";
print_int i;
print_string "---";
Printf.printf "%d,%d\n" (fst pos) (snd pos);
*) Graphics.set_color (if j then cBlack else cRed);
Graphics.fill_rect ((fst pos)*30 + virtual_table_xoffset-bounds)
(height - ((snd pos)*55 + 25)-60) 20 40

let draw_lines_won om nm =
for i=0 to 14 do
if om.(i) <> nm.(i) then drawb nm.(i) i
done
(*********************
let black_lines = Rep.lines_won_by_player mat Rep.Noir and
red_lines = Rep.lines_won_by_player mat Rep.Rouge
in
print_string "black : "; print_int (Rep.list_size black_lines);
print_newline () ;
print_string "red : "; print_int (Rep.list_size red_lines);
print_newline() ;

let rec draw l col =
match l with
[] -> ()
| h::t -> let pos = line_number_to_aff h in
Graphics.set_color col ;
Graphics.fill_rect ((fst pos)*30 + virtual_table_xoffset-bounds)
(height - ((snd pos)*55 + 25)-60) 20 40 ;
draw t col
in draw black_lines cBlack ;
draw red_lines cRed
***************************************************)

(* draw_poss : item list -> int -> unit *)
(* draw the pieces available for a player based on a list *)
(* the parameter "off" indicates the position at which to place the list *)
let draw_poss player lst off =
let c = ref (1) in
let rec draw l =
match l with
[] -> ()
| v::t -> if player then Graphics.set_color cBlack
else Graphics.set_color cRed;
let x = off and
y = 0+(!c)*50 in
Graphics.fill_circle x y brightness ;
Graphics.set_color cWhite ;
Graphics.moveto (x - 3) (y - 3) ;
Graphics.draw_string (string_of_int v) ;
c := !c + 1 ;
draw t

in draw (List.map (function P x -> x) lst)

(* draw_choice : game -> unit *)
(* draw the list of pieces still available for each player *)
let draw_choice (J (ca,ma,r1,r2)) =
Graphics.set_color cBlue ;
Graphics.fill_rect (choice_black_offset-30) 10 60
(height - (top_offset + bounds)) ;
Graphics.fill_rect (choice_red_offset-30) 10 60
(height - (top_offset + bounds)) ;
draw_poss true r1 choice_black_offset ;
draw_poss false r2 choice_red_offset

(* wait_click : unit -> unit *)
(* wait for a mouse click *)
let wait_click () = Graphics.wait_next_event [Graphics.Button_down]


(* item list -> item *)
(* return, for play, the piece chosen by the user *)
let select_pion player lst =
let ok = ref false and
choice = ref 99 and
pion = ref (P(-1))
in
while not !ok do
let st = wait_click () in
let size = List.length lst in
let x = st.Graphics.mouse_x and y = st.Graphics.mouse_y in
choice := (y+25)/50 - 1 ;
if !choice <= size && ( (player && x < 65 )
|| ( (not player) && (x > 535))) then ok := true
else ok := false ;
if !ok then
try
pion := (List.nth lst !choice) ;
Graphics.set_color cGreen ;
Graphics.set_line_width 2 ;
Graphics.draw_circle
(if player then choice_black_offset else choice_red_offset)
((!choice+1)*50) (brightness + 1)
with _ -> ok := false ;
done ;
!pion

(* choiceH : game -> move *)
(* return a move for the human player.
return the choice of the number, the case, and the piece *)
let rec choice player game = match game with (J(ca,ma,r1,r2)) ->
let choice = ref (P(-1))
and c = ref (-1, P(-1)) in
let lcl = legal_moves player game in
while not (List.mem !c lcl) do
print_newline();print_string "CHOICE";
List.iter (fun (c,P p) -> print_string "["; print_int c;print_string " ";
print_int p;print_string "]")
(legal_moves player game);
draw_choice game;
choice := select_pion player (if player then r1 else r2) ;
(* print_string "choice "; print_piece !choice;*)
c := (corresp2 (conv (wait_click())), !choice)
(* let (x,y) = !c in
(print_string "...";print_int x; print_string " "; print_piece y;
print_string " -> ";
print_string "END_CHOICE";print_newline())
*) done ;
!c (* case, piece *)


(* home : unit -> unit *)
(* place a message about the game *)
let home () =
Graphics.open_graph
(" " ^ (string_of_int (width + 10)) ^ "x" ^ (string_of_int (height + 10))
^ "+50+50") ;
Graphics.moveto (height / 2) (width / 2) ;
Graphics.set_color cBlue ;
Graphics.draw_string "Stonehenge" ;
Graphics.set_color cBlack ;
Graphics.moveto 2 2 ;
Graphics.draw_string "Mixte Projets Matrise & DESS GLA" ;
wait_click () ;
Graphics.clear_graph ()

(* exit : unit -> unit *)
(* close everything ! *)
let exit () =
Graphics.close_graph ()

(* draw_button : int -> int -> int -> int -> string -> unit *)
(* draw a button with a message *)
let draw_button x y w h s =
Graphics.set_line_width 1 ;
Graphics.set_color cBlack ;
Graphics.moveto x y ;
Graphics.lineto x (y+h) ;
Graphics.lineto (x+w) (y+h) ;
Graphics.lineto (x+w) y ;
Graphics.lineto x y ;
Graphics.moveto (x+bounds) (height - (top_offset/2)) ;
Graphics.draw_string s

(* draw_message : string -> unit *)
(* position a message *)
let draw_message s =
Graphics.set_color cBlack;
Graphics.moveto 3 (height - (top_offset/2)) ;
Graphics.draw_string s

(* erase_message : unit -> unit *)
(* as the name indicates *)
let erase_message () =
Graphics.set_color Graphics.white;
Graphics.fill_rect 0 (height-top_offset+bounds) width top_offset

(* question : string -> bool *)
(* pose the user a question, and wait for a yes/no response *)
let question s =
let xb1 = (width/2) and xb2 = (width/2 + 30) and wb = 25 and hb = 16
and yb = height - 20 in
let rec attente () =
let e = wait_click () in
if (e.Graphics.mouse_y < (yb+hb)) & (e.Graphics.mouse_y > yb) then
if (e.Graphics.mouse_x > xb1) & (e.Graphics.mouse_x < (xb1+wb)) then
true
else
if (e.Graphics.mouse_x > xb2) & (e.Graphics.mouse_x < (xb2+wb)) then
false
else
attente()
else
attente () in
draw_message s;
draw_button xb1 yb wb hb "yes";
draw_button xb2 yb wb hb "no";
attente()

(* q_begin : unit -> bool *)
(* Ask if the player wishes to be the first player or not *)
let q_begin () =
let b = question "Would you like to play first ?" in
erase_message();
b

(* q_continue : unit -> bool *)
(* Ask if the user wishes to play the game again *)
let q_continue () =
let b = question "Play again ?" in
erase_message();
b
(* won : unit -> unit *)
(* a message indicating the machine has won *)
let won () = draw_message "I won :-)"; wait_click(); erase_message()

(* lost : unit -> unit *)
(* a message indicating the machine has lost *)
let lost () = draw_message "You won :-("; wait_click(); erase_message()

(* nil : unit -> unit *)
(* a message indicating stalemate *)
let nil () = draw_message "Stalemate"; wait_click(); erase_message()

(* init : unit -> unit *)
(* draw the initial game board *)
let init () = let game = game_start () in
draw_table () ;
draw_choice game

(* drawH : move -> game -> unit *)
(* draw a piece for the human player *)
(* let drawH cp j = draw_piece cp cBlack ;
draw_lines_won j
*)
(* drawM : move -> game -> unit *)
(* draw a piece for the machine player *)
(* let drawM cp j = draw_piece cp cRed ;
draw_lines_won j
*)
let print_placement m = match m with
None -> print_string "None "
| M j -> print_string ("Pl "^(if j then "1 " else "2 "))

let position player move
(J(ca1,m1,r11,r12))
(J(ca2,m2,r21,r22) as new_game) =
draw_piece player move;
draw_choice new_game;
(* print_string "_______OLD___________________\n";
Array.iter print_placement m1; print_newline();
List.iter print_piece r11; print_newline();
List.iter print_piece r12; print_newline();
print_string "_______NEW___________________\n";
Array.iter print_placement m2; print_newline();
List.iter print_piece r21; print_newline();
List.iter print_piece r22; print_newline();
*) draw_lines_won m1 m2

(*
if player then draw_piece move cBlack
else draw_piece move cRed
*)
let q_player () =
let b = question "Is there a machine playing?" in
erase_message ();
b
end;;
Characters 11114-11127:
Warning: this expression should have type unit.
Characters 13197-13209:
Warning: this expression should have type unit.
Characters 13345-13357:
Warning: this expression should have type unit.
Characters 13478-13490:
Warning: this expression should have type unit.
module Stone_graph :
sig
type piece = Stone_rep.piece
and placement = Stone_rep.placement
and case = Stone_rep.case
and game = Stone_rep.game
and move = Stone_rep.move
val brightness : int
val cBlack : Graphics.color
val cRed : Graphics.color
val cYellow : Graphics.color
val cGreen : Graphics.color
val cWhite : Graphics.color
val cGray : Graphics.color
val cBlue : Graphics.color
val width : int
val height : int
val top_offset : int
val bounds : int
val virtual_table_xoffset : int
val choice_black_offset : int
val choice_red_offset : int
val virtual_case_size : int
val corresp : int -> int * int
val corresp2 : int * int -> int
val col : int
val lig : int
val draw_background : unit -> unit
val draw_places : unit -> unit
val draw_force_lines : unit -> unit
val draw_final_places : unit -> unit
val draw_table : unit -> unit
val draw_piece : bool -> int * Stone_rep.piece -> unit
val conv : Graphics.status -> int * int
val line_number_to_aff : int -> int * int
val drawb : Stone_rep.placement -> int -> unit
val draw_lines_won :
Stone_rep.placement array -> Stone_rep.placement array -> unit
val draw_poss : bool -> Stone_rep.piece list -> int -> unit
val draw_choice : Stone_rep.game -> unit
val wait_click : unit -> Graphics.status
val select_pion : bool -> Stone_rep.piece list -> Stone_rep.piece
val choice : bool -> Stone_rep.game -> int * Stone_rep.piece
val home : unit -> unit
val exit : unit -> unit
val draw_button : int -> int -> int -> int -> string -> unit
val draw_message : string -> unit
val erase_message : unit -> unit
val question : string -> bool
val q_begin : unit -> bool
val q_continue : unit -> bool
val won : unit -> unit
val lost : unit -> unit
val nil : unit -> unit
val init : unit -> unit
val print_placement : Stone_rep.placement -> unit
val position :
bool ->
int * Stone_rep.piece -> Stone_rep.game -> Stone_rep.game -> unit
val q_player : unit -> bool
end


Assembly.
We thus write module Stone_graph which describes a graphical interface compatible with signature DISPLAY. We construct Stone_skeletonG similar to C4_skeletonG, passing in the arguments appropriate for the Stonehenge game, applying the parametric module FSkeleton.


# module Stone_skeletonG = FSkeleton (Stone_rep)
(Stone_graph)
(Stone_eval)
(FAlphabeta (Stone_rep) (Stone_eval)) ;;
module Stone_skeletonG :
sig
val depth : int ref
exception Won
exception Lost
exception Nil
val won : unit -> unit
val lost : unit -> unit
val nil : unit -> unit
val again : unit -> bool
val play_game : Stone_graph.game ref
val exit : unit -> unit
val home : unit -> unit
val playH : bool -> unit -> unit
val playM : bool -> unit -> unit
val init : unit -> (unit -> unit) * (unit -> unit)
end


We may thus construct the principal module Stone_mainG.

# module Stone_mainG = FMain(Stone_skeletonG) ;;
module Stone_mainG :
sig
val play_game : (unit -> 'a) * (unit -> 'b) -> unit
val main : unit -> unit
end


Launching Stone_mainG.main () opens the window shown in figure 17.6. After displaying a dialogue to show who is playing, the game begins. A human player will select a piece and place it.

To Learn More

This organization of these applications involves using several parametric modules that permit direct reuse of FAlphabeta and FSkeleton for the two games we have written. With Stonehenge, some of the functions from Stone_rep, needed for play, which do not appear in REPRESENTATION, are used by the evaluation function. That is why the module Stone_rep was not closed immediately by REPRESENTATION. This partitioning of modules for the specific aspects of games allows incremental development without making the game schema dependencies (presented in figure 17.4) fragile.

A first enhancement involves games where given a position and a move, it is easy to determine the preceding position. In such cases, it may be more efficient to not bother making a copy of the game for function play, but rather to conserve a history of moves played to allow backtracking. This is the case for Connect 4, but not for Stonehenge.

A second improvement is to capitalize on a player's response time by evaluating future positions while the other player is selecting his next move. For this, one may use threads (see chapter 19), which allow concurrent calculation. If the player's response is one that has already been explored, the gain in time will be immediate, if not we must start again from the new position.

A third enhancement is to build and exploit dictionaries of opening moves. We have been able to do so with Stonehenge, but it is also useful for many other games where the set of legal moves to explore is particularly large and complex at the start of the game. There is much to be gained from estimating and precalculating some ``best'' moves from the starting positions and retaining them in some sort of database. One may add a bit of ``spice'' (and perhaps unpredictability) to the games by introducing an element of chance, by picking randomly from a set of moves with similar or identical values.

A fourth view is to not limit the search depth to a fixed depth value, but rather to limit the search by a calculation time period that is not to be exceeded. In this manner, the program will be able to efficiently search to deeper depths when the number of remaining moves becomes limited. This modification requires slight modification to minmax in order to be able to re-examine a tree to increase its depth.

A game-dependent heuristic, parameterized by minmax, may be to choose which branches in the search should be pursued and which may be quickly abandoned.

There are also many other games that require little more than to be implemented or reimplemented. We might cite many classic games: Checkers, Othello, Abalone, ..., but also many lesser-known games that are, nevertheless, readily playable by computer. You may find on the web various student projects including Checkers or the game Nuba.

Link


http://www.gamecabinet.com/rules/Nuba.html


Games with stochastic qualities, such as card games and dice games, necessitate a modification of the minimax-ab algorithm in order to take account of the probabilities of the selections.

We will return to the interfaces of games in chapter 21 in constructing web-based interfaces, providing without further cost the ability to return to the last move. This also allows further benefits from the modular organization that allows modifying no more than just an element, here the game state and interactions, to extend the functionality to support two player games.




Previous Contents Next ocaml-book-1.0/en/html/book-ora194.html0000644000000000000000000007444307453055401014505 0ustar Client-server Toolbox Previous Contents Next

Client-server Toolbox

We present a collection of modules to enable client-server interactions among Objective CAML programs. This toolbox will be used in the two applications that follow.

A client-server application differs from others in the protocol that it uses and in the processing that it associates with the protocol. Otherwise, all such applications use very similar mechanisms: waiting for a connection, starting a separate process to handle the connection, and reading and writing sockets.

Taking advantage of Objective CAML's ability to combine modular genericity and extension of objects, we will create a collection of functors which take as argument a communications protocol and produce generic classes implementing the mechanisms of clients and of servers. We can then subclass these to obtain the particular processing we need.

Protocols

A communications protocol is a type of data that can be translated into a sequence of characters and transmitted from one machine to another via a socket. This can be described using a signature.

# module type PROTOCOL =
sig
type t
val to_string : t -> string
val of_string : string -> t
end ;;


The signature requires that the data type be monomorphic; yet we can choose a data type as complex as we wish, as long as we can translate it to a sequence of characters and back. In particular, nothing prevents us from using objects as our data.

# module Integer =
struct
class integer x =
object
val v = x
method x = v
method str = string_of_int v
end
type t = integer
let to_string o = o#str
let of_string s = new integer (int_of_string s)
end ;;


By making some restrictions on the types of data to be manipulated, we can use the module Marshal, described on page ??, to define the translation functions.

# module Make_Protocol = functor ( T : sig type t end ) ->
struct
type t = T.t
let to_string (x:t) = Marshal.to_string x [Marshal.Closures]
let of_string s = (Marshal.from_string s 0 : t)
end ;;


Communication

Since a protocol is a type of value that can be translated into a sequence of characters, we can make these values persistent and store them in a file.

The only difficulty in reading such a value from a file when we do not know its type is that a priori we do not know the size of the data in question. And since the file in question is in fact a socket, we cannot simply check an end of file marker. To solve this problem, we will write the size of the data, as a number of characters, before the data itself. The first twelve characters will contain the size, padded with spaces.

The functor Com takes as its parameter a module with signature PROTOCOL and defines the functions for transmitting and receiving values encoded using the protocol.


# module Com = functor (P : PROTOCOL) ->
struct
let send fd m =
let mes = P.to_string m in
let l = (string_of_int (String.length mes)) in
let buffer = String.make 12 ' ' in
for i=0 to (String.length l)-1 do buffer.[i] <- l.[i] done ;
ignore (ThreadUnix.write fd buffer 0 12) ;
ignore (ThreadUnix.write fd mes 0 (String.length mes))

let receive fd =
let buffer = String.make 12 ' '
in
ignore (ThreadUnix.read fd buffer 0 12) ;
let l = let i = ref 0
in while (buffer.[!i]<>' ') do incr i done ;
int_of_string (String.sub buffer 0 !i)
in
let buffer = String.create l
in ignore (ThreadUnix.read fd buffer 0 l) ;
P.of_string buffer
end ;;
module Com :
functor(P : PROTOCOL) ->
sig
val send : Unix.file_descr -> P.t -> unit
val receive : Unix.file_descr -> P.t
end
Note that we use the functions read and write from module ThreadUnix and not those from module Unix; this will permit us to use our functions in a thread without blocking the execution of other processes.

Server

A server is built as an abstract class parameterized by the type of data in the protocol. Its constructor takes as arguments a port number and the maximum number of simultaneous connections allowed. The method for processing a request is abstract; it must be implemented in a subclass of server to obtain a concrete class.


# module Server = functor (P : PROTOCOL) ->
struct
module Com = Com (P)

class virtual ['a] server p np =
object (s)
constraint 'a = P.t
val port_num = p
val nb_pending = np
val sock = ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0

method start =
let host = Unix.gethostbyname (Unix.gethostname()) in
let h_addr = host.Unix.h_addr_list.(0) in
let sock_addr = Unix.ADDR_INET(h_addr, port_num) in
Unix.bind sock sock_addr ;
Unix.listen sock nb_pending ;
while true do
let (service_sock, client_sock_addr) = ThreadUnix.accept sock
in ignore (Thread.create s#process service_sock)
done
method send = Com.send
method receive = Com.receive
method virtual process : Unix.file_descr -> unit
end
end ;;


In order to show these ideas in use, let us revisit the capital service, adding the capability of sending lists of strings.

# type message = Str of string | LStr of string list ;;
# module Cap_Protocol = Make_Protocol (struct type t=message end) ;;
# module Cap_Server = Server (Cap_Protocol) ;;

# class cap_server p np =
object (self)
inherit [message] Cap_Server.server p np
method process fd =
match self#receive fd with
Str s -> self#send fd (Str (String.uppercase s)) ;
Unix.close fd
| LStr l -> self#send fd (LStr (List.map String.uppercase l)) ;
Unix.close fd
end ;;
class cap_server :
int ->
int ->
object
val nb_pending : int
val port_num : int
val sock : Unix.file_descr
method process : Unix.file_descr -> unit
method receive : Unix.file_descr -> Cap_Protocol.t
method send : Unix.file_descr -> Cap_Protocol.t -> unit
method start : unit
end


The processing consists of receiving a request, examining it, processing it and sending the result. The functor allows us to concentrate on this processing while constructing the server; the rest is generic. However, if we wanted a different mechanism, such as for example using acknowledgements, nothing would prevent us from redefining the inherited methods for communication.

Client

To construct clients using a given protocol, we define three general-purpose functions:
  • connect: establishes a connection with a server; it takes the address (IP address and port number) and returns a file descriptor corresponding to a socket connected to the server.
  • emit_simple: opens a connection, sends a message and closes the connection.
  • emit_answer: same as emit_simple, but waits for the server's response before closing the connection.

# module Client = functor (P : PROTOCOL) ->
struct
module Com = Com (P)

let connect addr port =
let sock = ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0
and in_addr = (Unix.gethostbyname addr).Unix.h_addr_list.(0)
in ThreadUnix.connect sock (Unix.ADDR_INET(in_addr, port)) ;
sock

let emit_simple addr port mes =
let sock = connect addr port
in Com.send sock mes ; Unix.close sock

let emit_answer addr port mes =
let sock = connect addr port
in Com.send sock mes ;
let res = Com.receive sock
in Unix.close sock ; res
end ;;
module Client :
functor(P : PROTOCOL) ->
sig
module Com :
sig
val send : Unix.file_descr -> P.t -> unit
val receive : Unix.file_descr -> P.t
end
val connect : string -> int -> Unix.file_descr
val emit_simple : string -> int -> P.t -> unit
val emit_answer : string -> int -> P.t -> P.t
end
The last two functions are of a higher level than the first: the mechanism linking the client and the server does not appear. The caller of emit_answer does not even need to know that the computation it is requesting is carried out by a remote machine. As far as the caller is concerned, it invokes a function that is represented by an address and port, with an argument which is the message to be sent, and a value is returned to it. The distributed aspect can seem entirely hypothetical.

A client of the capital service is extremely easy to construct. Assume that the boulmich machine provides the service on port number 12345; then the function list_uppercase can be defined by means of a call to the service.

# let list_uppercase l =
let module Cap_client = Client (Cap_Protocol)
in match Cap_client.emit_answer "boulmich" 12345 (LStr l)
with Str x -> [x]
| LStr x -> x ;;
val list_uppercase : string list -> string list = <fun>


To Learn More

The first improvement to be made to our toolbox is some error handling, which has been totally absent so far. Recovery from exceptions which arise from a broken connection, and a mechanism for retrying, would be most welcome.

In the same vein, the client and the server would benefit from a timeout mechanism which would make it possible to limit the time to wait for a response.

Because we have constructed the generic server as a class, which moreover is parameterized by the type of data to be transmitted over the network, it is easy to extend it to augment or modify its behavior in order to implement any desired improvements.

Another approach is to enrich the communication protocols. One can for example add requests for acknowledgement to the protocol, or accompany each request by a checksum allowing verification that the network has not corrupted the data.
Previous Contents Next ocaml-book-1.0/en/html/book-ora028.gif0000644000000000000000000000655507452056111014277 0ustar GIF89aʽ}}}{}{{y{y}׮!B,BBKC7&D E!F"H#IIA$HpXȐ!HbSȑ ?"I䊓(Q&X2A0aIs&8q2sg@6Jt腣HXʔ)P*JjX.u`2Kv쌳h6X˶A|iHGKӤ3AOB{ $)b @H2If:әAf43Xt6Mx|89Ń:׹N:p<9 > $@J=B8D'*QXF7xHG:&JWR .Lg*S7Nw?=PzAHMRԦ:TJժZXͪVծz` XJֲhEmZYpmZZ x+X>¯x Z-H]DbؽnbdDD֨!8Yu V?KYfdkTF,ֳmhgVҢu!8(pj[mlAZ&wЭjq1U]a+[: Tyⶼ,zoj|Ɋ\wͯ~L誗5k ,Vw+`Ŧ5}',V7~.fZV09 WzĻ]q7\ 8'.YLrq}`C;k1,cF>dL!ri3ZcJx V/όeՂB^/:կq>πW\aYЁ-n3*7'>-=iʖΰ]N۷5wM1r3mҦti/CմɫdCYֹt ЛsGdgus#i:¶6Wy[X$kW/lA\͂Ԅmlfzm\5S;ڿ7%pXw; ZKi\7X=^WTvm=gjO¶}]G|^nGnsI`[BotiɽLzoqtȭ-SwrMGy{n$o y췮qlguMwg=- p?^d[|?^xCU; [z۷9Y55{U]n8߷Gr5}xg~[mvz~Ym{VyƁF~urivn#&xG|x7fWl؂B~X6xwU{*v^W6w÷xyFswb Ha_|Z{HsyWzlprgZh5\'fxDVi׃c}8.H|؁]gac'uvvcȈfq ʇnaanduo0XpHsX8Xxؘڸ؍8Xx蘎긎؎h،Ja(Hz}8d؏V7Xvc☃G8((iHy_FfxUj{Ж/wՈQX]X(dȊ5*@B9ɖDa GI\-K)&vN)VV%YY)Qٕu|\ xdy;ocaml-book-1.0/en/html/book-ora076.gif0000644000000000000000000000502207452056112014267 0ustar GIF89a/UUUrrr!,/H0I8ͻ@(dihl_,txCpHȤl"h*nް,.9^ 7\,v->oރ!;C~|a<  >TYq NG\ P ­#DE6GP Þˑ|Ѱہ @A4\ĭ 1g9? +nv#EЎԂ˧ u 6[;l& jb)LN¹D@hx*(z 0ZUB25ԪFNѪk^' [aњ۷pX<_noxkLpUfCU!`2Q˃%O*BL<>+0EYi2|nڵ,pfDwS_`Žؐj2Ak}Ojިek?p+~4|J۞^/N` /oqM1 *ċ!to$P襟Srp_#2a&+Ɛb8/,3T&7V8-cO?rcAA Y$lGf"גL6 IeuSXnW—u[by0vycF &3gD&n[_phWsj|zy"zih=2s!:颳>Rk K&qiȦ1,Ji,hڲ2Xk `s\`ك=*^֮I!]q!xu z,xA#z :hGv:$[GDZ$irR 29<O)U5{.Gcǔ MkʣCM+k)D130~#ͺr{l|Pg*Kp63n r,7|-̤asl4aY ^,h9Se-nUjדڄc-s:)e% c/>7YϹǍ'ήf;(J{ƋwɞIvy 4z凪;o@Ym,K\ǻ2aB IoX5P&n&D?":#(.1^nѬx}3l'ɦ{s )g:=NT2'BIЅS:A:zRżhDrQcFҨ- HO4/H]:Ӡx8KfKVO,V.‘QB d!7]#*#!Uƒ/TVeM9qsCqCIe #&׊LƊ!I`2<9LlWʤo#c̫ތJt?cŒ@͏lE181X.VHJ) v5B0ГpgFdDK3N3P-wZPt(rUdE1f׻nKҭz]og2@~U,r.wku l5d[[,iAz$ՒmqE6gEPL8>|0<6:s ,uZHe]SiC[Rdﳤ|昐|կwF˟i*-+dE.`B'hZTl:FiclgW۝nʹkvhu9np6wnt1fw=ox{dw3o~)8M|u.Y=܄;|8#pS184qc q-SDFM*b%X. ?3)[ȧiiZ㷪#y(^X ,{_בT=lѱG,r,l"H}gSy4e1ku>&ŷ<^`xL-chX-4oپ=uTQUH$YrgT[4`|UNut 2w|DJ(%EL2MtSIT#-ǡ|5RukY@_oDem[!\[H 蔎lXf_ !h&IGDJ1ƒ3R^tY%VpQjQ`95f 9kFf7:E)IptHtmuvYaJk9&GM$dkLtM)eҗXRQaZnuH[+PZNicuE %IhiIVGC`&`qt(oUiwwM(]fHHZjnWゴ]U5H SQzEm{&ʾ +kq]؟{iZ(go,hSh8h{8h]h̕6 kO12Z$?yFlmOUmZebUZ.Å6UH!آ^VAYK4sQD5n: jf'P2 7{,N#GtopHgV҆ d_ Ig:smΆ>z\J78]2Kyp&erYVf)xY6nE},Yݣ!S~.㩒uafºx0&4*bɝV݄j9HCZpEZukdDsZ8S'ܚbZAK=CK̭|䌦T/܉wꧯT2"k@bBI(*bft!TXZXx08\[zJL=>l#LnYQ_-F XӴYXd Eo;,ɍ5eő,}+N2)t`! y rԬ.ųQիRQͫ* 7Jsv]}M^UR]?իN0\D3R봔%7T1>qi5-rK7l zie&d >wb+7V4(Pp$e[/TB\v57ŝoҗ3&Eɧ:USәzJ̫\J(`qIV 2p l=‚%^TSz4{rt1u #J2B0Ӆsn&FdN`avd_#) |#k w5Pgf8F->UltKƆgsW6?Rp!n@>fEu7+1gf/s' DZ9ucA3NvUv{⦪/`A6FLW7fjvأ'r%MAb&HPcp.:a`(g0 ]P 6ĝ4qNWΦNjGh(Q828.2E^a3idji%Q?Ȉwy" o0E0p`Bye%J7z:%\3k*Du3Yo rk;ZVfuC5R@9@v" 6# R (5'@< EМ9 SzL9>&WR&eK4#ccT*".7gN[oc 5@@YK%wgE`uIR#o0pf)4/EoDc湯>~NɍTU& ٪gB,8b8Z0Џ  poKPW .gе.;IuqȳX[#"[lKk;܈FQoc]VzpFb[4""pa p0p Iչqf? _Ks"VebA.['|E]rRQ NTE+n2Kh <{$p9 P p* 7gоQ*^jG0?Z< l,;@N܇'x'i<~d:v%_b[38g Җ!)0t?8x6]* }bvЀ,Kpa ۾. T*ŅGŠ)Gtr#wa)@SG8#0n*ۑ p>)5Cn\B)L09P#8IPE G]q%<}¡$R )})m',\qv. ' qМ y (!*KkоOpGV} 9`XlhJL NʽCAz$v4/e`q)],,CQ:`[pm Dxǎ 2x.֌h̿ mO+Iw@@?R, D٥]uw-S1S 8ȀAƕ1d C$IH$J\#PxуcԦ;,zRJѠaa W%C.m6*R OE5ɑ(LV]j IHl  3{ h" O?KaI{Rf0L NӁo\@%$0 }xR} P,fږ/Ł(ߴUj٣tiL{UVM0) 3Ll)"0?8J*@D#bh# x&p͂r(;`:<[,j]S0ǝ *` /1<3xX DO&AK @{`IVsp@qX "CN.v{ᴘ" -i$= Ls4K\5INLzs+ p,, w؃€D@ > $թU$e@QDV~2`&:P@| tsA߈s  * #@R(0@ BDdKX9+dR<&lfJKc82s@a  \`{h֖E%S K?Iu~)$aPD-nd@ԤH.Bz(ۉYifpV+KZ Ny^x*A5MuVK3\Az !`l@6pkPt@$.Z \ءMIhu CkHf: .'S<fC)f6 F*hfRK1di4"AI|\otF` ^`@:FD n`mjtHpE5|p"z %!Jd:U2`0 ua 85M@UzBl)ib{mes<;2A)+s`p+`(DH@+a5pW}BhMkWȄKȔ3׵Bp >5t w([A3+Od첖 2{ <>Y$ S%%0` x9X ~qӕQLB7Ve(vѹtxbM\3@P$J"> ahkfKǑw [ldV+sfRЦMr} ByKSD`gHA @ (Daq"pU(vXqKI7\eD3&D9U~ce4Je[i@]w[R/^cw PwB!p$GЄ2H* +ss8RE  o^c4`m6 #>L @t( k>VXd]SfP(je`rN;zk,cI#w   ,@hA:` `o_"⌈O5mdrl%pjr X҂ "10xo !r*Us#-(Y8/Z R; z"<=Ҁ6x08 3{!Zـ aAXm!.41#%_*@) %Ѐ)7 %C9Q f2 .[ j^c ̣; Z&0,'ق(1ض%X%M)+3ඐJP.%8,0 7؉}q H?؈;hs5:(;);f(p82Ѧ!i #as ;h781@80} :8`% @[Hq1ЂX8!!U%68 9 X; :-Clp'2C.c[*bIk +h?p鳮+P; O%1⺂("WZ䍈j48 bᐍ *Y?ڨ>ÁI=̟cbəD_p\H)P6:h X$|%Xk+#7! Ђyꀞ qëNq?N8X 0=`yI?\Puip I,SX5H/*H7+-19Nh 0xJv[3-X"q ' NZ he27C2)srbLh7(X z !0P=(p X78’10"04yUS'X8ƾ9b 蜝].x+ y m zAxDd."58783HUhYH)%x7;X8 a<.$.E8)#/SJxIuY882.N@kE3Њ̂Ʊ0 =\p+:`:(HF-h(9(80[Z%\%h!%@8H7+s()Ax'В̒;kY3;ì92[8 H M)>p>8h?ȁЂ6 ? @yKЛ UX͌JSDK׸1I 8IE6X5@!td-TQ(2q=# \1a` !`1 (p31:ߐ ) 1@ K P : RQY%58.EБnp!P=h0h\B`etA>e%:0P`7P\3"hx>&3 H@+\eV#u$f~$l=N!~yV_D $&@DwZq#<8#,lN?؂5i|: ("=ChE ޢ9ž=8 XP|Rj/p%d=d__tA\[f$2휦;$Pd-@@H7q4*%, S<  n;VH*H|MɈ/âeK̥5mAlVTbJ@ATwn)&`+hDтc @@ @'`ƻnuRH5ۼ!SIh?sV`_N̘A4e jmA #f!HY (8H?-$؃ȀDEB^C%<N+ļk3 + !0'eEXjCu,+d*Cy /P0$9hh;}Д ɼqX@41۰DBP ykHǐS4@[%X)  d,#W|oP;z;zg|3PZ*H" >$=xX*Uy]q=:8Œq:`bgǒ Y⟎$ ,a( Ft?S26d?QF&EiP?"E VLY,( Jv=ř7pϞ'm`( 5D,Q{D)YfdN@0pC$1c̃<<&О"ܙ(qlxpHCa 2T(ҥAeTjEҬa~U:hz~R:B!:̌Y0K00 4` TdtZ YA @n8J.pBDBO, H8S!Y'qA%1PIl\VZЁduǕ ^!BC 0AZL p,)"@K@gA$Au!CЙ.h@PKPt1ExQbB4H gn!vDuqQ>$E%a!erAuvT jA )aBAu8@ `9d Xg0lљHnFA R7 lP$F #IEPAI+ MGqMi5eVBheUQ*q"䠀@A PE#|-, 8t$! ѧY j04lpD4`Y0xi4omHsI2]qrژD_y\w#]R9 @0]t m|y@1Bm7G{RF9сlj!F~|g5AFQO0 V nx`Fle!8yď0 @A$%!bTFpF9vC* yό`0ADRSFqb3N!(+0|=4X 6 X@7 61(j :hΐ(A 1xB4 " l !Y@,0 vHA`RA&DX p(T SAްHL+dةpmG*ou#d`1` iAKhbkDB@܄vl_3HT;@rZBL@C "D P6BF\P$جf%;gIǑB @@X8J%DHpj A #F(x!b># 0."` u [A&@Z@`"CDB4;4T9DW> ~EIhT' PP$8@Lt!Ԡa ^t1qpi)D: ˆ!z` &EHedI!2"Ma Ĵ',>"; I:P6XL #PCi78@n `" 4ڇ!7{AX1Mx&RKpPFD٢6AybrJGk,"A=J r+wC~10т q PA N%-}CZ9@cڀa@'%|bL/bop`T [#&h (:J=8Iw-#kcսZj@L/ 0O@1A ,^JN#W )y4A:@8at;ڧwhk A@UK0hlNTBts] 8o8,`BբO@ L s8I``!Hp='(94m> 6&C.ٗL"2Te;2 L`4ϤCO4H ؈pHQ8HuTHA<A| L,A<N |G-%,ċA 20 L@nI8F$F4d@! `ME^t1JPZ4 @ `ІA@D4@ ey_MlI=F8})1DhPh,P@b@D@zJ<`^er!Vؕ)M`|el \@@xwBA(Kd8aIAlP1~Hb@EcƆMĹ,2.#BM A! Oatء|AA A UM@AXYK܁d<$,AH$m`\4@eI9@ t8DL\IԀ(ޤ@>#U5npǀD}i @|܁'EK4nDCb\ 04c/.xA  xD A `L&NΧTfe># `Os@t!ԁQ\AW5@ W@| hM M@ L5$J@TrnLlQDALL'FdJTȀ4э6@v]2D8AUA$4'~ Rc]瑊YŊ c*ɀc! 4Tzq@ ؋,KB ěANЁ ,dY/Ɓ"]@9H<p= @]: 4( NB\MN~pd56X؀zN L  B@@,܀ @@Klx1JPDQheH_I h@D ًIˍʼn2^f}2|2@5pݐeVtD h8^Q4p xd @t| 4X@ DѐĕM!iDBVFR ߹يlebH\m"BTBl |^&2*`qv]XQ(QXirlV ɦ\A M؀ A (@B ϙ F,\c(@ D/.YּR&]DX mi:MUIHA tKʔg$z!5b5[h(F h6`GUԀ \(A@ M`Re]V@|!/~8 [\yR5HN,XĪv.nAN%JTǬ)0ADX@I` yDL|H8_/RX^M eBOI$=-yctEd ylN5 ۮj1Um*uhXMR<8X @$ \dm%Il=*ֽHĆٌAyB@FEe2* +$gnYc C5rv#4AA8 . @4 } ^.f]*`Fl[$91lNIWJFL$fu@ZH ʔrf ` %rXe9A^ЁThi@Bx I 0\~XKKX-rΪEs;P@.!m֚AX\4Kt@F@,@dMMw![o3cuf0^t Sp@"wkA ăǶ \0Ac7AU1Dx\16Jh؁$RiJF >ۆ`6kygcz'6zs6FpIMp* $ TN\ XhF@@TIFMX,A l$/k G6@F,iGNdnH tpywMg#SiDw,) hD%  Ā A@9g=I 䈔#8AJ\1cǍ;@e)D$cK,a`Ă#@hfɄ )aęg P:L 7n:Sԍ8xGQ'Ah% eZp Lv0 H`CPf!B LqtMLB 7#Q F0bd E%\@ ]0-Q¬P Q𰤆԰n5'TvH=]4#%-* #@ ,޻@C(32#Ȅ', 6$"JAbA`*vka1xHa0#?=A ԋ @ H0`cNX.48%! ;2,0#0\"rȜ/J3؊c|H;pr>p2)3,tB!I=TJ( .8446#8mNȡR8X`D%0V (ONH@Z2bB/3 *"(7l(,Rb-@`*03D %R 젻5;S1oT"&AB6H` *HA?DˆP#0@?5 p/?dX*hJ,7&iwۥl Y J+`z f2J1".Ex rU4+NQ F͵`bGHa!"  (@ q=((R{:x(BYx,݁gP|`@QhBN[.%\)h !5x3@ >l4LgT[FXP7{( E 5a 09!Q{A308[ $J(Bla ;w< '!j ޤIl3`"bGeD(=L\A*#0&`@p4*KȀP6@9DCЭ8 xܠ7t*ӊ4'0@ i'Xm.@[Rڔ%!.  ^q7 P B/=tfCN$Eհ !xUa Ђ]BLrp%aFRN lH FZR -IZV'-L@Z_cBbT*bC3.h1C$7{ޓed$d;шGI W =Co- ȁ:p`Za*K `<4A;iAe+D?^JV إP C$4vY- YK [BdGgb#Hrj1ء b0CǶp=\!u#@h$3%< ei%ʢpE  !*&L( t@-\`, &Ð.@, @aCn-mY-G7]};Ș8P%A I@FІ F ƪ + 6%aiT ;twDJYڅ9u@zH ``0 rbP%R :0 on4_!/MU y!R9%4 [`" P`N'aDxʠ$=@Xj@B@a 4@=_6. WHT Fj0aHV4߈lkH&D[20bѤFbަN' m(@xCF!(C $/#A:0H{PBL8ޭ X K(=& tȒp>p@$\dO1(CY1fk} =JG>wI6n:gW< @LSA\ŒA h&X15H%>P0 !p{``ʫA kiV>/X|[!Z0; ZI~`ↂ`D` @+$$vlBZ:C,"v>SX` (` !& р*VD H =< < eԠ!@=@`ؚd< ]'krƃM*** * KGUoC. [O oNYN R b@q ^` H` D. vԣ` @ZV,b.dǁ:lj Zj Ⓘv=  k\@  d2#,am62?e#a a@J`  n) @ p<" b$ l :M (: FOt<.)Zҥs$ @ >@GT^m|0I^<hYP[ƃ  Pk %Dib 1c  -4_j̄(M<%5`#X @ L n`:fl  8 Z`` X| lu*@Wm@%&*C+: Z ` N$x\ ZB0B-@b ֠ @@E5- T*:#"T),Ep`g` `b@Wllk9x"Rcl@hgdOIƖF`0ԄJ( .W'CK*jJ` "/ C*D钤M jl,@- "+<@D+`EEqZ/l{2a8)A%bX` `  bԠN*G l V )`( )* V A \i~9@wM ;ժ`LfkI:`;d,d +2i喫*, v0d di'3пYa! .EI7) 4r/\_  @ C: ..]#n`bgEjI!Y5 l ŤDJخ\fEv -Gx̨" tF+F !TBYk@4 հ+L``@wT'zixyQAB Dzj2J P.`^2M䪲 AԊ./y Y IzI㌫|*@ LJti,kQ]+,F*`4gJ|.#4IW) Q^Kդ*JW I 0 -.m"9 &9  \c,سт"ibeJC :>er K+ {Q/O)l `j`2gQB`z<#Z 1 @> `B@ B%.@%@Vp:@Ġ Ө $oH[d GÀx#I R`-Pjd(db P s@b lԭ/ZfI ތ7+c"XY`@ wae\5l H`zŀ9``.Q @Dy<`? l$ 揜cQ˕ dž>@U8$3*ysD+ g*o]SlbfC ZZ (LTNje |` ƀ`(X ,.,hWZ j=^=*ѽB8rrl"(Yրa@E/A2Lf޹"esSʀD fj@ 0̻*WR` %I&޸` U1[x( c#.M$sv6N'6< Ǭ a?ڃ|E p¬1@LC&A&XYe6 R`@ $`Y@"@ W,P 1-qß?d0&pS`Q: SƝ8'x 049N79KGP3 |( 7J28KQZah?Zhu=G&c=Iwo߹I% 0a1cIDRCN< =L*8'`,pg+@@zfttեdiŒw?I* = 7N+e8A x1t`a[)d vgje#NKG2 @aE-[ $R4"a'w&! V'aCtSG )F 0̱D@ؘ,18 #hA܁rfT`p0f|AJxr,MP A->I07IF Psg{`O}udgahtІRK封rbn,BkDz^2`珩hjbzD 3aP  aP`WЀ`pE;a 8`szb#@:I@B0 :$@ "Pt 6KB̷/iAx  n)O@1šv% A fJ2,@r 6` ~Hz\8 @-Yd `hR,IO9K<51` [X&?\GJGޠNSjLWxnJ2V(:@yC-a L8È p*1$ Ձa &;i=IxbJ0ʊL} TH>@p%7fR=_*]؀Kf@ ؒ1tk< Vd 'o /}%<*+ 0D>p|wPrTNE65Zhb{ a <t\;L njP܁, 03?pg@J$ @LU`؃H6tY eBPR) H*u+@L“8XJb{遁`+-bXPf-Fs0eMjP!ivgaK^Qg#fpYQid4qvwux0&5"'zE7kL@/a65v%CAtP~w~ g5e`(@FpaP`Ew5 j q^5[%sQ+@6J!r5 ,%0- MH8C{(~w b9@{ "@Z 9Q7q$`2eB- ak{J"KE%JjpXIAYuM@44 Y4B%w`&K`7L 0Lm*Xn),i4ql'\58xGG(Nt@@>A?t @2m@HNk"['-@rK5䊫'$&0S]p#QItc`r]89"YA6n$`gA(&. u1)+lqc$愁S;]8IkH5#h.`L Z!hA5[r@GNF7;fALBZf!&".+`q8VD{99f@N0pO0_k!6]2.Ǟ Qqy٥vT[PZjbE4r(<p<@_ƷR:mOM͖40;] QjF`A%(-!qE.bB[hGN8a3f`C6.bB?+esoI yw8R  ZCn&˓K0X>EZp$0Z{P+0)Y{AYYeOza1^'bp5PgݤOpEd.BI'гwk[?G7!̳iN ?u~;}k-0Nsf01 @ g@o@:Q`@;þ8&3qnȚLTZ[%rvyxGTa56jA @(IX;`d"XnQ0NX "2CpXod*9y5 i@@1:Jq kjb,;>A_i%[pT:\QJ #K`.. Z&w7vig. |P;TaPkqt b (@f:7*hb ({9!vslbp˺$EEp'u`[J15"Q`"$S-Pf0C0N1-R[U<bo+To[>+/KX7P)`>]G;h6v8ʞ(Bpk-@|*g0f#@2pg0"u!\J,AfD8$5Ȗ`Mj(nB[ 6"0&f@q#@6f1cG#=H8s$t4q{#p pXp!s0vmpmB9^Chv`Xt$ P<dO%UETbpw25].M3+$,GXV ra'+%'vQ,~Np-@inV S"Z0?K|2@@s@ls [@;LRaWpKkǩC11awoc#ֲ$A}{.b/Av9mbKQY`%i0u@ܐxgxmp'; pdpyy0tPBFk jt|sLOXp SesD5!6KPIܽaD"N")Xc ]?-~u,P~LHpZP@q(s9ZF`_eKp3@zF41P 7abM4,-H5j H[!M&]n3J hJEE'+ vTCU5#1u=!s++jPt9"F ~p.0\t@k&D zyU'mE ;RaЕZ*-%AL#a@8Ck 5jpoJX_f}PaT.oa,7YGvt u1A{ [ Rv7#0|0`j0!ar`[6К>6}arR.D<..#-$@}BN!\Rt`&1Y` o[oҕa%CMbsB-8tW0>PHfzl+8S_Pmp {@kk^09'KƷ.Vpz$P}E]CL3 W Nh+LآQ`"L.&ɑ#I)s͙Iv$ٓEhةԜpÈRЂ?KPb0N0OJA(HannE0<,3މqt`{.l0r48 u IawXvࠀ &!M0xtPِׅysG@ ΁3cAd?<<0C r7ԠsÁ-:0&Ãȇ0($0#h858\A`ȍ:P`0`:莘i:ivЀ=160-NX=-ؘƒ+ `@.5 !6#+ &$;:T :<2%Kt UĎ5\SR7 PRI21670l3=`t5Φ)`. :`$"2+ 1 8!Xajh@`k# Tdh! 0-_Ȁ_2>x# 8CoTK`0[HЀ qAAtS AJkNvҭh \h 4Ѕ.Hs+*]` $~5~)/i$4B|l {Ђ j8`$R0OA%C؄2R>^`QŖO 4@$ fQ̀@\IAK-A] #xHx` ,/H{fS`tF4 :py/Ъ (,"8Fvy=pJùa?Op ĄBV2L C%c T iP `` nJ`bnZ2, v;!ᨭ0sg(q f`lde ~$ H=f(XC,&A_{1 p# JlOY^P~5B8.Jb8kuܒ 㲦` ((6 y(/xkZ"x9H] 1ᑏ@!1-S0xiÚ Ix(838vY Xh &P7w€5 :0Hp+[ "x.[ 9 !a!?65Y K7X "#)  qʐK*vD%8v:;Xa,-!-H0i; (pHc +X])`C*r[c` ̳392Ÿ9(Ǻ ð !:7P@`$(` &HjA6H2قPa X xh"8R2ƾ:E[B. *(0cO0tC̐ FiI!=nljHJ3tЍ)& C 0P? 43 8 @# +<0K#X{9:$BdCZ?˸/XŒM" JZ(%S =#YpN*; N8 `8 : x +:+E HiBc{K0pLr%% c2#X(BH*Ӌ]+xF  4*C!3p> 6e X%@ -ѩD=`[@*:8+XjO_[1;%$!CCI4̂C3$E D%  b-S16%94Ȟ:: )L ?@`:&ر0ȋ:S 1HsVKاX+ɾY `$P YH 7c (pi$ 5X` >(;dutkA#:=)D+-8\:y0H-غ L3 *p1TC+̾ăLd[1B8Lf; R Źِ䘕?D’({Qf4': (:8D vN11eQp3`"cLjHx#ۉH"ؚ9pH$`:]\:()<(1S2 ' a1 o $ꙅl"0B|WG4hc'H H@3xA4 @X BX Hء82ؾЁ:Eޓ|t#%[1q6p%N P&(n=Jӹ0L48 MD:wŁ 3(?h>8&120p0D(D1#+/0"&>X C V5p02$8@0` *4- hI# x)!c)cU #`N:ĮR !P2Oy a1C%TAE[#h9 ߌ \],%,0Sfc 0P#>!ݠ5hH>V)=h+*=!fkf>c8 X#:X I#9p"'ncu\5%yOVR"X!.(H\ʥ!*e\1-?>@?U4o\;7f=;.ik|{I.b2a%O0V! @(vZL(u}2<#˫5<:Lː(3hm#C~Rc+ؠ 6ۉ?7H {2xf  c8W: 0@O_PX:۸ݡ#B,#6h${&Z[: +/9؜!b]2YBS.UrPU 듮b@)i Hj>Z4H=*h8 `/%"XPp,0> !ˀtZLm. #shr$HK`>@EI{".Wc9w i!!jc>'""`d$8H&[?`@Y/.k2OЁ&I%܁Q&H8Lm4h.Pʡ:*vdUڈ.h ċ `ql~l#G%JH hÈ#?DpY5BBⲪ[Q=Zl`j\^ЖE*EݱhKP~4IYK ,~k6=?c@#Q0`: W?I΀:')u8$Rx%S%X9xnes_q(8h+["i'ĸ"^_ 'Mb _REBf0 W8!@4DA uĂhe Is I\GK fe(&gPX43aa;?6jj1BR_* DQBF$ q,jY1! i@aPt@+X,Z\,A!p&`EWYA0.A @&@J!ɡ}yh6@QEp}A5lWBAY 1 >l LtԀ稁CXbEHR .DwF *:5gS 6Mt A&=xXS5) "PQ0l!TH@&)rpHT}V3A`41 ?)X%88 \lޭ/hq@@[3) IE uu3KfchK Ő16.l 1i̠0e/ؿlk&NtV) |C0CB!5 +< o#>_<_;ocaml-book-1.0/en/html/book-ora075.html0000644000000000000000000000664007453055400014474 0ustar Preloaded Library Previous Contents Next

Preloaded Library

The Pervasives library is always preloaded so that it will be available at the toplevel (interactive) loop or for inline compilation. It is always linked and is the initial environment of the language. It contains the declarations of:
  • type: basic types (int, char, string, float, bool, unit, exn, 'a array, 'a list) and the types 'a option (see page ??) and ('a, 'b, 'c) format (see page ??).

  • exceptions: A number of exceptions are raisable by the execution library. Some of the more common ones are the following:
    • Failure of string that is raised by the function failwith applied to a string.
    • Invalid_argument of string that indicates that an argument cannot be handled by the function having raised the exception. The function invalid_arg applied to a string starts this exception.
    • Sys_error of string, for the input/output, typically in attempting to open a nonexistent file for reading.
    • End_of_file for detecting the end of a file.
    • Division_by_zero for zero divide errors between integers.
    As well as internal exceptions like:
    • Out_of_memory and Stack_overflow for going beyond the memory of the heap or the stack. It should be noted that a program cannot recover from the Out_of_memory exception. In effect, when it is raised it is too late to allocate new memory space to continue functioning.
      Handling the Stack_Overflow exception differs depending on whether the program was compiled in byte code or native code. In the latter case, it is not possible to recover.

  • functions: there are roughly 140, half of which correspond to the C functions of the execution library. There you may find mathematical and comparison operators, functions on integer and floating-point numbers, functions on character strings, on references and input-output. It should be noted that a certain number of these declarations are in fact synonyms for declarations defined in other modules. They are nevertheless declared here for historical and implementation reasons.

Previous Contents Next ocaml-book-1.0/en/html/book-ora048.html0000644000000000000000000014416607453055377014517 0ustar Graphical display Previous Contents Next

Graphical display

The elements of the graphical display are: the reference point and the graphical context, the colors, the drawings, the filling pattern of closed forms, the texts and the bitmaps.

Reference point and graphical context

The Graphics library manages a unique main window. The coordinates of the reference point of the window range from point (0,0) at the bottom left to the upper right corner of the window. The main functions on this window are:

  • open_graph, of type string -> unit, which opens a window;
  • close_graph, of type unit -> unit, which closes it;
  • clear_graph, of type unit -> unit, which clears it.
The dimensions of the graphical window are given by the functions size_x and size_y.

The string argument of the function open_graph depends on the window system of the machine on which the program is executed and is therefore not platform independent. The empty string, however, opens a window with default settings. It is possible to specify the size of the window: under X-Windows, " 200x300" yields a window which is 200 pixels wide and 300 pixels high. Beware, the space at the beginning of the string " 200x300" is required!

The graphical context contains a certain number of readable and/or modifiable parameters:
the current point: current_point : unit -> int * int
  moveto : int -> int -> unit
the current color: set_color : color -> unit
the width of lines: set_line_width : int -> unit
the current character font: set_font : string -> unit
the size of characters: set_text_size : int -> unit

Colors

Colors are represented by three bytes: each stands for the intensity value of a main color in the RGB-model (red, green, blue), ranging from a minimum of 0 to a maximum of 255. The function rgb (of type int -> int -> int -> color) allows the generation of a new color from these three components. If the three components are identical, the resulting color is a gray which is more or less intense depending on the intensity value. Black corresponds to the minimum intensity of each component (0 0 0) and white is the maximum (255 255 255). Certain colors are predefined: black, white, red, green, blue, yellow, cyan and magenta.



The variables foreground and background correspond to the color of the fore- and the background respectively. Clearing the screen is equivalent to filling the screen with the background color.

A color (a value of type color) is in fact an integer which can be manipulated to, for example, decompose the color into its three components (from_rgb) or to apply a function to it that inverts it (inv_color).

(* color == R * 256 * 256 + G * 256 + B *)
# let from_rgb (c : Graphics.color) =
let r = c / 65536 and g = c / 256 mod 256 and b = c mod 256
in (r,g,b);;
val from_rgb : Graphics.color -> int * int * int = <fun>
# let inv_color (c : Graphics.color) =
let (r,g,b) = from_rgb c
in Graphics.rgb (255-r) (255-g) (255-b);;
val inv_color : Graphics.color -> Graphics.color = <fun>


The function point_color, of type int -> int -> color, returns the color of a point when given its coordinates.

Drawing and filling

A drawing function draws a line on the screen. The line is of the current width and color. A filling function fills a closed form with the current color. The various line- and filling functions are presented in figure 5.1.


drawing filling type
plot   int -> int -> unit
lineto   int -> int -> unit
  fill_rect int -> int -> int -> int -> unit
  fill_poly ( int * int) array -> unit
draw_arc fill_arc int -> int -> int -> int -> int -> unit
draw_ellipse fill_ellipse int -> int -> int -> int -> unit
draw_circle fill_circle int -> int -> int -> unit

Figure 5.1: Drawing- and filling functions.


Beware, the function lineto changes the position of the current point to make drawing of vertices more convenient.

Drawing polygons
To give an example, we add drawing primitives which are not predefined. A polygon is described by a table of its vertices.

# let draw_rect x0 y0 w h =
let (a,b) = Graphics.current_point()
and x1 = x0+w and y1 = y0+h
in
Graphics.moveto x0 y0;
Graphics.lineto x0 y1; Graphics.lineto x1 y1;
Graphics.lineto x1 y0; Graphics.lineto x0 y0;
Graphics.moveto a b;;
val draw_rect : int -> int -> int -> int -> unit = <fun>

# let draw_poly r =
let (a,b) = Graphics.current_point () in
let (x0,y0) = r.(0) in Graphics.moveto x0 y0;
for i = 1 to (Array.length r)-1 do
let (x,y) = r.(i) in Graphics.lineto x y
done;
Graphics.lineto x0 y0;
Graphics.moveto a b;;
val draw_poly : (int * int) array -> unit = <fun>


Please note that these functions take the same arguments as the predefined ones for filling forms. Like the other functions for drawing forms, they do not change the current point.

Illustrations in the painter's model
This example generates an illustration of a token ring network (figure 5.2). Each machine is represented by a small circle. We place the set of machines on a big circle and draw a line between the connected machines. The current position of the token in the network is indicated by a small black disk.

The function net_points generates the coordinates of the machines in the network. The resulting data is stored in a table.

# let pi = 3.1415927;;
val pi : float = 3.1415927
# let net_points (x,y) l n =
let a = 2. *. pi /. (float n) in
let rec aux (xa,ya) i =
if i > n then []
else
let na = (float i) *. a in
let x1 = xa + (int_of_float ( cos(na) *. l))
and y1 = ya + (int_of_float ( sin(na) *. l)) in
let np = (x1,y1) in
np::(aux np (i+1))
in Array.of_list (aux (x,y) 1);;
val net_points : int * int -> float -> int -> (int * int) array = <fun>


The function draw_net displays the connections, the machines and the token.

# let draw_net (x,y) l n sc st =
let r = net_points (x,y) l n in
draw_poly r;
let draw_machine (x,y) =
Graphics.set_color Graphics.background;
Graphics.fill_circle x y sc;
Graphics.set_color Graphics.foreground;
Graphics.draw_circle x y sc
in
Array.iter draw_machine r;
Graphics.fill_circle x y st;;
val draw_net : int * int -> float -> int -> int -> int -> unit = <fun>




The following function call corresponds to the left drawing in figure 5.2.

# draw_net (140,20) 60.0 10 10 3;;
- : unit = ()

# save_screen "IMAGES/tokenring.caa";;
- : unit = ()




Figure 5.2: Tokenring network.


We note that the order of drawing objects is important. We first plot the connections then the nodes. The drawing of network nodes erases some part of the connecting lines. Therefore, there is no need to calculate the point of intersection between the connection segments and the circles of the vertices. The right illustration of figure 5.2 inverts the order in which the objects are displayed. We see that the segments appear inside of the circles representing the nodes.

Text

The functions for displaying texts are rather simple. The two functions draw_char (of type char -> unit) and draw_string (of type string -> unit) display a character and a character string respectively at the current point. After displaying, the latter is modified. These functions do not change the current font and its current size.

Note


The displaying of strings may differ depending on the graphical interface.


The function text_size takes a string as input and returns a pair of integers that correspond to the dimensions of this string when it is displayed in the current font and size.

Displaying strings vertically
This example describes the function draw_string_v, which displays a character string vertically at the current point. It is used in figure 5.3. Each letter is displayed separately by changing the vertical coordinate.

# let draw_string_v s =
let (xi,yi) = Graphics.current_point()
and l = String.length s
and (_,h) = Graphics.text_size s
in
Graphics.draw_char s.[0];
for i=1 to l-1 do
let (_,b) = Graphics.current_point()
in Graphics.moveto xi (b-h);
Graphics.draw_char s.[i]
done;
let (a,_) = Graphics.current_point() in Graphics.moveto a yi;;
val draw_string_v : string -> unit = <fun>
This function modifies the current point. After displaying, the point is placed at the initial position offset by the width of one character.

The following program permits displaying a legend around the axes (figure 5.3)

#
Graphics.moveto 0 150; Graphics.lineto 300 150;
Graphics.moveto 2 130; Graphics.draw_string "abscissa";
Graphics.moveto 150 0; Graphics.lineto 150 300;
Graphics.moveto 135 280; draw_string_v "ordinate";;
- : unit = ()




Figure 5.3: Legend around axes.


If we wish to realize vertical displaying of text, it is necessary to account for the fact that the current point is modified by the function draw_string_v. To do this, we define the function draw_text_v, which accepts the spacing between columns and a list of words as parameters.

# let draw_text_v n l =
let f s = let (a,b) = Graphics.current_point()
in draw_string_v s;
Graphics.moveto (a+n) b
in List.iter f l;;
val draw_text_v : int -> string list -> unit = <fun>


If we need further text transformations like, for example, rotation, we will have to take the bitmap of each letter and perform the rotation on this set of pixels.

Bitmaps

A bitmap may be represented by either a color matrix (color array array) or a value of abstract type 1 image, which is declared in library Graphics. The names and types of the functions for manipulating bitmaps are given in figure 5.4.


function type
make_image color array array -> image
dump_image image -> color array array
draw_image image -> int -> int -> unit
get_image int -> int -> int -> int -> image
blit_image image -> int -> int -> unit
create_image int -> int -> image

Figure 5.4: Functions for manipulating bitmaps.


The functions make_image and dump_image are conversion functions between types image and color array array. The function draw_image displays a bitmap starting at the coordinates of its bottom left corner.

The other way round, one can capture a rectangular part of the screen to create an image using the function get_image and by indicating the bottom left corner and the upper right one of the area to be captured. The function blit_image modifies its first parameter (of type image) and captures the region of the screen where the lower left corner is given by the point passed as parameter. The size of the captured region is the one of the image argument. The function create_image allows initializing images by specifying their size to use them with blit_image.

The predefined color transp can be used to create transparent points in an image. This makes it possible to display an image within a rectangular area only; the transparent points do not modify the initial screen.

Polarization of Jussieu
This example inverts the color of points of a bitmap. To do this, we use the function for color inversion presented on page ??, applying it to each pixel of a bitmap.

# let inv_image i =
let inv_vec = Array.map (fun c -> inv_color c) in
let inv_mat = Array.map inv_vec in
let inverted_matrix = inv_mat (Graphics.dump_image i) in
Graphics.make_image inverted_matrix;;
val inv_image : Graphics.image -> Graphics.image = <fun>


Given the bitmap jussieu, which is displayed in the left half of figure 5.5, we use the function inv_image and obtain a new ``solarized'' bitmap, which is displayed in the right half of the same figure.


# let f_jussieu2 () = inv_image jussieu1;;
val f_jussieu2 : unit -> Graphics.image = <fun>




Figure 5.5: Inversion of Jussieu.


Example: drawing of boxes with relief patterns

In this example we will define a few utility functions for drawing boxes that carry relief patterns. A box is a generic object that is useful in many cases. It is inscribed in a rectangle which is characterized by a point of origin, a height and a width.

To give an impression of a box with a relief pattern, it is sufficient to surround it with two trapezoids in a light color and two others in a somewhat darker shade.
  

Inverting the colors, one can give the impression that the boxes are on top or at the bottom.
 

Implementation
We add the border width, the display mode (top, bottom, flat) and the colors of its edges and of its bottom. This information is collected in a record.

# type relief = Top | Bot | Flat;;
# type box_config =
{ x:int; y:int; w:int; h:int; bw:int; mutable r:relief;
b1_col : Graphics.color;
b2_col : Graphics.color;
b_col : Graphics.color};;
Only field r can be modified. We use the function draw_rect defined at page ??, which draws a rectangle.

For convenience, we define a function for drawing the outline of a box.

# let draw_box_outline bcf col =
Graphics.set_color col;
draw_rect bcf.x bcf.y bcf.w bcf.h;;
val draw_box_outline : box_config -> Graphics.color -> unit = <fun>


The function of displaying a box consists of three parts: drawing the first edge, drawing the second edge and drawing the interior of the box.

# let draw_box bcf =
let x1 = bcf.x and y1 = bcf.y in
let x2 = x1+bcf.w and y2 = y1+bcf.h in
let ix1 = x1+bcf.bw and ix2 = x2-bcf.bw
and iy1 = y1+bcf.bw and iy2 = y2-bcf.bw in
let border1 g =
Graphics.set_color g;
Graphics.fill_poly
[| (x1,y1);(ix1,iy1);(ix2,iy1);(ix2,iy2);(x2,y2);(x2,y1) |]
in
let border2 g =
Graphics.set_color g;
Graphics.fill_poly
[| (x1,y1);(ix1,iy1);(ix1,iy2);(ix2,iy2);(x2,y2);(x1,y2) |]
in
Graphics.set_color bcf.b_col;
( match bcf.r with
Top ->
Graphics.fill_rect ix1 iy1 (ix2-ix1) (iy2-iy1);
border1 bcf.b1_col;
border2 bcf.b2_col
| Bot ->
Graphics.fill_rect ix1 iy1 (ix2-ix1) (iy2-iy1);
border1 bcf.b2_col;
border2 bcf.b1_col
| Flat ->
Graphics.fill_rect x1 y1 bcf.w bcf.h );
draw_box_outline bcf Graphics.black;;
val draw_box : box_config -> unit = <fun>


The outline of boxes is highlighted in black. Erasing a box fills the area it covers with the background color.

# let erase_box bcf =
Graphics.set_color bcf.b_col;
Graphics.fill_rect (bcf.x+bcf.bw) (bcf.y+bcf.bw)
(bcf.w-(2*bcf.bw)) (bcf.h-(2*bcf.bw));;
val erase_box : box_config -> unit = <fun>


Finally, we define a function for displaying a character string at the left, right or in the middle of the box. We use the type position to describe the placement of the string.

# type position = Left | Center | Right;;
type position = | Left | Center | Right
# let draw_string_in_box pos str bcf col =
let (w, h) = Graphics.text_size str in
let ty = bcf.y + (bcf.h-h)/2 in
( match pos with
Center -> Graphics.moveto (bcf.x + (bcf.w-w)/2) ty
| Right -> let tx = bcf.x + bcf.w - w - bcf.bw - 1 in
Graphics.moveto tx ty
| Left -> let tx = bcf.x + bcf.bw + 1 in Graphics.moveto tx ty );
Graphics.set_color col;
Graphics.draw_string str;;
val draw_string_in_box :
position -> string -> box_config -> Graphics.color -> unit = <fun>


Example: drawing of a game
We illustrate the use of boxes by displaying the position of a game of type ``tic-tac-toe'' as shown in figure 5.6. To simplify the creation of boxes, we predefine colors.

# let set_gray x = (Graphics.rgb x x x);;
val set_gray : int -> Graphics.color = <fun>
# let gray1= set_gray 100 and gray2= set_gray 170 and gray3= set_gray 240;;
val gray1 : Graphics.color = 6579300
val gray2 : Graphics.color = 11184810
val gray3 : Graphics.color = 15790320


We define a function for creating a grid of boxes of same size.

# let rec create_grid nb_col n sep b =
if n < 0 then []
else
let px = n mod nb_col and py = n / nb_col in
let nx = b.x +sep + px*(b.w+sep)
and ny = b.y +sep + py*(b.h+sep) in
let b1 = {b with x=nx; y=ny} in
b1::(create_grid nb_col (n-1) sep b);;
val create_grid : int -> int -> int -> box_config -> box_config list = <fun>


And we create the vector of boxes:

# let vb =
let b = {x=0; y=0; w=20;h=20; bw=2;
b1_col=gray1; b2_col=gray3; b_col=gray2; r=Top} in
Array.of_list (create_grid 5 24 2 b);;
val vb : box_config array =
[|{x=90; y=90; w=20; h=20; bw=2; r=Top; b1_col=6579300; b2_col=15790320;
b_col=11184810};
{x=68; y=90; w=20; h=20; bw=2; r=Top; b1_col=6579300; b2_col=15790320;
b_col=...};
...|]


Figure 5.6 corresponds to the following function calls:


# Array.iter draw_box vb;
draw_string_in_box Center "X" vb.(5) Graphics.black;
draw_string_in_box Center "X" vb.(8) Graphics.black;
draw_string_in_box Center "O" vb.(12) Graphics.yellow;
draw_string_in_box Center "O" vb.(11) Graphics.yellow;;
- : unit = ()




Figure 5.6: Displaying of boxes with text.



Previous Contents Next ocaml-book-1.0/en/html/book-ora023.gif0000644000000000000000000001267607452056111014273 0ustar GIF89aYUUU!,Yڋ޼HF@wGf19_xW9ʿS9CӯA{/é2jָ} cIdr4&Ѝ]&('觘'3HC9xyAЈ YisWJj:jHxz Ku5;WY̬)) \m +M]\k;>N=\N &|n^]N? ?{3S<^d "a$u])sR )~ʢ:z~z'( i*,f+ꩳӾ>ikv计K^j~ˬ2I ׭..B++ .,u.):  򨡳}Ͼynx#o8ڼG1On;CO~^3o/n} ؿyC?.mT?  ."a Z̈́ڱ0B`X\"]L/pA-!1.2%:Pdb wC Y /^ # E*m9BW+J HJ!![z,୬B$~cƿP"{J<b(F24`&?)CNcSrg&Aѓe,SRu!ȲW:>/KX; 'ӈ L&'3JsL. HiN$'1iE]r7iN3f< 3 s2cs~뙾(<zO6|\ό3E(D5QsjHu r Rԥ6h85:ӛN 6 h˹9 u0(ۈTUImRkT4T)::`ZjNը>!3Nү>0PO=V E= P+G? T.4!l`jO(溾El;Y6 h 9t=cKXԲhͬfΊv,hxPk [Almu㮲EnG [ĵ+f_7tto[Ɩc-'+Y-m1uBC}Ӌ%rnw[笷pi%]{; oS*4"׶gM3 nk{KZ冖pKO+ Xcebć~ޗ~G_%fU{'ivGb~Dv{Yxw7EPr;G'u~VcH/jHx~c#H2)H굀ch灠:ͳ!w86(=(?H=H2JAu(]DŽ rXX&>ׅ+@؅Xf8DR؄i؂gYtX[(Vwsxk'|VQ؆Cw ȄMhx~~*((W8]x}ȉ~rc4X(~Wy~ XȈׁ7gX8zH1vNjBp،z8"h8H蘎ȌG`8}f֏8}وGV(oI|vH؉wlظ(oV~n I)x(c(. d/i8#3i_@v*h獉x~!$pT+y!K928T9"p:GCB`#=nfh,G@B;p_JtD)`J4rJ)Z|~-腏RVδ fȂ/zo6yɃ!AdG:t.1Iyyqա٘BT1yjyiFB)- yq2ŜOd)չ yɇOr.yB鐟 ɖ7QF =i3VyѠ-Y@9 fӔ9& ȡAi,Z؋gm9& ;)Hr(:<ڣ/zG0YXǏYvVkyh^U&cJejgi;mo q*sj ^Jwy{ʧ} *Jjʨꨏ *Jjʩ꩟ *Jjʪꪯ *Jjʫ꫿ *JjNJɪʬ *Jj׊٪ʭ:a *Jj犮H5 *Jj躮ʯ +Kk ˰  +Kk˱ !+#K%k')+˲-/ 1+3K5k79;˳=? jV0Zh"يyC;x9ʆ(ʴeڴMX<&eDERZdzhzjPkcڶ'G "jm:NIyk"}k[zjCV۸|z{ۯ˵IkQSK@[|Eص븮 x*J/ ˍZS ڻe;;K +3~ۼ۹ċ뼯{ūucC:{b{K۾[z;lʼ˸+tJ* k+뿥f nZl{f¾+;l-zB{FHAFO L<9V|/̿\L%6 +éeMK@̽X Qc<5@tv|ɖi\]kLN{\Dp\G@D6gDxǍ[\MGtXdż{ɠ! e,6V@Jr/b"n Lé@n)!bjnw,0duF!y, ~ݙs+-.`J9ıBGGR^V_詭33p.M !B"1_ζ\n&nHALt^iۙN[a SRn覴D ~9'1&>0NN~xE2i'L 9\.NA>NΤ+pBp}ޠSƉGN3@,_ZaܚB!~JVo m B?/f=_d-,niks`~rouv\r;Vi'Z.ϲOZ;ocaml-book-1.0/en/html/book-ora058.html0000644000000000000000000034313207453055400014475 0ustar BASIC interpreter Previous Contents Next

BASIC interpreter

The application described in this section is a program interpreter for Basic. Thus, it is a program that can run other programs written in Basic. Of course, we will only deal with a restricted language, which contains the following commands:

  • PRINT expression

       Prints the result of the evaluation of the expression.

  • INPUT variable

       Prints a prompt (?), reads an integer typed in by the user, and assigns its value to the variable.

  • LET variable = expression

       Assigns the result of the evaluation of expression to the variable.

  • GOTO line number

       Continues execution at the given line.

  • IF condition THEN line number

       Continues execution at the given line if the condition is true.

  • REM any string

       One-line comment.
Every line of a Basic program is labelled with a line number, and contains only one command. For instance, a program that computes and then prints the factorial of an integer given by the user is written:
 
 5  REM inputting the argument
10  PRINT " factorial of:"
20  INPUT A
30  LET B = 1 
35  REM beginning of the loop
40  IF A <= 1 THEN 80 
50  LET B = B * A
60  LET A = A - 1
70  GOTO 40 
75  REM prints the result
80  PRINT B
We also wish to write a small text editor, working as a toplevel interactive loop. It should be able to add new lines, display a program, execute it, and display the result. Execution of the program is started with the RUN command. Here is an example of the evaluation of this program:
> RUN
 factorial of: ? 5
120
The interpreter is implemented in several distinct parts:
Description of the abstract syntax
: describes the definition of data types to represent Basic programs, as well as their components (lines, commands, expressions, etc.).

Program pretty printing
: consists of transforming the internal representation of Basic programs to strings, in order to display them.

Lexing and parsing
: accomplish the inverse transformation, that is, transform a string into the internal representation of a Basic program (the abstract syntax).

Evaluation
: is the heart of the interpreter. It controls and runs the program. As we will see, functional languages, such as Objective CAML, are particularly well adapted for this kind of problem.

Toplevel interactive loop
: glues together all the previous parts.

Abstract syntax

Figure 6.2 introduces the concrete syntax, as a BNF grammar, of the Basic we will implement. This kind of description for language syntaxes is described in chapter 11, page ??.

Unary_Op ::= -    |    !
Binary_Op ::= +    |    -    |    *    |    /    |    %
  | =    |    <    |    >    |    <=    |    >=    |    <>
  | &    |    ' | '
Expression ::= integer
  | variable
  | "string"
  | Unary_Op   Expression
  | Expression   Binary_Op   Expression
  | ( Expression )
Command ::= REM string
  | GOTO integer
  | LET variable = Expression
  | PRINT Expression
  | INPUT variable
  | IF Expression THEN integer
 
Line ::= integer Command
 
Program ::= Line
  | Line Program
 
Phrase ::= Line | RUN | LIST | END

Figure 6.2: BASIC Grammar.


We can see that the way expressions are defined does not ensure that a well formed expression can be evaluated. For instance, 1+"hello" is an expression, and yet it is not possible to evaluate it. This deliberate choice lets us simplify both the abstract syntax and the parsing of the Basic language. The price to pay for this choice is that a syntactically correct Basic program may generate a runtime error because of a type mismatch.

Defining Objective CAML data types for this abstract syntax is easy, we simply translate the concrete syntax into a sum type:

# type unr_op = UMINUS | NOT ;;
# type bin_op = PLUS | MINUS | MULT | DIV | MOD
| EQUAL | LESS | LESSEQ | GREAT | GREATEQ | DIFF
| AND | OR ;;
# type expression =
ExpInt of int
| ExpVar of string
| ExpStr of string
| ExpUnr of unr_op * expression
| ExpBin of expression * bin_op * expression ;;
# type command =
Rem of string
| Goto of int
| Print of expression
| Input of string
| If of expression * int
| Let of string * expression ;;
# type line = { num : int ; cmd : command } ;;
# type program = line list ;;


We also define the abstract syntax for the commands for the small program editor:

# type phrase = Line of line | List | Run | PEnd ;;


It is convenient to allow the programmer to skip some parentheses in arithmetic expressions. For instance, the expression 1+3*4 is usually interpreted as 1+(3*4). To this end, we associate an integer with each operator of the language:

# let priority_uop = function NOT -> 1 | UMINUS -> 7
let priority_binop = function
MULT | DIV -> 6
| PLUS | MINUS -> 5
| MOD -> 4
| EQUAL | LESS | LESSEQ | GREAT | GREATEQ | DIFF -> 3
| AND | OR -> 2 ;;
val priority_uop : unr_op -> int = <fun>
val priority_binop : bin_op -> int = <fun>
These integers indicate the priority of the operators. They will be used to print and parse programs.

Program pretty printing

To print a program, one needs to be able to convert abstract syntax program lines into strings.

Converting operators is easy:

# let pp_binop = function
PLUS -> "+" | MULT -> "*" | MOD -> "%" | MINUS -> "-"
| DIV -> "/" | EQUAL -> " = " | LESS -> " < "
| LESSEQ -> " <= " | GREAT -> " > "
| GREATEQ -> " >= " | DIFF -> " <> " | AND -> " & " | OR -> " | "
let pp_unrop = function UMINUS -> "-" | NOT -> "!" ;;
val pp_binop : bin_op -> string = <fun>
val pp_unrop : unr_op -> string = <fun>
Expression printing needs to take into account operator priority to print as few parentheses as possible. For instance, parentheses are put around a subexpression at the right of an operator only if the subexpression's main operator has a lower priority that the main operator of the whole expression. Also, arithmetic operators are left-associative, thus the expression 1-2-3 is interpreted as (1-2)-3.

To deal with this, we use two auxiliary functions ppl and ppr to print left and right subtrees, respectively. These functions take two arguments: the tree to print and the priority of the enclosing operator, which is used to decide if parentheses are necessary. Left and right subtrees are distinguished to deal with associativity. If the current operator priority is the same than the enclosing operator priority, left trees do not need parentheses whereas right ones may require them, as in 1-(2-3) or 1-(2+3).

The initial tree is taken as a left subtree with minimal priority (0). The expression pretty printing function pp_expression is:

# let parenthesis x = "(" ^ x ^ ")";;
val parenthesis : string -> string = <fun>
# let pp_expression =
let rec ppl pr = function
ExpInt n -> (string_of_int n)
| ExpVar v -> v
| ExpStr s -> "\"" ^ s ^ "\""
| ExpUnr (op,e) ->
let res = (pp_unrop op)^(ppl (priority_uop op) e)
in if pr=0 then res else parenthesis res
| ExpBin (e1,op,e2) ->
let pr2 = priority_binop op
in let res = (ppl pr2 e1)^(pp_binop op)^(ppr pr2 e2)
(* parenthesis if priority is not greater *)
in if pr2 >= pr then res else parenthesis res
and ppr pr exp = match exp with
(* right subtrees only differ for binary operators *)
ExpBin (e1,op,e2) ->
let pr2 = priority_binop op
in let res = (ppl pr2 e1)^(pp_binop op)^(ppr pr2 e2)
in if pr2 > pr then res else parenthesis res
| _ -> ppl pr exp
in ppl 0 ;;
val pp_expression : expression -> string = <fun>


Command pretty printing uses the expression pretty printing function. Printing a line consists of printing the line number before the command.

# let pp_command = function
Rem s -> "REM " ^ s
| Goto n -> "GOTO " ^ (string_of_int n)
| Print e -> "PRINT " ^ (pp_expression e)
| Input v -> "INPUT " ^ v
| If (e,n) -> "IF "^(pp_expression e)^" THEN "^(string_of_int n)
| Let (v,e) -> "LET " ^ v ^ " = " ^ (pp_expression e) ;;
val pp_command : command -> string = <fun>
# let pp_line l = (string_of_int l.num) ^ " " ^ (pp_command l.cmd) ;;
val pp_line : line -> string = <fun>


Lexing

Lexing and parsing do the inverse transformation of printing, going from a string to a syntax tree. Lexing splits the text of a command line into independent lexical units called lexemes, with Objective CAML type:

# type lexeme = Lint of int
| Lident of string
| Lsymbol of string
| Lstring of string
| Lend ;;
A particular lexeme denotes the end of an expression: Lend. It is not present in the text of the expression, but is created by the lexing function (see the lexer function, page ??).

The string being lexed is kept in a record that contains a mutable field indicating the position after which lexing has not been done yet. Since the size of the string is used several times and does not change, it is also stored in the record:

# type string_lexer = {string:string; mutable current:int; size:int } ;;
This representation lets us define the lexing of a string as the application of a function to a value of type string_lexer returning a value of type lexeme. Modifying the current position in the string is done as a side effect.


# let init_lex s = { string=s; current=0 ; size=String.length s } ;;
val init_lex : string -> string_lexer = <fun>
# let forward cl = cl.current <- cl.current+1 ;;
val forward : string_lexer -> unit = <fun>
# let forward_n cl n = cl.current <- cl.current+n ;;
val forward_n : string_lexer -> int -> unit = <fun>
# let extract pred cl =
let st = cl.string and pos = cl.current in
let rec ext n = if n<cl.size && (pred st.[n]) then ext (n+1) else n in
let res = ext pos
in cl.current <- res ; String.sub cl.string pos (res-pos) ;;
val extract : (char -> bool) -> string_lexer -> string = <fun>


The following functions extract a lexeme from the string and modify the current position. The two functions extract_int and extract_ident extract an integer and an identifier, respectively.

# let extract_int =
let is_int = function '0'..'9' -> true | _ -> false
in function cl -> int_of_string (extract is_int cl)
let extract_ident =
let is_alpha_num = function
'a'..'z' | 'A'..'Z' | '0' .. '9' | '_' -> true
| _ -> false
in extract is_alpha_num ;;
val extract_int : string_lexer -> int = <fun>
val extract_ident : string_lexer -> string = <fun>


The lexer function uses the two previous functions to extract a lexeme.

# exception LexerError ;;
exception LexerError
# let rec lexer cl =
let lexer_char c = match c with
' '
| '\t' -> forward cl ; lexer cl
| 'a'..'z'
| 'A'..'Z' -> Lident (extract_ident cl)
| '0'..'9' -> Lint (extract_int cl)
| '"' -> forward cl ;
let res = Lstring (extract ((<>) '"') cl)
in forward cl ; res
| '+' | '-' | '*' | '/' | '%' | '&' | '|' | '!' | '=' | '(' | ')' ->
forward cl; Lsymbol (String.make 1 c)
| '<'
| '>' -> forward cl;
if cl.current >= cl.size then Lsymbol (String.make 1 c)
else let cs = cl.string.[cl.current]
in ( match (c,cs) with
('<','=') -> forward cl; Lsymbol "<="
| ('>','=') -> forward cl; Lsymbol ">="
| ('<','>') -> forward cl; Lsymbol "<>"
| _ -> Lsymbol (String.make 1 c) )
| _ -> raise LexerError
in
if cl.current >= cl.size then Lend
else lexer_char cl.string.[cl.current] ;;
val lexer : string_lexer -> lexeme = <fun>


The lexer function is very simple: it matches the current character of a string and, based on its value, extracts the corresponding lexeme and modifies the current position to the start of the next lexeme. The code is simple because, for all characters except two, the current character defines which lexeme to extract. In the more complicated cases of '<', we need to look at the next character, which might be a '=' or a '>', producing two different lexemes. The same problem arises with '>'.

Parsing

The only difficulty in parsing our language comes from expressions. Indeed, knowing the beginning of an expression is not enough to know its structure. For instance, having parsed the beginning of an expression as being 1+2+3, the resulting syntax tree for this part depends on the rest of the expression: its structure is different when it is followed by +4 or *4 (see figure 6.3).


Figure 6.3: Basic: abstract syntax tree examples.


However, since the tree structure for 1+2 is the same in both cases, it can be built. As the position of +3 in the structure is not fully known, it is temporarily stored.

To build the abstract syntax tree, we use a pushdown automaton similar to the one built by yacc (see page ??). Lexemes are read one by one and put on a stack until there is enough information to build the expression. They are then removed from the stack and replaced by the expression. This latter operation is called reduction.

The stack elements have type:

# type exp_elem =
Texp of expression (* expression *)
| Tbin of bin_op (* binary operator *)
| Tunr of unr_op (* unary operator *)
| Tlp (* left parenthesis *) ;;
Right parentheses are not stored on the stack as only left parentheses matter for reduction.

Figure 6.4 illustrates the way the stack is used to parse the expression (1+2*3)+4. The character above the arrow is the current character of the string.


Figure 6.4: Basic: abstract syntax tree construction example.


We define an exception for syntax errors.

# exception ParseError ;;
The first step consists of transforming symbols into operators:

# let unr_symb = function
"!" -> NOT | "-" -> UMINUS | _ -> raise ParseError
let bin_symb = function
"+" -> PLUS | "-" -> MINUS | "*" -> MULT | "/" -> DIV | "%" -> MOD
| "=" -> EQUAL | "<" -> LESS | "<=" -> LESSEQ | ">" -> GREAT
| ">=" -> GREATEQ | "<>" -> DIFF | "&" -> AND | "|" -> OR
| _ -> raise ParseError
let tsymb s = try Tbin (bin_symb s) with ParseError -> Tunr (unr_symb s) ;;
val unr_symb : string -> unr_op = <fun>
val bin_symb : string -> bin_op = <fun>
val tsymb : string -> exp_elem = <fun>


The reduce function implements stack reduction. There are two cases to consider, whether the stack starts with:
  • an expression followed by a unary operator,
  • an expression followed by a binary operator and an expression.
Moreover, reduce takes an argument indicating the minimal priority that an operator should have to trigger reduction. To avoid this reduction condition, it suffices to give the minimal value, zero, as the priority.

# let reduce pr = function
(Texp e)::(Tunr op)::st when (priority_uop op) >= pr
-> (Texp (ExpUnr (op,e)))::st
| (Texp e1)::(Tbin op)::(Texp e2)::st when (priority_binop op) >= pr
-> (Texp (ExpBin (e2,op,e1)))::st
| _ -> raise ParseError ;;
val reduce : int -> exp_elem list -> exp_elem list = <fun>


Notice that expression elements are stacked as they are read. Thus it is necessary to swap them when they are arguments of a binary operator.

The main function of our parser is stack_or_reduce that, according to the lexeme given in argument, puts it on the stack or triggers a reduction.

# let rec stack_or_reduce lex stack = match lex , stack with
Lint n , _ -> (Texp (ExpInt n))::stack
| Lident v , _ -> (Texp (ExpVar v))::stack
| Lstring s , _ -> (Texp (ExpStr s))::stack
| Lsymbol "(" , _ -> Tlp::stack
| Lsymbol ")" , (Texp e)::Tlp::st -> (Texp e)::st
| Lsymbol ")" , _ -> stack_or_reduce lex (reduce 0 stack)
| Lsymbol s , _
-> let symbol =
if s<>"-" then tsymb s
(* remove the ambiguity of the ``-'' symbol *)
(* according to the last exp element put on the stack *)
else match stack
with (Texp _)::_ -> Tbin MINUS
| _ -> Tunr UMINUS
in ( match symbol with
Tunr op -> (Tunr op)::stack
| Tbin op ->
( try stack_or_reduce lex (reduce (priority_binop op)
stack )
with ParseError -> (Tbin op)::stack )
| _ -> raise ParseError )
| _ , _ -> raise ParseError ;;
val stack_or_reduce : lexeme -> exp_elem list -> exp_elem list = <fun>


Once all lexemes are defined and stacked, the function reduce_all builds the abstract syntax tree with the elements remaining in the stack. If the expression being parsed is well formed, only one element should remain in the stack, containing the tree for this expression.

# let rec reduce_all = function
| [] -> raise ParseError
| [Texp x] -> x
| st -> reduce_all (reduce 0 st) ;;
val reduce_all : exp_elem list -> expression = <fun>


The parse_exp function is the main expression parsing function. It reads a string, extracts its lexemes and passes them to the stack_or_reduce function. Parsing stops when the current lexeme satisfies a predicate that is given as an argument.

# let parse_exp stop cl =
let p = ref 0 in
let rec parse_one stack =
let l = ( p:=cl.current ; lexer cl)
in if not (stop l) then parse_one (stack_or_reduce l stack)
else ( cl.current <- !p ; reduce_all stack )
in parse_one [] ;;
val parse_exp : (lexeme -> bool) -> string_lexer -> expression = <fun>
Notice that the lexeme that made the parsing stop is not used to build the expression. It is thus necessary to modify the current position to its beginning (variable p) to parse it later.

We can now parse a command line:

# let parse_cmd cl = match lexer cl with
Lident s -> ( match s with
"REM" -> Rem (extract (fun _ -> true) cl)
| "GOTO" -> Goto (match lexer cl with
Lint p -> p
| _ -> raise ParseError)
| "INPUT" -> Input (match lexer cl with
Lident v -> v
| _ -> raise ParseError)
| "PRINT" -> Print (parse_exp ((=) Lend) cl)
| "LET" ->
let l2 = lexer cl and l3 = lexer cl
in ( match l2 ,l3 with
(Lident v,Lsymbol "=") -> Let (v,parse_exp ((=) Lend) cl)
| _ -> raise ParseError )
| "IF" ->
let test = parse_exp ((=) (Lident "THEN")) cl
in ( match ignore (lexer cl) ; lexer cl with
Lint n -> If (test,n)
| _ -> raise ParseError )
| _ -> raise ParseError )
| _ -> raise ParseError ;;
val parse_cmd : string_lexer -> command = <fun>


Finally, we implement the function to parse commands typed by the user:

# let parse str =
let cl = init_lex str
in match lexer cl with
Lint n -> Line { num=n ; cmd=parse_cmd cl }
| Lident "LIST" -> List
| Lident "RUN" -> Run
| Lident "END" -> PEnd
| _ -> raise ParseError ;;
val parse : string -> phrase = <fun>


Evaluation

A Basic program is a list of lines. Execution starts at the first line. Interpreting a program line consists of executing the task corresponding to its command. There are three different kinds of commands: input-output (PRINT and INPUT), variable declaration or modification (LET), and flow control (GOTO and IF...THEN). Input-output commands interact with the user and use the corresponding Objective CAML functions.

Variable declaration and modification commands need to know how to compute the value of an arithmetic expression and the memory location to store the result. Expression evaluation returns an integer, a boolean, or a string. Their type is value.

# type value = Vint of int | Vstr of string | Vbool of bool ;;


Variable declaration should allocate some memory to store the associated value. Similarly, variable modification requires the modification of the associated value. Thus, evaluation of a Basic program uses an environment that stores the association between a variable name and its value. It is represented by an association list of tuples (name,value):
 
# type environment = (string * value) list ;;
The variable name is used to access its value. Variable modification modifies the association.

Flow control commands, conditional or unconditional, specify the number of the next line to execute. By default, it is the next line. To do this, it is necessary to remember the number of the current line.

The list of commands representing the program being edited under the toplevel is not an efficient data structure for running the program. Indeed, it is then necessary to look at the whole list of lines to find the line indicated by a flow control command (If and goto). Replacing the list of lines with an array of commands allows direct access to the command following a flow control command, using the array index instead of the line number in the flow control command. This solution requires some preprocessing called assembly before executing a RUN command. For reasons that will be detailed shortly, a program after assembly is not represented as an array of commands but as an array of lines:

# type code = line array ;;


As in the calculator example of previous chapters, the interpreter uses a state that is modified for each command evaluation. At each step, we need to remember the whole program, the next line to interpret and the values of the variables. The program being interpreted is not exactly the one that was entered in the toplevel: instead of a list of commands, it is an array of commands. Thus the state of a program during execution is:

# type state_exec = { line:int ; xprog:code ; xenv:environment } ;;


Two different reasons may lead to an error during the evaluation of a line: an error while computing an expression, or branching to an absent line. They must be dealt with so that the interpreter exits nicely, printing an error message. We define an exception as well as a function to raise it, indicating the line where the error occurred.

# exception RunError of int
let runerr n = raise (RunError n) ;;
exception RunError of int
val runerr : int -> 'a = <fun>


Assembly
Assembling a program that is a list of numbered lines (type program) consists of transforming this list into an array and modifying the flow control commands. This last modification only needs an association table between line numbers and array indexes. This is easily provided by storing lines (with their line numbers), instead of commands, in the array: to find the association between a line number and the index in the array, we look the line number up in the array and return the corresponding index. If no line is found with this number, the index returned is -1.

# exception Result_lookup_index of int ;;
exception Result_lookup_index of int
# let lookup_index tprog num_line =
try
for i=0 to (Array.length tprog)-1 do
let num_i = tprog.(i).num
in if num_i=num_line then raise (Result_lookup_index i)
else if num_i>num_line then raise (Result_lookup_index (-1))
done ;
(-1 )
with Result_lookup_index i -> i ;;
val lookup_index : line array -> int -> int = <fun>

# let assemble prog =
let tprog = Array.of_list prog in
for i=0 to (Array.length tprog)-1 do
match tprog.(i).cmd with
Goto n -> let index = lookup_index tprog n
in tprog.(i) <- { tprog.(i) with cmd = Goto index }
| If(c,n) -> let index = lookup_index tprog n
in tprog.(i) <- { tprog.(i) with cmd = If (c,index) }
| _ -> ()
done ;
tprog ;;
val assemble : line list -> line array = <fun>


Expression evaluation
The evaluation function does a depth-first traversal on the abstract syntax tree, and executes the operations indicated at each node.

The RunError exception is raised in case of type inconsistency, division by zero, or an undeclared variable.

# let rec eval_exp n envt expr = match expr with
ExpInt p -> Vint p
| ExpVar v -> ( try List.assoc v envt with Not_found -> runerr n )
| ExpUnr (UMINUS,e) ->
( match eval_exp n envt e with
Vint p -> Vint (-p)
| _ -> runerr n )
| ExpUnr (NOT,e) ->
( match eval_exp n envt e with
Vbool p -> Vbool (not p)
| _ -> runerr n )
| ExpStr s -> Vstr s
| ExpBin (e1,op,e2)
-> match eval_exp n envt e1 , op , eval_exp n envt e2 with
Vint v1 , PLUS , Vint v2 -> Vint (v1 + v2)
| Vint v1 , MINUS , Vint v2 -> Vint (v1 - v2)
| Vint v1 , MULT , Vint v2 -> Vint (v1 * v2)
| Vint v1 , DIV , Vint v2 when v2<>0 -> Vint (v1 / v2)
| Vint v1 , MOD , Vint v2 when v2<>0 -> Vint (v1 mod v2)

| Vint v1 , EQUAL , Vint v2 -> Vbool (v1 = v2)
| Vint v1 , DIFF , Vint v2 -> Vbool (v1 <> v2)
| Vint v1 , LESS , Vint v2 -> Vbool (v1 < v2)
| Vint v1 , GREAT , Vint v2 -> Vbool (v1 > v2)
| Vint v1 , LESSEQ , Vint v2 -> Vbool (v1 <= v2)
| Vint v1 , GREATEQ , Vint v2 -> Vbool (v1 >= v2)

| Vbool v1 , AND , Vbool v2 -> Vbool (v1 && v2)
| Vbool v1 , OR , Vbool v2 -> Vbool (v1 || v2)

| Vstr v1 , PLUS , Vstr v2 -> Vstr (v1 ^ v2)
| _ , _ , _ -> runerr n ;;
val eval_exp : int -> (string * value) list -> expression -> value = <fun>


Command evaluation
To evaluate a command, we need a few additional functions.

We add an association to an environment by removing a previous association for the same variable name if there is one:

# let rec add v e env = match env with
[] -> [v,e]
| (w,f)::l -> if w=v then (v,e)::l else (w,f)::(add v e l) ;;
val add : 'a -> 'b -> ('a * 'b) list -> ('a * 'b) list = <fun>


A function that prints the value of an integer or string is useful for evaluation of the PRINT command.

# let print_value v = match v with
Vint n -> print_int n
| Vbool true -> print_string "true"
| Vbool false -> print_string "false"
| Vstr s -> print_string s ;;
val print_value : value -> unit = <fun>


The execution of a command corresponds to a transition from one state to another. More precisely, the environment is modified if the command is an assignment. Furthermore, the next line to execute is always modified. As a convention, if the next line to execute does not exist, we set its value to -1

# let next_line state =
let n = state.line+1 in
if n < Array.length state.xprog then n else -1 ;;
val next_line : state_exec -> int = <fun>
# let eval_cmd state =
match state.xprog.(state.line).cmd with
Rem _ -> { state with line = next_line state }
| Print e -> print_value (eval_exp state.line state.xenv e) ;
print_newline () ;
{ state with line = next_line state }
| Let(v,e) -> let ev = eval_exp state.line state.xenv e
in { state with line = next_line state ;
xenv = add v ev state.xenv }
| Goto n -> { state with line = n }
| Input v -> let x = try read_int ()
with Failure "int_of_string" -> 0
in { state with line = next_line state;
xenv = add v (Vint x) state.xenv }
| If (t,n) -> match eval_exp state.line state.xenv t with
Vbool true -> { state with line = n }
| Vbool false -> { state with line = next_line state }
| _ -> runerr state.line ;;
val eval_cmd : state_exec -> state_exec = <fun>


On each call of the transition function eval_cmd, we look up the current line, run it, then set the number of the next line to run as the current line. If the last line of the program is reached, the current line is given the value -1. This will tell us when to stop.

Program evaluation
We recursively apply the transition function until we reach a state where the current line number is -1.

# let rec run state =
if state.line = -1 then state else run (eval_cmd state) ;;
val run : state_exec -> state_exec = <fun>


Finishing touches

The only thing left to do is to write a small editor and to plug together all the functions we wrote in the previous sections.

The insert function adds a new line in the program at the requested place.

# let rec insert line p = match p with
[] -> [line]
| l::prog ->
if l.num < line.num then l::(insert line prog)
else if l.num=line.num then line::prog
else line::l::prog ;;
val insert : line -> line list -> line list = <fun>


The print_prog function prints the source code of a program.

# let print_prog prog =
let print_line x = print_string (pp_line x) ; print_newline () in
print_newline () ;
List.iter print_line prog ;
print_newline () ;;
val print_prog : line list -> unit = <fun>


The one_command function processes the insertion of a line or the execution of a command. It modifies the state of the toplevel loop, which consists of a program and an environment. This state, represented by the loop_state type, is different from the evaluation state.

# type loop_state = { prog:program; env:environment } ;;
# exception End ;;

# let one_command state =
print_string "> " ; flush stdout ;
try
match parse (input_line stdin) with
Line l -> { state with prog = insert l state.prog }
| List -> (print_prog state.prog ; state )
| Run
-> let tprog = assemble state.prog in
let xstate = run { line = 0; xprog = tprog; xenv = state.env } in
{state with env = xstate.xenv }
| PEnd -> raise End
with
LexerError -> print_string "Illegal character\n"; state
| ParseError -> print_string "syntax error\n"; state
| RunError n ->
print_string "runtime error at line ";
print_int n ;
print_string "\n";
state ;;
val one_command : loop_state -> loop_state = <fun>


The main function is the go function, which starts the toplevel loop of our Basic.

# let go () =
try
print_string "Mini-BASIC version 0.1\n\n";
let rec loop state = loop (one_command state) in
loop { prog = []; env = [] }
with End -> print_string "See you later...\n";;
val go : unit -> unit = <fun>
The loop is implemented by the local function loop. It stops when the End exception is raised by the one_command function.

Example: C+/C-

We return to the example of the C+/C- game described in chapter 3, page ??. Here is the Basic program corresponding to that Objective CAML program:

10 PRINT "Give the hidden number: "
20 INPUT N
30 PRINT "Give a number: "
40 INPUT R
50 IF R = N THEN 110
60 IF R < N THEN 90
70 PRINT "C-"
80 GOTO 30
90 PRINT "C+"
100 GOTO 30
110 PRINT "CONGRATULATIONS"
And here is a sample run of this program.

> RUN
Give the hidden number:
64
Give a number:
88
C-
Give a number:
44
C+
Give a number:
64
CONGRATULATIONS

Further work

The Basic we implemented is minimalist. If you want to go further, the following exercises hint at some possible extensions.

  1. Floating-point numbers: as is, our language only deals with integers, strings and booleans. Add floats, as well as the corresponding arithmetic operations in the language grammar. We need to modify not only parsing, but also evaluation, taking into account the implicit conversions between integers and floats.

  2. Arrays: Add to the syntax the command DIM var[x] that declares an array var of size x, and the expression var[i] that references the ith element of the array var.

  3. Toplevel directives: Add the toplevel directives SAVE "file_name" and LOAD "file_name" that save a Basic program to the hard disk, and load a Basic program from the hard disk, respectively.

  4. Sub-program: Add sub-programs. The GOSUB line number command calls a sub-program by branching to the given line number while storing the line from where the call is made. The RETURN command resumes execution at the line following the last GOSUB call executed, if there is one, or exits the program otherwise. Adding sub-programs requires evaluation to manage not only the environement but also a stack containing the return addresses of the current GOSUB calls. The GOSUB command adds the possibility of defining recursive sub-programs.

Previous Contents Next ocaml-book-1.0/en/html/book-ora186.html0000644000000000000000000002503407453055400014475 0ustar Sockets Previous Contents Next

Sockets

We saw in chapters 18 and 19 two ways to perform interprocess communication, namely, pipes and channels. These first two methods use a logical model of concurrency. In general, they do not give better performance to the degree that the communicating processes share resources, in particular, the same processor. The third possibility, which we present in this section, uses sockets for communication. This method originated in the Unix world. Sockets allow communication between processes executing on the same machine or on different machines.

Description and Creation

A socket is responsible for establishing communication with another socket, with the goal of transferring information. We enumerate the different situations that may be encountered as well as the commands and datatypes that are used by TCP/IP sockets. The classic metaphor is to compare sockets to telephone sets.
  • In order to work, the machine must be connected to the network (socket).
  • To receive a call, it is necessary to possess a number of the type sock_addr (bind).
  • During a call, it is possible to receive another call if the configuration allows it (listen).
  • It is not necessary to have one's own number to call another set, once the connection is established in both directions (connect).
Domains.
Sockets belong to different domains, according to whether they are meant to communicate internally or externally. The Unix library defines two possible domains corresponding to the type constructors:

# type socket_domain = PF_UNIX | PF_INET;;
The first domain corresponds to local communication, and the second, to communication over the Internet. These are the principal domains for sockets.

In the following, we use sockets belonging only to the Internet domain.

Types and protocols.
Regardless of their domain, sockets define certain communications properties (reliability, ordering, etc.) represented by the type constructors:

# type socket_type = SOCK_STREAM | SOCK_DGRAM | SOCK_SEQPACKET | SOCK_RAW ;;
According to the type of socket used, the underlying communications protocol obeys definite characteristics. Each type of communication is associated with a default protocol.

In fact, we will only use the first kind of communication --- SOCK_STREAM --- with the default protocol TCP. This guarantees reliability, order, prevents duplication of exchanged messages, and works in connected mode.

For more information, we refer the reader to the Unix literature, for example [Ste92].

Creation.
The function to create sockets is:

# Unix.socket ;;
- : Unix.socket_domain -> Unix.socket_type -> int -> Unix.file_descr = <fun>
The third argument allows specification of the protocol associated with communication. The value 0 is interpreted as ``the default protocol'' associated with the pair (domain, type) argument used for the creation of the socket. The value returned by this function is a file descriptor. Thus such a value can be used with the standard input-output functions in the Unix library.

We can create a TCP/IP socket with:

# let s_descr = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 ;;
val s_descr : Unix.file_descr = <abstr>


Warning


Even though the socket function returns a value of type file_descr, the system distinguishes descriptors for a files and those associated with sockets. You can use the file functions in the Unix library with descriptors for sockets; but an exception is raised when a classical file descriptor is passed to a function expecting a descriptor for a socket.


Closing.
Like all file descriptors, a socket is closed by the function:

# Unix.close ;;
- : Unix.file_descr -> unit = <fun>
When a process finishes via a call to exit, all open file descriptors are closed automatically.

Addresses and Connections

A socket does not have an address when it is created. In order to setup a connection between two sockets, the caller must know the address of the receiver.

The address of a socket (TCP/IP) consists of an IP address and a port number. A socket in the Unix domain consists simply of a file name.


# type sockaddr =
ADDR_UNIX of string | ADDR_INET of inet_addr * int ;;


Binding a socket to an address.
The first thing to do in order to receive calls after the creation of a socket is to bind the socket to an address. This is the job of the function:

# Unix.bind ;;
- : Unix.file_descr -> Unix.sockaddr -> unit = <fun>


In effect, we already have a socket descriptor, but the address that is associated with it at creation is hardly useful, as shown by the following example:

# let (addr_in, p_num) =
match Unix.getsockname s_descr with
Unix.ADDR_INET (a,n) -> (a,n)
| _ -> failwith "not INET" ;;
val addr_in : Unix.inet_addr = <abstr>
val p_num : int = 0
# Unix.string_of_inet_addr addr_in ;;
- : string = "0.0.0.0"


We need to create a useful address and to associate it with our socket. We reuse our local address my_addr as described on page ?? and choose port 12345 which, in general, is unused.

# Unix.bind s_descr (Unix.ADDR_INET(my_addr, 12345)) ;;
- : unit = ()


Listening and accepting connections.
It is necessary to use two operations before our socket is completely operational to receive calls: define its listening capacity and allow it to accept connections. Those are the respective roles of the two functions:

# Unix.listen ;;
- : Unix.file_descr -> int -> unit = <fun>
# Unix.accept ;;
- : Unix.file_descr -> Unix.file_descr * Unix.sockaddr = <fun>


The second argument to the listen function gives the maximum number of connections. The call to the accept function waits for a connection request. When accept finishes, it returns the descriptor for a socket, the so-called service socket. This service socket is automatically linked to an address. The accept function may only be applied to sockets that have called listen, that is, to sockets that have setup a queue of connection requests.

Connection requests.
The function reciprocal to accept is;

# Unix.connect ;;
- : Unix.file_descr -> Unix.sockaddr -> unit = <fun>


A call to Unix.connect s_descr s_addr establishes a connection between the local socket s_descr (which is automatically bound) and the socket with address s_addr (which must exist).

Communication.
From the moment that a connection is established between two sockets, the processes owning them can communicate in both directions. The input-output functions are those in the Unix module, described in Chapter 18.


Previous Contents Next ocaml-book-1.0/en/html/book-ora040.gif0000644000000000000000000000612407452056111014261 0ustar GIF89aUUU!,ڋ޼HYjF BdgRJԪ5C4s\ŚjȕGe[<0Whxײė)9IIXEY ʡr􂣤* +;K[k{ ,nވ!^Noߟ < Bn4 0HH" 1zE :v:-|yleE4ILƁllK'=ke+zD :T-5+-Xj׫`R;ٵZ6!ogʽKG޽$](SKb-8+ݴ\3bÇ-\V;q`!Ng7AJ=z"Ѣ\mRm-7ooF]ᓄ'.u$YңA!cHD={{{ \G__I΀"\ x{Mxh4i!Y[ri"ā(&fߩwCa%&y NF^;d'Kn֤H2Sd}#Ifffj&]qGL]y@ t }ֹ砂&dIXE[nyhghQBn٧T ,ާ3x(<8īoNG ;Ǯ'G:kMJؘRzkB;{ 5 B(:JjoB*oJɨ{F,g-*n'|bf֬ͺlKC B3,LJ űFQ41 ѵM0Y/Aד./ hrʭ5tn|n>t;rn) n㽂yL*n1#SS.O>C$gŎnP?=mMܱ94;S. Ϸ?Ů' 4bj?N>9-6-`,ӵ[?"3o^}{+J ~<=gQ}YV-y~b؁| t>P)} -  ȧ " B}A4{ha<0ohX,vɕ̆:C,JNQ Ĉ?<4m y|=LdOVHúyo2>o@sA1bs\eD-XZ(_ b !HQH_uUd`*3܈#-I$X@;@r"?y-@%&$AKQv*j$_i+vdA.6Ia~Ϙ WĀ UPLH:t ]=cV9&ly֩Nu,/xHr2~@FP^P74x1W+`!(RX4sdG!)¸Zm0Pνg#$p934ɕ6CNY!KUQ5@ ըJuT}L%ew\jUcm]'jhP[D`' w5[Wh !]4رE=Xw,HdTEJls6kz6ye Ѐ@MRrrVsm8 -=IU-q[YvMp !=7ԝ檋]׆5=v 68-ykV]y^7/}k׺n~pt l"8m/ 7F+labްr5av8Y1*^1V[aό1;d{':N{|9BB)ld9ɜ1'%R2lec0T޲Y寀9̒2l39jʘ693^];#z6?#N4lmhξ8n1q]\ό4a\ZZ&+6Nt3]Rsڡn5jUOV+G>u|]9y+9pzQc<׍޸+^xn]\$9/lY>bOEϺ,JLOgKfV||~&\}яz05P:Á qCY~>`?VU?ćpLQ|s/pyVM$YTKsM y%T1t#,xv8sTtww[Vm5v%.m<uv6twFh|pݶޖtv7G؅;'cHe%SWFkZmtl?srqH\zo1^㐂6o[7ȇ}H{&aO!}(pUXn&4wG,RP PR'xcbjmdL؆W!W(WvpV{ rӘMxv`-ؘ G0"w؃o8ЍL9ix؏9Uy { Y )Ii@y0| fW&v% ?Gt?(9m't{ocR%<ߣ Relations between Classes Previous Contents Next

Relations between Classes

Classes can be related in two ways:
  1. An aggregation relation, named Has-a:
    class C2 is related by Has-a with class C1 when C2 has a field whose type is that of class C1. This relation can be generalized as: C2 has at least one field whose type is that of class C1.
  2. An inheritance relation, named Is-a:
    class C2 is a subclass of class C1 when C2 extends the behavior of C1. One big advantage of object-oriented programming is the ability to extend the behavior of an existing class while reusing the code written for the original class. When a class is extended, the new class inherits all the fields (data and methods) of the class being extended.

Aggregation

Class C1 aggregates class C2 when at least one of its instance variables has type C2. One gives the arity of the aggregation relation when it is known.

An Example of Aggregation

Let us define a figure as a set of points. Therefore we declare class picture (see figure 15.2), in which one of the fields is an array of points. Then the class picture aggregates point, using the generalized relation Has-a.

# class picture n =
object
val mutable ind = 0
val tab = Array.create n (new point(0,0))
method add p =
try tab.(ind)<-p ; ind <- ind + 1
with Invalid_argument("Array.set")
-> failwith ("picture.add:ind =" ^ (string_of_int ind))
method remove () = if (ind > 0) then ind <-ind-1
method to_string () =
let s = ref "["
in for i=0 to ind-1 do s:= !s ^ " " ^ tab.(i)#to_string() done ;
(!s) ^ "]"
end ;;
class picture :
int ->
object
val mutable ind : int
val tab : point array
method add : point -> unit
method remove : unit -> unit
method to_string : unit -> string
end


To build a figure, we create an instance of class picture, and insert the points as required.

# let pic = new picture 8;;
val pic : picture = <obj>
# pic#add p1; pic#add p2; pic#add p3;;
- : unit = ()
# pic#to_string ();;
- : string = "[ ( 0, 0) ( 3, 4) ( 3, 0)]"


A Graphical Notation for Aggregation

The relation between class picture and class point is represented graphically in figure 15.2. An arrow with a diamond at the tail represents aggregation. In this example, class picture has 0 or more points.


Figure 15.2: Aggregation relation.


Furthermore, we show above the arrow the arity of the relation.

Inheritance Relation

This is the main relation in object-oriented programming. When class c2 inherits from class c1, it inherits all fields from the parent class. It can also define new fields, or redefine inherited methods to specialize them. Since the parent class has not been modified, the applications using it do not need to be adapted to the changes introduced in the new class.

The syntax of inheritance is as follows:

Syntax


inherit name1 p1 ...pn [ as name2 ]
Parameters p1, ..., pn are what is expected from the constructor of class name1. The optional keyword as associates a name with the parent class to provide access to its methods. This feature is particularly useful when the child class redefines a method of the parent class (see page ??).

An Example of Simple Inheritance

Using the classic example, we can extend class point by adding a color attribute to the points. We define the class colored_point inheriting from class point. The color is represented by the field c of type string. We add a method get_color that returns the value of the field. Finally, the string conversion method is overridden to recognize the new attribute.

Note


The x and y variables seen in to_string are the fields, not the class initialization arguments.

# class colored_point (x,y) c =
object
inherit point (x,y)
val mutable c = c
method get_color = c
method set_color nc = c <- nc
method to_string () = "( " ^ (string_of_int x) ^
", " ^ (string_of_int y) ^ ")" ^
" [" ^ c ^ "] "
end ;;
class colored_point :
int * int ->
string ->
object
val mutable c : string
val mutable x : int
val mutable y : int
method distance : unit -> float
method get_color : string
method get_x : int
method get_y : int
method moveto : int * int -> unit
method rmoveto : int * int -> unit
method set_color : string -> unit
method to_string : unit -> string
end


The constructor arguments for colored_point are the pair of coordinates required for the construction of a point and the color for the colored point.

The methods inherited, newly defined or redefined correspond to the behaviors of instances of the class.

# let pc = new colored_point (2,3) "white";;
val pc : colored_point = <obj>
# pc#get_color;;
- : string = "white"
# pc#get_x;;
- : int = 2
# pc#to_string();;
- : string = "( 2, 3) [white] "
# pc#distance;;
- : unit -> float = <fun>
We say that the class point is a parent class of class colored_point and that the latter is the child of the former.

Warning


When redefining a method in a child class, you must respect the method type defined in the parent class.


A Graphical Notation for Inheritance

The inheritance relation between classes is denoted by an arrow from the child class to the parent class. The head of the arrow is a closed triangle. In the graphical representation of inheritance, we only show the new fields and methods, and redefined methods in the child class. Figure 15.3 displays the relation between class colored_point and its parent point.




Figure 15.3: Inheritance Relation.


Since it contains additional methods, type colored_point differs from type point. Testing for equality between instances of these classes produces a long error message containing the whole type of each class, in order to display the differences.

# p1 = pc;;
Characters 6-8:
This expression has type
colored_point =
< distance : unit -> float; get_color : string; get_x : int; get_y :
int; moveto : int * int -> unit; rmoveto : int * int -> unit;
set_color : string -> unit; to_string : unit -> string >
but is here used with type
point =
< distance : unit -> float; get_x : int; get_y : int;
moveto : int * int -> unit; rmoveto : int * int -> unit;
to_string : unit -> string >
Only the first object type has a method get_color



Previous Contents Next ocaml-book-1.0/en/html/book-ora033.html0000644000000000000000000000347607453055377014507 0ustar To Learn More Previous Contents Next

To Learn More

Imperative programming is the style of programming which has been most widely used since the first computer languages such as Fortran, C, or Pascal. For this reason numerous algorithms are described in this style, often using some kind of pseudo-Pascal. While they could be implemented in a functional style, the use of arrays promotes the use of an imperative style. The data structures and algorithms presented in classic algorithms books, such as [AHU83] and [Sed88], can be carried over directly in the appropriate style. An additional advantage of including these two styles in a single language is being able to define new programming models by mixing the two. This is precisely the subject of the next chapter.




Previous Contents Next ocaml-book-1.0/en/html/book-ora015.html0000644000000000000000000024441307453055377014505 0ustar Functional core of Objective CAML Previous Contents Next

Functional core of Objective CAML

Like all functional languages, Objective CAML is an expression oriented language, where programming consists mainly of creating functions and applying them. The result of the evaluation of one of these expressions is a value in the language and the execution of a program is the evaluation of all the expressions which comprise it.

Primitive values, functions, and types

Integers and floating-point numbers, characters, character strings, and booleans are predefined in Objective CAML.

Numbers

There are two kinds of numbers: integers1 of type int and floating-point numbers of type float. Objective CAML follows the IEEE 754 standard2 for representing double-precision floating-point numbers. The operations on integers and floating-point numbers are described in figure 2.1. Let us note that when the result of an integer operation is outside the interval on which values of type int are defined, this does not produce an error, but the result is an integer within the system's interval of integers. In other words, all integer operations are operations modulo the boundaries of the interval.


integer numbers floating-point numbers
+ addition
- subtraction and unary negation
* multiplication
/ integer division
mod remainder of integer division
+. addition
-. subtraction and unary negation
*. multiplication
/. division
** exponentiation

# 1 ;;
- : int = 1
# 1 + 2 ;;
- : int = 3
# 9 / 2 ;;
- : int = 4
# 11 mod 3 ;;
- : int = 2
(* limits of the representation *)
(* of integers *)
# 2147483650 ;;
- : int = 2
 

# 2.0 ;;
- : float = 2
# 1.1 +. 2.2 ;;
- : float = 3.3
# 9.1 /. 2.2 ;;
- : float = 4.13636363636
# 1. /. 0. ;;
- : float = inf
(* limits of the representation *)
(* of floating-point numbers *)
# 222222222222.11111 ;;
- : float = 222222222222
 

Figure 2.1: Operations on numbers.


Differences between integers and floating-point numbers
Values having different types such as float and int can never be compared directly. But there are functions for conversion (float_of_int and int_of_float) between one and the other.

# 2 = 2.0 ;;
Characters 5-8:
This expression has type float but is here used with type int
# 3.0 = float_of_int 3 ;;
- : bool = true


In the same way, operations on floating-point numbers are distinct from those on integers.


# 3 + 2 ;;
- : int = 5
# 3.0 +. 2.0 ;;
- : float = 5
# 3.0 + 2.0 ;;
Characters 0-3:
This expression has type float but is here used with type int
# sin 3.14159 ;;
- : float = 2.65358979335e-06


An ill-defined computation, such as a division by zero, will raise an exception (see page ??) which interrupts the computation. Floating-point numbers have a representation for infinite values (printed as Inf) and ill-defined computations (printed as NaN3). The main functions on floating-point numbers are described in figure 2.2.




functions on floats trigonometric functions
ceil  
floor  
sqrt square root
exp exponential
log natural log
log10 log base 10
cos cosine
sin sine
tan tangent
acos arccosine
asin arcsine
atan arctangent

# ceil 3.4 ;;
- : float = 4
# floor 3.4 ;;
- : float = 3
# ceil (-.3.4) ;;
- : float = -3
# floor (-.3.4) ;;
- : float = -4
 

# sin 1.57078 ;;
- : float = 0.999999999867
# sin (asin 0.707) ;;
- : float = 0.707
# acos 0.0 ;;
- : float = 1.57079632679
# asin 3.14 ;;
- : float = nan
 

Figure 2.2: Functions on floats.


Characters and Strings

Characters, type char, correspond to integers between 0 and 255 inclusive, following the ASCII encoding for the first 128. The functions char_of_int and int_of_char support conversion between integers and characters. Character strings, type string, are sequences of characters of definite length (less than 224-6). The concatenation operator is  . The functions int_of_string, string_of_int, string_of_float and float_of_string carry out the various conversions between numbers and character strings.

# 'B' ;;
- : char = 'B'
# int_of_char 'B' ;;
- : int = 66
# "is a string" ;;
- : string = "is a string"
# (string_of_int 1987) ^ " is the year Caml was created" ;;
- : string = "1987 is the year Caml was created"


Even if a string contains the characters of a number, it won't be possible to use it in operations on numbers without carrying out an explicit conversion.

# "1999" + 1 ;;
Characters 1-7:
This expression has type string but is here used with type int
# (int_of_string "1999") + 1 ;;
- : int = 2000
Numerous functions on character strings are gathered in the String module (see page ??).

Booleans

Booleans, of type bool, belong to a set consisting of two values: true and false. The primitive operators are described in figure 2.3. For historical reasons, the ``and'' and ``or'' operators each have two forms.

not negation
&& sequential and
|| sequential or
 
& synonym for &&
or synonym for ||
 

Figure 2.3: Operators on booleans.



# true ;;
- : bool = true
# not true ;;
- : bool = false
# true && false ;;
- : bool = false
The operators && and ||, or their synonyms, evaluate their left argument and then, depending on its value, evaluate their right argument. They can be rewritten in the form of conditional constructs (see page ??).


= structural equality
== physical equality
<> negation of =
!= negation of ==
< less than
> greater than
<= less than or equal to
>= greater than or equal to

Figure 2.4: Equality and comparison operators.


The equality and comparison operators are described in figure 2.4. They are polymorphic, i.e., they can be used to compare two integers as well as two character strings. The only constraint is that their two operands must be of the same type (see page ??).

# 1<=118 && (1=2 || not(1=2)) ;;
- : bool = true
# 1.0 <= 118.0 && (1.0 = 2.0 || not (1.0 = 2.0)) ;;
- : bool = true
# "one" < "two" ;;
- : bool = true
# 0 < '0' ;;
Characters 4-7:
This expression has type char but is here used with type int


Structural equality tests the equality of two values by traversing their structure, whereas physical equality tests whether the two values occupy the same region in memory. These two equality operators return the same result for simple values: booleans, characters, integers and constant constructors (page ??).

Warning


Floating-point numbers and character strings are considered structured values.


Unit

The unit type describes a set which possesses only a single element, denoted: ().

# () ;;
- : unit = ()
This value will often be used in imperative programs (3, page ??) for functions which carry out side effects. Functions whose result is the value () simulate the notion of procedure, which doesn't exist in Objective CAML, just as the type void does in the C language.

Cartesian product, tuple

Values of possibly different types can be gathered in pairs or more generally in tuples. The values making up a tuple are separated by commas. The type constructor * indicates a tuple. The type int * string is the type of pairs whose first element is an integer (of type int) and whose second is a character string (of type string).

# ( 12 , "October" ) ;;
- : int * string = 12, "October"
When there is no ambiguity, it can be written more simply:

# 12 , "October" ;;
- : int * string = 12, "October"
The functions fst and snd allow access to the first and second elements of a pair.

# fst ( 12 , "October" ) ;;
- : int = 12
# snd ( 12 , "October" ) ;;
- : string = "October"
These two functions accept pairs whose components are of any type whatsoever. They are polymorphic, in the same way as equality.

# fst;;
- : 'a * 'b -> 'a = <fun>
# fst ( "October", 12 ) ;;
- : string = "October"
The type int * char * string is that of triplets whose first element is of type int, whose second is of type char, and whose third is of type string. Its values are written

# ( 65 , 'B' , "ascii" ) ;;
- : int * char * string = 65, 'B', "ascii"


Warning


The functions fst and snd applied to a tuple, other than a pair, result in a type error.

# snd ( 65 , 'B' , "ascii" ) ;;
Characters 7-25:
This expression has type int * char * string but is here used with type
'a * 'b
There is indeed a difference between the type of a pair and that of a triplet. The type int * int * int is different from the types (int * int) * int and int * (int * int). Functions to access a triplet (and other tuples) are not defined by the core library. One can use pattern matching to define them if need be (see page ??).

Lists

Values of the same type can be gathered into a list. A list can either be empty or consist of elements of the same type.

# [] ;;
- : 'a list = []
# [ 1 ; 2 ; 3 ] ;;
- : int list = [1; 2; 3]
# [ 1 ; "two" ; 3 ] ;;
Characters 14-17:
This expression has type int list but is here used with type string list


The function which adds an element at the head of a list is the infix operator :: . It is the analogue of Lisp's cons.

# 1 :: 2 :: 3 :: [] ;;
- : int list = [1; 2; 3]


Concatenation of two lists is also an infix operator @.

# [ 1 ] @ [ 2 ; 3 ] ;;
- : int list = [1; 2; 3]
# [ 1 ; 2 ] @ [ 3 ] ;;
- : int list = [1; 2; 3]


The other list manipulation functions are defined in the List library. The functions hd and tl from this library give respectively the head and the tail of a list when these values exist. These functions are denoted by List.hd and List.tl to indicate to the system that they can be found in the module List4.

# List.hd [ 1 ; 2 ; 3 ] ;;
- : int = 1
# List.hd [] ;;
Uncaught exception: Failure("hd")
In this last example, it is indeed problematic to request retrieval of the first element of an empty list. It is for this reason that the system raises an exception (see page ??).

Conditional control structure

One of the indispensable control structures in any programming language is the structure called conditional (or branch) which guides the computation as a function of a condition.

Syntax


if expr1 then expr2 else expr3
The expression expr1 is of type bool. The expressions expr2 and expr3 must be of the same type, whatever it may be.


# if 3=4 then 0 else 4 ;;
- : int = 4
# if 3=4 then "0" else "4" ;;
- : string = "4"
# if 3=4 then 0 else "4";;
Characters 20-23:
This expression has type string but is here used with type int


A conditional construct is itself an expression and its evaluation returns a value.


# (if 3=5 then 8 else 10) + 5 ;;
- : int = 15


Note


The else branch can be omitted, but in this case it is implicitly replaced by else (). Consequently, the type of the expression expr2 must be unit (see page ??).


Value declarations

A declaration binds a name to a value. There are two types: global declarations and local declarations. In the first case, the declared names are known to all the expressions following the declaration; in the second, the declared names are only known to one expression. It is equally possible to simultaneously declare several name-value bindings.

Global declarations

Syntax


let name = expr ;;
A global declaration defines the binding between the name name and the value of the expression expr which will be known to all subsequent expressions.

# let yr = "1999" ;;
val yr : string = "1999"
# let x = int_of_string(yr) ;;
val x : int = 1999
# x ;;
- : int = 1999
# x + 1 ;;
- : int = 2000
# let new_yr = string_of_int (x + 1) ;;
val new_yr : string = "2000"


Simultaneous global declarations

Syntax


let name1 = expr1
and name2 = expr2
:
and namen = exprn ;;



A simultaneous declaration declares different symbols at the same level. They won't be known until the end of all the declarations.

# let x = 1 and y = 2 ;;
val x : int = 1
val y : int = 2
# x + y ;;
- : int = 3
# let z = 3 and t = z + 2 ;;
Characters 18-19:
Unbound value z


It is possible to gather several global declarations in the same phrase; then printing of their types and their values does not take place until the end of the phrase, marked by double ``;;''. These declarations are evaluated sequentially, in contrast with a simultaneous declaration.

# let x = 2
let y = x + 3 ;;
val x : int = 2
val y : int = 5
A global declaration can be masked by a new declaration of the same name (see page ??).

Local declarations

Syntax


let name = expr1 in expr2;;
The name name is only known during the evaluation of expr2. The local declaration binds it to the value of expr1.

# let xl = 3 in xl * xl ;;
- : int = 9
The local declaration binding xl to the value 3 is only in effect during the evaluation of xl * xl.

# xl ;;
Characters 1-3:
Unbound value xl
A local declaration masks all previous declarations of the same name, but the previous value is reinstated upon leaving the scope of the local declaration:

# let x = 2 ;;
val x : int = 2
# let x = 3 in x * x ;;
- : int = 9
# x * x ;;
- : int = 4
A local declaration is an expression and can thus be used to construct other expressions:

# (let x = 3 in x * x) + 1 ;;
- : int = 10


Local declarations can also be simultaneous.

Syntax


let name1 = expr1
and name2 = expr2
:
and namen = exprn
in expr ;;




# let a = 3.0 and b = 4.0 in sqrt (a*.a +. b*.b) ;;
- : float = 5
# b ;;
Characters 0-1:
Unbound value b


Function expressions, functions

A function expression consists of a parameter and a body. The formal parameter is a variable name and the body an expression. The parameter is said to be abstract. For this reason, a function expression is also called an abstraction.

Syntax


function p -> expr
Thus the function which squares its argument is written:

# function x -> x*x ;;
- : int -> int = <fun>
The Objective CAML system deduces its type. The function type int -> int indicates a function expecting a parameter of type int and returning a value of type int.

Application of a function to an argument is written as the function followed by the argument.

# (function x -> x * x) 5 ;;
- : int = 25
The evaluation of an application amounts to evaluating the body of the function, here x * x, where the formal parameter, x, is replaced by the value of the argument (or the actual parameter), here 5.

In the construction of a function expression, expr is any expression whatsoever. In particular, expr may itself be a function expression.


# function x -> (function y -> 3*x + y) ;;
- : int -> int -> int = <fun>


The parentheses are not required. One can write more simply:

# function x -> function y -> 3*x + y ;;
- : int -> int -> int = <fun>
The type of this expression can be read in the usual way as the type of a function which expects two integers and returns an integer value. But in the context of a functional language such as Objective CAML we are dealing more precisely with the type of a function which expects an integer and returns a function value of type int -> int:

# (function x -> function y -> 3*x + y) 5 ;;
- : int -> int = <fun>


One can, of course, use the function expression in the usual way by applying it to two arguments. One writes:

# (function x -> function y -> 3*x + y) 4 5 ;;
- : int = 17
When one writes f a b, there is an implicit parenthesization on the left which makes this expression equivalent to: (f a) b.

Let's examine the application
(function x -> function y -> 3*x + y) 4 5
in detail. To compute the value of this expression, it is necessary to compute the value of
(function x -> function y -> 3*x + y) 4
which is a function expression equivalent to
function y -> 3*4 + y
obtained by replacing x by 4 in 3*x + y. Applying this value (which is a function) to 5 we get the final value 3*4+5 = 17:

# (function x -> function y -> 3*x + y) 4 5 ;;
- : int = 17


Arity of a function

The number of arguments of a function is called its arity. Usage inherited from mathematics demands that the arguments of a function be given in parentheses after the name of the function. One writes: f(4,5). We've just seen that in Objective CAML, one more usually writes: f 4 5. One can, of course, write a function expression in Objective CAML which can be applied to (4,5):

# function (x,y) -> 3*x + y ;;
- : int * int -> int = <fun>
But, as its type indicates, this last expression expects not two, but only one argument: a pair of integers. Trying to pass two arguments to a function which expects a pair or trying to pass a pair to a function which expects two arguments results in a type error:

# (function (x,y) -> 3*x + y) 4 5 ;;
Characters 29-30:
This expression has type int but is here used with type int * int
# (function x -> function y -> 3*x + y) (4, 5) ;;
Characters 39-43:
This expression has type int * int but is here used with type int


Alternative syntax

There is a more compact way of writing function expressions with several parameters. It is a legacy of former versions of the Caml language. Its form is as follows:

Syntax


fun p1 ...pn -> expr
It allows one to omit repetitions of the function keyword and the arrows. It is equivalent to the following translation:
function p1 -> -> function pn -> expr

# fun x y -> 3*x + y ;;
- : int -> int -> int = <fun>
# (fun x y -> 3*x + y) 4 5 ;;
- : int = 17
This form is still encountered often, in particular in the libraries provided with the Objective CAML distribution.

Closure

Objective CAML treats a function expression like any other expression and is able to compute its value. The value returned by the computation is a function expression and is called a closure. Every Objective CAML expression is evaluated in an environment consisting of name-value bindings coming from the declarations preceding the expression being computed. A closure can be described as a triplet consisting of the name of the formal parameter, the body of the function, and the environment of the expression. This environment needs to be preserved because the body of a function expression may use, in addition to the formal parameters, every other variable declared previously. These variables are said to be ``free'' in the function expression. Their values will be needed when the function expression is applied.

# let m = 3 ;;
val m : int = 3
# function x -> x + m ;;
- : int -> int = <fun>
# (function x -> x + m) 5 ;;
- : int = 8


When application of a closure to an argument returns a new closure, the latter possesses within its environment all the bindings necessary for a future application. The subsection on the scope of variables (see page ??) details this notion. We will return to the memory representation of a closure in chapter 4 (page ??) as well as chapter 12 (page ??).

The function expressions used until now are anonymous. It is rather useful to be able to name them.

Function value declarations

Function values are declared in the same way as other language values, by the let construct.

# let succ = function x -> x + 1 ;;
val succ : int -> int = <fun>
# succ 420 ;;
- : int = 421
# let g = function x -> function y -> 2*x + 3*y ;;
val g : int -> int -> int = <fun>
# g 1 2;;
- : int = 8


To simplify writing, the following notation is allowed:

Syntax


let name p1 ... pn = expr
which is equivalent to the following form:
let name = function p1 -> -> function pn -> expr

The following declarations of succ and g are equivalent to their previous declaration.

# let succ x = x + 1 ;;
val succ : int -> int = <fun>
# let g x y = 2*x + 3*y ;;
val g : int -> int -> int = <fun>


The completely functional character of Objective CAML is brought out by the following example, in which the function h1 is obtained by the application of g to a single integer. In this case one speaks of partial application:

# let h1 = g 1 ;;
val h1 : int -> int = <fun>
# h1 2 ;;
- : int = 8


One can also, starting from g, define a function h2 by fixing the value of the second parameter, y, of g:

# let h2 = function x -> g x 2 ;;
val h2 : int -> int = <fun>
# h2 1 ;;
- : int = 8


Declaration of infix functions

Certain functions taking two arguments can be applied in infix form. This is the case with addition of integers. One writes 3 + 5 for the application of + to 3 and 5. To use the symbol + as a regular function value, this must be syntactically indicated by surrounding the infix symbol with parentheses. The syntax is as follows:

Syntax


( op )


The following example defines the function succ using ( + ).

# ( + ) ;;
- : int -> int -> int = <fun>
# let succ = ( + ) 1 ;;
val succ : int -> int = <fun>
# succ 3 ;;
- : int = 4


It is also possible to define new operators. We define an operator ++, addition on pairs of integers

# let ( ++ ) c1 c2 = (fst c1)+(fst c2), (snd c1)+(snd c2) ;;
val ++ : int * int -> int * int -> int * int = <fun>
# let c = (2,3) ;;
val c : int * int = 2, 3
# c ++ c ;;
- : int * int = 4, 6


There is an important limitation on the possible operators. They must contain only symbols (such as *, +, @, etc. ) and not letters or digits. Certain functions predefined as infixes are exceptions to the rule. They are listed as follows: or mod land lor lxor lsl lsr asr.

Higher order functions

A function value (a closure) can be returned as a result. It can equally well be passed as an argument to a function. Functions taking function values as arguments or returning them as results are called higher order.

# let h = function f -> function y -> (f y) + y ;;
val h : (int -> int) -> int -> int = <fun>


Note


Application is implicitly parenthesized to the left, but function types are implicitly parenthesized to the right. Thus the type of the function h can be written
(int -> int) -> int -> int  or  (int -> int) -> (int -> int)



Higher order functions offer elegant possibilities for dealing with lists. For example the function List.map can apply a function to all the elements of a list and return the results in a list.

# List.map ;;
- : ('a -> 'b) -> 'a list -> 'b list = <fun>
# let square x = string_of_int (x*x) ;;
val square : int -> string = <fun>
# List.map square [1; 2; 3; 4] ;;
- : string list = ["1"; "4"; "9"; "16"]


As another example, the function List.for_all can find out whether all the elements of a list satisfy a given criterion.

# List.for_all ;;
- : ('a -> bool) -> 'a list -> bool = <fun>
# List.for_all (function n -> n<>0) [-3; -2; -1; 1; 2; 3] ;;
- : bool = true
# List.for_all (function n -> n<>0) [-3; -2; 0; 1; 2; 3] ;;
- : bool = false


Scope of variables

In order for it to be possible to evaluate an expression, all the variables appearing therein must be defined. This is the case in particular for the expression e in the declaration let p = e. But since p is not yet known within this expression, this variable can only be present if it refers to another value issued by a previous declaration.

# let p = p ^ "-suffix" ;;
Characters 9-10:
Unbound value p
# let p = "prefix" ;;
val p : string = "prefix"
# let p = p ^ "-suffix" ;;
val p : string = "prefix-suffix"


In Objective CAML, variables are statically bound. The environment used to execute the application of a closure is the one in effect at the moment of its declaration (static scope) and not the one in effect at the moment of application (dynamic scope).


# let p = 10 ;;
val p : int = 10
# let k x = (x, p, x+p) ;;
val k : int -> int * int * int = <fun>
# k p ;;
- : int * int * int = 10, 10, 20
# let p = 1000 ;;
val p : int = 1000
# k p ;;
- : int * int * int = 1000, 10, 1010
The function k contains a free variable: p. Since the latter is defined in the global environment, the definition of k is legal. The binding between the name p and the value 10 in the environment of the closure k is static, i.e., does not depend on the most recent definition of p.

Recursive declarations

A variable declaration is called recursive if it uses its own identifier in its definition. This facility is used mainly for functions, notably to simulate a definition by recurrence. We have just seen that the let declaration does not support this. To declare a recursive function we will use a dedicated syntactic construct.

Syntax


let rec name = expr ;;
We can equally well use the syntactic facility for defining function values while indicating the function parameters:

Syntax


let rec name p1 ...pn = expr ;;


By way of example, here is the function sigma which computes the sum of the (non-negative) integers between 0 and the value of its argument, inclusive.

# let rec sigma x = if x = 0 then 0 else x + sigma (x-1) ;;
val sigma : int -> int = <fun>
# sigma 10 ;;
- : int = 55
It may be noted that this function does not terminate if its argument is strictly negative.

A recursive value is in general a function. The compiler rejects some recursive declarations whose values are not functions:

# let rec x = x + 1 ;;
Characters 13-18:
This kind of expression is not allowed as right-hand side of `let rec'
We will see however that in certain cases such declarations are allowed (see page ??).

The let rec declaration may be combined with the and construction for simultaneous declarations. In this case, all the functions defined at the same level are known within the bodies of each of the others. This permits, among other things, the declaration of mutually recursive functions.

# let rec even n = (n<>1) && ((n=0) or (odd (n-1)))
and odd n = (n<>0) && ((n=1) or (even (n-1))) ;;
val even : int -> bool = <fun>
val odd : int -> bool = <fun>
# even 4 ;;
- : bool = true
# odd 5 ;;
- : bool = true


In the same way, local declarations can be recursive. This new definition of sigma tests the validity of its argument before carrying out the computation of the sum defined by a local function sigma_rec.

# let sigma x =
let rec sigma_rec x = if x = 0 then 0 else x + sigma_rec (x-1) in
if (x<0) then "error: negative argument"
else "sigma = " ^ (string_of_int (sigma_rec x)) ;;
val sigma : int -> string = <fun>


Note


The need to give a return value of the same type, whether the argument is negative or not, has forced us to give the result in the form of a character string. Indeed, what value should be returned by sigma when its argument is negative? We will see the proper way to manage this problem, using exceptions (see page ??).


Polymorphism and type constraints

Some functions execute the same code for arguments having different types. For example, creation of a pair from two values doesn't require different functions for each type known to the system5. In the same way, the function to access the first field of a pair doesn't have to be differentiated according to the type of the value of this first field.

# let make_pair a b = (a,b) ;;
val make_pair : 'a -> 'b -> 'a * 'b = <fun>
# let p = make_pair "paper" 451 ;;
val p : string * int = "paper", 451
# let a = make_pair 'B' 65 ;;
val a : char * int = 'B', 65
# fst p ;;
- : string = "paper"
# fst a ;;
- : char = 'B'


Functions are called polymorphic if their return value or one of their parameters is of a type which need not be specified. The type synthesizer contained in the Objective CAML compiler finds the most general type for each expression. In this case, Objective CAML uses variables, here 'a and 'b, to designate these general types. These variables are instantiated to the type of the argument during application of the function.

With Objective CAML's polymorphic functions, we get the advantages of being able to write generic code usable for values of every type, while still preserving the execution safety of static typing. Indeed, although make_pair is polymorphic, the value created by (make_pair 'B' 65) has a well-specified type which is different from that of (make_pair "paper" 451). Moreover, type verification is carried out on compilation, so the generality of the code does not hamper the efficiency of the program.

Examples of polymorphic functions and values

The following examples of polymorphic functions have functional parameters whose type is parameterized.

The app function applies a function to an argument.

# let app = function f -> function x -> f x ;;
val app : ('a -> 'b) -> 'a -> 'b = <fun>
So it can be applied to the function odd defined previously:

# app odd 2;;
- : bool = false


The identity function (id ) takes a parameter and returns it as is.

# let id x = x ;;
val id : 'a -> 'a = <fun>
# app id 1 ;;
- : int = 1


The compose function takes two functions and another value and composes the application of these two functions to this value.

# let compose f g x = f (g x) ;;
val compose : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun>
# let add1 x = x+1 and mul5 x = x*5 in compose mul5 add1 9 ;;
- : int = 50
It can be seen that the result of g must be of the same type as the argument of f.

Values other than functions can be polymorphic as well. For example, this is the case for the empty list:

# let l = [] ;;
val l : 'a list = []


The following example demonstrates that type synthesis indeed arises from resolution of the constraints coming from function application and not from the value obtained upon execution.

# let t = List.tl [2] ;;
val t : int list = []
The type of List.tl is 'a list -> 'a list, so this function applied to a list of integers returns a list of integers. The fact that upon execution it is the empty list which is obtained doesn't change its type at all.

Objective CAML generates parameterized types for every function which doesn't use the form of its arguments. This polymorphism is called parametric polymorphism6.

Type constraint

As the Caml type synthesizer generates the most general type, it may be useful or necessary to specify the type of an expression.

The syntactic form of a type constraint is as follows:

Syntax


( expr : t )
When it runs into such a constraint, the type synthesizer will take it into account while constructing the type of the expression. Using type constraints lets one:
  • make the type of the parameters of a function visible;
  • forbid the use of a function outside its intended context;
  • specify the type of an expression, which will be particularly useful for mutable values (see page ??).
The following examples demonstrate the use of such type constraints

# let add (x:int) (y:int) = x + y ;;
val add : int -> int -> int = <fun>
# let make_pair_int (x:int) (y:int) = x,y;;
val make_pair_int : int -> int -> int * int = <fun>
# let compose_fn_int (f : int -> int) (g : int -> int) (x:int) =
compose f g x;;
val compose_fn_int : (int -> int) -> (int -> int) -> int -> int = <fun>
# let nil = ([] : string list);;
val nil : string list = []
# 'H'::nil;;
Characters 5-8:
This expression has type string list but is here used with type char list


Restricting polymorphism this way lets us control the type of an expression better by constraining the polymorphism of the type deduced by the system. Any defined type whatsoever may be used, including ones containing type variables, as the following example shows:

# let llnil = ([] : 'a list list) ;;
val llnil : 'a list list = []
# [1;2;3]:: llnil ;;
- : int list list = [[1; 2; 3]]
The symbol llnil is a list of lists of any type whatsoever.

Here we are dealing with constraints, and not replacing Objective CAML's type synthesis with explicit typing. In particular, one cannot generalize types beyond what inference permits.

# let add_general (x:'a) (y:'b) = add x y ;;
val add_general : int -> int -> int = <fun>


Type constraints will be used in module interfaces (14) as well as in class declarations (15).

Examples

In this section we will give several somewhat elaborate examples of functions. Most of these functions are predefined in Objective CAML. We will redefine them for the sake of ``pedagogy''.

Here, the test for the terminal case of recursive functions is implemented by a conditional. Hence a programming style closer to Lisp. We will see how to give a more ML character to these definitions when we present another way of defining functions by case (see page ??).

Length of a list

Let's start with the function null which tests whether a list is empty.

# let null l = (l = []) ;;
val null : 'a list -> bool = <fun>
Next, we define the function size to compute the length of a list (i.e., the number of its elements).

# let rec size l =
if null l then 0
else 1 + (size (List.tl l)) ;;
val size : 'a list -> int = <fun>
# size [] ;;
- : int = 0
# size [1;2;18;22] ;;
- : int = 4
The function size tests whether the list argument is empty. If so it returns 0, if not it returns 1 plus the value resulting from computing the length of the tail of the list.

Iteration of composition

The expression iterate n f computes the value f iterated n times.

# let rec iterate n f =
if n = 0 then (function x -> x)
else compose f (iterate (n-1) f) ;;
val iterate : int -> ('a -> 'a) -> 'a -> 'a = <fun>
The iterate function tests whether n is 0, if yes it returns the identity function, if not it composes f with the iteration of f n-1 times.

Using iterate, one can define exponentiation as iteration of multiplication.
 
# let rec power i n =
let i_times = ( * ) i in
iterate n i_times 1 ;;
val power : int -> int -> int = <fun>
# power 2 8 ;;
- : int = 256
The power function iterates n times the function expression i_times, then applies this result to 1, which does indeed compute the nth power of an integer.

Multiplication table

We want to write a function multab which computes the multiplication table of an integer passed as an argument.

First we define the function apply_fun_list such that, if f_list is a list of functions, apply_fun_list x f_list returns the list of results of applying each element of f_list to x.

# let rec apply_fun_list x f_list =
if null f_list then []
else ((List.hd f_list) x)::(apply_fun_list x (List.tl f_list)) ;;
val apply_fun_list : 'a -> ('a -> 'b) list -> 'b list = <fun>
# apply_fun_list 1 [( + ) 1;( + ) 2;( + ) 3] ;;
- : int list = [2; 3; 4]


The function mk_mult_fun_list returns the list of functions multiplying their argument by i, for i varying from 0 to n.

# let mk_mult_fun_list n =
let rec mmfl_aux p =
if p = n then [ ( * ) n ]
else (( * ) p) :: (mmfl_aux (p+1))
in (mmfl_aux 1) ;;
val mk_mult_fun_list : int -> (int -> int) list = <fun>


We obtain the multiplication table of 7 by:

# let multab n = apply_fun_list n (mk_mult_fun_list 10) ;;
val multab : int -> int list = <fun>
# multab 7 ;;
- : int list = [7; 14; 21; 28; 35; 42; 49; 56; 63; 70]


Iteration over lists

The function call fold_left f a [e1; e2; ... ; en] returns f ... (f (f a e1) e2) ... en. So there are n applications.

# let rec fold_left f a l =
if null l then a
else fold_left f ( f a (List.hd l)) (List.tl l) ;;
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a = <fun>


The function fold_left permits the compact definition of a function to compute the sum of the elements of a list of integers:

# let sum_list = fold_left (+) 0 ;;
val sum_list : int list -> int = <fun>
# sum_list [2;4;7] ;;
- : int = 13


Or else, the concatenation of the elements of a list of strings:

# let concat_list = fold_left (^) "" ;;
val concat_list : string list -> string = <fun>
# concat_list ["Hello "; "world" ; "!"] ;;
- : string = "Hello world!"



Previous Contents Next ocaml-book-1.0/en/html/book-ora066.gif0000644000000000000000000000253507452056112014274 0ustar GIF89aUUU!,ڋO0r扦 #`$KC! DFH !}"^j2 +LAU` 'J(8HXhx؄(1iiT yɉ)P:q:J:1 KkWd[lklMK*}L]} M^w+>^޽l?!oYߨߺE 8`.e*МÇHxEŚxƱ C.͝QZ\IO]I0/e_2g)(ϟ@Ae;JЧFNA(+u)5U<{&bVTh0?@|qˬ$oӺMH$D\:VE?s&}VGX8^YZe@Mpc]~*Fj&dI \ #k|6/!0͑}{Y`B]\!+ ]rUc&`QGi(\ :Q z<M-8ԗ"R%QJMRѕNiU9v# fnlP46709q0 Egy[$&*f!K]N(F*( |\09!MX*j '9ɪ~VZ)YJ0+Hhdʒc&>{l 6J,\+Ҍ2[׎nzԪr.E+. /ޫXo @ ? 7DJM[[_ rE{\r'+-2"+ 3/,#ۜsO iL&r<|!Mė̴m\Rja {3 ~Dqg6jrw`?7kSRm}QlϹsJeA_ ԃȻeٌ:lOf fmbs%Raݹutg1wX%Y+rwĻ3EOt||}FW;}+}]Cqt蛯~FOߏI 8bj? p9A0p #Ȍt`mm17ՠ@p.hZ ! j@O+j,E.d[V¡DTS A򥉌}_3Cl:l\T`ĊZBBc9_ev3r'B@n^U, /p6 ĪX7 pG;ocaml-book-1.0/en/html/book-ora192.html0000644000000000000000000000171307453055401014471 0ustar Notes
1
Note of translator: ``boulmich'' is a colloquial abbreviation for ``Boulevard Saint-Michel'', one the principal avenues of Quartier Latin in Paris...
2
Such as the XDR representation (eXternal Data Representation), which was designed for C programs.
ocaml-book-1.0/en/html/book-ora185.html0000644000000000000000000002644307453055400014501 0ustar The Internet Previous Contents Next

The Internet

The Internet is a network of networks. Their interconnection is organized as a hierarchy of domains, subdomains, and so on, through interfaces. An interface is the hardware in a computer that allows it to be connected (typically, an Ethernet card). Some computers may have several interfaces. Each interface has a unique IP address that respects, in general, the interconnection hierarchy. Message routing is also organized hierarchically: from domain to domain; then from domain to subdomains, and so on, until a message reaches its destination interface. Besides their interface addresses, computers usually also have a name, as do domains and subdomains. Some machines have a particular role in the network:
bridges
connect one network to another;
routers
use their knowledge of the topology of the Internet to route data;
name servers
track the correspondence between machine names and network addresses.
The purpose of the Internet protocol (i.e., of the IP) is to make the network of networks into a single entity. This is why one can speak of the Internet. Any two machines connected via the Internet can communicate. Many kinds of machines and systems coexist on the Internet. All of them use IP protocols and most of them, the UDP and TCP layers.

The different protocols and services used by the Internet are described in RFC's (Requests For Comments), which can be found on the Jussieu mirror site:

Link


ftp://ftp.lip6.fr/pub/rfc


Internet Protocols and Services

The unit of transfer used by the IP protocol is the datagram or packet. This protocol in unreliable: it does not assure proper order, safe arrival, or non-duplication of transmitted packets. It only deals with correct routing of packets and signaling of errors when a packet is unable to reach its destination. Addresses are coded into 32 bits in the current version of the protocol: IPv4. These 32 bits are divided into four fields, each containing values between 0 and 255. IP addresses are written with the four fields separated by periods, for example: 132.227.60.30.

The IP protocol is in the midst of an important change made necessary by the exhaustion of address space and the growing complexity of routing problems due to the expansion of the Internet. The new version of the IP protocol is IPv6, which is described in [Hui97].

Above IP, two protocols allow higher-level transmissions: UDP (User Datagram Protocol, and TCP (Transfer Control Protocol). These two protocols use IP for communication between machines, also allowing communication between applications (or programs) running on those machines. They deal with correct transmission of information, independent of contents. The identification of applications on a machine is done via a port number.

UDP is a connectionless, unreliable protocol: it is to applications as IP is to interfaces. TCP is a connection-oriented, reliable protocol: it manages acknowledgement, retransmission, and ordering of packets. Further, it is capable of optimizing transmission by a windowing technique.

The standard services (applications) of the Internet most often use the client-server model. The server manages requests by clients, offering them a specific service. There is an asymmetry between client and server. The services establish high-level protocols for keeping track of transmitted contents. Among the standard services, we note:
  • FTP (File Transfer Protocol);
  • TELNET (Terminal Protocol);
  • SMTP (Simple Mail Transfer Protocol);
  • HTTP (Hypertext Transfer Protocol).
Other services use the client-server model:
  • NFS (Network File System);
  • X-Windows
  • Unix services: rlogin, rwho ...
Communication between applications takes place via sockets. Sockets allow communication between processes residing on possibly different machines. Different processes can read and write to sockets.

The Unix Module and IP Addressing

The Unix library defines the abstract type inet_addr representing Internet addresses, as well as two conversion functions between an internal representation of addresses and strings:

# Unix.inet_addr_of_string ;;
- : string -> Unix.inet_addr = <fun>
# Unix.string_of_inet_addr ;;
- : Unix.inet_addr -> string = <fun>


In applications, Internet addresses and port numbers for services (or service numbers) are often replaced by names. The correspondence between names and address or number is managed using databases. The Unix library provides functions to request data from these databases and provides datatypes to allow storage of the obtained information. We briefly describe these functions below.

Address tables.
The table of addresses (hosts database) contains the assocation between machine name(s) and interface address(es). The structure of entries in the address table is represented by:

# type host_entry =
{ h_name : string;
h_aliases : string array;
h_addrtype : socket_domain;
h_addr_list : inet_addr array } ;;
The first two fields contain the machine name and its aliases; the third contains the address type (see page ??); the last contains a list of machine addresses.

A machine name is obtained by using the function:

# Unix.gethostname ;;
- : unit -> string = <fun>
# let my_name = Unix.gethostname() ;;
val my_name : string = "estephe.inria.fr"


The functions that query the address table require an entry, either the name or the machine address.

# Unix.gethostbyname ;;
- : string -> Unix.host_entry = <fun>
# Unix.gethostbyaddr ;;
- : Unix.inet_addr -> Unix.host_entry = <fun>
# let my_entry_byname = Unix.gethostbyname my_name ;;
val my_entry_byname : Unix.host_entry =
{Unix.h_name="estephe.inria.fr"; Unix.h_aliases=[|"estephe"|];
Unix.h_addrtype=Unix.PF_INET; Unix.h_addr_list=[|<abstr>|]}
# let my_addr = my_entry_byname.Unix.h_addr_list.(0) ;;
val my_addr : Unix.inet_addr = <abstr>

# let my_entry_byaddr = Unix.gethostbyaddr my_addr ;;
val my_entry_byaddr : Unix.host_entry =
{Unix.h_name="estephe.inria.fr"; Unix.h_aliases=[|"estephe"|];
Unix.h_addrtype=Unix.PF_INET; Unix.h_addr_list=[|<abstr>|]}

# let my_full_name = my_entry_byaddr.Unix.h_name ;;
val my_full_name : string = "estephe.inria.fr"
These functions raise the Not_found exception in case the request fails.

Table of services.
The table of services contains the correspondence between service names and port numbers. The majority of Internet services are standardized. The structure of entries in the table of services is:

# type service_entry =
{ s_name : string;
s_aliases : string array;
s_port : int;
s_proto : string } ;;
The first two fields are the service name and its eventual aliases; the third field contains the port number; the last field contains the name of the protocol used.

A service is in fact characterized by its port number and the underlying protocol. The query functions are:

# Unix.getservbyname ;;
- : string -> string -> Unix.service_entry = <fun>
# Unix.getservbyport ;;
- : int -> string -> Unix.service_entry = <fun>
# Unix.getservbyport 80 "tcp" ;;
- : Unix.service_entry =
{Unix.s_name="www"; Unix.s_aliases=[|"http"|]; Unix.s_port=80;
Unix.s_proto="tcp"}
# Unix.getservbyname "ftp" "tcp" ;;
- : Unix.service_entry =
{Unix.s_name="ftp"; Unix.s_aliases=[||]; Unix.s_port=21; Unix.s_proto="tcp"}
These functions raise the Not_found exception if they cannot find the service requested.


Previous Contents Next ocaml-book-1.0/en/html/book-ora001.html0000644000000000000000000000560607453055377014477 0ustar Preface Contents Next

Preface

The desire to write a book on Objective CAML sprang from the authors' pedagogical experience in teaching programming concepts through the Objective CAML language. The students in various majors and the engineers in continuing education at Pierre and Marie Curie University have, through their dynamism and their critiques, caused our presentation of the Objective CAML language to evolve greatly. Several examples in this book are directly inspired by their projects.

The implementation of the Caml language has been ongoing for fifteen years. Its development comes from the Formel and then Cristal projects at INRIA, in collaboration with Denis Diderot University and the cole Normale Suprieure. The continuous efforts of the researchers on these teams, as much to develop the theoretical underpinnings as the implementation itself, have produced over the span of years a language of very high quality. They have been able to keep pace with the constant evolution of the field while integrating new programming paradigms into a formal framework. We hope through this exposition to contribute to the widespread diffusion which this work deserves.

The form and the foundation of this book wouldn't be what they are without the help of numerous colleagues. They were not put off by rereading our first manuscripts. Their remarks and their comments have allowed this exposition to improve throughout the course of its development. We wish particularly to thank Mara-Virginia Aponte, Sylvain Baro, Christian Codognet, Hlne Cottier, Guy Cousineau, Pierre Crgut, Titou Durand, Christophe Gonzales, Michelle Morcrette, Christian Queinnec, Attila Raksany and Didier Rmy.

The HTML version of this book would not have seen the light of day without the tools hevea and VideoC. A big thank you to their respective authors, Luc Maranget and Christian Queinnec, who have always responded in the briefest intervals to our questions and our demands for changes.






Contents Next ocaml-book-1.0/en/html/book-ora064.gif0000644000000000000000000000335707452056112014275 0ustar GIF89a UUU!, H0I8ͻ@(dihl~+tm |ooH,rƤ2#4hZUMMR3ȳS߰/|F4~hW|wroxSs]Q a}UvlRjgDz>`G>YԠ"B!: B!=m9^N"BkEvO8%dHo@Z*? $G \ṁ_bE(1UgyT[$%n3EEڼk{YBQ6bX `9 1ʽ va R>5T&å3ㇸn,H:-g6&.e F}0^)W`8Sm8atÉ(b>++BRҌ6.!Xe>8|Ed/A K6$O0+O*L]3lIy^^nAf]fp(YK nsfYg&w~{g(KU(efUi~yM^T*YjR:w*sʠnjhڟ^X۝O"G2I;8ˈ?RK9b[$#n{(*Abn&Ja.zk:ooFogz/ʻ0X Ra711 PŎb1J2㱖 , S`tL|M8 yr_KGEFېFqd?MtqmuѺqdX}"6u`ͱ WMѭNiqm7dGm$a=Q:|` XQ*Se_iWYL4g9Zz`Аwap3xzNŃc.C>hDU 4oVX_jݟ꼭Dfʭĸu_VqWG9vhm<:VhU*8Tu.h?`TA>qBaNA<%T5L`ڢCJ]- EvB|Pb=dEt%0De1[~!rتvEh \좠H$nd aθ9Iltcu8RQA'"?)1*h<$"E2R_\"#IR2źdI96s!(h%PVM|SyS2ꆮlH!گe.]6T#,+`3[<&fL1&!49iNDU2jrZf) ':vlP-ɥy2>|i%?MO1)'@ Bj;H'gP{V Fu~REjz FiM}<2WQL([4uEɃ:-UI0T4_R斑/vq+=/"}Shyɟ TU Qw% t)e4W= XM dobbMF>lI $;ocaml-book-1.0/en/html/book-ora171.html0000644000000000000000000000517207453055400014470 0ustar To Learn More Previous Contents Next

To Learn More

The Unix module provides functions of the Unix system library. Most of the underlying programming paradigms are not described in Objective CAML. The reader may refer to standard books about system programming. We cite [Ste92], or [CDM98], more specific to Linux.

Further, the excellent lecture notes from Xavier Leroy [Ler92] have for subject system programming in Caml-Light. They can be accessed under the following address:

Link


http://pauillac.inria.fr/~xleroy/publi/unix-in-caml.ps.gz


The implementation of the Unix module is a good example for the cooperation between C and Objective CAML. A large number of functions are just calls to C system functions, with the additional type transcription of the data. The implementation sources are good examples of how to interface an Objective CAML program with a C library. The programs can be found in the directories otherlibs/unix and otherlibs/win32unix of the Objective CAML distribution.

The chapter did present several functionalities of the Unix module. Some more points will be approached in chapter 20 about communication sockets and Internet addresses. Other notions, like that of terminals, of file systems, etc. are not discussed in this book. They can be explored in one of the books mentioned above.








Previous Contents Next ocaml-book-1.0/en/html/contents_motif.gif0000644000000000000000000000047407452056124015365 0ustar GIF89ap!" Imported from XPM image: toc.xpm!,@6313c B0 0 A0 0 0 0 `0@`0 `  `0@`0 `0@`0000000000 0000000000 00000000 000000 0000 000000000 00000000000 00000000000000` ;ocaml-book-1.0/en/html/book-ora156.html0000644000000000000000000010200707453055400014466 0ustar Exercises Previous Contents Next

Exercises

Classes and Modules for Data Structures

We wish to construct class hierarchies based on the application of functors for classical data structures.

We define the following structures


# module type ELEMENT =
sig
class element :
string ->
object
method to_string : unit -> string
method of_string : string -> unit
end
end ;;

# module type STRUCTURE =
sig
class ['a] structure :
object
method add : 'a -> unit
method del : 'a -> unit
method mem : 'a -> bool
method get : unit -> 'a
method all : unit -> 'a list
method iter : ('a -> unit) -> unit
end
end ;;
  1. Write a module with 2 parameters M1 and M2 of types ELEMENT and STRUCTURE, constructing a sub-class of ['a] structure in which 'a is constrained to M1.element.

    # module Struct_Elmt (E:ELEMENT) (S:STRUCTURE) =
    struct
    class e_structure =
    object
    inherit [E.element] S.structure
    end
    end ;;
    module Struct_Elmt :
    functor(E : ELEMENT) ->
    functor(S : STRUCTURE) ->
    sig
    class e_structure :
    object
    method add : E.element -> unit
    method all : unit -> E.element list
    method del : E.element -> unit
    method get : unit -> E.element
    method iter : (E.element -> unit) -> unit
    method mem : E.element -> bool
    end
    end


  2. Write a simple module Integer which respects the signature ELEMENT.

    # module Entier : ELEMENT =
    struct
    class element v =
    object
    val mutable n = int_of_string v
    method to_string () = string_of_int n
    method of_string x = n <- (int_of_string x)
    end
    end ;;
    module Entier : ELEMENT


  3. Write a simple moduleStack which respects the signature STRUCTURE.

    # module Pile : STRUCTURE =
    struct
    class ['a] structure =
    object
    val mutable p = ( [] : 'a list )
    method add x = p <- x::p
    method del x = p <- List.filter ((<>) x) p
    method mem x = List.mem x p
    method get () = List.hd p
    method all () = p
    method iter f = List.iter f p
    end
    end ;;
    module Pile : STRUCTURE


  4. Apply the functor to its two parameters.

    # module Pile_Entier = Struct_Elmt(Entier)(Pile) ;;
    module Pile_Entier :
    sig
    class e_structure :
    object
    method add : Entier.element -> unit
    method all : unit -> Entier.element list
    method del : Entier.element -> unit
    method get : unit -> Entier.element
    method iter : (Entier.element -> unit) -> unit
    method mem : Entier.element -> bool
    end
    end


  5. Modify the functor by adding the methods to_string and of_string.

    # let split c s =
    let suffix s i =
    try String.sub s i ((String.length s)-i)
    with Invalid_argument("String.sub") -> ""
    in
    let rec split_from n =
    try let p = String.index_from s n c
    in (String.sub s n (p-n)) :: (split_from (p+1))
    with Not_found -> [ suffix s n ]
    in
    if s="" then [] else split_from 0 ;;
    val split : char -> string -> string list = <fun>

    # module Elmt_Struct (E:ELEMENT) (S:STRUCTURE) =
    struct
    class element v =
    object (self)
    val n = new S.structure
    method to_string () =
    let res = ref "" in
    let f x = res := !res ^ ((x#to_string ()) ^ " ") in
    n#iter f ;
    !res
    method of_string x =
    let l = split ' ' x in
    List.iter (fun x -> n#add (new E.element x)) l
    initializer self#of_string v
    end
    end ;;
    module Elmt_Struct :
    functor(E : ELEMENT) ->
    functor(S : STRUCTURE) ->
    sig
    class element :
    string ->
    object
    val n : E.element S.structure
    method of_string : string -> unit
    method to_string : unit -> string
    end
    end


  6. Apply the functor again , and then apply it to the result .

# module Entier_Pile = Elmt_Struct (Entier) (Pile) ;;
module Entier_Pile :
sig
class element :
string ->
object
val n : Entier.element Pile.structure
method of_string : string -> unit
method to_string : unit -> string
end
end

# module Entier_Pile_Pile = Elmt_Struct (Entier_Pile) (Pile) ;;
module Entier_Pile_Pile :
sig
class element :
string ->
object
val n : Entier_Pile.element Pile.structure
method of_string : string -> unit
method to_string : unit -> string
end
end


Abstract Types

Continuing from the previous exercise, we wish to implement a module with signature ELEMENT of which the class element uses one instance variable of abstract type.

We define the following parameterized type:

# type 'a t = {mutable x : 'a t; f : 'a t -> unit};;


  1. Write the functions apply, from_string and to_string. These last two functions will use the Marshal module.

    # let apply val_abs = val_abs.f val_abs.x ;;
    val apply : 'a t -> unit = <fun>
    # let from_string s = Marshal.from_string s 0 ;;
    val from_string : string -> 'a = <fun>
    # let to_string v = Marshal.to_string v [Marshal.Closures] ;;
    val to_string : 'a -> string = <fun>


  2. Write a signature S which corresponds to the signature previously inferred by abstracting the type t.

    # module type S =
    sig
    type t
    val apply : t -> unit
    val from_string : string -> t
    val to_string : t -> string
    end ;;
    module type S =
    sig
    type t
    val apply : t -> unit
    val from_string : string -> t
    val to_string : t -> string
    end


  3. Write a functor which takes a parameter with signature S and returns a module of which the signature is compatible with ELEMENT.

    # module Element_of (M:S) =
    struct
    class element v =
    object
    val mutable n = M.from_string v
    method to_string () = M.to_string n
    method of_string x = n <- M.from_string x
    end
    end ;;
    module Element_of :
    functor(M : S) ->
    sig
    class element :
    string ->
    object
    val mutable n : M.t
    method of_string : string -> unit
    method to_string : unit -> string
    end
    end


  4. Use the resulting module as the parameter of the module from the previous exercise.

    # module Abstrait_Pile (M:S) = Elmt_Struct (Element_of(M)) ;;
    module Abstrait_Pile :
    functor(M : S) ->
    functor(S : STRUCTURE) ->
    sig
    class element :
    string ->
    object
    val n : Element_of(M).element S.structure
    method of_string : string -> unit
    method to_string : unit -> string
    end
    end

Previous Contents Next ocaml-book-1.0/en/html/book-ora133.html0000644000000000000000000010050407453055400014461 0ustar Extended Example: Managing Bank Accounts Previous Contents Next

Extended Example: Managing Bank Accounts

We conclude this chapter by an example illustrating the main aspects of modular programming: type abstraction, multiple views of a module, and functor-based code reuse.

The goal of this example is to provide two modules for managing a bank account. One is intended to be used by the bank, and the other by the customer. The approach is to implement a general-purpose parameterized functor providing all the needed operations, then apply it twice to the correct parameters, constraining it by the signature corresponding to its final user: the bank or the customer.

Organization of the Program




Figure 14.1: Modules dependency graph.


The two end modules BManager and CManager are obtained by constraining the module Manager. The latter is obtained by applying the functor FManager to the modules Account, Date and two additional modules built by application of the functors FLog and FStatement. Figure 14.1 illustrates these dependencies.

Signatures for the Module Parameters

The module for account management is parameterized by four other modules, whose signatures we now detail.

The bank account.
This module provides the basic operations on the contents of the account.

# module type ACCOUNT = sig
type t
exception BadOperation
val create : float -> float -> t
val deposit : float -> t -> unit
val withdraw : float -> t -> unit
val balance : t -> float
end ;;
This set of functions provide the minimal operations on an account. The creation operation takes as arguments the initial balance and the maximal overdraft allowed. Excessive withdrawals may raise the BadOperation exception.

Ordered keys.
Operations are recorded in an operation log described in the next paragraph. Each log entry is identified by a key. Key management functions are described by the following signature:

# module type OKEY =
sig
type t
val create : unit -> t
val of_string : string -> t
val to_string : t -> string
val eq : t -> t -> bool
val lt : t -> t -> bool
val gt : t -> t -> bool
end ;;
The create function returns a new, unique key. The functions of_string and to_string convert between keys and character strings. The three remaining functions are key comparison functions.

History.
Logs of operations performed on an account are represented by the following abstract types and functions:

# module type LOG =
sig
type tkey
type tinfo
type t
val create : unit -> t
val add : tkey -> tinfo -> t -> unit
val nth : int -> t -> tkey*tinfo
val get : (tkey -> bool) -> t -> (tkey*tinfo) list
end ;;


We keep unspecified for now the types of the log keys (type tkey) and of the associated data (type tinfo), as well as the data structure for storing logs (type t). We assume that new informations added with the add function are kept in sequence. Two access functions are provided: access by position in the log (function nth) and access following a search predicate on keys (function get).

Account statements.
The last parameter of the manager module provides two functions for editing a statement for an account:

# module type STATEMENT =
sig
type tdata
type tinfo
val editB : tdata -> tinfo
val editC : tdata -> tinfo
end ;;
We leave abstract the type of data to process (tdata) as well as the type of informations extracted from the data (tinfo).

The Parameterized Module for Managing Accounts

Using only the information provided by the signatures above, we now define the general-purpose functor for managing accounts.

# module FManager =
functor (C:ACCOUNT) ->
functor (K:OKEY) ->
functor (L:LOG with type tkey=K.t and type tinfo=float) ->
functor (S:STATEMENT with type tdata=L.t and type tinfo
= (L.tkey*L.tinfo) list) ->
struct
type t = { accnt : C.t; log : L.t }
let create s d = { accnt = C.create s d; log = L.create() }
let deposit s g =
C.deposit s g.accnt ; L.add (K.create()) s g.log
let withdraw s g =
C.withdraw s g.accnt ; L.add (K.create()) (-.s) g.log
let balance g = C.balance g.accnt
let statement edit g =
let f (d,i) = (K.to_string d) ^ ":" ^ (string_of_float i)
in List.map f (edit g.log)
let statementB = statement S.editB
let statementC = statement S.editC
end ;;
module FManager :
functor(C : ACCOUNT) ->
functor(K : OKEY) ->
functor
(L : sig
type tkey = K.t
and tinfo = float
and t
val create : unit -> t
val add : tkey -> tinfo -> t -> unit
val nth : int -> t -> tkey * tinfo
val get : (tkey -> bool) -> t -> (tkey * tinfo) list
end) ->
functor
(S : sig
type tdata = L.t
and tinfo = (L.tkey * L.tinfo) list
val editB : tdata -> tinfo
val editC : tdata -> tinfo
end) ->
sig
type t = { accnt: C.t; log: L.t }
val create : float -> float -> t
val deposit : L.tinfo -> t -> unit
val withdraw : float -> t -> unit
val balance : t -> float
val statement : (L.t -> (K.t * float) list) -> t -> string list
val statementB : t -> string list
val statementC : t -> string list
end


Sharing between types.
The type constraint over the parameter L of the FManager functor indicates that the keys of the log are those provided by the K parameter, and that the informations stored in the log are floating-point numbers (the transaction amounts). The type constraint over the S parameter indicates that the informations contained in the statement come from the log (the L parameter). The signature inferred for the FManager functor reflects the type sharing constraints in the inferred signatures for the functor parameters.

The type t in the result of FManager is a pair of an account (C.t) and its transaction log.

Operations.
All operations defined in this functor are defined in terms of lower-level functions provided by the module parameters. The creation, deposit and withdrawal operations affect the contents of the account and add an entry in its transaction log. The other functions return the account balance and edit statements.

Implementing the Parameters

Before building the end modules, we must first implement the parameters to the FManager module.

Accounts.
The data structure for an account is composed of a float representing the current balance, plus the maximum overdraft allowed. The latter is used to check withdrawals.

# module Account:ACCOUNT =
struct
type t = { mutable balance:float; overdraft:float }
exception BadOperation
let create b o = { balance=b; overdraft=(-. o) }
let deposit s c = c.balance <- c.balance +. s
let balance c = c.balance
let withdraw s c =
let ss = c.balance -. s in
if ss < c.overdraft then raise BadOperation
else c.balance <- ss
end ;;
module Account : ACCOUNT


Choosing log keys.
We decide that keys for transaction logs should be the date of the transaction, expressed as a floating-point number as returned by the time function from module Unix.

# module Date:OKEY =
struct
type t = float
let create() = Unix.time()
let of_string = float_of_string
let to_string = string_of_float
let eq = (=)
let lt = (<)
let gt = (>)
end ;;
module Date : OKEY


The log.
The transaction log depends on a particular choice of log keys. Hence we define logs as a functor parameterized by a key structure.

# module FLog (K:OKEY) =
struct
type tkey = K.t
type tinfo = float
type t = { mutable contents : (tkey*tinfo) list }
let create() = { contents = [] }
let add c i l = l.contents <- (c,i) :: l.contents
let nth i l = List.nth l.contents i
let get f l = List.filter (fun (c,_) -> (f c)) l.contents
end ;;
module FLog :
functor(K : OKEY) ->
sig
type tkey = K.t
and tinfo = float
and t = { mutable contents: (tkey * tinfo) list }
val create : unit -> t
val add : tkey -> tinfo -> t -> unit
val nth : int -> t -> tkey * tinfo
val get : (tkey -> bool) -> t -> (tkey * tinfo) list
end


Notice that the type of informations stored in log entries must be consistent with the type used in the account manager functor.

Statements.
We define two functions for editing statements. The first (editB) lists the five most recent transactions, and is intended for the bank; the second (editC) lists all transactions performed during the last 10 days, and is intended for the customer.


# module FStatement (K:OKEY) (L:LOG with type tkey=K.t) =
struct
type tdata = L.t
type tinfo = (L.tkey*L.tinfo) list
let editB h =
List.map (fun i -> L.nth i h) [0;1;2;3;4]
let editC h =
let c0 = K.of_string (string_of_float ((Unix.time()) -. 864000.)) in
let f = K.lt c0 in
L.get f h
end ;;
module FStatement :
functor(K : OKEY) ->
functor
(L : sig
type tkey = K.t
and tinfo
and t
val create : unit -> t
val add : tkey -> tinfo -> t -> unit
val nth : int -> t -> tkey * tinfo
val get : (tkey -> bool) -> t -> (tkey * tinfo) list
end) ->
sig
type tdata = L.t
and tinfo = (L.tkey * L.tinfo) list
val editB : L.t -> (L.tkey * L.tinfo) list
val editC : L.t -> (L.tkey * L.tinfo) list
end


In order to define the 10-day statement, we need to know exactly the implementation of keys as floats. This arguably goes against the principles of type abstraction. However, the key corresponding to ten days ago is obtained from its string representation by calling the K.of_string function, instead of directly computing the internal representation of this date. (Our example is probably too simple to make this subtle distinction obvious.)

End modules.
To build the modules MBank and MCustomer, for use by the bank and the customer respectively, we proceed as follows:
  1. define a common ``account manager'' structure by application of the FManager functor;
  2. declare two signatures listing only the functions accessible to the bank or to the customer;
  3. constrain the structure obtained in 1 with the signatures declared in 2.

# module Manager =
FManager (Account)
(Date)
(FLog(Date))
(FStatement (Date) (FLog(Date))) ;;
module Manager :
sig
type t =
FManager(Account)(Date)(FLog(Date))(FStatement(Date)(FLog(Date))).t =
{ accnt: Account.t;
log: FLog(Date).t }
val create : float -> float -> t
val deposit : FLog(Date).tinfo -> t -> unit
val withdraw : float -> t -> unit
val balance : t -> float
val statement :
(FLog(Date).t -> (Date.t * float) list) -> t -> string list
val statementB : t -> string list
val statementC : t -> string list
end

# module type MANAGER_BANK =
sig
type t
val create : float -> float -> t
val deposit : float -> t -> unit
val withdraw : float -> t -> unit
val balance : t -> float
val statementB : t -> string list
end ;;

# module MBank = (Manager:MANAGER_BANK with type t=Manager.t) ;;
module MBank :
sig
type t = Manager.t
val create : float -> float -> t
val deposit : float -> t -> unit
val withdraw : float -> t -> unit
val balance : t -> float
val statementB : t -> string list
end

# module type MANAGER_CUSTOMER =
sig
type t
val deposit : float -> t -> unit
val withdraw : float -> t -> unit
val balance : t -> float
val statementC : t -> string list
end ;;

# module MCustomer = (Manager:MANAGER_CUSTOMER with type t=Manager.t) ;;
module MCustomer :
sig
type t = Manager.t
val deposit : float -> t -> unit
val withdraw : float -> t -> unit
val balance : t -> float
val statementC : t -> string list
end


In order for accounts created by the bank to be usable by clients, we added the type constraint on Manager.t in the definition of the MBank and MCustomer structures, to ensure that their t type components are compatible.








Previous Contents Next ocaml-book-1.0/en/html/book-ora158.html0000644000000000000000000000472707453055400014502 0ustar To Learn More Previous Contents Next

To Learn More

The modular model suffers from weak code reuse and difficulties for incremental development. The article "Modular Programming with overloading and delayed linking" ([AC96]) describes a simple extension of the module language, allowing the extension of a module as well as overloading. The choice of code for an overloaded function derives from the techniques used for generic functions in CLOS. The correction of the type system to accommodate these extended modules has not been established.

The issues of mixing the models are well discussed in the article "Modular Object-Oriented Programming with Units and Mixing"([FF98]), in terms of the ease with which code can be reused. The problem of extensibility of components is described in detail.

This article is available in HTML at the following address:

Link


http://www.cs.rice.edu/CS/PLT/Publications/icfp98-ff/paper.shtml


We can see in these concepts that there is still some dynamic typing involved in type constraints and/or the resolution of type conflicts. It is probably not unreasonable to relax static typing to obtain languages that are "primarily" statically typed in the pursuit of increasing the reusability of the code by facilitating its incremental development.








Previous Contents Next ocaml-book-1.0/en/html/book-ora057.gif0000644000000000000000000000520707452056112014273 0ustar GIF89a UUU!, ڋ޼扦ʶʽL3`hL*LyVP j{!&5˳pyqw߰ FxH8ض%ihP9v)u٩9'II`Z: "0Pth{k&K4`0l1l9 ;Q=r<(-=p[Nlz"ޭh.m۳}m'O؝8k_m*Zf"ϊD_iV:B孕4&<@ŝRx'G1[N@j :dQdɔxڏBnC9"d^5 eehezTe[bµfh͙jb"Kkio9ɜvzbw~zij JahN"h ui>(E~XJjjfuJ)"TjLcöXlY+'&K,BKR#vN1hlNt+Di+"u?݄O=~9tiD9ng~DST&#ځ+TneTrYj/<|q]ic-7=s٤uYb]֘`qL! Gsa17Kа\YFS J;ZXٺt:EhU+ʌ QEHүxP)v5hp`5_w{vZ.uIߌ/G8f'NkGw9!,.zEWy&o8N(щ!ƀRIo<$\7+2SA[;r=$J >3?q3׏#φ<]y'~+1$%VM+.⿢Q<]2 rL%{H!\8.H| )MT0^ D>!5CBOX:rD/EMSD8ȁ\RE&v8W,X&+gUt6Yzϔ0mPcz7IoѤ BgqxTE)F],DIУ^# JQ@j^@gjjnؼRBJ%ƴ qk:QBˤLdVd`)#udIZ;AI[~˛窖LeSVoz%n_i9(.\$pҐߕ9ρНb(y 4=gzbrVBcPK4ǣ-&Dx'M@V/zM/HFGxXe^h!܍!dҵ%&jfԂtfX%)8S"nCQ0S%U@hQUEj[EZ tC0? X2$v,3+a&E*A خ3kK8͎:ȣ.p,[?'^FH-c;ڬ=ť Lᢴzˀ\% Lg;vX!CݩKYs6Țr,5N)Qhw :!1* mop!T 32Vd`KW{U-LDFpl D,}Kbl()jZ-|aH3hF^R!F(?׵ecj]V0 ujbɸ+\b&0ipf9[,% }'  iE6ٲ:K&1L-> =#1y2$f,j pt!_.W0'h6A0E}JR9X^Z-WȚuό|֗oLv 6[[І;cL6-ԆfvCj?fGZkV~Wq$FY+Z$-u\܂,]s^+daPB; ganz-3I7-xg4fe[&ǭ,u\ 6g>ky3үIs8GxwNܔ6yk~[9_ \PXazo>h~轻ƶ3}tw-Һ w-7xF.|Jng7GTЫXT)z>=#hE̽J v=('9sz]? /V}c?#9,b Ͻ*- n x!7olt"VvmT F4e~Xy3#Vnxt2Odxr&Gv%$_+|L.s;ocaml-book-1.0/en/html/book-ora053.html0000644000000000000000000000347607453055377014511 0ustar Summary Previous Contents Next

Summary

This chapter has presented the basic notions of graphics programming and event-driven programming using the Graphics library in the distribution of Objective CAML. After having explained the basic graphical elements (colors, drawing, filling, text and bitmaps) we have approached the problem of animating them. The mechanism of handling events in Graphics was then described in a way that allowed the introduction of a general method of handling user interaction. This was accomplished by taking a game as model for event-driven programming. To improve user interactions and to provide interactive graphical components to the programmer, we have developed a new library called Awi, which facilitates the construction of graphical interfaces. This library was used for writing the interface to the imperative calculator.


Previous Contents Next ocaml-book-1.0/en/html/book-ora054.gif0000644000000000000000000000223407452056112014265 0ustar GIF89a?twww!,?tڋH扦ʶ L׮a p¢L*%" JK'j"+ wޱN e:y`09HX6huxLjx(Y)F9uY Iyj :ښe {0{UkK% a{{0,%3`|ʬ  ]m[w1NRkZ1/l5/S@y7auı׾v5{B Mf<-AG 0z㣵8twK Mw'&8[ַ FƒMHfXW*4am(b#"'$Ȣ-܊04؄8<7)DH(6dPFTr)Z\v9`'i{hIޚln)grtyϝg~ h YnCh.'Z"NڨARDV`ˣE|J' Rjᩪj"`k|ʞ /!(+뱶N !+D~^ QעJmO,>8 mʻsN/"jjξ9;p p+py gZ0]{qi +>$SkdL.*c"z$OӺ̳ʬl/9w+Ͳ:t{0M+s'\1S ]ŔxSstٙ 0w7)˭c=u ΀N0{8#$q9fWxەKqEyzJ' ۢZ^ Oz)E{2"o*'O{D/$Wt= yk;ocaml-book-1.0/en/html/book-ora070.gif0000644000000000000000000001006107452056112014260 0ustar GIF89aywvJHF!,ڋ޼nHy Dz\qSdL$g$fsɀ2#b\Am 墶Zyy={8-w7(u Gg(gxٸW5vwxfFhIDV幪*K; ee6,;V[z57X.>.!}Dn.?noG?eS|/@+ d :Đ0ĉ+Z1;zhȑ$K<2ʕ,[| 3̙4kڼ2$Ν<{ 4Р:=4ҥ@2} 5ԩ+*EHQۭ3Vخ ǚr[.A‹!)d Mnt>n:D*lS`լ~RxqCohk?ъBh 3JA R XXA* 8A>j_j!`(.a. !æ gpWJ>HZbQ!@4 X-k)c"$E]h]/1NA=юHcCBrDh}cBC&2<%G6^Rb!MI:~R',UJNq|c>E\)he(HFd|KP2%1L'rEl-IY|0OgvӛTf8M 3}5rQ0's(Nv1g5׷zSg42~3ټC)͵3ˣD y4P/>PhJt!)L!il*ST^7@%w-^de4+Q=*p  ~2.q% X8[[[ߊVu士]j?u۫_K,t[7Ɓ{ONɱE\\fYi#$p6u6BՊϨZk]ӛImHZ^ymoG*ۥg#-Mj6x kJyK,ͩW>6]w0.A܆涃wE&+b=vA:}*7e[zZnӽhIyNW/H"Tes~+\`)a wSB#"w8uU1`\L*r~Ӝ f= ܞO8Ao5cJ)f|!J>"\_.8p"}e*2LKV\r|g:ޤٌ]<ǸL|M@U*ڋ3w5o xem6!Z8G.CzQ&_~a}T7ǽwl[)Hsuwcc<5kfz-_7.xYI#7-?ho~ L4 zef2{tHuK|GGTLPb}hN c X{h_G5zfVog 8fGT8z(hDM'GhDM3Q{dSF"7z,xlVa|ewOtizg|aMcOXzv0NGOGr~G owr'GxgxXXNxU(oR8{_xWY.c1vxXz@aAx u{KxC(lSfWpc8W_özhy'V^7i&b8x 5#szyN7uWrR'VuYv1WUd>еT8GŘt(=gȌL898(t戎}Zgt5Xp1wZWre8xr؋9 '{erHx8AuWvuy]gHmwƑW QɃjXX|ىo*)ji)I TXF7-4yPIuIjRq0I)؉^X}YI|X98ؒ\X'|KZaĖ}-}^hf {y鑏>i;e(hG5R+~Ofh.\9Sh7%X2٧u)^EK)cb )9nI};ƙuvQEygIB|wٖHhv {}b)TWNdW kٞ&|{_9﹕Oy5Y J9k[uX㇦h: A:xwsAwJ@m垝vDIBIv`)9aW1J·S6i'82)C)j/9gt(H`[jcXdjezi*VjʦئozYp*b:u:pu8BzD Jzx:*:٨mFfaz~z$oM*PJNڐ53zd bȨt磀J<Ɉ=٩FړVZ 'ڟ"*eY+x {Ѻ6ʞGjڪzwV7)kys|z|ziiFiƔi!*ǘϚJN8~{KJu3F i KZ7bٜǯ [ $[(,KXQ!kʱe13[ q7c9+˳= aZ#+~*ʞʟېچ>ڱ*Kz(jxJd ?k!:;ZNڒʪЪhvGJ9Jtk{[CJǪ\ڟUgZj+Gz}ڻG: { Uʋ6+q̺tkڑvZȽߛ+H;t狾?s7KP;ocaml-book-1.0/en/html/book-ora157.html0000644000000000000000000000311507453055400014467 0ustar Summary Previous Contents Next

Summary

This chapter has compared the respective merits of the functional/modular and object models of organisation. Each tries to address in its own way the problems of reusability and modifiability of software. The main differences come from their type systems, equality of types between parameters of functors and sub-typing in the object model, and the evaluation of objects with late binding. The two models do not succeed on their own in resolving the problem of the extensibility of components, from whence we get the idea of a mixed organization. This organization mix also permits new ways of structuring.


Previous Contents Next ocaml-book-1.0/en/html/book-ora116.html0000644000000000000000000007131407453055400014470 0ustar Creating and modifying Objective CAML values from C Previous Contents Next

Creating and modifying Objective CAML values from C

A C function called from Objective CAML can modify its arguments in place, or return a newly-created value. This value must match the Objective CAML type for the function result. For base types, several C macros are provided to convert a C datum to an Objective CAML value. For structured types, the new value must be allocated in the Objective CAML heap, with the correct size, and its fields initialized with values of the correct types. Considerable care is required here: it is easy to construct bad values from C, and these bad values may crash the Objective CAML program.

Any allocation in the Objective CAML heap can trigger a garbage collection, which will deallocate unused memory blocks and may move live blocks. Therefore, any Objective CAML value manipulated from C must be registered with the Objective CAML garbage collector, if they are to survive the allocation of a new block. These values must be treated as extra memory roots by the garbage collector. To this end, several macros are provided for registering extra roots with the garbage collector.

Finally, C code can allocate Objective CAML heap blocks that contain C data instead of Objective CAML values. This C data will then benefit from Objective CAML's automatic memory management. If the C data requires explicit deallocation, a finalization function can be attached to the heap block.

Modifying Objective CAML values

The following macros allow the creation of immediate Objective CAML values from the corresponding C data, and the modification of structured values in place.


   
Val_long(l) return the value representing the long integer l
Val_int(i) return the value representing the integer l
Val_bool(x) return false if x=0, true otherwise
Val_true the representation of true
Val_false the representation of false
Val_unit the representation of ()
   
Store_field(b,n,v) store the value v in the n-th field of block b
Store_double_field(b,n,d) store the float d in the n-th field of the float array b

Figure 12.10: Creation of immediate values and modification of structured blocks.


Moreover, the macros Byte and Byte_u can be used on the left-hand side of an assignment to modify the characters of a string. The Field macro can also be used for assignment on blocks with tag Abstract_tag or Final_tag; use Store_field for blocks with tag between 0 and No_scan_tag-1. The following function reverses a character string in place:

#include <caml/mlvalues.h>
value swap_char(value v, int i, int j)
{ char c=Byte(v,i); Byte(v,i)=Byte(v,j); Byte(v,j)=c; }
value swap_string (value v)
{
int i,j,t = string_length(v) ;
for (i=0,j=t-1; i<t/2; i++,j--) swap_char(v,i,j) ;
return v ;
}

# external mirror : string -> string = "swap_string" ;;
external mirror : string -> string = "swap_string"
# mirror "abcdefg" ;;
- : string = "gfedcba"


Allocating new blocks

The functions listed in figure 12.11 allocate new blocks in the Objective CAML heap.

   
alloc(n, t) return a new block of size n words and tag t
alloc_tuple(n) same, with tag 0
alloc_string(n) return an uninitialized string of length n characters
copy_string(s) return a string initialized with the C string s
copy_double(d) return a block containing the double float d
alloc_array(f, a) return a block representing an array, initialized by applying
  the conversion function f to each element of the C array of
  pointers a, null-terminated.
copy_string_array(p) return a block representing an array of strings, obtained
  from the C string array p (of type char **), null-terminated.

Figure 12.11: Functions for allocating blocks.


The function alloc_array takes an array of pointers a, terminated by a null pointer, and a conversion function f taking a pointer and returning a value. The result of alloc_array is an Objective CAML array containing the results of applying f in turn to each pointer in a. In the following example, the function make_str_array uses alloc_array to convert a C array of strings.

#include <caml/mlvalues.h>
value make_str (char *s) { return copy_string(s); }
value make_str_array (char **p) { return alloc_array(make_str,p) ; }


It is sometimes necessary to allocate blocks of size 0, for instance to represent an empty Objective CAML array. Such a block is called an atom.

# inspect [| |] ;;
....memory block: size=0 - structured block (tag=0):
- : '_a array = [||]


Because atoms are allocated statically and do not reside in the dynamic part of the Objective CAML heap, the allocation functions in figure 12.11 must not be used to allocate atoms. Instead, atoms are created in C by the macro Atom(t), where t is the desired tag for the block of size 0.

Storing C data in the Objective CAML heap

It is sometimes convenient to use the Objective CAML heap to store arbitrary C data that does not respect the constraints imposed by the garbage collector. In this case, blocks with tag Abstract_tag must be used.

A natural example is the manipulation of native C integers (of size 32 or 64 bits) in Objective CAML. Since these integers are not tagged as the Objective CAML garbage collector expects, they must be kept in one-word heap blocks with tag Abstract_tag.

#include <caml/mlvalues.h>
#include <stdio.h>

value Cint_of_OCAMLint (value v)
{
value res = alloc(1,Abstract_tag) ;
Field(res,0) = Long_val(v) ;
return res ;
}

value OCAMLint_of_Cint (value v) { return Val_long(Field(v,0)) ; }

value Cplus (value v1,value v2)
{
value res = alloc(1,Abstract_tag) ;
Field(res,0) = Field(v1,0) + Field(v2,0) ;
return res ;
}

value printCint (value v)
{
printf ("%d",(long) Field(v,0)) ; fflush(stdout) ;
return Val_unit ;
}

# type cint
external cint_of_int : int -> cint = "Cint_of_OCAMLint"
external int_of_cint : cint -> int = "OCAMLint_of_Cint"
external plus_cint : cint -> cint -> cint = "Cplus"
external print_cint : cint -> unit = "printCint" ;;


We can now work on native C integers, without losing the use of the tag bit, while remaining compatible with Objective CAML's garbage collector. However, such integers are heap-allocated, instead of being immediate values, which renders arithmetic operations less efficient.

# let a = 1000000000 ;;
val a : int = 1000000000
# a+a ;;
- : int = -147483648
# let c = let b = cint_of_int a in plus_cint b b ;;
val c : cint = <abstr>
# print_cint c ; print_newline () ;;
2000000000
- : unit = ()
# int_of_cint c ;;
- : int = -147483648


Finalization functions

Abstract blocks can also contain pointers to memory blocks allocated outside the Objective CAML heap. We know that Objective CAML blocks that are no longer used by the program are deallocated by the garbage collector. But what happens to a block allocated in the C heap and referenced by an abstract block that was reclaimed by the GC? To avoid memory leaks, we can associate a finalization function to the abstract block; this function is called by the GC before reclaiming the abstract block.

An abstract block with an attached finalization function is allocated via the function alloc_final (n, f, used, max) .
  • n is the size of the block, in words. The first word of the block is used to store the finalization function; hence the size occupied by the user data must be increased by one word.
  • f is the finalization function itself, with type void f (value). It receives the abstract block as argument, just before this block is reclaimed by the GC.
  • used represents the memory space (outside the Objective CAML heap) occupied by the C data. used must be <= max.
  • max is the maximum memory space outside the Objective CAML heap that we tolerate not being reclaimed immediately.
For efficiency reasons, the Objective CAML garbage collector does not reclaim heap blocks as soon as they become unused, but some time later. The ratio used/max controls the proportion of finalized abstract blocks that the garbage collector may leave allocated while they are no longer used. A ratio of 0 (that is, used = 0) lets the garbage collector work at its usual pace; higher ratios (no greater than 1) cause it to work harder and spend more CPU time finding unused finalized blocks and reclaiming them.

The following program manipulates arrays of C integers allocated in the C heap via malloc. To allow the Objective CAML garbage collector to reclaim these arrays automatically, the create function wraps them in a finalized abstract block, containing both a pointer to the array and the finalization function finalize_it.

#include <malloc.h>
#include <stdio.h>
#include <caml/mlvalues.h>

typedef struct {
int size ;
long * tab ; } IntTab ;

IntTab *alloc_it (int s)
{
IntTab *res = malloc(sizeof(IntTab)) ;
res->size = s ;
res->tab = (long *) malloc(sizeof(long)*s) ;
return res ;
}
void free_it (IntTab *p) { free(p->tab) ; free(p) ; }
void put_it (int n,long q,IntTab *p) { p->tab[n] = q ; }
long get_it (int n,IntTab *p) { return p->tab[n]; }

void finalize_it (value v)
{
IntTab *p = (IntTab *) Field(v,1) ;
int i;
printf("reclamation of an IntTab by finalization [") ;
for (i=0;i<p->size;i++) printf("%d ",p->tab[i]) ;
printf("]\n"); fflush(stdout) ;
free_it ((IntTab *) Field(v,1)) ;
}
value create (value s)
{
value block ;
block = alloc_final (2, finalize_it,Int_val(s)*sizeof(IntTab),100000) ;
Field(block,1) = (value) alloc_it(Int_val(s)) ;
return block ;
}
value put (value n,value q,value t)
{
put_it (Int_val(n), Long_val(q), (IntTab *) Field(t,1)) ;
return Val_unit ;
}
value get (value n,value t)
{
long res = get_it (Int_val(n), (IntTab *) Field(t,1)) ;
return Val_long(res) ;
}
The C functions visible from Objective CAML are: create, put and get.

# type c_int_array
external cia_create : int -> c_int_array = "create"
external cia_get : int -> c_int_array -> int = "get"
external cia_put : int-> int -> c_int_array -> unit = "put" ;;


We can now manipulate our new data structure from Objective CAML:

# let tbl = cia_create 10 and tbl2 = cia_create 10
in for i=0 to 9 do cia_put i (i*2) tbl done ;
for i=0 to 9 do print_int (cia_get i tbl) ; print_string " " done ;
print_newline () ;
for i=0 to 9 do cia_put (9-i) (cia_get i tbl) tbl2 done ;
for i=0 to 9 do print_int (cia_get i tbl2) ; print_string " " done ;;
0 2 4 6 8 10 12 14 16 18
18 16 14 12 10 8 6 4 2 0 - : unit = ()


We now force a garbage collection to check that the finalization function is called:

# Gc.full_major () ;;
reclaimation of an IntTab by finalization [18 16 14 12 10 8 6 4 2 0 ]
reclaimation of an IntTab by finalization [0 2 4 6 8 10 12 14 16 18 ]
- : unit = ()
In addition to freeing C heap blocks, finalization functions can also be used to close files, terminate processes, etc.

Garbage collection and C parameters and local variables

A C function can trigger a garbage collection, either during an allocation (if the heap is full), or voluntarily by calling void Garbage_collection_function ().

Consider the following example. Can you spot the error?

#include <caml/mlvalues.h>
#include <caml/memory.h>

value identity (value x)
{
Garbage_collection_function() ;
return x;
}

# external id : 'a -> 'a = "identity" ;;
external id : 'a -> 'a = "identity"
# id [1;2;3;4;5] ;;
- : int list = [538917758; 538917752; 538917746; 538917740; 538917734]
The list passed as parameter to id, hence to the C function identity, can be moved or reclaimed by the garbage collector. In the example, we forced a garbage collection, but any allocation in the Objective CAML heap could have triggered a garbage collection as well. The anonymous list passed to id was reclaimed by the garbage collector, because it is not reachable from the set of known roots. To avoid this, any C function that allocates anything in the Objective CAML heap must tell the garbage collector about the C function's parameters and local variables of type value. This is achieved by using the macros described next.

For parameters, these macros are used within the body of the C function as if they were additional declarations:
CAMLparam1(v) : for one parameter v of type value
CAMLparam2(v1,v2) : for two parameters
...   ...
CAMLparam5(v1,...,v5) : for five parameters
CAMLparam0 ; : required when there are no value parameters.
If the C function has more than five value parameters, the first five are declared with the CAMLparam5 macro, and the remaining parameters with the macros CAMLxparam1, ..., CAMLxparam5, used as many times as necessary to list all value parameters.

CAMLparam5(v1,...,v5);
CAMLxparam5(v6,...,v10);
CAMLxparam2(v11,v12); : for 12 parameters of type value

For local variables, these macros are used instead of normal C declarations of the variables. Local variables of type value must also be registered with the garbage collector, using the macros CAMLlocal1, ..., CAMLlocal5. An array of values is declared with CAMLlocalN(tbl,n) where n is the number of elements of the array tbl. Finally, to return from the C function, we must use the macro CAMLreturn instead of C's return construct.

Here is the corrected version of the previous example:

#include <caml/mlvalues.h>
#include <caml/memory.h>
value identity2 (value x)
{
CAMLparam1(x) ;
Garbage_collection_function() ;
CAMLreturn x;
}

# external id : 'a -> 'a = "identity2" ;;
external id : 'a -> 'a = "identity2"
# let a = id [1;2;3;4;5] ;;
val a : int list = [1; 2; 3; 4; 5]
We now obtain the expected result.

Calling an Objective CAML closure from C

To apply a closure (i.e. an Objective CAML function value) to one or several arguments from C, we can use the functions declared in the header file callback.h.
callback(f,v) : apply the closure f to the argument v,
callback2(f,v1,v2) : same, to two arguments,
callback3(f,v1,v2,v3) : same, to three arguments,
callbackN(f,n,tbl) : same, to n arguments stored in the array tbl.
All these functions return a value, which is the result of the application.

Registering Objective CAML functions with C

The callback functions require the Objective CAML function to be applied as a closure, that is, as a value that was passed as an argument to the C function. We can also register a closure from Objective CAML, giving it a name, then later refer to the closure by its name in a C function.

The function register from module Callback associates a name (of type string) with a closure or with any other Objective CAML value (of any type, that is, 'a). This closure or value can be recovered from C using the C function caml_named_value, which takes a character string as argument and returns a pointer to the closure or value associated with that name, if it exists, or the null pointer otherwise.

An example is in order:

# let plus x y = x + y ;;
val plus : int -> int -> int = <fun>
# Callback.register "plus3_ocaml" (plus 3);;
- : unit = ()
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/callback.h>

value plus3_C (value v)
{
CAMLparam1(v);
CAMLlocal1(f);
f = *(caml_named_value("plus3_ocaml")) ;
CAMLreturn callback(f,v) ;
}

# external plusC : int -> int = "plus3_C" ;;
external plusC : int -> int = "plus3_C"
# plusC 1 ;;
- : int = 4
# Callback.register "plus3_ocaml" (plus 5);;
- : unit = ()
# plusC 1 ;;
- : int = 6
Do not confuse the declaration of a C function with external and the registration of an Objective CAML closure with the function register. In the former case, the declaration is static, the correspondence between the two names is established at link time. In the latter case, the binding is dynamic: the correspondence between the name and the closure is performed at run time. In particular, the name--closure binding can be modified dynamically by registering a different closure with the same name, thus modifying the behavior of C functions using that name.


Previous Contents Next ocaml-book-1.0/en/html/book-ora046.gif0000644000000000000000000000061407452056111014265 0ustar GIF89a$www!,$޼HaʶLil. v|(cfSzByLsjbsU+rK҂iwNg{>x2aBXcg!X(F&(7X9 !8Ƙ*H7rJ ;[+›uK̺G,|ٌky]j=y.N]Mm;m>6\-nOfmbӗ!z)D8^oE^O8#D-eTQȒ Syntax Previous Contents Next

Syntax

Thanks to lexical analysis, we can split up input streams into more structured units: lexical units. We still need to know how to assemble these units so that they amount to syntactically correct sentences in a given language. The syntactic assembly rules are defined by grammar rules. This formalism was originally developed in the field of linguistics, and has proven immensely useful to language-theoretical mathematicians and computer scientists in that field. We have already seen on page ?? an instance of a grammar for the Basic language. We will resume this example to introduce the basic concepts for grammars.

Grammar

Formally, a grammar is made up of four elements:
  1. a set of symbols called terminals. Such symbols represent the lexical units of the language. In Basic, the lexical units (terminals) are: the operator- and arithmetical and logical relation-symbols (+, &, <, <=, ..), the keywords of the language (GOTO, PRINT, IF, THEN, ..), integers (integer units) and variables (variable units).

  2. A set of symbols called non-terminals. Such symbols stand for syntactic terms of the language. For example, a Basic program is composed of lines (and thus we have the term Line), a line may contain and Expression, etc.

  3. A set of so-called production rules. These describe how terminal and non-terminals symbols may be combined to produce a syntactic term. A Basic line is made up of a number followed by an instruction. This is expressed in the following rule:
    Line ::= integer Instruction
    For any given term, there may be several alternative ways to form that term. We separate the alternatives with the symbol | as in
    Instruction ::= LET variable = Expression
      | GOTO integer
      | PRINT Expression
    etc.

  4. Finally, we designate a particular non-terminal as the start symbol. The start symbol identifies a complete translation unit (program) in our language, and the corresponding production rule is used as the starting point for parsing.

Production and Recognition

Production rules allow recognition that a sequence of lexemes belongs to a particular language.

Let's consider, for instance, a simple language for arithmetic expressions:
Exp ::= integer (R1)
  | Exp + Exp (R2)
  | Exp * Exp (R3)
  | ( Exp ) (R4)

where (R1) (R2) (R3) and (R4) are the names given to our rules. After lexical analysis, the expression 1*(2+3) becomes the sequence of lexemes:
integer * ( integer + integer )

To analyze this sentence and recognize that it really belongs to the language of arithmetic expressions, we are going to use the rules from right to left: if a subexpression matches the right-side member of a rule, we replace it with the corresponding left-side member and we re-run the process until reducing the expression to the non-terminal start (here Exp). Here are the stages of such an analysis4:
integer * ( integer + integer ) (R1) Exp * ( integer + integer )
  (R1) Exp * ( Exp + integer )
  (R1) Exp * ( Exp + Exp )
  (R2) Exp * ( Exp )
  (R4) Exp * Exp
  (R3) Exp

Starting from the last line containing only Exp and following the arrows upwards we read how our expression could be produced from the start rule Exp: therefore it is a well-formed sentence of the language defined by the grammar.

The translation of grammars into programs capable of recognizing that a sequence of lexemes belongs to the language defined by a grammar is a much more complex problem than that of using regular expressions. Indeed, a mathematical result tells us that all sets (of words) defined by means of a regular expression formalism can also be defined by another formalism: deterministic finite automata. And these latter are easy to explain as programs taking as input a sequence of characters. We do not have a similar result for the case of generic grammars. However, we have weaker results establishing the equivalence between certain classes of grammars and somewhat richer automata: pushdown automata. We do not want to enter into the details of such results, nor give an exact definition of what an automaton is. Still, we need to identify a class of grammars that may be used with parser-generating tools or parsed directly.

Top-down Parsing

The analysis of the expresion 1*(2+3) introduced in the previous paragraph is not unique: it could also have started by reducing integers from right to left, which would have permitted rule (R2) to reduce 2+3 from the beginning instead. These two ways to proceed constitute two types of analysis: top-down parsing (right-to-left) and bottom-up parsing (left-to-right). The latter is easily realizable with lexeme streams using module Stream. Bottom-up parsing is that carried-out by the ocamlyacc tool. It uses an explicit stack mechanism like the one already described for the parsing of Basic programs. The choice of parsing type is significant, as top-down analysis may or may not be possible given the form of the grammar used to specify the language.

A Simple Case

The canonical example for top-down parsing is the prefix notation of arithmetic expressions defined by:
Expr ::= integer
  | + Expr Expr
  | * Expr Expr

In this case, knowing the first lexeme is enough to decide which production rule can be used. This immediate predictability obviates managing the parse stack explicitly by instead using the stack of recursive calls in the parser. Therefore, it is very easy to write a program implementing top-down analysis using the features in modules Genlex and Stream. Function infix_of is an example; it takes a prefix expression and returns its equivalent infix expression.


# let lexer s =
let ll = Genlex.make_lexer ["+";"*"]
in ll (Stream.of_string s) ;;
val lexer : string -> Genlex.token Stream.t = <fun>
# let rec stream_parse s =
match s with parser
[<'Genlex.Ident x>] -> x
| [<'Genlex.Int n>] -> string_of_int n
| [<'Genlex.Kwd "+"; e1=stream_parse; e2=stream_parse>] -> "("^e1^"+"^e2^")"
| [<'Genlex.Kwd "*"; e1=stream_parse; e2=stream_parse>] -> "("^e1^"*"^e2^")"
| [<>] -> failwith "Parse error"
;;
val stream_parse : Genlex.token Stream.t -> string = <fun>
# let infix_of s = stream_parse (lexer s) ;;
val infix_of : string -> string = <fun>
# infix_of "* +3 11 22";;
- : string = "((3+11)*22)"


One has to be careful, because this parser is rather unforgiving. It is advisable to introduce a blank between lexical units in the input string systematically.

# infix_of "*+3 11 22";;
- : string = "*+"


A Less Simple Case

Parsing using streams is predictive. It imposes two conditions on grammars.
  1. There must be no left-recursive rules in the grammar. A rule is left-recursive when a right-hand expression starts with a non-terminal which is the left-hand member of the expression, as in Exp ::= Exp + Exp;
  2. No two rules may start with the same expression.
The usual grammar for arithmetical expressions on page ?? is not directly suitable for top-down analysis: it does not satisfy any of the above-stated criteria. To be able to use top-down parsing, we must reformulate the grammar so as to suppress left-recursion and non-determinism in the rules. For arithmetic expressions, we may use, for instance:
Expr ::= Atom NextExpr
NextExpr ::= + Atom
  | - Atom
  | * Atom
  | / Atom
  | e
Atom ::= integer
  | ( Expr )

Note that the use of the empty word e in the definition of NextExpr is compulsory if we want a single integer to be an expression.

Our grammar allows the implementation of the following parser which is a simple translation of the production rules. This parser produces the abstract syntax tree of arithmetic expressions.

# let rec rest = parser
[< 'Lsymbol "+"; e2 = atom >] -> Some (PLUS,e2)
| [< 'Lsymbol "-"; e2 = atom >] -> Some (MINUS,e2)
| [< 'Lsymbol "*"; e2 = atom >] -> Some (MULT,e2)
| [< 'Lsymbol "/"; e2 = atom >] -> Some (DIV,e2)
| [< >] -> None
and atom = parser
[< 'Lint i >] -> ExpInt i
| [< 'Lsymbol "("; e = expr ; 'Lsymbol ")" >] -> e
and expr s =
match s with parser
[< e1 = atom >] ->
match rest s with
None -> e1
| Some (op,e2) -> ExpBin(e1,op,e2) ;;
val rest : lexeme Stream.t -> (bin_op * expression) option = <fun>
val atom : lexeme Stream.t -> expression = <fun>
val expr : lexeme Stream.t -> expression = <fun>


The problem with using top-down parsing is that it forces us to use a grammar which is very restricted in its form. Moreover, when the object language is naturally described with a left-recursive grammar (as in the case of infix expressions) it is not always trivial to find an equivalent grammar (i.e. one defining the same language) that satisfies the requirements of top-down parsing. This is the reason why tools such as yacc and ocamlyacc use a bottom-up parsing mechanism which allows the definition of more natural-looking grammars. We will see, however, that not everything is possible with them, either.

Bottom-up Parsing

On page ??, we introduced intuitively the actions of bottom-up parsing: shift and reduce. With each of these actions the state of the stack is modified. We can deduce from this sequence of actions the grammar rules, provided the grammar allows it, as in the case of top-down parsing. Here, also, the difficulty lies in the non-determinism of the rules which prevents choosing between shifting and reducing. We are going to illustrate the inner workings of bottom-up parsing and its failures by considering those pervasive arithmetic expressions in postfix and prefix notation.

The Good News
The simplified grammar for postfix arithmetic expressions is:
Expr ::= integer (R1)
  | Expr Expr + (R2)
  | Expr Expr * (R3)

This grammar is dual to that of prefix expressions: it is necessary to wait until the end of each analysis to know which rule has been used, but then one knows exactly what to do. In fact, the bottom-up analysis of such expressions resembles quite closely a stack-based evaluation mechanism. Instead of pushing the results of each calculation, we simply push the grammar symbols. The idea is to start with an empty stack, then obtain a stack which contains only the start symbol once the input is used up. The modifications to the stack are the following: when we shift, we push the present non-terminal; if we may reduce, it is because the first elements in the stack match the right-hand member of a rule (in reverse order), in which case we replace these elements by the corresponding left-hand non-terminal.

Figure 11.2 illustrates how bottom-up parsing processes expression: 1 2 + 3 * 4 +. The input lexical unit is underlined. The end of input is noted with a $ sign.

Action Input Stack
  12+3*4+$ []
Shift  
  2+3*4+$ [1]
Reduce (R1)  
  2+3*4+$ [Expr]
Shift    
  +3*4+$ [2Expr]
Reduce (R1)  
  +3*4+$ [Expr Expr]
Shift, Reduce (R2)  
  3*4+$ [Expr]
Shift, Reduce (R1)  
  *4+$ [Expr Expr]
Shift, Reduce (R3)  
  4+$ [Expr]
Shift, Reduce (R1)  
  +$ [Expr Expr]
Shift, Reduce (R2)  
  $ [Expr]

Figure 11.2: Bottom-up parsing example.


The Bad News
The difficulty of migrating the grammar into the recognition program is determining which type of action to apply. We will illustrate this difficulty with three examples which generate three types of indeterminacy.

The first example is a grammar for expressions using only addition:
E0 ::= integer (R1)
  | E0 + E0 (R2)
The indeterminacy in this grammar stems from rule (R2). Let's suppose the following situation:
Action Input Stack
:    
  +... [E0 + E0 ...]
:    

In such a case, it is impossible to determine whether we have to shift and push the + or to reduce using (R2) both E0's and the + in the stack. We are in the presence of a shift-reduce conflict. This is because expression integer + integer + integer can be produced in two ways by right-derivation.

First way: E0 (R2) E0 + E0
    (R1) E0 + integer
    (R2) E0 + E0 + integer
  etc.

Second way: E0 (R2) E0 + E0
    (R2) E0 + E0 + E0
    (R1) E0 + E0 + integer
  etc.

The expressions obtained by each derivation may look similar from the point of view of expression evaluation:
(integer + integer) + integer and integer + (integer + integer)
but different for building a syntax tree (see figure 6.3 on page ??).

The second instance of a grammar generating a conflict between shifting and reducing has the same type of ambiguity: an implicit parenthesizing. But contrary to the previous case, the choice between shifting and reducing modifies the meaning of the parsed expression. Let's consider the following grammar:
E1 ::= integer (R1)
  | E1 + E1 (R2)
  | E1 * E1 (R3)
We find in this grammar the above-mentioned conflict both for + and for *. But there is an added conflict between + and *. Here again, an expression may be produced in two ways. There are two right-hand derivations of

integer + integer * integer

First way: E1 (R3) E1 * E1
    (R1) E1 * integer
    (R2) E1 + E1 * integer
  etc.

Second way: E1 (R2) E1 + E1
    (R3) E1 + E1 * E1
    (R1) E1 + E1 * integer
  etc.

Here both pairs of parenthesis (implicit) are not equivalent:
(integer + integer) * integer integer + (integer * integer)

This problem has already been cited for Basic expressions (see page ??). It was solved by attributing different precedence to each operator: we reduce (R3) before (R2), which is equivalent to parenthesizing products.

We can also solve the problem of choosing between + and * by modifying the grammar. We introduce two new terminals: T (for terms), and F (for factors), which gives:
E ::= E + T (R1)
  | T (R2)
T ::= T * F (R3)
  | F (R4)
F ::= integer (R5)
There is now but a single way to reach the production sequence integer + integer * integer: using rule (R1).

The third example concerns conditional instructions in programming languages. A language such as Pascal offers two conditionals : if .. then and if .. then .. else. Let's imagine the following grammar:
Instr ::= if Exp then Instr (R1)
  | if Exp then Instr else Instr (R2)
  | etc...
In the following situation:
Action Input Stack
:    
  else... [Instr then Exp if...]
:    
We cannot decide whether the first elements in the stack relate to conditional (R1), in which case it must be reduced, or to the first Instr in rule (R2), in which case it must be shifted.

Besides shift-reduce conflicts, bottom-up parsing may also generate reduce-reduce conflicts.

We now introduce the ocamlyacc tool which uses the bottom-up parsing technique and may find these conflicts.

The ocamlyacc Tool

The ocamlyacc tools is built with the same principles as ocamllex: it takes as input a file describing a grammar whose rules have semantic actions attached, and returns two Objective CAML files with extensions .ml ant .mli containing a parsing function and its interface.

General format
The syntax description files for ocamlyacc use extension .mly by convention and they have the following structure:

%{  
  header
}%  
  declarations
%%  
  rules
%%  
  trailer-and-end

The rule format is:
non-terminal : symbol...symbol { semantic action }
  | ...
  | symbol...symbol { semantic action }
  ;

A symbol is either a terminal or a non-terminal. Sections ``header'' and ``trailer-and-end'' play the same role as in ocamllex with the only exception that the header is only visible by the rules and not by declarations. In particular, this implies that module openings (open) are not taken into consideration in the declaration part and the types must therefore be fully qualified.

Semantic actions
Semantic actions are pieces of Objective CAML code executed when the parser reduces the rule they are associated with. The body of a semantic action may reference the components of the right-hand term of the rule. These are numbered from left to right starting with 1. The first component is referenced by $1, the second by $2, etc.

Start Symbols
We may declare several start symbols in the grammar, by writing in the declaration section:
 %start non-terminal .. non-terminal
For each of them a parsing function will be generated. We must precisely note, always in the declaration section, the output type of these functions.
 %type <output-type> non-terminal
The output-type must be qualified.

Warning


Non-terminal symbols become the name of parsing functions. Therefore, they must not start with a capital letter which is reserved for constructors.


Lexical units
Grammar rules make reference to lexical units, the terminals or terminal symbols in the rules.

One (or several) lexemes are declared in the following fashion:
 %token PLUS MINUS MULT DIV MOD
Certain lexical units, like identifiers, represent a set of (character) strings. When we find an identifier we may be interested in recovering its character string. We specify in the parser that these lexemes have an associated value by enclosing the type of this value between < and >:
 %token <string> IDENT

Warning


After being processed by ocamlyacc all these declarations are transformed into constructors of type token. Therefore, they must start with a capital letter.


We may use character strings as implicit terminals as in:
expr : expr "+" expr   { ... }
     | expr "*" expr   { ... }
     | ...             
     ;
in which case it is pointless to declare a symbol which represents them: they are directly processed by the parser without passing through the lexer. In the interest of uniformity, we do not advise this procedure.

Precedence, associativity
We have seen that many bottom-up parsing conflicts arise from implicit operator association rules or precedence conflicts between operators. To handle these conflicts, we may declare default associativity rules (left-to-right or non-associative) for operators as well as precedence rules. The following declaration states that operators + (lexeme PLUS) and * (lexeme MULT) associate to the right by default and * has a higher precedence than + because MULT is declared after PLUS.
 %left PLUS
 %left MULT
Two operators declared on the same line have the same precedence.

Command options
ocamlyacc has two options:
  • -b name: the generated Objective CAML files are name.ml and name.mli;
  • -v: create a file with extension .output contaning rule numeration, the states in the automaton recognizing the grammar and the sources of conflicts.
Joint usage with ocamllex
We may compose both tools ocamllex and ocamlyacc so that the transformation of a character stream into a lexeme stream is the input to the parser. To do this, type lexeme should be known to both. This type is defined in the files with extensions .mli and .ml generated by ocamlyacc from the declaration of the tokens in the matching file with extension .mly. The .mll file imports this type; ocamllex translates this file into an Objective CAML function of type Lexing.lexbuf -> lexeme. The example on page ?? illustrates this interaction and describes the different phases of compilation.

Contextual Grammars

Types generated by ocamlyacc process languages produced by so-called context-free grammars. A parser for such a grammar does not depend on previously processed syntactic values to process the next lexeme. This is not the case of the language L described by the following formula:

L ::= wCw | w with w (A|B)*
where A, B and C are terminal symbols. We have written wCw (with w (A|B)*) and not simply (A|B)*C(A|B)* because we want the same word to the left and right of the middle C.

To parse the words in L, we must remember what has already been found before letter C to verify that we find exactly the same thing afterwards. Here is a solution for this problem based on ``visiting'' a stream. The general idea of the algorithm is to build a stream parsing function which will recognize exactly the subword before the possible occurrence of C.

We use the type:

# type token = A | B | C ;;


Function parse_w1 builds the memorizing function for the first w under the guise of a list of atomic stream parsers (i.e. for a single token):


# let rec parse_w1 s =
match s with parser
[<'A; l = parse_w1 >] -> (parser [<'A >] -> "a")::l
| [<'B; l = parse_w1 >] -> (parser [<'B >] -> "b")::l
| [< >] -> [] ;;
val parse_w1 : token Stream.t -> (token Stream.t -> string) list = <fun>


The result of the function returned by parse_w1 is simply the character string containing the parsed lexical unit.

Function parse_w2 takes as argument a list built by parse_w1 to compose each of its elements into a single parsing function:

# let rec parse_w2 l =
match l with
p::pl -> (parser [< x = p; l = (parse_w2 pl) >] -> x^l)
| [] -> parser [<>] -> "" ;;
val parse_w2 : ('a Stream.t -> string) list -> 'a Stream.t -> string = <fun>


The result of applying parse_w2 will be the string representing subword w. By construction, function parse_w2 will not be able to recognize anything but the subword visited by parse_w1.

Using the ability to name intermediate results in streams, we write the recognition function for the words in the language L:

# let parse_L = parser [< l = parse_w1 ; 'C; r = (parse_w2 l) >] -> r ;;
val parse_L : token Stream.t -> string = <fun>


Here are two small examples. The first results in the string surrounding C, the second fails because the words surrounding C are different:

# parse_L [< 'A; 'B; 'B; 'C; 'A; 'B; 'B >];;
- : string = "abb"
# parse_L [< 'A; 'B; 'C; 'B; 'A >];;
Uncaught exception: Stream.Error("")



Previous Contents Next ocaml-book-1.0/en/html/book-ora020.html0000644000000000000000000011532307453055377014476 0ustar Exercises Previous Contents Next

Exercises

Merging two lists

  1. Write a function merge_i which takes as input two integer lists sorted in increasing order and returns a new sorted list containing the elements of the first two.

    # let rec merge_i l1 l2 = match (l1,l2) with
    [],_ -> l2
    | _,[] -> l1
    | h1::t1,h2::t2 ->
    if h1 < h2 then h1::(merge_i t1 l2)
    else h2::(merge_i l1 t2);;
    val merge_i : 'a list -> 'a list -> 'a list = <fun>


  2. Write a general function merge which takes as argument a comparison function and two lists sorted in this order and returns the list merged in the same order. The comparison function will be of type 'a -> 'a -> bool.

    # let rec merge ord_fn l1 l2 = match (l1,l2) with
    [],_ -> l2
    | _,[] -> l1
    | h1::t1,h2::t2 ->
    if ord_fn h1 h2 then h1::(merge ord_fn t1 l2)
    else h2::(merge ord_fn l1 t2);;
    val merge : ('a -> 'a -> bool) -> 'a list -> 'a list -> 'a list = <fun>


  3. Apply this function to two integer lists sorted in decreasing order, then to two string lists sorted in decreasing order.

    # merge (>) [44;33;22;11] [55;30;10];;
    - : int list = [55; 44; 33; 30; 22; 11; 10]
    # merge (>) ["my"; "first"; "effort"]
    ["wonderful";"try";"number";"1"];;
    - : string list =
    ["wonderful"; "try"; "number"; "my"; "first"; "effort"; "1"]


  4. What happens if one of the lists is not in the required decreasing order? If at least one of the lists is not in the given order, then the result probably won't be.

  5. Write a new list type in the form of a record containing three fields: the conventional list, an order function and a boolean indicating whether the list is in that order.

    # type 'a slist = {l: 'a list; ord_fn : 'a -> 'a -> bool; is_in_order : bool};;
    type 'a slist = { l: 'a list; ord_fn: 'a -> 'a -> bool; is_in_order: bool }


  6. Write the function insert which adds an element to a list of this type.

  7. Write a function sort which insertion sorts the elements of a list.

    # let insert e ls =
    let rec insert_rec e l = match l with
    [] -> [e]
    | h::t -> if ls.ord_fn e h then e::l
    else h::(insert_rec e t)
    in
    if ls.is_in_order then {ls with l=insert_rec e ls.l}
    else {ls with l = e::ls.l};;
    val insert : 'a -> 'a slist -> 'a slist = <fun>

    # let sort ls =
    if ls.is_in_order then ls
    else List.fold_right insert ls.l {l=[]; ord_fn=ls.ord_fn; is_in_order=true};;
    val sort : 'a slist -> 'a slist = <fun>


  8. Write a new function merge for these lists.

    # let rec merge_ls l1 l2 =
    if l1.is_in_order then
    if l2.is_in_order then {l = merge l1.ord_fn l1.l l2.l; ord_fn = l1.ord_fn; is_in_order = true}
    else List.fold_right insert l2.l l1
    else
    if l2.is_in_order then merge_ls l2 l1
    else merge_ls (sort l1) l2;;
    val merge_ls : 'a slist -> 'a slist -> 'a slist = <fun>

Lexical trees

Lexical trees (or tries) are used for the representation of dictionaries.

# type lex_node = Letter of char * bool * lex_tree
and lex_tree = lex_node list;;
# type word = string;;
The boolean value in lex_node marks the end of a word when it equals true. In such a structure, the sequence of words ``fa, false, far, fare, fried, frieze'' is stored in the following way:
An asterisk (*) marks the end of a word.
  1. Write the function exists which tests whether a word belongs to a dictionary of type lex_tree.

    # let rec exists w d =
    let aux sw i n = match d with
    [] -> false
    | (Letter (c,b,l))::t when c=sw.[i] ->
    if n = 1 then b else exists (String.sub sw (i+1) (n-1)) l
    | (Letter (c,b,l))::t -> exists sw t
    in aux w 0 (String.length w) ;;
    val exists : string -> lex_tree -> bool = <fun>


  2. Write a function insert which takes a word and a dictionary and returns a new dictionary which additionally contains this word. If the word is already in the dictionary, it is not necessary to insert it.

    # let rec insert w d =
    let aux sw i n =
    if n = 0 then d
    else match d with
    [] -> [Letter (sw.[i], n = 1, insert (String.sub sw (i+1) (n-1)) [])]
    | (Letter(c,b,l))::t when c=sw.[i]->
    if n = 1 then (Letter(c,true,l))::t
    else Letter(c,b,insert (String.sub sw (i+1) (n-1)) l)::t
    | (Letter(c,b,l))::t -> (Letter(c,b,l))::(insert sw t)
    in aux w 0 (String.length w) ;;
    val insert : string -> lex_tree -> lex_tree = <fun>


  3. Write a function construct which takes a list of words and constructs the corresponding dictionary.

    # let construct l =
    let rec aux l d = match l with
    [] -> d
    | h::t -> aux t (insert h d)
    in aux l [] ;;
    val construct : string list -> lex_tree = <fun>


  4. Write a function verify which takes a list of words and a dictionary and returns the list of words not belonging to this dictionary.

    # let rec filter p = function
    [] -> []
    | h::t -> if p h then h::(filter p t) else filter p t ;;
    val filter : ('a -> bool) -> 'a list -> 'a list = <fun>

    # let verify l d = filter (function x -> not (exists x d)) l ;;
    val verify : string list -> lex_tree -> string list = <fun>


  5. Write a function select which takes a dictionary and a length and returns the set of words of this length.

    # let string_of_char c = String.make 1 c ;;
    val string_of_char : char -> string = <fun>
    # let rec select n d = match d with
    [] -> []
    | (Letter(c,b,l))::t when n=1 ->
    let f (Letter (c,b,_)) = if b then string_of_char c else "!" in
    filter (function x -> x <> "!") (List.map f d)
    | (Letter(c,b,l))::t ->
    let r1 = select (n-1) l
    and r2 = select n t in
    let pr = List.map (function s -> (string_of_char c)^s) r1 in
    pr@r2 ;;
    val select : int -> lex_tree -> string list = <fun>

Graph traversal

We define a type 'a graph representing directed graphs by adjacency lists containing for each vertex the list of its successors:

# type 'a graph = ( 'a * 'a list) list ;;


  1. Write a function insert_vtx which inserts a vertex into a graph and returns the new graph.

    # let rec insert_vtx v g =
    match g with
    [] -> [(v,[])]
    | (h,_)::_ when h=v -> failwith "existing vertex"
    | vl::t -> vl::(insert_vtx v t) ;;
    val insert_vtx : 'a -> ('a * 'b list) list -> ('a * 'b list) list = <fun>

    # let insert_vtx = (insert_vtx : 'a -> 'a graph -> 'a graph) ;;
    val insert_vtx : 'a -> 'a graph -> 'a graph = <fun>


  2. Write a function insert_edge which adds an edge to a graph already possessing these two vertices.

    # let rec insert_edge v1 v2 g =
    match g with
    [] -> failwith "unknown vertex"
    | (h,el)::t when h=v1 ->
    if List.mem v2 el then failwith "existing edge"
    else (v1,v2::el)::t
    | vl::t -> vl::(insert_edge v1 v2 t) ;;
    val insert_edge : 'a -> 'b -> ('a * 'b list) list -> ('a * 'b list) list =
    <fun>

    # let insert_edge = (insert_edge : 'a -> 'a -> 'a graph -> 'a graph ) ;;
    val insert_edge : 'a -> 'a -> 'a graph -> 'a graph = <fun>


  3. Write a function has_edges_to which returns all the vertices following directly from a given vertex.

    # let rec has_edges_to v g = match g with
    [] -> []
    | (v',el)::_ when v=v' -> el
    | _::t -> has_edges_to v t ;;
    val has_edges_to : 'a -> ('a * 'b list) list -> 'b list = <fun>

    # let has_edges_to = (has_edges_to : 'a -> 'a graph -> 'a list) ;;
    val has_edges_to : 'a -> 'a graph -> 'a list = <fun>


  4. Write a function has_edges_from which returns the list of all the vertices leading directly to a given vertex.

    # let rec has_edges_from v g = match g with
    [] -> []
    | (h,el)::t -> if List.mem v el then h::(has_edges_to v t)
    else (has_edges_to v t) ;;
    val has_edges_from : 'a -> ('a * 'a list) list -> 'a list = <fun>

    # let has_edges_from = (has_edges_from : 'a -> 'a graph -> 'a list) ;;
    val has_edges_from : 'a -> 'a graph -> 'a list = <fun>



Previous Contents Next ocaml-book-1.0/en/html/book-ora184.html0000644000000000000000000000363407453055400014475 0ustar Outline of the Chapter Previous Contents Next

Outline of the Chapter

This chapter presents the basic elements of the Internet, sockets, for the purpose of building distributed applications (particularly client-server applications) while detailing the problems in designing communications protocols.

The first section briefly explains the Internet, its addressing system and its main services.

The second section illustrates communications through sockets between different Objective CAML processes, both local and remote.

The third section describes the client-server model, while presenting server programs and universal clients.

The fourth section shows the importance of communications protocols for building network services.

This chapter is best read after the chapters on systems programming (Chapter 18) and on concurrent programming (Chapter 19).


Previous Contents Next ocaml-book-1.0/en/html/book-ora209.html0000644000000000000000000001173107453055401014471 0ustar Option -rectypes Previous Contents Next

Option -rectypes

With a compiler option, we can avoid this restriction to objects in cyclic types.
$ ocamlc -rectypes  ...
$ ocamlopt -rectypes  ...
$ ocaml -rectypes 
If we take up the above examples in a toplevel started with this option, here is what we get.


# type 'a tree = 'a * 'a tree list ;;
type 'a tree = 'a * 'a tree list

# let rec height = function
(_,[]) -> 1
| (_,sons) -> 1 + (max_list (List.map height sons)) ;;
val height : ('b * 'a list as 'a) -> int = <fun>


The values tree_1, tree_2 and tree_3 previously defined don't have the same type, but they all have a type compatible with that of height.


# height tree_1 ;;
- : int = 1
# height tree_2 ;;
- : int = 2
# height tree_3 ;;
- : int = 3


The keyword as belongs to the type language, and as such, it can be used in a type declaration.

Syntax


type nom = typedef as 'var ;;


We can use this syntax to define type tree.

# type 'a tree = ( 'a * 'vertex list ) as 'vertex ;;
type 'a tree = 'a * 'a tree list


Warning


If this mode may be useful in some cases, it tends to accept the typing of too many values, giving them types that are not easy to read.


Without the option -rectypes, the function below would have been rejected by the typing system.

# let inclus l1 l2 =
let rec mem x = function
[] -> false
| a::l -> (l=x) || (mem x a) (* an error on purpose: a and l inverted *)
in List.for_all (fun x -> mem x l2) l1 ;;
val inclus : ('a list as 'a) list list -> ('b list as 'b) -> bool = <fun>
Although a quick examination of the type allows to conclude to an error, we no longer have an error message to help us locating this error.






Previous Contents Next ocaml-book-1.0/en/html/book-ora067.gif0000644000000000000000000000677707452056112014311 0ustar GIF89avUUU!,vڋ޼ KI L+ (L*fШBԪ-jӬ r2B*NlK{nt5R!pXhxC )9IYiy R IZ3j+3{Qn*ٵjɎ]{ցpҕW|!hի`u&p]VVȎ#udM2ʖ'\>GQϤKï 3$"$ԫUΑmr#g{mݷaY4Φvknw!/ D+ȞtpIBPSH1A`oko%_^4. gL  h]wuhbn%^ VFhuGcn(&tkxB~'^hYq A$1ʸ% f'eObe W9!]i"lNWvyVggppYb(Jgj6*hL9%dj)~^pJj"R%SA; kL0xE=ȵ)A&iE B,p8 DVF6[=L?챇 rT"5 y[͵njj&f{U5$,n.YdېQ \Ra ZpA%F*OVV#o 2zF9Jϧ =;HQITbՠ@>`)A ,$cbLH%'ȟH-X}N[.b*QCTq@G~GI+5`+ q(d;Gp+g*N~ԛhC ҇t դIK".s4-hSR&5;] Zu7[,NAC:kMC<}d/D) sW.a+- k=ϳ]JEgZ &z:x)ø-{ce޹wR]۸?zhJzpxHt/q9B7f<>I9AץAZ3а﷠a35&yloT5fzrzJ#'Ǎ0TR8R% =8-Mgw0 4a-8~MȚޙU,5;/f8-!v5!zify݂Ŕ7 U4sE):˪_+VtiO Tլ}m?j~ÿm<_ \4nB=c4}dÞ@7zYys'~e|U7ozx~FMz/~ (b◀Ef|p(%]kMӂFXoz1C釃ր|g'R# );H#cwkaWuJB<~SJőe-Ņ`F5sr Y P_nGbo%cqfovhuXSxmB(9Gv Ò(s}X@V73!An~PMQr,< Nvp`IGXrwr'q%@Gc-mbO ]g7U9׃*(nY uݷHx)%v\d}~}zy|9)IɘzI.2wU)#@bx@Cҙ9SȦ9U':sJ55UCJSQhIj ؛DUiS_љUm)EyAdUYϢK,G۩-r0 n8IKM`LMٞR1NW;y,K_M?ay%cd 0ftNIыeBOL717* b*ZfcnAysjK2,Ͷ#&9'/W>ARIKb:iBH* ^U:y1]zXΙ1S `U^Im& y 8;ocaml-book-1.0/en/html/book-ora032.html0000644000000000000000000000321307453055377014473 0ustar Summary Previous Contents Next

Summary

This chapter has shown the integration of the main features of imperative programming (mutable values, I/O, iterative control structures) into a functional language. Only mutable values, such as strings, arrays, and records with mutable fields, can be physically modified. Other values, once created, are immutable. In this way we obtain read-only (RO) values for the functional part and read-write (RW) values for the imperative part.

It should be noted that, if we don't make use of the imperative features of the language, this extension to the functional core does not change the functional part, except for typing considerations which we can get around.


Previous Contents Next ocaml-book-1.0/en/html/book-ora082.html0000644000000000000000000001025607453055400014470 0ustar Introduction Previous Contents Next

Introduction

The execution model of a program on a microprocessor corresponds to that of imperative programming. More precisely, a program is a series of instructions whose execution modifies the memory state of the machine. Memory consists mainly of values created and manipulated by the program. However, like any computer resource, available memory has a finite size; a program trying to use more memory than the system provides will be in an incoherent state. For this reason, it is necessary to reuse the space of values that are at a given moment no longer used by future computations during continued execution. Such memory management has a strong influence on program execution and its efficiency.

The action of reserving a block of memory for a certain use is called allocation. We distinguish static allocation, which happens at program load time, i.e. before execution starts, from dynamic allocation, which happens during program execution. Whereas statically allocated memory is never reclaimed during execution, dynamically allocated regions are susceptible to being freed, or to being reused during execution.

Explicit memory management is risky for two reasons:
  • if a block of memory is freed while it contains a value still in use, this value may become corrupted before being accessed. References to such values are called dangling pointers;
  • if the address of a memory block is no longer known to the program, then the corresponding block cannot be freed before the end of program execution. In such cases, we speak of a memory leak.
Explicit memory management by the programmer requires much care to avoid these two possibilities. This task becomes rather difficult if programs manipulate complicated data structures, and in particular if data structures share common regions of memory.

To free the programmer from this difficult exercise, automatic memory management mechanisms have been introduced into numerous programming languages. The main idea is that at any moment during execution, the only dynamically allocated values potentially useful to the program are those whose addresses are known by the program, directly or indirectly. All values that can no longer be reached at that moment cannot be accessed in the future and thus their associated memory can be reclaimed. This deallocation can be effected either immediately when a value becomes unreachable, or later when the program requires more free space than is available.

Objective CAML uses a mechanism called garbage collection (GC) to perform automatic memory management. Memory is allocated at value construction (i.e., when a constructor is applied) and it is freed implicitly. Most programs do not have to deal with the garbage collector directly, since it works transparently behind the scenes. However, garbage collection can have an effect on efficiency for allocation-intensive programs. In such cases, it is useful to control the GC parameters, or even to invoke the collector explicitly. Moreover, in order to interface Objective CAML with other languages (see chapter 12), it is necessary to understand what constraints the garbage collector imposes on data representations.


Previous Contents Next ocaml-book-1.0/en/html/book-ora007.gif0000644000000000000000000000230707452056110014262 0ustar GIF89a!,ڋޜ~ݨ!:^p2K 0:ȦSyjGd-z*-76v '<_u>XGxH2`h8ɡx`'Ii)9)Wh:z(f&u [{akǫ;l\3L֬*Eel'Xnޡ~ _;l?Ԯ\  :'TBv G lA)${HQQvc!zYblѸF^~"N6l+Q$sјb6AؖESX3Re,AX* cWIZTG4T+'ۗpkxMq{_RfeEYv,0Y̸"7&eˌL&K:i3.u9{Xht.qo>OBm\ʽ2W9tQn݊pDs9/z*___gр{9H ؃J8! 6 j/l[8:$KK(fB ܬSiV‹:JNm>`gH$FvWbOdRPW]W[V_&fbc:TBg٥kcoRVs9wNd;X]U}'XYXb)X.>ktRJ!B ⦜ji"#Z<ϩJ g }j뉸dRY䯇 *skܱB%E[Y uQYAy[ZRZֺKn"ǭ{ooo(MzT͸f,H($Py쏂nzrtkôU'Wʗj+kز//X3rR;O9ƎڱC7VrE=Uz5Qe0p* 1fl;ct>g#btp:7Lu;$Xyjw4_xWr8]%ި2hoSlb@f}yGIuACCMCKI__>NY_)|t`9K] l$O p@o=Pk|3|[c̀1JV4}̩.bNJM%r3.;ocaml-book-1.0/en/html/book-ora155.html0000644000000000000000000000425007453055400014466 0ustar Mixed Organisations Previous Contents Next

Mixed Organisations

The last example of the preceding section showed the advantages that there are in mixing the two models for the problem of the extensibility of components. We now propose to mix parameterized modules and late binding to benefit from the power of these two features. The application of the functor will produce new modules containing classes which use the type and functions of the parameterized module. If, moreover, the signature obtained is compatible with the signature of the parameterized module, it is then possible to re-apply the parameterized module to the resulting module, thus making it possible to construct new classes automatically.

A concrete example is given in the last part of this book which is dedicated to concurrent and/or distributed programs (page ??). We use a functor to generate a communication protocol starting from a data type; a second functor permits us to then deduce from this protocol a class which implements a generic server which handles requests expressed in the protocol. Inheritance can then be used to specialize the server into the service that is actually required.






Previous Contents Next ocaml-book-1.0/en/html/book-ora063.html0000644000000000000000000000461107453055400014465 0ustar Introduction Previous Contents Next

Introduction

The transformation from human readable source code to an executable requires a number of steps. Together these steps constitute the process of compilation. The compilation process produces an abstract syntax tree (for an example, see page ??) and a sequence of instructions for a cpu or virtual machine. In Objective CAML, the product of compilation is linked with the Objective CAML runtime library. The library is provided with the compiler distribution and is adapted to different host environments (operating system and CPU). The runtime library contains primitive functions such as operations over numbers, the interface to the operating system, and memory management.

Objective CAML has two compilers. The first compiler produces bytecode for the Objective CAML virtual machine. The second compiler generates instructions for a number of ``real'' processors, such as the Intel, Motorola, SPARC, HP-PA, Power-PC and Alpha CPUs. The Objective CAML bytecode compiler produces compact portable code, while the native-code compiler generates high performance architecture dependent code. The Objective CAML toplevel system, which appeared in the first part of this book, uses the bytecode compiler; each user input is compiled and executed in the symbolic environment defined by the current interactive session.


Previous Contents Next ocaml-book-1.0/en/html/book-ora009.gif0000644000000000000000000000165707452056110014273 0ustar GIF89a  !,  ڋ޼H扦 '+t_bNhL*$$ Ԫj, yrilN1z _︝]=ZN^*?/Vo/q,HK1D="Rcb2rd}Ѝ$)qXTnbҥAeTd(0KN27wy!HmXtO ic}wպfw^F|x˕7ztөWaxvw]wxm}>hۻ?ۿ?Ӈl6 kvi `h ̃ĢaOU!Ѡ!1}"rs !ad Categorization and Use of the Libraries Previous Contents Next

Categorization and Use of the Libraries

The libraries in the Objective CAML distribution fall into three categories. The first contains preloaded global declarations. The second is called the standard library and is subdivided into four parts:
  • data structures;
  • input/output
  • system interface;
  • lexical and syntactic analysis.
Finally there are the libraries in the third group that generally extend the language, such as the Graphics library (see chapter 5). In this last group you will find libraries dealing with the following areas: regular expressions (Str), arbitrary-precision math (Num), Unix system calls (Unix), lightweight processes (Threads) and dynamic loading of bytecode (Dynlink).

The I/O and the system interface portions of the standard library are compatible with different operating systems such as Unix, Windows and MacOS. This is not always the case with the libraries in the third group (those that extend the language). There are also many independently written libraries that are not part of the Objective CAML distribution.

Usage and naming
To use modules or libraries in a program, one has to use dot notation to specify the module name and the object to access. For example if one wants to use a function f in a library called Name, one qualifies it as Name.f. To avoid having to prefix everything with the name of the library, it is possible to open the library and use f directly.

Syntax


open Name


From then on, all the global declarations of the library Name will be considered as if they belonged to the global environment. If two declarations have the same name in two distinct open libraries, then only the last declaration is visible. To be able to call the first, it would be necessary to use the point notation.


Previous Contents Next ocaml-book-1.0/en/html/book-ora079.gif0000644000000000000000000000356107452056112014300 0ustar GIF89a!,ڋ\~HY* JN 1a<*5|J)$j͢k pEgj4mç9n$H ANy|Zפ^~&} <.)G%2WU7S]Yv)&g?y–{ژ8$S‰h"2 B"_Uå'3C1-:)$iujV:"jޚRkfD "Kel7Y{R)(0N[ j])š-f.wԠϫIzHo 烙b ph.,qJ,Mo1j/>[Gqc clWhsrwl"Zb0!&%F#"]0鬽5=Y/T d bk`c2i*vuMYLjd7~ ꚉ7m3+23*{gmOn~<־f.:N?fw.چ^ώ -iO<`wos{{+{KMXҳ^rQo'z2./W8HeO1dyU>kW[ޢ=qDPΑh<2]2 tW$utǛXAc]"An(d ;ݢjrÍi.4{gІ!Z] #Aq-̣Т(Ea>NF 5%3DZ(9kT]Q_g)Ř=~nш9N-oTG#EͽsFzG\$OhD]B& O04(9tA\딄y[BIa\eIQ\ Zʐbf]8#&K*2 f3Jt5i.[V#69KoEO,#1Lj1S7ѹ<}gUѢ|Z=s A5]>t3YO UU"AG'*T!4 ɉKsKQ&<;qSJ4$eO әgAW*ԃNjG*ҩUj;Φ xORUI՝/j:TB&%%QuzLE1 ~*bPEAumɆ,: tdj\ c9l6-gI Jd{XVO/+lFVm+۶R! ߌOo{ .7Ýn ]Z|v;Wn׵ef^d4 #5 ^P}sW{ԯYY;ocaml-book-1.0/en/html/book-ora104.html0000644000000000000000000000406007453055400014457 0ustar Chapter Structure Previous Contents Next

Chapter Structure

This chapter introduces the tools of the Objective CAML distribution for lexical analysis and parsing. The latter normally supposes that the former has already taken place. In the first section, we introduce a simple tool for lexical analysis provided by module Genlex. Next we give details about the definition of sets of lexical units by introducing the formalism of regular expressions. We illustrate their behavior within module Str and the ocamllex tool. In section two we define grammars and give details about sentence production rules for a language to introduce two types of parsing: bottom-up and top-down. They are further illustrated by using Stream and the ocamlyacc tool. These examples use context-free grammars. We then show how to carry out contextual analysis with Streams. In the third section we go back to the example of a BASIC interpreter from page ??, using ocamllex and ocamlyacc to implement the lexical analysis and parsing functions.


Previous Contents Next ocaml-book-1.0/en/html/book-ora027.html0000644000000000000000000002547307453055377014513 0ustar Input-Output Previous Contents Next

Input-Output

Input-output functions do calculate a value (often of type unit) but during their calculation they cause a modification of the state of the input-output peripherals: modification of the state of the keyboard buffer, outputting to the screen, writing in a file, or modification of a read pointer. The following two types are predefined: in_channel and out_channel for, respectively, input channels and output channels. When an end of file is met, the exception End_of_file is raised. Finally, the following three constants correspond to the standard channels for input, output, and error in Unix fashion: stdin, stdout, and stderr.

Channels

The input-output functions from the Objective CAML standard library manipulate communication channels: values of type in_channel or out_channel. Apart from the three standard predefined values, the creation of a channel uses one of the following functions:

# open_in;;
- : string -> in_channel = <fun>
# open_out;;
- : string -> out_channel = <fun>
open_in opens the file if it exists2, and otherwise raises the exception Sys_error. open_out creates the specified file if it does not exist or truncates it if it does.

# let ic = open_in "koala";;
val ic : in_channel = <abstr>
# let oc = open_out "koala";;
val oc : out_channel = <abstr>
The functions for closing channels are:

# close_in ;;
- : in_channel -> unit = <fun>
# close_out ;;
- : out_channel -> unit = <fun>


Reading and Writing

The most general functions for reading and writing are the following:

# input_line ;;
- : in_channel -> string = <fun>
# input ;;
- : in_channel -> string -> int -> int -> int = <fun>
# output ;;
- : out_channel -> string -> int -> int -> unit = <fun>


  • input_line ic: reads from input channel ic all the characters up to the first carriage return or end of file, and returns them in the form of a list of characters (excluding the carriage return).
  • input ic s p l: attempts to read l characters from an input channel ic and stores them in the list s starting from the pth character. The number of characters actually read is returned.
  • output oc s p l: writes on an output channel oc part of the list s, starting at the p-th character, with length l.
The following functions read from standard input or write to standard output:

# read_line ;;
- : unit -> string = <fun>
# print_string ;;
- : string -> unit = <fun>
# print_newline ;;
- : unit -> unit = <fun>


Other values of simple types can also be read directly or appended. These are the values of types which can be converted into lists of characters.

Local declarations and order of evaluation
We can simulate a sequence of printouts with expressions of the form let x = e1 in e2. Knowing that, in general, x is a local variable which can be used in e2, we know that e1 is evaluated first and then comes the turn of e2. If the two expressions are imperative functions whose results are () but which have side effects, then we have executed them in the right order. In particular, since we know the return value of e1---the constant () of type unit---we get a sequence of printouts by writing the sequence of nested declarations which pattern match on ().


# let () = print_string "and one," in
let () = print_string " and two," in
let () = print_string " and three" in
print_string " zero";;
and one, and two, and three zero- : unit = ()


Example: Higher/Lower

The following example concerns the game ``Higher/Lower'' which consists of choosing a number which the user must guess at. The program indicates at each turn whether the chosen number is smaller or bigger than the proposed number.


# let rec hilo n =
let () = print_string "type a number: " in
let i = read_int ()
in
if i = n then
let () = print_string "BRAVO" in
let () = print_newline ()
in print_newline ()
else
let () =
if i < n then
let () = print_string "Higher"
in print_newline ()
else
let () = print_string "Lower"
in print_newline ()
in hilo n ;;
val hilo : int -> unit = <fun>


Here is an example session:
# hilo 64;;
type a number: 88
Lower
type a number: 44
Higher
type a number: 64
BRAVO

- : unit = ()

Previous Contents Next ocaml-book-1.0/en/html/book-ora125.html0000644000000000000000000033412007453055400014465 0ustar Finding Least Cost Paths Previous Contents Next

Finding Least Cost Paths

Many applications need to find least cost paths through weighted directed graphs. The problem is to find a path through a graph in which non-negative weights are associated with the arcs. We will use Dijkstra's algorithm to determine the path.

This will be an opportunity to use several previously introduced libraries. In the order of appearance, the following modules will be used: Genlex and Printf for input and output, the module Weak to implement a cache, the module Sys to track the time saved by a cache, and the Awi library to construct a graphical user interface. The module Sys is also used to construct a standalone application that takes the name of a file describing the graph as a command line argument.

Graph Representions

A weighted directed graph is defined by a set of nodes, a set of edges, and a mapping from the set of edges to a set of values. There are numerous implementations of the data type weighted directed graph.
  • adjacency matrices:
    each element (m(i,j)) of the matrix represents an edge from node i to j and the value of the element is the weight of the edge;
  • adjacency lists:
    each node i is associated with a list [(j1,w1); ..; (jn,wn)] of nodes and each triple (i, jk, wk) is an edge of the graph, with weight wk;
  • a triple:
    a list of nodes, a list of edges and a function that computes the weights of the edges.
The behavior of the representations depends on the size and the number of edges in the graph. Since one goal of this application is to show how to cache certain previously executed computations without using all of memory, an adjacency matrix is used to represent weighted directed graphs. In this way, memory usage will not be increased by list manipulations.


# type cost = Nan | Cost of float;;
# type adj_mat = cost array array;;
# type 'a graph = { mutable ind : int;
size : int;
nodes : 'a array;
m : adj_mat};;
The field size indicates the maximal number of nodes, the field ind the actual number of nodes.

We define functions to let us create graphs edge by edge.

The function to create a graph takes as arguments a node and the maximal number of nodes.

# let create_graph n s =
{ ind = 0; size = s; nodes = Array.create s n;
m = Array.create_matrix s s Nan } ;;
val create_graph : 'a -> int -> 'a graph = <fun>


The function belongs_to checks if the node n is contained in the graph g.

# let belongs_to n g =
let rec aux i =
(i < g.size) & ((g.nodes.(i) = n) or (aux (i+1)))
in aux 0;;
val belongs_to : 'a -> 'a graph -> bool = <fun>


The function index returns the index of the node n in the graph g. If the node does not exist, a Not_found exception is thrown.

# let index n g =
let rec aux i =
if i >= g.size then raise Not_found
else if g.nodes.(i) = n then i
else aux (i+1)
in aux 0 ;;
val index : 'a -> 'a graph -> int = <fun>


The next two functions are for adding nodes and edges of cost c to graphs.

# let add_node n g =
if g.ind = g.size then failwith "the graph is full"
else if belongs_to n g then failwith "the node already exists"
else (g.nodes.(g.ind) <- n; g.ind <- g.ind + 1) ;;
val add_node : 'a -> 'a graph -> unit = <fun>
# let add_edge e1 e2 c g =
try
let x = index e1 g and y = index e2 g in
g.m.(x).(y) <- Cost c
with Not_found -> failwith "node does not exist" ;;
val add_edge : 'a -> 'a -> float -> 'a graph -> unit = <fun>


Now it is easy to create a complete weighted directed graph starting with a list of nodes and edges. The function test_aho constructs the graph of figure 13.8:

# let test_aho () =
let g = create_graph "nothing" 5 in
List.iter (fun x -> add_node x g) ["A"; "B"; "C"; "D"; "E"];
List.iter (fun (a,b,c) -> add_edge a b c g)
["A","B",10.;
"A","D",30.;
"A","E",100.0;
"B","C",50.;
"C","E",10.;
"D","C",20.;
"D","E",60.];
for i=0 to g.ind -1 do g.m.(i).(i) <- Cost 0.0 done;
g;;
val test_aho : unit -> string graph = <fun>
# let a = test_aho();;
val a : string graph =
{ind=5; size=5; nodes=[|"A"; "B"; "C"; "D"; "E"|];
m=[|[|Cost 0; Cost 10; Nan; Cost 30; Cost ...|]; ...|]}




Figure 13.8: The test graph


Constructing Graphs

It is tedious to directly construct graphs in a program. To avoid this, we define a concise textual representation for graphs. We can define the graphs in text files and construct them in applications by reading the text files.

The textual representation for a graph consists of lines of the following forms:
  • the number of nodes: SIZE number;
  • the name of a node: NODE name;
  • the cost of an edge: EDGE name1 name2 cost;
  • a comment: # comment.
For example, the following file, aho.dat, describes the graph of figure 13.8 :
SIZE 5
NODE A
NODE B
NODE C
NODE D
NODE E
EDGE A B 10.0
EDGE A D 30.0
EDGE A E 100.0
EDGE B C 50.
EDGE C E 10.
EDGE D C 20.
EDGE D E 60.
To read graph files, we use the lexical analysis module Genlex. The lexical analyser is constructed from a list of keywords keywords.

The function parse_line executes the actions associated to the key words by modifying the reference to a graph.

# let keywords = [ "SIZE"; "NODE"; "EDGE"; "#"];;
val keywords : string list = ["SIZE"; "NODE"; "EDGE"; "#"]
# let lex_line l = Genlex.make_lexer keywords (Stream.of_string l);;
val lex_line : string -> Genlex.token Stream.t = <fun>
# let parse_line g s = match s with parser
[< '(Genlex.Kwd "SIZE"); '(Genlex.Int n) >] ->
g := create_graph "" n
| [< '(Genlex.Kwd "NODE"); '(Genlex.Ident name) >] ->
add_node name !g
| [< '(Genlex.Kwd "EDGE"); '(Genlex.Ident e1);
'(Genlex.Ident e2); '(Genlex.Float c) >] ->
add_edge e1 e2 c !g
| [< '(Genlex.Kwd "#") >] -> ()
| [<>] -> () ;;
val parse_line : string graph ref -> Genlex.token Stream.t -> unit = <fun>


The analyzer is used to define the function creating a graph from the description in the file:

# let create_graph name =
let g = ref {ind=0; size=0; nodes =[||]; m = [||]} in
let ic = open_in name in
try
print_string ("Loading "^name^": ");
while true do
print_string ".";
let l = input_line ic in parse_line g (lex_line l)
done;
!g
with End_of_file -> print_newline(); close_in ic; !g ;;
val create_graph : string -> string graph = <fun>
The following command constructs a graph from the file aho.dat.

# let b = create_graph "PROGRAMMES/aho.dat" ;;
Loading PROGRAMMES/aho.dat: ..............
val b : string graph =
{ind=5; size=5; nodes=[|"A"; "B"; "C"; "D"; "E"|];
m=[|[|Nan; Cost 10; Nan; Cost 30; Cost 100|]; ...|]}


Dijkstra's Algorithm

Dijkstra's algorithm finds a least cost path between two nodes. The cost of a path between node n1 and node n2 is the sum of the costs of the edges on that path. The algorithm requires that costs always be positive, so there is no benefit in passing through a node more than once.

Dijkstra's algorithm effectively computes the minimal cost paths of all nodes of the graph which can be reached from a source node n1. The idea is to consider a set containing only nodes of which the least cost path to n1 is already known. This set is enlarged successively, considering nodes which can be accessed directly by an edge from one of the nodes already contained in the set. From these candidates, the one with the best cost path to the source node is added to the set.

To keep track of the state of the computation, the type comp_state is defined, as well as a function for creating an initial state:

# type comp_state = { paths : int array;
already_treated : bool array;
distances : cost array;
source : int;
nn : int};;
# let create_state () = { paths = [||]; already_treated = [||]; distances = [||];
nn = 0; source = 0};;
The field source contains the start node. The field already_treated indicates the nodes whose optimal path from the source is already known. The field nn indicates the total number of the graph's nodes. The vector distances holds the minimal distances between the source and the other nodes. For each node, the vector path contains the preceding node on the least cost path. The path to the source can be reconstructed from each node by using path.

Cost Functions

Four functions on costs are defined: a_cost to test for the existence of an edge, float_of_cost to return the floating point value, add_cost to add two costs and less_cost to check if one cost is smaller than another.


# let a_cost c = match c with Nan -> false | _-> true;;
val a_cost : cost -> bool = <fun>
# let float_of_cost c = match c with
Nan -> failwith "float_of_cost"
| Cost x -> x;;
val float_of_cost : cost -> float = <fun>
# let add_cost c1 c2 = match (c1,c2) with
Cost x, Cost y -> Cost (x+.y)
| Nan, Cost y -> c2
| Cost x, Nan -> c1
| Nan, Nan -> c1;;
val add_cost : cost -> cost -> cost = <fun>
# let less_cost c1 c2 = match (c1,c2) with
Cost x, Cost y -> x < y
| Cost x, Nan -> true
| _, _ -> false;;
val less_cost : cost -> cost -> bool = <fun>
The value Nan plays a special role in the computations and in the comparison. We will come back to this when we have presented the main function (page ??).

Implementing the Algorithm

The search for the next node with known least cost path is divided into two functions. The first, first_not_treated, selects the first node not already contained in the set of nodes with known least cost paths. This node serves as the initial value for the second function, least_not_treated, which returns a node not already in the set with a best cost path to the source. This path will be added to the set.

# exception Found of int;;
exception Found of int
# let first_not_treated cs =
try
for i=0 to cs.nn-1 do
if not cs.already_treated.(i) then raise (Found i)
done;
raise Not_found;
0
with Found i -> i ;;
val first_not_treated : comp_state -> int = <fun>
# let least_not_treated p cs =
let ni = ref p
and nd = ref cs.distances.(p) in
for i=p+1 to cs.nn-1 do
if not cs.already_treated.(i) then
if less_cost cs.distances.(i) !nd then
( nd := cs.distances.(i);
ni := i )
done;
!ni,!nd;;
val least_not_treated : int -> comp_state -> int * cost = <fun>


The function one_round selects a new node, adds it to the set of treated nodes and computes the distances for any next candidates.

# exception No_way;;
exception No_way
# let one_round cs g =
let p = first_not_treated cs in
let np,nc = least_not_treated p cs in
if not(a_cost nc ) then raise No_way
else
begin
cs.already_treated.(np) <- true;
for i = 0 to cs.nn -1 do
if not cs.already_treated.(i) then
if a_cost g.m.(np).(i) then
let ic = add_cost cs.distances.(np) g.m.(np).(i) in
if less_cost ic cs.distances.(i) then (
cs.paths.(i) <- np;
cs.distances.(i) <- ic
)
done;
cs
end;;
val one_round : comp_state -> 'a graph -> comp_state = <fun>


The only thing left in the implementation of Dijkstra's algorithm is to iterate the preceding function. The function dij takes a node and a graph as arguments and returns a value of type comp_state, with the information from which the least cost paths from the source to all the reachable nodes of the graph can be deduced.

# let dij s g =
if belongs_to s g then
begin
let i = index s g in
let cs = { paths = Array.create g.ind (-1) ;
already_treated = Array.create g.ind false;
distances = Array.create g.ind Nan;
nn = g.ind;
source = i} in
cs.already_treated.(i) <- true;
for j=0 to g.ind-1 do
let c = g.m.(i).(j) in
cs.distances.(j) <- c;
if a_cost c then cs.paths.(j) <- i
done;
try
for k = 0 to cs.nn-2 do
ignore(one_round cs g)
done;
cs
with No_way -> cs
end
else failwith "dij: node unknown";;
val dij : 'a -> 'a graph -> comp_state = <fun>


Nan is the initial value of the distances. It represents an infinite distance, which conforms to the comparison function less_cost. In contrast, for the addition of costs (function add_cost), this value is treated as a zero. This allows a simple implementation of the table of distances.

Now the search with Dijkstra's algorithm can be tested.

# let g = test_aho ();;
# let r = dij "A" g;;


The return values are:

# r.paths;;
- : int array = [|0; 0; 3; 0; 2|]
# r.distances;;
- : cost array = [|Cost 0; Cost 10; Cost 50; Cost 30; Cost 60|]


Displaying the Results

To make the results more readable, we now define a display function.

The table paths of the state returned by dij only contains the last edges of the computed paths. In order to get the entire paths, it is necessary to recursively go back to the source.

# let display_state f (g,st) dest =
if belongs_to dest g then
let d = index dest g in
let rec aux is =
if is = st.source then Printf.printf "%a" f g.nodes.(is)
else (
let old = st.paths.(is) in
aux old;
Printf.printf " -> (%4.1f) %a" (float_of_cost g.m.(old).(is))
f g.nodes.(is)
)
in
if not(a_cost st.distances.(d)) then Printf.printf "no way\n"
else (
aux d;
Printf.printf " = %4.1f\n" (float_of_cost st.distances.(d)));;
val display_state :
(out_channel -> 'a -> unit) -> 'a graph * comp_state -> 'a -> unit = <fun>
This recursive function uses the command stack to display the nodes in the right order. Note that the use of the format "a" requires the function parameter f to preserve the polymorphism of the graphs for the display.

The optimal path between the nodes "A" (index 0) and "E" (index 4) is displayed in the following way:

# display_state (fun x y -> Printf.printf "%s!" y) (a,r) "E";;
A! -> (30.0) D! -> (20.0) C! -> (10.0) E! = 60.0
- : unit = ()


The different nodes of the path and the costs of each route are shown.

Introducing a Cache

Dijkstra's algorithm computes all least cost paths starting from a source. The idea of preserving these least cost paths for the next inquiry with the same source suggests itself. However, this storage could occupy a considerable amount of memory. This suggests the use of ``weak pointers.'' If the results of a computation starting from a source are stored in a table of weak pointers, it will be possible for the next computation to check if the computation has already been done. Because the pointers are weak, the memory occupied by the states can be freed by the garbage collector if needed. This avoids interrupting the rest of the program through the allocation of too much memory. In the worst case, the computation has to be repeated for a future inquiry.

Implementing a Cache

A new type 'a comp_graph is defined:

# type 'a comp_graph =
{ g : 'a graph; w : comp_state Weak.t } ;;
The fields g and w correspond to the graph and to the table of weak pointers, pointing to the computation states for each possible source.

Such values are constructed by the function create_comp_graph.

# let create_comp_graph g =
{ g = g;
w = Weak.create g.ind } ;;
val create_comp_graph : 'a graph -> 'a comp_graph = <fun>


The function dij_quick checks to see if the computation has already been done. If it has, the stored result is returned. Otherwise, the computation is executed and the result is registered in the table of weak pointers.

# let dij_quick s cg =
let i = index s cg.g in
match Weak.get cg.w i with
None -> let cs = dij s cg.g in
Weak.set cg.w i (Some cs);
cs
| Some cs -> cs;;
val dij_quick : 'a -> 'a comp_graph -> comp_state = <fun>


The display function still can be used:

# let cg_a = create_comp_graph a in
let r = dij_quick "A" cg_a in
display_state (fun x y -> Printf.printf "%s!" y) (a,r) "E" ;;
A! -> (30.0) D! -> (20.0) C! -> (10.0) E! = 60.0
- : unit = ()


Performance Evaluation

We will test the performance of the functions dij and dij_quick by iterating each one on a random list of sources. In this way an application which frequently computes least cost paths is simulated (for example a railway route planning system).

We define the following function to time the calculations:

# let exe_time f g ss =
let t0 = Sys.time() in
Printf.printf "Start (%5.2f)\n" t0;
List.iter (fun s -> ignore(f s g)) ss;
let t1 = Sys.time() in
Printf.printf "End (%5.2f)\n" t1;
Printf.printf "Duration = (%5.2f)\n" (t1 -. t0) ;;
val exe_time : ('a -> 'b -> 'c) -> 'b -> 'a list -> unit = <fun>


We create a random list of 20000 nodes and measure the performance on the graph a:

# let ss =
let ss0 = ref [] in
let i0 = int_of_char 'A' in
let new_s i = Char.escaped (char_of_int (i0+i)) in
for i=0 to 20000 do ss0 := (new_s (Random.int a.size))::!ss0 done;
!ss0 ;;
val ss : string list =
["A"; "B"; "D"; "A"; "E"; "C"; "B"; "B"; "D"; "E"; "B"; "E"; "C"; "E"; "E";
"D"; "D"; "A"; "E"; ...]
# Printf.printf"Function dij :\n";
exe_time dij a ss ;;
Function dij :
Start ( 1.14)
End ( 1.46)
Duration = ( 0.32)
- : unit = ()
# Printf.printf"Function dij_quick :\n";
exe_time dij_quick (create_comp_graph a) ss ;;
Function dij_quick :
Start ( 1.46)
End ( 1.50)
Duration = ( 0.04)
- : unit = ()


The results confirm our assumption. The direct access to a result held in the cache is considerably faster than a second computation of the result.

A Graphical Interface

We use the Awi library to construct a graphical interface to display graphs. The interface allows selection of the source and destination nodes of the path. When the path is found, it is displayed graphically. We define the type 'a gg, containing fields describing the graph and the computation, as well as fields of the graphical interface.


# #load "PROGRAMMES/awi.cmo";;

# type 'a gg = { mutable src : 'a * Awi.component;
mutable dest : 'a * Awi.component;
pos : (int * int) array;
cg : 'a comp_graph;
mutable state : comp_state;
mutable main : Awi.component;
to_string : 'a -> string;
from_string : string -> 'a } ;;


The fields src and dest are tuples (node, component), associating a node and a component. The field pos contains the position of each component. The field main is the main container of the set of components. The two functions to_string and from_string are conversion functions between type 'a and strings. The elements necessary to construct these values are the graph information, the position table and the conversion functions.


# let create_gg cg vpos ts fs =
{src = cg.g.nodes.(0),Awi.empty_component;
dest = cg.g.nodes.(0),Awi.empty_component;
pos = vpos;
cg = cg;
state = create_state () ;
main = Awi.empty_component;
to_string = ts;
from_string = fs};;
val create_gg :
'a comp_graph ->
(int * int) array -> ('a -> string) -> (string -> 'a) -> 'a gg = <fun>


Visualisation

In order to display the graph, the nodes have to be drawn, and the edges have to be traced. The nodes are represented by button components of the Awi library. The edges are traced directly in the main window. The function display_edge displays the edges. The function display_shortest_path displays the found path in a different color.

Drawing Edges
An edge connects two nodes and has an associated weight. The connection between two nodes can be represented by a line. The main difficulty is indicating the orientation of the line. We choose to represent it by an arrow. The arrow is rotated by the angle the line has with the abscissa (the x-axis) to give it the proper orientation. Finally, the costs are displayed beside the edge.

To draw the arrow of an edge we define the functions rotate and translate which care respectively for rotation and shifting. The function display_arrow draws the arrow.

# let rotate l a =
let ca = cos a and sa = sin a in
List.map (function (x,y) -> ( x*.ca +. -.y*.sa, x*.sa +. y*.ca)) l;;
val rotate : (float * float) list -> float -> (float * float) list = <fun>
# let translate l (tx,ty) =
List.map (function (x,y) -> (x +. tx, y +. ty)) l;;
val translate :
(float * float) list -> float * float -> (float * float) list = <fun>
# let display_arrow (mx,my) a =
let triangle = [(5.,0.); (-3.,3.); (1.,0.); (-3.,-3.); (5.,0.)] in
let tr = rotate triangle a in
let ttr = translate tr (mx,my) in
let tt = List.map (function (x,y) -> (int_of_float x, int_of_float y)) ttr
in
Graphics.fill_poly (Array.of_list tt);;
val display_arrow : float * float -> float -> unit = <fun>


The position of the text indicating the weight of an edge depends on the angle of the edge.

# let display_label (mx,my) a lab =
let (sx,sy) = Graphics.text_size lab in
let pos = [ float(-sx/2),float(-sy) ] in
let pr = rotate pos a in
let pt = translate pr (mx,my) in
let px,py = List.hd pt in
let ox,oy = Graphics.current_point () in
Graphics.moveto ((int_of_float mx)-sx-6)
((int_of_float my) );
Graphics.draw_string lab;
Graphics.moveto ox oy;;
val display_label : float * float -> float -> string -> unit = <fun>


The preceding functions are now used by the function display_edge. Parameters are the graphical interface gg, the nodes i and j, and the color (col) to use.

# let display_edge gg col i j =
let g = gg.cg.g in
let x,y = gg.main.Awi.x,gg.main.Awi.y in
if a_cost g.m.(i).(j) then (
let (a1,b1) = gg.pos.(i)
and (a2,b2) = gg.pos.(j) in
let x0,y0 = x+a1,y+b1 and x1,y1 = x+a2,y+b2 in
let rxm = (float(x1-x0)) /. 2. and rym = (float(y1-y0)) /. 2. in
let xm = (float x0) +. rxm and ym = (float y0) +. rym in
Graphics.set_color col;
Graphics.moveto x0 y0;
Graphics.lineto x1 y1;
let a = atan2 rym rxm in
display_arrow (xm,ym) a;
display_label (xm,ym) a
(string_of_float(float_of_cost g.m.(i).(j))));;
val display_edge : 'a gg -> Graphics.color -> int -> int -> unit = <fun>


Displaying a Path
To display a path, all edges along the path are displayed. The graphical display of a path towards a destination uses the same technique as the textual display.

# let rec display_shortest_path gg col dest =
let g = gg.cg.g in
if belongs_to dest g then
let d = index dest g in
let rec aux is =
if is = gg.state.source then ()
else (
let old = gg.state.paths.(is) in
display_edge gg col old is;
aux old )
in
if not(a_cost gg.state.distances.(d)) then Printf.printf "no way\n"
else aux d;;
val display_shortest_path : 'a gg -> Graphics.color -> 'a -> unit = <fun>


Displaying a Graph
The function display_gg displays a complete graph. If the destination node is not empty, the path between the source and the destination is traced.

# let display_gg gg () =
Awi.display_rect gg.main ();
for i=0 to gg.cg.g.ind -1 do
for j=0 to gg.cg.g.ind -1 do
if i<> j then display_edge gg (Graphics.black) i j
done
done;
if snd gg.dest != Awi.empty_component then
display_shortest_path gg Graphics.red (fst gg.dest);;
val display_gg : 'a gg -> unit -> unit = <fun>


The Node Component

The nodes still need to be drawn. Since the user is allowed to choose the source and destination nodes, we define a component for nodes.

The user's main action is choosing the end nodes of the path to be found. Thus a node must be a component that reacts to mouse clicks, using its state to indicate if it has been chosen as a source or destination. We choose the button component, which reacts to mouse clicks.

Node Actions
It is necessary to indicate node selection. To show this, the background color of a node is changed by the function inverse.

# let inverse b =
let gc = Awi.get_gc b in
let fcol = Awi.get_gc_fcol gc
and bcol = Awi.get_gc_bcol gc in
Awi.set_gc_bcol gc fcol;
Awi.set_gc_fcol gc bcol;;
val inverse : Awi.component -> unit = <fun>


The function action_click effects this selection. It is called when a node is clicked on by the mouse. As parameters it takes the node associated with the button and the graph to modify the source or the destination of the search. When both nodes are selected, the function dij_quick finds a least cost path.
            
# let action_click node gg b bs =
let (s1,s) = gg.src
and (s2,d) = gg.dest in
if s == Awi.empty_component then (
gg.src <- (node,b); inverse b )
else
if d == Awi.empty_component then (
inverse b;
gg.dest <- (node,b);
gg.state <- dij_quick s1 gg.cg;
display_shortest_path gg (Graphics.red) node
)
else (inverse s; inverse d;
gg.dest <- (s2,Awi.empty_component);
gg.src <- node,b; inverse b);;
val action_click : 'a -> 'a gg -> Awi.component -> 'b -> unit = <fun>


Creating an Interface
The main function to create an interface takes an interface graph and a list of options, creates the different components and associates them with the graph. The parameters are the graph (gg), its dimensions (gw and gh), a list of graph and node options (lopt) and a list of node border options (lopt2).
  
# let main_gg gg gw gh lopt lopt2 =
let gc = Awi.make_default_context () in
Awi.set_gc gc lopt;
(* compute the maximal button size *)
let vs = Array.map gg.to_string gg.cg.g.nodes in
let vsize = Array.map Graphics.text_size vs in
let w = Array.fold_right (fun (x,y) -> max x) vsize 0
and h = Array.fold_right (fun (x,y) -> max y) vsize 0 in
(* create the main panel *)
gg.main <- Awi.create_panel true gw gh lopt;
gg.main.Awi.display <- display_gg gg;
(* create the buttons *)
let vb_bs =
Array.map (fun x -> x,Awi.create_button (" "^(gg.to_string x)^" ")
lopt)
gg.cg.g.nodes in
let f_act_b = Array.map (fun (x,(b,bs)) ->
let ac = action_click x gg b
in Awi.set_bs_action bs ac) vb_bs in
let bb =
Array.map (function (_,(b,_)) -> Awi.create_border b lopt2) vb_bs
in
Array.iteri
(fun i (b) -> let x,y = gg.pos.(i) in
Awi.add_component gg.main b
["PosX",Awi.Iopt (x-w/2);
"PosY", Awi.Iopt (y-h/2)]) bb;
();;
val main_gg :
'a gg ->
int ->
int -> (string * Awi.opt_val) list -> (string * Awi.opt_val) list -> unit =
<fun>
The buttons are created automatically. They are positioned on the main window.

Testing the Interface
Everything is ready to create an interface now. We use a graph whose nodes are character strings to simplify the conversion functions. We construct the graph gg as follows:

# let id x = x;;
# let pos = [| 200, 300; 80, 200 ; 100, 100; 200, 100; 260, 200 |];;
# let gg = create_gg (create_comp_graph (test_aho())) pos id id;;
# main_gg gg 400 400 ["Background", Awi.Copt (Graphics.rgb 130 130 130);
"Foreground",Awi.Copt Graphics.green]
[ "Relief", Awi.Sopt "Top";"Border_size", Awi.Iopt 2];;


Calling Awi.loop true false gg.main;; starts the interaction loop of the Awi library.



Figure 13.9: Selecting the nodes for a search


Figure 13.9 shows the computed path between the nodes "A" and "E". The edges on the path have changed their color.

Creating a Standalone Application

We will now show the steps needed to construct a standalone application. The application takes the name of a file describing the graph as an argument. For standalone applications, it is not necesary to have an Objective CAML distribution on the execution machine.

A Graph Description File

The file containes information about the graph as well as information used for the graphical interface. For the latter information, we define a second format. From this graphical description, we construct a value of the type g_info.

# type g_info = {npos : (int * int) array;
mutable opt : Awi.lopt;
mutable g_w : int;
mutable g_h : int};;


The format for the graphical information is described by the four key words of list key2.

# let key2 = ["HEIGHT"; "LENGTH"; "POSITION"; "COLOR"];;
val key2 : string list = ["HEIGHT"; "LENGTH"; "POSITION"; "COLOR"]
# let lex2 l = Genlex.make_lexer key2 (Stream.of_string l);;
val lex2 : string -> Genlex.token Stream.t = <fun>

# let pars2 g gi s = match s with parser
[< '(Genlex.Kwd "HEIGHT"); '(Genlex.Int i) >] -> gi.g_h <- i
| [< '(Genlex.Kwd "LENGTH"); '(Genlex.Int i) >] -> gi.g_w <- i
| [< '(Genlex.Kwd "POSITION"); '(Genlex.Ident s);
'(Genlex.Int i); '(Genlex.Int j) >] -> gi.npos.(index s g) <- (i,j)
| [< '(Genlex.Kwd "COLOR"); '(Genlex.Ident s);
'(Genlex.Int r); '(Genlex.Int g); '(Genlex.Int b) >] ->
gi.opt <- (s, Awi.Copt (Graphics.rgb r g b))::gi.opt
| [<>] -> ();;
val pars2 : string graph -> g_info -> Genlex.token Stream.t -> unit = <fun>


Creating the Application

The function create_graph takes the name of a file as input and returns a couple composed of a graph and associated graphical information.

# let create_gg_graph name =
let g = create_graph name in
let gi = {npos = Array.create g.size (0,0); opt=[]; g_w =0; g_h = 0;} in
let ic = open_in name in
try
print_string ("Loading (pass 2) " ^name ^" : ");
while true do
print_string ".";
let l = input_line ic in pars2 g gi (lex2 l)
done ;
g,gi
with End_of_file -> print_newline(); close_in ic; g,gi;;
val create_gg_graph : string -> string graph * g_info = <fun>


The function create_app constructs the interface of a graph.

# let create_app name =
let g,gi = create_gg_graph name in
let size = (string_of_int gi.g_w) ^ "x" ^ (string_of_int gi.g_h) in
Graphics.open_graph (" "^size);
let gg = create_gg (create_comp_graph g) gi.npos id id in
main_gg gg gi.g_w gi.g_h
[ "Background", Awi.Copt (Graphics.rgb 130 130 130) ;
"Foreground", Awi.Copt Graphics.green ]
[ "Relief", Awi.Sopt "Top" ; "Border_size", Awi.Iopt 2 ] ;
gg;;
val create_app : string -> string gg = <fun>


Finally, the function main takes the name of the file from the command line, constructs a graph with an interface and starts the interaction loop on the main component of the graph interface.

# let main () =
if (Array.length Sys.argv ) <> 2
then Printf.printf "Usage: dij.exe filename\n"
else
let gg = create_app Sys.argv.(1) in
Awi.loop true false gg.main;;
val main : unit -> unit = <fun>


The last expression of that program starts the function main.

The Executable

The motivation for making a standalone application is to support its distribution. We collect the types and functions described in this section in the file dij.ml. Then we compile the file, adding the different libraries which are used. Here is the command to compile it under Linux.
ocamlc -custom -o dij.exe graphics.cma awi.cmo graphs.ml \
    -cclib -lgraphics -cclib -L/usr/X11/lib -cclib -lX11
Compiling standalone applications using the Graphics library is described in chapters 5 and 7.

Final Notes

The skeleton of this application is sufficiently general to be used in contexts other than the search for traveling paths. Different types of problems can be represented by a weighted graph. For example the search for a path in a labyrinth can be coded in a graph where each intersection is a node. Finding a solution corresponds to computing the shortest path between the start and the goal.

To compare the performance betwen C and Objective CAML, we wrote Dijkstra's algorithm in C. The C program uses the Objective CAML data structures to perform the calculations.

To improve the graphical interface, we add a textfield for the name of the file and two buttons to load and to store a graph. The user may then modify the positions of the nodes by mouse to improve the appearance.

A second improvement of the graphical interface is the ability to choose the form of the nodes. To display a button, a function tracing a rectangle is called. The display functions can be specialized to use polygons for nodes.






Previous Contents Next ocaml-book-1.0/en/html/book-ora164.html0000644000000000000000000000357407453055400014476 0ustar Chapter Overview Previous Contents Next

Chapter Overview

The first section indicates how to use the Unix module. We will talk about the handling of errors specific to that module and about the portability of system calls to Windows.

The second section presents file descriptors in the Unix sense, and their use for input and output operations of a lower level than those provided by the preloaded module Pervasives.

Processes are introduced in the third section. We talk about their creation, their disappearance and about the way in which all processes support their descendence relation in the Unix model.

The fourth section describes the basic means of communications between processes: pipes and signals.

The two last sections will be continued in chapters 19 and 20 by the presentation of lightweight processes and sockets.


Previous Contents Next ocaml-book-1.0/en/html/book-ora036.html0000644000000000000000000000350307453055377014501 0ustar Plan of the Chapter Previous Contents Next

Plan of the Chapter

This chapter provides a comparison between the functional and imperative models in the Objective CAML language, at the level both of control structure and of the memory representation of values. The mixture of these two styles allows new data structures to be created. The first section studies this comparison by example. The second section discusses the ingredients in the choice between composition of functions and sequencing of instructions, and in the choice between sharing and copying values. The third section brings out the interest of mixing these two styles to create mutable functional data, thus permitting data to be constructed without being completely evaluated. The fourth section describes streams, potentially infinite sequences of data, and their integration into the language via pattern-matching.


Previous Contents Next ocaml-book-1.0/en/html/book-ora021.gif0000644000000000000000000001047207452056111014261 0ustar GIF89aUUUrrr!,H0I8ͻ`(di++tm8ڮZJ0]My ĨtZ9cIr^*$k%iD𸼑^|Oߣyk}|4~Emsox{m`tv7yxJ Pz'R+0[=űLn!ͫΪc]f5v,t呬u]zP}4'PU40:DZb_q@I;"'{5Lo:q0R!Y\.wO:s]<楃MJ=p(.KJNOO9}o:_R&E[`&xKL4|GāSf@ * &{pf 8•魄h x΃!\>V%E oC P||!Y;14JWDznt+:@,䳻1cj"H35꠺0xdcHv+(U=8 ""DD/(GU2Pi|'I1vruf$_(aejDnGda3ǜɖJA6$0l9n1@tihԤՋ N,N-PEyѨnmK3ֶVtgZ6lb՛nlPWv=1ڒkS1hn]sΰ/u[٭w;sK[Զt l|ϭ۱LN8nϚr >XΎ#$ Ύ>Bq fʃȻә>b<Ofq'3=."7X2c[J薁1r{h^O)4y.޼{۝*ݳ^J;ːOd~ ~܂hwȠ/KBL6vt @R~Ǒ57Vo?nw%ytĀFqf&[RnlXVu(fB%[ˆ‚O"x77ަ8wc'9`g_DnFTA8wT2]UHwIdRy?uhgJhLHM`Zgs]'][xo7tlHS؅`'~w#Fuy|І xGXrGi0LvdQR" h,H| HR3bQS)"!{F2iUqxxˆ^7d!G_4?v$a4~5F׋jGx -z$"6F"F;4K'qQ<؃i~wBLx;K 4Nxix E9Rm֓!=y!eaI & O> +!;(C hI"#g%p7WY/L(ܧv Rs[ _6Ho}1bn}(14X}ėGgC{uyt %Ps19orSI9g-Uwmpe}.Kjrq(E~jY8<ɃiG[ )y vYfY$9Iɐ$g:p1Ɇd8ӂSohHɩeY'Na9)iDS=*@9#Xv E#>uS$@Rh(٠J,ءcYkq u֢Y"::Z\x)%ڠHsEGx7ڏMj (KjYJ< ڄ{gIe>nWf7 IYAN؇ G9j|#I{hjרUz ijh%JA@ ]BLڪpU{W5AM^ Ɗ:$QB*tJrvhkڅj,b 劫jPHѕ )J*:*L]+ QV#J| i/ʟӰ ( 0e;k˰˱ !-'ۑ%˯+)*'Kk39[h# /uǞ= Q(ks,$2 .I{5JXۛ$PXtW+OY_rô6<;%cS擶E lU[[^KJڲ[Z+\`;귞x;}[⊸ۦu۹;!:+ X;0 ۺFuKpj⪻ !ۄ[{țʻۼ3JK?ػ&L=;⫽[{苻껾۾+7#`8C Y暤Sv{E(19C>4ȓǁ!LS$?,?ő)u"8 7+I$ l8c!.#0xY: ì ~c::sG+P;]Ob`?Lѣ6Ch=.:hE3N^۞^ ?j=3wkO>#P2  b,5^OEdE9ѣ5T*V,\9ɗĦݼ)_. 3NnJdhFN]T Ti4FEU묦Ua z+b]v*٣pz}JcޱygŻW-@V&]"ފ\ߊhݠÈ j=meY.=Ŋiw;IC14fШՎY:iho׵Z$9瞫iV.8\g5{~=Ff:qEƌ}*?ok=+nϲ_,} xNdyt]l17n.p%΂f߀Z{!!9xzW]NVl:v]\_( c` )@BFdF:fT B?^5X VVBRH%\IdfV fnXnYfZMS~矂jN3&h09 i,*ixiCJ*駢@ɨj(ɪj꫖*$ȭ*뮆 ǰa6v$6 dzв!{jm(f;ƶx+.;Hn ;ocaml-book-1.0/en/html/book-ora036.gif0000644000000000000000000000513507452056111014267 0ustar GIF89aUUU!,ڋ޼扖ʶ Lhu䄪L*ɌJZܮWLU.f' `'wpxPXxygW𗠷Gyhs0 *Kyc3$Xj04 ʫ1V<[k -I&FyxM 3(R\Nr^ov \@P #$T0q&Z0Pb)IZe!Ei$MX9~_*QԂf>2ϟ'w zx2y聧h!2)H" pޝ[+"Zl~ &j::G+d;]0֧Kn枋nF5 -OΊ-nwYYj{d/1 밾'˰ nj;oyҲvC2 _2.dK|roq(_b) 3҂T7 ;3ERtO/!l6\! f;ʘ #b32Ǵ]1-7*8KX&\l{zׄz XmcwA7^bQ.31sCcyapu'Ҭ85֢Kp $1}&otcM{&~֏? {~̂ To Learn More Previous Contents Next

To Learn More

Other examples of modules and functors can be found in chapter 4 of the Objective Caml manual.

The underlying theory and the type checking for modules can be found in a number of research articles and course notes by Xavier Leroy, at

Link


http://cristal.inria.fr/~xleroy


The Objective CAML module system follows the same principles as that of its cousin the SML language. Chapter 22 compares these two languages in more details and provides bibliographical references for the interested reader.

Other languages feature advanced module systems, in particular Modula-3 (2 and 3), and ADA. They support the definition of modules parameterized by types and values.








Previous Contents Next ocaml-book-1.0/en/html/book-ora080.html0000644000000000000000000000611507453055400014465 0ustar To Learn More Previous Contents Next

To Learn More

The overview of the libraries in the distribution of the language showed the richness of the basic environment. For the Printf module nothing is worth more than reading a work on the C language, such as [HS94]. In [FW00] a solution is proposed for the typing of intput-output of values (module Marshal). The MD5 algorithm of the Digest module is described on the web page of its designer:

Link


http://theory.lcs.mit.edu/~rivest/homepage.html
In the same way you may find many articles on exact arithmetic used by the num library on the web page of Valrie Mnissier-Morain :

Link


http://www-calfor.lip6.fr/~vmm/


There are also other libraries than those in the distribution, developed by the community of Objective CAML programmers. Objective CAML. The majority of them are listed on the ``Camel's hump'' site:

Link


http://caml.inria.fr/hump.html
Some of them will be presented and discussed in the chapter on applications development (22).

To know the exact contents of the various modules, don't hesitate to read the description of the libraries in the reference manual [LRVD99] or consult the online version in HTML format (1). To enter into the details of the implementations of these libraries, nothing is better than reading the source code, available in the distribution of the language (1).
Chapter 14 presents the language of Objective CAML modules. This allows you to build simple modules seen as independent compilation units, which will be similar to the modules presented in this chapter.




Previous Contents Next ocaml-book-1.0/en/html/book-ora035.html0000644000000000000000000001130007453055377014472 0ustar Introduction Previous Contents Next

Introduction

Functional and imperative programming languages are primarily distinguished by the control over program execution and the data memory management.
  • A functional program computes an expression. This computation results in a value. The order in which the operations needed for this computation occur does not matter, nor does the physical representation of the data manipulated, because the result is the same anyway. In this setting, deallocation of memory is managed implicitly by the language itself: it relies on an automatic garbage collector or GC; see chapter 9.

  • An imperative program is a sequence of instructions modifying a memory state. Each execution step is enforced by rigid control structures that indicate the next instruction to be executed. Imperative programs manipulate pointers or references to values more often than the values themselves. Hence, the memory space needed to store values must be allocated and reclaimed explicitly, which sometimes leads to errors in accessing memory. Nevertheless, nothing prevents use of a GC.
Imperative languages provide greater control over execution and the memory representation of data. Being closer to the actual machine, the code can be more efficient, but loses in execution safety. Functional programming, offering a higher level of abstraction, achieves a better level of execution safety: Typing (dynamic or static) may be stricter in this case, thus avoiding operations on incoherent values. Automatic storage reclamation, in exchange for giving up efficiency, ensures the current existence of the values being manipulated.

Historically, the two programming paradigms have been seen as belonging to different universes: symbolic applications being suitable for the former, and numerical applications being suitable for the latter. But certain things have changed, especially techniques for compiling functional programming languages, and the efficiency of GCs. From another side, execution safety has become an important, sometimes the predominant criterion in the quality of an application. Also familiar is the ``selling point'' of the Java language, according to which efficiency need not preempt assurance, especially if efficiency remains reasonably good. And this idea is spreading among software producers.

Objective CAML belongs to this class. It combines the two programming paradigms, thus enlarging its domain of application by allowing algorithms to be written in either style. It retains, nevertheless, a good degree of execution safety because of its static typing, its GC, and its exception mechanism. Exceptions are a first explicit execution control structure; they make it possible to break out of a computation or restart it. This trait is at the boundary of the two models, because although it does not replace the result of a computation, it can modify the order of execution. Introducing physically mutable data can alter the behavior of the purely functional part of the language. For instance, the order in which the arguments to a function are evaluated can be determined, if that evaluation causes side effects. For this reason, such languages are called ``impure functional languages.'' One loses in level of abstraction, because the programmer must take account of the memory model, as well as the order of events in running the program. This is not always negative, especially for the efficiency of the code. On the other hand, the imperative aspects change the type system of the language: some functional programs, correctly typed in theory, are no longer in fact correctly typed because of the introduction of references. However, such programs can easily be rewritten.


Previous Contents Next ocaml-book-1.0/en/html/book-ora127.html0000644000000000000000000001350207453055400014465 0ustar Presentation of Part III Previous Contents Next

Presentation of Part III

The third part of this work is dedicated to application development and describes two ways of organizing applications: modules and objects. The goal is to easily structure an application for incremental and rapid development, maintenance facilitated by the ability to change gracefully, and the possibility of reusing large parts for future development.

We have already presented the language's predefined modules (8) viewed as compilation units. Objective CAML's module language supports on the one hand the definition of new simple modules in order to build one's own libraries, perhaps including abstract types, and on the other hand the definition of modules parameterized by other modules, called functors. The advantage of this parameterization lies in being able to ``apply'' a module to different argument modules in order to create specialized modules. Communication between modules is thus explicit, via the parameter module signature, which contains the types of its global declarations. However, nothing stops you from applying a functor to a module with a more extended signature, as long as it remains compatible with the specified parameter signature.

Besides, the Objective CAML language has an object-oriented extension. First of all object-oriented programming permits structured communication between objects. Rather than applying a function to some arguments, one sends a message (a request) to an object which knows how to deal with it. The object, an instance of a class (a structure gathering together data and methods), then executes the corresponding code. The main relation between classes is inheritance, which lets one describe subclasses which retain all the declarations of the ancestor class. Late binding between the name of a message and the corresponding code within the object takes place during program execution. Nevertheless Objective CAML typing guarantees that the receiving object will always have a method of this name, otherwise type inference would have raised a compile-time error. The second important relation is subtyping, where an object of a certain class can always be used in place of an object of another class. In this way a new type of polymorphism is introduced: inclusion polymorphism.

Finally the construction of a graphical interface, begun in chapter 5, uses different event management models. One puts together in an interface several components with respect to which the user or the system can produce events. The association of a component with a handler for one or more events taking place on it allows one to easily add to and modify such interfaces. The component-event-handler association can be cloaked in several forms: definition of a function (called a callback), inheritance with redefinition of handler methods, or finally registration of a handling object (delegation model).

Chapter 14 is a presentation of modular programming. The different prevailing terminologies of abstract data types and module languages are explained and illustrated by simple modules. Then the module language is detailed. The correspondence between modules (simple or not) and compilation units is made clear.

Chapter 15 contains an introduction to object-oriented programming. It brings a new way of structuring Objective CAML programs, an alternative to modules. This chapters shows how the notions of object-oriented programming (simple and multiple inheritance, abstract classes, parameterized classes, late binding) are articulated with respect to the language's type system, and extend it by the subtyping relation to inclusion polymorphism.

Chapter 16 compares the two preceding software models and explains what factors to consider in deciding between the two, while also demonstrating how to simulate one by the other. It treats various cases of mixed models. Mixing leads to the enrichment of each of these two models, in particular with parameterized classes using the abstract type of a module.

Chapter 17 presents two classes of applications: two-player games, and the construction of a world of virtual robots. The first example is organized via various parameterized modules. In particular, a parameterized module is used to represent games for application of the minimax ab algorithm. It is then applied to two specific games: Connect 4 and Stone Henge. The second example uses an object model of a world and of abstract robots, from which, by inheritence, various simulations are derived. This example is presented in chapter 21.




Previous Contents Next ocaml-book-1.0/en/html/book-ora005.gif0000644000000000000000000000171507452056110014262 0ustar GIF89aUUU!,ڋ޼7H扦ʶ xL+q}xS )"m˦Q4@Sg5\[d ˾CDL@5v1VhXȶ!q8)&iʳi sH&Qj5k! U{[ &Ql9Lx|L1Ml[|ݴi`n]ʱN>/Oi?/ ~h(0a> bBx0"DITx1C<\FFz QI|DUڌ9JK%W6DTNN9} 5TL{ͪVeްn!dv=[6@\պEz۹pȥWFy Q z{>-`Č58r:%[Fe˭:oygКg&-ˠ2 ^ͨuXبk\zM;j߶G7]ZCZyC9+:g'o.ϻoǮկ?}xKW~dϯ㱧%!XVנǠUxn! fE't$"")&C&S⋬ 9h1S)gbhYˉ +eЊVlfRH.[G3nw[n@Nk6p`ܩ>"pjrswټCaݩY [BM4UjW;ocaml-book-1.0/en/html/book-ora003.gif0000644000000000000000000000305307452056110014255 0ustar GIF89aUUU!,ڋ<HB o:b3 ;oL*q 55-۬V'RVf <-ϟ5_(EWgІ5xbX)8vw(D JzqYZB:A+K{` \[d\ؼ }<Dz{={GMEj- Um$|].&?M>䞏. p_}O&a0Ąh'.ubUDۂ5 2Uk8b(j@͊/CA Π*-`礟Cy|UIT3G蟨>P9iUT uѰS8yZNycar0n-,jLC|n`%CvPǗLyΞ5 􈄩la*kDs-`]0D>ߥQx9f87ߕogn 6եnOǚ|[io-e_,Ϝ6eh aXeUl&d e%Z1 sd MA,7%NRv06<2jUT&U>&IV^!idҒq)WJI"=YY'N%]dNh!qQ7r8d%\`8Y~8 ctydey]Z]&y)~3UGsuvt> )-dc{zPR+xG&C :{\K0;#-pw韰nl}[1bx\lNMN(W_* ocQ+[:+B 6oL؊>9BE6ڠ?1$pQjd(YP DAYF~T^q.D DV- Wq%ND^e&zD=HЕGijXJG㒱,"kRj.!_6”ٍW@`=YLcj$a4ÛКT)_vR8F&t#*Im k &=P{}BOC':%@ /O"\' ;ocaml-book-1.0/en/html/book-ora095.html0000644000000000000000000000304707453055400014474 0ustar Chapter Overview Previous Contents Next

Chapter Overview

This short chapter presents the program analysis tools in the Objective CAML distribution. The first chapter describes the ocamldep command, which finds the dependencies in a set of Objective CAML files that make up an application.

The second section deals with debugging tools including tracing the execution of functions and the ocamldebug debugger, running under Unix.

The third section takes a look at the profiler, which can be used to analyze the execution of a program with an eye towards its optimization.


Previous Contents Next ocaml-book-1.0/en/html/book-ora081.html0000644000000000000000000000174107453055401014467 0ustar Notes
1
JAVA uses the term serialization
2
Arrays of characters, for example.
3
The interactive mode has a general exception handler that prints a message signaling that an exception was not handled.
ocaml-book-1.0/en/html/book-ora162.html0000644000000000000000000002353707453055400014475 0ustar Presentation of Part IV Previous Contents Next

Presentation of Part IV

The fourth part introduces the concepts of parallel programming and presents models for shared and distributed memory. It is not necessary to have access to a parallel supercomputer to express concurrent algorithms or to implement distributed applications. In this preamble we define the different terms used in the following chapters.

In sequential programming, one instruction is executed after another. This is called causal dependency. Sequential programs have the property of being deterministic. For the same input, one program will always terminate or never terminate. In the case of termination, always the same result will be produced. Determinism implies that the same circumstances will always lead to the same effects. The only exceptions, which we have already met in Objective CAML, are provided by functions taking external information as input, such as the function Sys.time.

In parallel programming, a program is split into several active processes. Each process is sequential, but several instructions, belonging to different processes, are executed in parallel, ``at the same time.'' The sequence is transformed into concurrency. This is called causal independence. The same circumstances may lead to different, mutually exclusive effects (only one effect is produced). An immediate consequence is the loss of determinism: the same program with the same input may or may not terminate. In the case of termination different results may be produced.

In order to control the execution of a parallel program, it is necessary to introduce two new notions:
  • synchronization, which introduces a conditional wait to several processes;
  • communication through the passing of messages between processes.
From the point of view of causality, synchronization assures that several independent circumstances have to be reproduced before an effect may take place. Communications have a temporal constraint: a message can not be received before it is sent. Communication can occur in different variants: communication directed from one process to one other (point-to-point) or as distribution (one-to-all, or all-to-all).

The two models of parallel programming described by figure 17.13 differ in execution control by the forms of synchronization and communication.


Figure 17.13: Models of parallelism


Each process Pi corresponds to a sequential process. The set of these processes, interacting via shared memory (M) or via a medium, constitutes a parallel application.

Shared Memory Model
Communication is implicit in the shared memory model. An information is given through writing into a zone of the shared memory. It is received when another process reads this zone. The synchronization, in contrast, has to be explicit. Constructs of mutual exclusion and waiting conditions are used.

This model is used when shared resources are used in a concurrent way. The construction of operating systems can be cited as an example.

Distributed Memory Model
In this model each sequential process Pi has a private memory Mi, to which no other process has access. The processes have to communicate in order to transmit information through a medium. The difficulties in this model arise from the implementation of the medium. The programs which care for this are called protocols.

Protocols are organized in layers. The higher-level protocols implement more elaborate services, using the lower-level services.

There exist several types of communication. They depend on the capability of the medium to store information and of the blocking, respectively non-blocking character of sender and receiver. We talk about synchronous communication when the transfer of information is not possible before a global synchronization between sender and receiver takes place. In his case both sender and receiver may be blocked.

If the medium has the storage capabilities, it can store messages for a later transmission. Therefore the communication can be asynchronous and non-blocking. It may be necessary to indicate the storage capacity of the medium, the order of transmission, the delay and the reliability of transmissions.

Finally, if the transmission is non-blocking with a medium not able to store messages, a volatile communication results: only the receiving processes which are ready will receive the sent message, which is lost for the other processes.

In the model of distributed memory the communication is explicit, but the synchronization is implicit (Synchronization is produced by communication). It is dual to the model of shared memory.

Physical and Logical Parallelism
The model of distributed memory is valid in the case of physical and logical parallelism. Physical parallelism refers for example to a computer network. Examples for logical parallelism are Unix processes communicating via pipes, or lightweight processes communicating via channels. There are no common global values known by all proceses like, for example, a global clock.

The model of distributed memory is closer to physical parallelism, where there is no effectively shared memory. Nevertheless, shared memory can be simulated across a computer nerwork.

The fourth part will show how to construct parallel applications with Objective CAML using the two presented models. It relies on the Unix library, which interfaces Unix system calls to Objective CAML, and on the Thread library, which implements lightweight processes. A major part of the Unix library is ported to Windows, especially the functions on file descriptors. These are used to read and to write on files, but also for communication pipes and for sockets of computer networks.

Chapter 18 describes essential concepts of the Unix library. It concentrates on the communication of a process with it's exterior and with other processes. The notion of process in this chapter is that of a ``heavyweight process'' as in Unix. They are created by the fork system call which duplicates the execution context and the memory for the data, producing a chain of processes. The interaction between processes is implemented by signals or by communication pipes.

Chapter 19 concentrates on the notion of lightweight processes of the Thread library. In contrast to the heavy processes mentioned before, they duplicate nothing but the execution context of an existing process. The memory is shared between the creator and the thread. Depending on the programming style, the light Objective CAML processes permit to use the parallelism model of shared memory (imperative style) or the model of separated memory (purely functional style). The Thread library contains several modules allowing to start and stop threads, to manage locks for mutual exclusion, to wait for a condition and to communicate between threads via channels. In this model, there is no gain in execution time, not even for multi-processor machines. But the formulation of parallel algorithms is made easier.

Chapter 20 is devoted to the construction of distributed Internet applications. The Internet is presented from the point of view of low-level protocols. With the help of communication sockets several processes running on different machines are able to communicate with each other. The communication through sockets is an asynchronous point-to-point communication. The role of the different processes taking part in the communication of a distributed application is in general asymmetrical. This is the case for client-server architectures. The server is a process accepting requests and trying to respond. The other process, the client, sends a request to the server and waits for a response. Many services accessible in the Internet follow this architecture.

Chapter 21 presents a library and two complete applications. The library allows to define the communication between clients and servers starting from a given protocol. The first application revisits the robots of chapter 17 to give a distributed version. The second application constructs an HTTP server to manage a request form taking up again the management of associations presented in chapter 6.




Previous Contents Next ocaml-book-1.0/en/html/book-ora142.html0000644000000000000000000004562707453055400014477 0ustar Other Object-oriented Features Previous Contents Next

Other Object-oriented Features

References: self and super

When defining a method in a class, it may be convenient to be able to invoke a method from a parent class. For this purpose, Objective CAML allows the object itself, as well as (the objects of) the parent class to be named. In the former case, the chosen name is given after the keyword object, and in the latter, after the inheritance declaration.

For example, in order to define the method to_string of colored points, it is better to invoke the method to_string from the parent class and to extend its behavior with a new method, get_color.

# class colored_point (x,y) c =
object (self)
inherit point (x,y) as super
val c = c
method get_color = c
method to_string () = super#to_string() ^ " [" ^ self#get_color ^ "] "
end ;;


Arbitrary names may be given to the parent and child class objects, but the names self and this for the current class and super for the parent are conventional. Choosing other names may be useful with multiple inheritance since it makes it easy to differentiate the parents (see page ??).

Warning


You may not reference a variable of an instance's parent if you declare a new variable with the same name since it masks the former.


Delayed Binding

With delayed binding the method used when a message is sent is decided at run-time; this is opposed to static binding where the decision is made at compile time. In Objective CAML, delayed binding of methods is used; therefore, the exact piece of code to be executed is determined by the recipient of the message.

The above declaration of class colored_point redefines the method to_string. This new definition uses method get_color from this class. Now let us define another class colored_point_1, inheriting from colored_point; this new class redefines method get_color (testing that the character string is appropriate), but does not redefine to_string.


# class colored_point_1 coord c =
object
inherit colored_point coord c
val true_colors = ["white"; "black"; "red"; "green"; "blue"; "yellow"]
method get_color = if List.mem c true_colors then c else "UNKNOWN"
end ;;


Method to_string is the same in both classes of colored points; but two objects from these classes will have a different behavior.

# let p1 = new colored_point (1,1) "blue as an orange" ;;
val p1 : colored_point = <obj>
# p1#to_string();;
- : string = "( 1, 1) [blue as an orange] "
# let p2 = new colored_point_1 (1,1) "blue as an orange" ;;
val p2 : colored_point_1 = <obj>
# p2#to_string();;
- : string = "( 1, 1) [UNKNOWN] "


The binding of get_color within to_string is not fixed when the class colored_point is compiled. The code to be executed when invoking the method get_color is determined from the methods associated with instances of classes colored_point and colored_point_1. For an instance of colored_point, sending the message to_string causes the execution of get_color, defined in class colored_point. On the other hand, sending the same message to an instance of colored_point_1 invokes the method from the parent class, and the latter triggers method get_color from the child class, controlling the relevance of the string representing the color.

Object Representation and Message Dispatch

An object is split in two parts: one may vary, the other is fixed. The varying part contains the instance variables, just as for a record. The fixed part corresponds to a methods table, shared by all instances of the class.

The methods table is a sparse array of functions. Every method name in an application is given a unique id that serves as an index into the methods table. We assume the existence of a machine instruction GETMETHOD(o,n), that takes two parameters: an object o and an index n. It returns the function associated with this index in the methods table. We write f_n for the result of the call GETMETHOD(o,n). Compiling the message send o#m computes the index n of the method name m and produces the code for applying GETMETHOD(o,n) to object o. This corresponds to applying function f_n to the receiving object o. Delayed binding is implemented through a call to GETMETHOD at run time.

Sending a message to self within a method is also compiled as a search for the index of the message, followed by a call to the function found in the methods table.

In the case of inheritance, since the method name always has the same index, regardless of redefinition, only the entry in new class' methods table is changed for redefinitions. So sending message to_string to an instance of class point will apply the conversion function of a point, while sending the same message to an instance of colored_point will find at the same index the function corresponding to the method which has been redefined to recognize the color field.

Thanks to this index invariance, subtyping (see page ??) is insured to be coherent with respect to the execution. Indeed if a colored point is explicitly constrained to be a point, then upon sending the message to_string the method index from class point is computed, which coincides with that from class colored_point. Searching for the method will be done within the table associated with the receiving instance, i.e. the colored_point table.

Although the actual implementation in Objective CAML is different, the principle of dynamic search for the method to be used is still the same.

Initialization

The class definition keyword initializer is used to specify code to be executed during object construction. An initializer can perform any computation and field access that is legal in a method.

Syntax


initializer expr


Let us again extend the class point, this time by defining a verbose point that will announce its creation.

# class verbose_point p =
object(self)
inherit point p
initializer
let xm = string_of_int x and ym = string_of_int y
in Printf.printf ">> Creation of a point at (%s %s)\n"
xm ym ;
Printf.printf " , at distance %f from the origin\n"
(self#distance()) ;
end ;;

# new verbose_point (1,1);;
>> Creation of a point at (1 1)
, at distance 1.414214 from the origin
- : verbose_point = <obj>


An amusing but instructive use of initializers is tracing class inheritance on instance creation. Here is an example:

# class c1 =
object
initializer print_string "Creating an instance of c1\n"
end ;;

# class c2 =
object
inherit c1
initializer print_string "Creating an instance of c2\n"
end ;;

# new c1 ;;
Creating an instance of c1
- : c1 = <obj>
# new c2 ;;
Creating an instance of c1
Creating an instance of c2
- : c2 = <obj>
Constructing an instance of c2 requires first constructing an instance of the parent class.

Private Methods

A method may be declared private with the keyword private. It will appear in the interface to the class but not in instances of the class. A private method can only be invoked from other methods; it cannot be sent to an instance of the class. However, private methods are inherited, and therefore can be used in definitions of the hierarchy3.

Syntax


method private name = expr


Let us extend the class point: we add a method undo that revokes the last move. To do this, we must remember the position held before performing a move, so we introduce two new fields, old_x and old_y, together with their update method. Since we do not want the user to have direct access to this method, we declare it as private. We redefine the methods moveto and rmoveto, keeping note of the current position before calling the previous methods for performing a move.

# class point_m1 (x0,y0) =
object(self)
inherit point (x0,y0) as super
val mutable old_x = x0
val mutable old_y = y0
method private mem_pos () = old_x <- x ; old_y <- y
method undo () = x <- old_x; y <- old_y
method moveto (x1, y1) = self#mem_pos () ; super#moveto (x1, y1)
method rmoveto (dx, dy) = self#mem_pos () ; super#rmoveto (dx, dy)
end ;;
class point_m1 :
int * int ->
object
val mutable old_x : int
val mutable old_y : int
val mutable x : int
val mutable y : int
method distance : unit -> float
method get_x : int
method get_y : int
method private mem_pos : unit -> unit
method moveto : int * int -> unit
method rmoveto : int * int -> unit
method to_string : unit -> string
method undo : unit -> unit
end


We note that method mem_pos is preceded by the keyword private in type point_m1. It can be invoked from within method undo, but not on another instance. The situation is the same as for instance variables. Even though fields old_x and old_y appear in the results shown by compilation, that does not imply that they may be handled directly (see page ??).

# let p = new point_m1 (0, 0) ;;
val p : point_m1 = <obj>
# p#mem_pos() ;;
Characters 0-1:
This expression has type point_m1
It has no method mem_pos
# p#moveto(1, 1) ; p#to_string() ;;
- : string = "( 1, 1)"
# p#undo() ; p#to_string() ;;
- : string = "( 0, 0)"


Warning


A type constraint may make public a method declared with attribute private.



Previous Contents Next ocaml-book-1.0/en/html/book-ora179.html0000644000000000000000000013167007453055400014503 0ustar Exercises Previous Contents Next

Exercises

The Philosophers Disentangled

To solve the possible deadlock of the dining philosophers, it suffices to limit access to the table to four at once. Implement this solution. We add a counter indicating the number of philosophers present as well as two functions, enter and leave, tasked with limiting arrivals, and signalling departures, respectively.

#

let enter, leave =
let n = ref 0 in
let m = Mutex.create() in
let c = Condition.create() in
let loc_enter () =
Mutex.lock m ;
while not (!n < 4) do Condition.wait c m done ;
incr n ;
if !n > 1 then Printf.printf "%d philosophers are at the table\n" !n
else Printf.printf "%d philosopher is at the table\n" !n ;
flush stdout;
Mutex.unlock m
in
let loc_leave () =
Mutex.lock m ;
decr n ;
Mutex.unlock m ;
Condition.broadcast c
in
loc_enter, loc_leave ;;
val enter : unit -> unit = <fun>
val leave : unit -> unit = <fun>
Then all that is necessary is to call these functions at the start and end of the loop of a philosopher.

# let philosopher i =
let ii = (i+1) mod 4 in
while true do
Printf.printf "Philosopher (%d) arrives\n" i ;
enter () ;
meditate 3. ;
Mutex.lock b.(i);
Printf.printf
"Philosopher (%d) picks up his left-hand baguette and meditates a while longer\n" i;
meditate 0.2; Mutex.lock b.(ii) ;
Printf.printf "Philosopher (%d) picks up his right-hand baguette\n" i ;
eat 0.5 ;
Mutex.unlock b.(i) ;
Printf.printf
"Philosopher (%d) puts down his left-hand baguette and goes back to meditating\n" i;
meditate 0.15 ;
Mutex.unlock b.(ii) ;
Printf.printf "Philosopher (%d) puts down his right-hand baguette" i ;
leave () ;
Printf.printf "Philosophe (%d) heads off \n" i ;
done ;;
val philosopher : int -> unit = <fun>
Attention, cette solution supprime les inter-blocages, mais pas les famines. Pour rsoudre ce dernier problme, on peut soit se fier au hasard en introduisant un dlai d'attente en aprs la sortie d'un philosophe, soit grer explicitement une file d'attente.


More of the Post Office

We suggest the following modification to the post office described on page ??: some impatient clients may leave before there number has been called.
  1. Add a method wait (with type int -> unit) to the class dispenser which causes the caller to wait while the last number distributed is less than or equal to the parameter of the method (it is necessary to modify take so that it emits a signal).

    # class distrib () =
    object
    val mutable n = 0
    val m = Mutex.create ()
    val c = Condition.create ()

    method attendre nc =
    Mutex.lock m ;
    while (n <= nc) do Condition.wait c m done ;
    Mutex.unlock m

    method prendre () =
    Mutex.lock m ;
    n <- n+1 ;
    let nn = n in
    Condition.broadcast c ;
    Mutex.unlock m ;
    nn
    end ;;
    class distrib :
    unit ->
    object
    val c : Condition.t
    val m : Mutex.t
    val mutable n : int
    method attendre : int -> unit
    method prendre : unit -> int
    end
  2. Modify the method await_arrival of class counter, so that it returns the boolean value true if the expected client arrives, and false if the client has not arrived at the end of a certain time. On rajoute une mthode prive reveil charge de rveiller le guichetier en attente au bout d'un temps donn.

    #
    method private reveil t =
    let dt = delai_attente_appel /. 10.0 in
    while (Unix.gettimeofday() < t) do Thread.delay dt done;
    Condition.signal c

    method attendre_arrivee () =
    let t = Unix.gettimeofday() +. delai_attente_appel in
    let r = Thread.create self#reveil t in
    Mutex.lock m;
    while libre && (Unix.gettimeofday() < t) do
    Condition.wait c m
    done;
    (try Thread.kill r with _ -> ());
    let b = not libre in (Mutex.unlock m; b)
  3. Modify the class announcer by passing it a number dispenser as a parameter and:
    1. adding a method wait_until which returns true if the expected number has been called during a given waiting period, and false otherwise; La modification de la mthode appel a pour but de garantir la fois que ne sont appels que des numros effectivement distribus et que lorsqu'un numro d'appel est affich, il existe bien un guichet qui attend ce numro.

      # class affich (d:distrib) =
      object
      val mutable nc = 0
      val m = Mutex.create ()
      val c = Condition.create ()

      method attendre n =
      Mutex.lock m ;
      while nc < n do Condition.wait c m done ;
      Mutex.unlock m

      method attendre_jusqu'a n t =
      Mutex.lock m ;
      while (nc < n) && (Unix.gettimeofday() < t) do Condition.wait c m done ;
      let b = not (nc < n) in
      Mutex.unlock m ;
      b

      method appel (g:guichet) =
      Mutex.lock m ;
      d#attendre nc ;
      nc <- nc+1 ;
      g#set_nc nc ;
      Condition.broadcast c ;
      Mutex.unlock m

      end ;;
      class affich :
      distrib ->
      object
      val c : Condition.t
      val m : Mutex.t
      val mutable nc : int
      method appel : guichet -> unit
      method attendre : int -> unit
      method attendre_jusqu'a : int -> float -> bool
      end
    2. modifying the method call to take a counter as parameter and update the field nclient of this counter (it is necessary to add an update method in the counter class).


  4. Modify the function clerk to take fruitless waits into account.

    # type bureau = { d: distrib; a: affich; gs: guichet array }
    val delai_service : float = 4
    val delai_arrivee : float = 2
    val delai_guichet : float = 0.5
    val delai_attente_client : float = 0.7

    let guichetier ((a : affich), (g : guichet)) =
    while true do
    a#appel g ;
    Printf.printf"Guichet %d appelle %d\n" g#get_ng g#get_nc ;
    if g#attendre_arrivee () then g#attendre_depart ()
    else
    begin
    Printf.printf"Guichet %d n'attend plus %d\n" g#get_ng g#get_nc ;
    flush stdout
    end ;
    Thread.delay (Random.float delai_guichet)
    done ;;
    val guichetier : affich * guichet -> unit = <fun>
  5. Write a function impatient_client which simulates the behaviour of an impatient client.

    # val chercher_guichet : 'a -> < get_nc : 'a; .. > array -> int = <fun>

    let client_impatient b =
    let n = b.d#prendre() in
    let t = Unix.gettimeofday() +. (Random.float delai_attente_client) in
    Printf.printf "Arrivee client impatient %d\n" n; flush stdout;
    if b.a#attendre_jusqu'a n t
    then
    let ig = chercher_guichet n b.gs in
    b.gs.(ig)#arriver() ;
    Printf.printf "Le client %d occupe le guichet %d\n" n ig ;
    flush stdout ;
    Thread.delay (Random.float delai_service) ;
    b.gs.(ig)#partir() ;
    Printf.printf "Le client %d s'en va\n" n
    else
    Printf.printf "Le client %d, las d'attendre, s'en va\n" n
    flush stdout ;;
    Characters 518-531:
    This function is applied to too many arguments

Object Producers and Consumers

This exercise revisits the producer-consumer algorithm with the following variation: the storage warehouse is of finite size (i.e. a table rather than a list managed as a FIFO). Also, we propose to make an implementation that uses objects to model resources, like the post office.

  1. Define a class product with signature:

    # class produit (s:string) =
    object
    val nom = s
    method nom = nom
    end ;;
    class produit : string -> object val nom : string method nom : string end
     
    class product : string ->
    object
    val name : string
    method name : string
    end
  2. Define a class shop such that:

    # class magasin n =
    object(self)
    val mutable taille = n;
    val mutable np = 0
    val mutable buffer = ([||] : produit array)
    val mutable ip = 0 (* Indice producteur *)
    val mutable ic = 0 (* Indice consommateur *)

    val m = Mutex.create ()
    val c = Condition.create ()

    initializer buffer<-Array.create n (new produit "empty")

    method display1 () =
    let i = ip mod taille in
    Printf.printf "Ajout (%d)%s\n" i ((buffer.(i))#nom)

    method deposer p =
    Mutex.lock m ;
    while (ip-ic+1 > Array.length(buffer)) do Condition.wait c m done ;
    buffer.(ip mod taille) <- p ;
    self#display1() ;
    ip <- ip+1 ;
    Mutex.unlock m ;
    Condition.signal c

    method display2 () =
    let i = ic mod taille in
    Printf.printf "Retrait (%d)%s\n" i ((buffer.(i))#nom)

    method prendre () =
    Mutex.lock m ;
    while(ip == ic) do Condition.wait c m done ;
    self#display2() ;
    let r = buffer.(ic mod taille) in
    ic<- ic+1 ;
    Mutex.unlock m ;
    Condition.signal c ;
    r
    end ;;
    class magasin :
    int ->
    object
    val mutable buffer : produit array
    val c : Condition.t
    val mutable ic : int
    val mutable ip : int
    val m : Mutex.t
    val mutable np : int
    val mutable taille : int
    method deposer : produit -> unit
    method display1 : unit -> unit
    method display2 : unit -> unit
    method prendre : unit -> produit
    end
     
    class show : int ->
    object
    val mutable buffer : product array
    val c : Condition.t
    val mutable ic : int
    val mutable ip : int
    val m : Mutex.t
    val mutable np : int
    val size : int
    method dispose : product -> unit
    method acquire : unit -> product
    end
    The indexes ic and ip are manipulated by the producers and the consumers, respectively. The index ic holds the index of the last product taken and ip that of the last product stored. The counter np gives the number of products in stock. Mutual exclusion and control of the waiting of producers and consumers will be managed by the methods of this class.

  3. Define a function consumer: shop -> string -> unit.

    # let consommateur mag na =
    while true do
    let p = mag#prendre() in
    Printf.printf "Le consommateur %s prend le produit %s\n" na p#nom ;
    flush stdout ;
    Thread.delay(Random.float(3.0))
    done ;;
    val consommateur :
    < prendre : unit -> < nom : string; .. >; .. > -> string -> unit = <fun>
  4. Define a function create_product of type string -> product. The name given to a product will be composed of the string passed as an argument concatenated with a product number incremented at every invocation of the function.
    Use this function to define producer: shop -> string -> unit.

    # let producteur =
    let num = ref 0 in
    let creer_produit () =
    let p = new produit("lessive-"^(string_of_int !num)) in
    incr num ;
    p
    in
    function mag -> function nm ->
    while true do
    let p = creer_produit () in
    mag#deposer(p) ;
    Printf.printf"Production de %s\n" p#nom ;
    flush stdout ;
    Thread.delay (Random.float (1.0))
    done ;;
    val producteur : < deposer : produit -> '_a; _.. > -> '_b -> unit = <fun>

Previous Contents Next ocaml-book-1.0/en/html/book-ora026.gif0000644000000000000000000000742107452056111014266 0ustar GIF89a'UUU999rrr!,'X0I8ͻ`(di&lp,rmxzpH,Ȥr)1)Z*vzxL&jnQ|N1~o9QJE.B X_ [~?Wzڍ8y} `A @`a!|HQ!C4T  !vBY"|'$YЁZ t $ېs =c=:%2 ehP]ԩgXYM@ MH3ʝKݻxs__ʅ /$ݱ/e//V&*h7s 4̍fG Ħk^\+9F:tlg+3 ={ -A!:Z}S?at3+/~8\}?+n*Bd-X}tZ1 YFtGwBnCJe{ItCt&hvLv@Dތ-Ƹ cd;Eoӏ@"Q#i!$L] ne{OZI$lif)xil&r Zb%Ye%Sz{('JjhX钙Z:*]zaug@RRz)2V%spj*&QdJ8 }ctw ],[J`jEK,mbS/9*;x bd. zMFfg/;m[←M//^f$\QC鵅^4* spgăQ(S19bX`w2dPaI$kDDnȊ́-iJei<̭͐3%Jwrk[jHOz ;$nնpߠ˻ } ')-zšj{*/ek;}i»-ץm3 AA;#TKڷ-e&b@1ˌ+0-y>1 u{ T ˅ P9{B=Lm@٢@ȥ%|: i"+q'{KG hi *"h ,*ѤQs 05(lpKNRb)R S;T#L Gϛҗ>mVAE*8ET80"'TUԩӪVUuUe_kXJֲjxKZֶpiUJ׺Buvd^o׾W,{/pM,V ͻedYnլkGJ@"4 5EmUmAohZ%KJHGx K=;DJEhBjS'q(Da$WRX̊)YnG9p:\mo33qo}$4Ks$NR YŖ87>6٨VL+hlpF)of*`.嚱ZgL{.~.gQȕ89.L>0r&bKa-9a WimaےI[`{ݐsBy7s^6-Y+)~b]ֻ3W"O)PzZeBCn$@7MTc! e'-r m603H{K :64GGZذ|ʜaMecwwnұdC`9O)R DjsNFeoj*u/NzrgxurENE.C=Z3NXT>dGImOGxy{79S1^~M"ݣc6_fN8brj0I> [$`6q.j3r!lm5ͪυ j0f6z}sׁi_WTo3D_tK[Α{/ea/|q&&m;4_6r~Ϯ2C 8wE2\vwtbbBB"Clv''pw'iqe^wowUf2<#e'Wxg\ZGFT364g=7MG3f\z;?xhĕ#4 5j)LaJ9v1AB(^1 _ *Vhu#zHMFJ- z&5!Z#&9z4YgOB|UPHoó3OP Oڤ0;S.b1UٙsiGFS^Gc* /f4I YJt%@%IKUdewV22ڈgYGNIn7FOƜasئ()i;pDjɉ Gy~X*s: z7 *:ڏGZZ\Hr^,k§R>Z 1:Ԋy(IڙJF*Q*+ZT;~:ڭZzʒފf8Wگ: ;Hc]ܚ [GiH59(6+ "7 Z: ;ocaml-book-1.0/en/html/book-ora011.gif0000644000000000000000000010536407452056110014264 0ustar GIF89a 3.(y[ӈŵ]H[aulh5]ɈhN]pyDDY=P}(W=D}−LR{3.H 9S7 &ŽlP99JNLD[ !Yfyl[uhWF=(Ct_ANUWauyJD5Nsy.P&3CphP9]s`yNjqw;;@U5A[ @__w5wYAcƵᗙLe{!S]j황ۙ]Q{"3bqjtbN779f]Yj*CpLa!5}1F !;(`wtՅyu[yj9Ljy"(9aF;.]jl뵬lP(!c, H*\ȰÇ#JŋG㜍!B(S’% -<% 4`xL-)chX-4oپ=uTQUH$YrgT['`|UN,t 2w|DJ(% (FL2MtSIT#-ǡ|5RukІ_oDem[! [H 蔎lXf_^h&I_J1 3R 4tY%VQjQ`95f 9kFf7:E)IptHtmuv a k9&GM$dkLM)eҗXRQ`ZnuH[+PZNicuE %IhiIVwB`&`qt(oUiwwM(]fHxZjnWゴ]U5HRSQzEm{&ʾ +kq]؟{iZ(go,hSh8h{8hQ]h̕6 kO12Z$?yFlmOUmZebUZ.Å6UH!آ^VDYK4sQD5na jf'P2 7{@N#GtopHgV҆ d_ Ig:sm$Ά>z\Jl}8]2Kyp&erYVf)xY6nE9@Yݣ!S~.㩒uafº0&4$*bɝV݄j9HCZpEZukdDsZ8S'ܚbZAK=CK̭|䌦T/ ܉wꧯT2"k@bBI(*bft!TXZXx08\[zJL=>l#LnYQ_-F XӴYXd Eo;,ɍ5eő,}+N2)t`! yqi5-rK7l zie&d >wb+7V'A@>p$[/TB\v57ŝoҗ3&Eɧ:USәzJ̫\J(`qI!  Opp=‚%^T"Sz4{rt1u #J2z8sn&FdN`avd_#`|k`hf8F->UltKƆgsW6?Rp!n@>fEu7+1gf/s' DZ9ucA3NvUv{⦪/`A6FLW7fjvأ'r%MAb`1poPU|n V(r` h0JP 6ĝ4qNWΦNjGh(Q828.2E^a3idji%*@Ȉr`wyvf0 `6@|UXz ye%J7:%\3k*Du3Yo rk;ZVfuC5:w ?0v@8r: Hh 0Wt М6wSzL9>&WR&eK4#ccT*".7gN[foh`)@%wQ 6uIR"0f`pOtf)4/EoDc湯>~NɍTU& ٪gB,8$F6ЏZ\poPWXP nPQеn;IuqȳX[#"[lKk;܈Qoc]VpFb[4"CvV@\pU0Um%IU)#/?Y_Ks"VebA.['|E]rRQ NTE+An2Kh ihpCn\%BiL0wP8-PE G]q%<}¡$R )})m',\{@#p?|nP"` #М؜qYK (^kOоXPjG!9}wpXlhJL NʽCAz$v4/e`q)],,Ca@YpEp]@,uǎrxn֌hUm/kI~t@?R, D٥]uw-S1S ,]N 9fT!kAx0 }XR}ܕPu,fږ/Ł(ߴUj ٣tOL{\VM0 3Ll)"*/$CJ.<ö ϲcJClA# ;㣄bh0,,p- 9`<[,ֈB$87jlPHFB T] ::-,()G>L `-i6("ct*خ7ٿ LYȼ|֬, 2e/;.װp :L/",@A(%T(a mP6 Pj Ƀ( 5ViMD j 5V8I;\qͽʲS|vg$ZSs-@q뜓Œ3аbAlB*8BG<mx"bj]S0ǝ *` lP@ГA7 O&6@CKP>SjF0IVsHH@ #BR3p`v .v{ᴘ" -i$@ Ls4K\5INLzs+KB ⰁtP 8\$թU@DBV~2`& P!tsC߈] #:$ 4 zDdKX9+dR<&lfJKc82s@a #gx  jxE%S KBa-u~R`$ęPDunX :ԤHnBz(ۉYifpV+KZ Ny^x*A5MuVK3\A|@ `p kPT8@$.\9MIh dkHـ/: >'S<+LPfC)f6 F*hfRK1di4"AI|\otF`4~ J#pA8$\~`mj*(HUEf ` p"z %Jd-U2A ,ua 85M@UzBl)ib{mes<22A)+s`~$ء Bj`:@`؃ka5tpW}%Bh kWȄ0Ȕ3׵@qpC >5t$ ([A3+Od첖 2{ <>Y$ S%%G0L xwTqӕLB7VeW%vѹ[xbM\3P$J">"spkf+Ǒw [ldV+sfRѦMr} ByKS;\`Q@ta%ȁ`?`qvAfUr(vXq=I7\eD3D9;@U~c44Je[i@]w[R/^cp0  %^p$9H* +ss8R]@ % o^cam6#f>L@tp"kVXd]SfP (je`rN;zk,cI#w`̡qC"aBp_vA⌈O5ٗmdR9l%rpаr]X26 "1 + 0o=r*Us/-(Y8/Z R; z"<=x0(8P+(`{!Zـ7 A0m!. H /%,X_88 %>)7 %C9Q f 2 .[ j^c ̣; Z% 40 ')9= 6%78X%HM)ඐ.P.4(Գ0? ?؉}q ؈ hs5:(;);f 1p882Ѧ!i #asXh%؃@ 8Pف,h} :4`%@[H 08X!`@!U%8 9 ; X :-Clp'2C.c[*bIkx%鳮$; P ؂O%1J6("WZ䍈j48 bᐍ **@>#I=̟cbəD_p\Ȁ; -p;*(|R+k+#?;! >0yꀞ3qëNPN!X 0@`yI?\Puip I, PP>`?$0#(Ё ?9Nh 9ԛ,xJv("X"q? ' NZ he27C2)srbA;L;h3!X 1z  @2@ ,5(.p3X78’P 0"04yUS%8ƾ)b 蜝].x+ y m zAxDds<@: (?(?pYH+3 X Ёa<.$.E88)B#/SJ;xIuY882.N@kE3Њ̂Ʊ0 =\ 6$p-`0,HF#p%( ?;),Ё[ZMU\*h %( H.3+s(,)Ax-В̒;kY3;ì92[8 0M)Xh.0 @?@yKЛ3UX͌JSDK׸+%岁7.YI)8IE67 @!td-TQ(2q=/ \1a9Ѓ .H2 17(-@ߐ )ˈ1? KR P : R;(;&>P H9 (p͆%4B:Wh +1 fS3x?A昐 ԛ)j&뼙$gG GGA/j/]b2 %(H/`x۰`k/(OR%xi`cP9A d.QY% 8ĵ.E,) Бn=5h0h\B`etA>e(+-,%?PPp%(x>( ;H@+\eV#uZ$f&~$l5(N!~yV_D &@DwZq#<.0 l);(5i |*("5hChxE ޢ9قž 5 XP|RJp%d@d__tA\[f2휦;܃`"Xq!9H3!4*%, S<  n;VH*H|MɈâeK̥5mAlVTbJ@ATwN()8+PxD1/6c: @@. ƻnłH5ۼ!SIx)sV;`_N̘A4e jmA #f =HYa& `?#$PxDEB^CH<N$ļk3 + =0'EXjCu,+d*Cy +`%X(U4ph;}Д ɼqX41۰DB7 yk:HǐS4@[)  Qd,#W|oP;z;zg|Ё( ZH" 5 X*y;XK 5aF`ƍ4#ba7Q6X XFb$`hG,]8tc 2B ?!QF&EiP?"3&VL,(Jv=H3e,rH*hjd) `dN2C7Kx1c̃% $E%Va!erAuT?a p! ra,`Yˀwd]XgulЙH!hFA RJ7 P#I qZIiGqMi5eVBh eUQpʪ 6Dv)PmtExa|.`C$t$ |lЧH 04Z ,Dq{`xdCiāfmH6sI2]qrB_y\w#]R9F)@(aq`0B|Qe7GjF9l!FRѧ5QFQ[0 Vم~B%le`G$ŏ$80 @%C$!bTFFa!?C΀!ypg0;@D:ȓFqb3N!(+0|=4؁ tp?8  (Jap;\ P4 "Xp !ؠ "0+?HA``RA,D%RpG1 A̰HL+ةpMG*ou HlC<*pb DB܄vp_3T;Ar  X@C "9\ )T6 CF|جf%;gI`@XA8JA\ 1p X 8`1x!A>c80n.` ! [A,P@F@hA "D"64;4T9W> ~EIhT@(p`AL[P2ag^T>1#pi: A H!a,EHedl2"Ma Ĵ',>"; IB7hL"/0@i78An 갠a"Dڇ 7{CX12 K)RpPFD٢6ybrJGk,"@= $ T l #`P N%-}JCZa ԍ`@' A`/۠ fp(`%!mЁ[#&h (:4 J@8I`?R Ff3H$AZ`;w- /kcսZj@LZ86p 3h` !,@^JN#W )y4rAb $t;ڧhk;!ABUK+hlNTBts]֞ 8o8@`BբO .Sv` 0Ãkg@P5ȡ9Ѥm> 6&Cʰٗ "2Te;2qL`4dCO4 ؈pHQ8HuT`L@L,Xm <G %tċA 2h X@nI8F̟$FT d@! `ME^t1JPZ Dx ̀<`ІāDAey؟lI= F`}]1DhPhtAb@De@<H^er!Vؕ)M`ɀ!8A|wtDA\K<ʼn8aI|]LlP1x~HbAE2cƆMĹ2.#BMA! Oatء|AP UYY4  R@ @@XYKld<$ H$m`\䴴AeaAt8A DL\I(ޤ@>#U5npǀD}i fll']@p KpnS DCb\d 0c/ xA D aAAH@L&NΧTfe># `Os@t!ԁQ$AWA$912lNIWJFX$f)AH ʔrf ` %rXe9U^lATh@B@ I0\~XKKX-rΪEs;Y T@.!m֚AX4Ktm @F@tAG ddMw![o3cuf0^t Sprb wk@RHA< ăفp@Ƕ\VL@c@7AU1Dx$16Jh$RiJF́>F H6kygcz'6zs6FpIMp*$nTLXUF@IFMXL@l$/k G6@F,iGNdnH pywMg#Si Dw,)MDPz  @@RAxi`K<@D4{JrNNP5%M莀(R TH @E`A[TuAk攎tx2Aqm, Alu|(HWDt@A ̉;Va@TYw/|inݽF&`3yn|' d)F~@wN^}BYD$@LliDp\AD@,H@A DlҢp/dudl1nFMŃd&-m`$,XNy'ԧ>@|h @- ⋎/$~3&DEC\1cǍ2@p@͖Jx(BNC%)aC%TyE X(Q VF0 Pba?~bRԏ6:BQ-@h6ڰFLR%Xv H`CuPf!B MqtML G 3gJ8LA`s"dF] 2*#b >Q#԰|n5Z"TvH`=]4#.* @,(#32#'6$"JA9%`` vsA7:Ȁ>x8;;FÃ6ԋ*.< )+;𰸂&,+ ?@ 6JPjS :C-J@2BH22{4Ã@L&TOCh B.xa8b%@+(a. @$";+ܰ=Ȝ>J(a 0H׆pr;*^p2tGA!I=J(" .816#8M= !Jp2(aֈ肃( J` 0 9(O @Z24bB/3 *.(8pì*2a-` >.Ɗ-+ %R 젻5;S1oTӃ4l!n jCr ) # 6Jࠃ,@/ p/X*p ,?F&iۥp Y J%޴!:~`Z3Bf2J1".Ex rU4+N8 b ͍b9Ƞ 2. F1AJ5((R{p+AYSx,+݁gP0bHC [nؖ$)h! l`5x3@Z >l4Lg)T[F3jP6 p A^`5@Iz Qj308[2$JXBA;we>@'$jޤIl N`6h_eD(@L\A*# 4ܠ g8(6QT؀ ^xPA9u Э Gx?(*ӊ 4'0@ i'6XnD[R)ڔ4dan8 9 ^#Pڃ3ZP B/=#tfCN$E^0CUI]0]b-BL$`FR FF2 -IZV'"X@Z_b=T*b@3Eh$0QC${ޓe d$d;шG IHB03>@(0o CH Fa*K,a<4A iA ?^JV;إP4vYI YK [Bd_gb#Hrj1 AxANJ5$,#5H3+@ 6B ppei%ʢpEr !*&L(](R/(A-\ʇ ,]&p.J@;t!@Vn-mY-_7]}2X0@8A F ƚ0CJ3p8baiT B ;tw%DJY9 ,@zjIH 0 rbP%XR@- fpn4_!/MU R9*ܡA D;!  %f Y pl`Dx$=@X@B`@a 4`@=_@6<I`T #Fm%@`1D߈lkH&D[20bѤFbަ ` :C!6 0 /,HjBL8- XJ( tȒPp|XB$$qBd (Y1k} =JG>w-6n:g I`< AL pܰ¼thb4D15݂He 0 2pj@`SkiV>X|[^@ZG0ZIT L < +$$vlBZ:C,"v>S *@@ l &d `ng . ԰@6h~ R¾- :Ѓ _hlR*VDH =` < (!G&@= &`ؚd< ]kƃM* * K GUoC. [O oN 2 >  @q x@ j` n v#@X@ZV,b.dǁҔlj< ZjⒾv=1 kB܀ d2#,am62?e#l >`l`@J@@ @ @6 n)# H< b p :M ( FOt<.)Zҥs$ JF 6 "C*D钤M jl`,@-"+ D+` `EEqZ/l{2a8)A%b @ R v bb( *'lD` +`( )  li~9P@wM;ժ`LfkI"b :`;d, +2i喫*,"v0di'3пYa!`ʀEI7+` D/\_5    ..]`bgEjI!Y lŤJؒ\fEv& -Gx̨ tF+8!TBYk@D` t հ+2 `@wT'zixyQABz |j2 J @ ʀ T^2M䪲* l@Ԋ./y Y IzI+b|MLJti,K Q]+, *6`4gJ|.#4IW) ^Kդ*GJIs $ -..9 & \c,سb "ibeJCx :>eRK+{Q/O)p `j`2gQB`z<#Z 1 @ Ā %@%@VpŔ  Ө$$oH[DGx#2`-Pj( XB @blԭ/lZfI ތ7+c"Xwe\5`5LHz9`.@=Dy<`?ڠp 揜cQ˕ F@U8$3*F yD+P g*o]SdlB fCZZ* TNjE0 @ ޠ`(X>@ ,.,hWZ=^=*iBxrl"G` (YB րa) E/A2Lf޹"esS@ Ԡ ` $@̻*`2`"%I&l޸`U1[x(Gc#.M$sv6Ng6< Ǭ a?F ڃ0p¬%@LC&A&XYe2 `Yb  A*It` .DaIؘxEC+,pG b:͆-C+49#`?F2FGXܡs0(i?JȡrXQ>Viæ,F&h @_t. @-~wo߹-%,c11c - FɃ%t @0! 5Sθbțt $@p"GNz5 /t;diwHd= w$eK` [<flϛF⌐mEIԵ(XqS_w]U`abIFad QlnB0d@QCfaE *Q 6AtnA 8R| Bs4)e/D xpG avqݕqJطMiAPOK1O#KXXЅ(z) ~q ea-(& R؂?la %G|Qntpf-Fh\qF 7peWrp=v$&/QG#H9Y>[iKdvge#NKY[AVPE [mxRr!"a'w&!V(d  q(a 01T! T|P Eqt @9ԝHRRМKVAQ]g{fwuaA_Pk $tYJ8Ev]}eM\"2L=vb>ؘ@A$Q|l/aJ,/|D%<0Wx1r,Y &->Q17I`EPsj XO}udgVT [RKٰbI~tkDz^2`珩hjb aPA@ GC# I04 EXA$`szb#8@:I `: x6 .P [ Յ!6KB̷/٩ &0d0 n)O1Jv% A fJ2@A@R`z(;$aGP#d.6D6B|e5ԡ 8Lb&x EBЅ#K!0+ `XxDZq4 (A יc.a c YdR l `htIO9е AEX ,`)\GJG̠NSjLWxnJ2!@ %1@ Sˆp x` @ *A, i=x`Nʊi S} =H>@p%?/R|Prw%NE65Z hb{ a q<7(\;L njсPta qx3g J ,UYPHt ePR)*u+bL“8` 0=W{A C`+-70/`6- 000ei i2?gaK^Qg#/YQid4qvux3G@&e"'zE7kaS/a65V%AtJP~w~ 5`(K pVpYp 0@J5]Q|^hE%sl^mkpKb/`@<C8z[F ˒Eb'ty([bW#''(&WPLlV5RE/X%]mTp6\~Gu 'O$JgPOf0X@2p\4 =PA%= #TxKeP8J!rh ,C0-%bg@MH8C{(~w bwPjvqF "pwb7q5`2eR.pVkjJv  %JUXI&YuMq@44pY4B`&7L]Sm*Xnc),i4#0'60\58xGG(N#[rg&gK`*]i0@H= kvP[g6.@rK5䊫'$& 0S#qItXc`r]89"YA6~`5`g6A(&n u1)+lqc$J愁S;6]$IkHh@#hnS\F!hh[r6PGNFq7;fQSBZf!&"ne`#8VD{99/@N0_k!8]nǞ Qy٥vTE@KFPTbE4r`@_ƧRamOM͖4X;] jF`A%(. !#E.bB[hGN8/C6.bB?esf@I yw8: `Fdn%&˓0XJ@|P F5`0Zjp+0'Y{AYYeOza1^'b{hPgtpEdnBI'Xгk[?G7!3lN ?u~;}kpX .`= 0/1ZP%QPf@:Q3q);þ8&qnȚLTZ[%5K3vyx5GTV56jAu@(IX;6`d"X~Q0NX "`d7god*wyht l@1:JqX k@jb,U;>A_iEEpT:\abJ]#.. Z&w7vign(P;TVPkq[ b (@Df:7*hA (jwvAP˺r5E p,`[J15"Q`"5S-rp/0C{0N1-R[U1bo+To[>+/KX}P`>]G;h6v8ʞ(+zk0-6|*%Q0/`#) pQ Pu!\J,!$fD$$5Ȗ`Mj(nB[ 6v0&%f%Pq#K@6faoG#=H8s5[4jpZP7!U@GvxpB9^Chu?`Xtq@$]dO%UmETbp25].%M3+$,GXV 13VL'+%vQ!@~!=.@i~VFr K*P(2qP@0@l0`E@;LRVIpKkǩC11wBfc#ֲ$A}{.b/6"AvwpmbK|Y`%i,@ܐxgxe'\pdpyytPBq0 k   Pt|sLOaXnp Se0@D5!6`P6`IrܽVD"N")o@]?-~,@~ sL1FP)q(K9 `_eP3|)z 41P 7bM4,-H5 H[^M&]n3JqL hW0t`EE'+ vTRdU5#1,=!s++P[9Bq{` `Rp.|x0[Pkfu zYU'mE ;RVЕF0*-%AL#VP8Ck 5poJX_/}PaT.oV,7YBGvt ,1A)j [ r6?P7#0(0!30pY[8К>6}arR.D<..#-5@}BN^\R[`&1Y` o[oҕa%dMb-sB-$tIp1fZ+8 _rp {)kk^r@w'KƷ.V4pz5}E]d^Xb I Za‹$qF(q͋X(r8%e %Lvŗ/-)s͙-dH c)Ǖ=6("`%%20"!~F % LYRD Wر#D;gn`(H,8~ȠM_+6b މ#`(`jʼl06$ ug!>XƂ%%]&ce+x(׍ِ!ySF H $Œ" =#&X$԰ ࣋PJX.5 6#ˊ)8 :T2<2%KĂ U5\SR7(PRI2ɍ 6?xalK 5` ΦX/ZC.-"8#"za.$j c=8j`k#GÆ T 9:0a%ⱏ`T, *!`;P!$Ja !t- Pp㮔 5oV^Tc kqB%x);=H"61 \@xa8X D`A7ܠX*Ƃ1XH5@S?SV/@.@B=&͊,8PVm*0QP"QV5# NcV>2jdvJ 5 ~7PW;pÃ"H+  !/AЂ6dh! $-_Kx_2TxA̠ 8%BoTA K`0A[І#CAtS ^݌@JkNv-h \cHB DP ؂8T}H8ؼ 5P ,@ aC"C- }K/_r.` ? p0%dm]d‰ &!A Z8`5 P3H{<J5a v@` Mx <vj0 @/@fP>hAn#/   M"p@c NL:cC/gQdt3rޥ PɯP 4|Dž}WfbӛK;5pA .UxEC \jIX@H v|Xh[?.h*~Zb@#cCP58 B>0#M /s*݀2$@A~~/i4B90p;ls j0ݑ8g^Đ`QŖOG 4.`/Q_@$ @["Cݐcd! xH @/H{f B9(F4 :.,y/Ъlv8Fvuy-pJ *Op 4BV:Ls h+񛂋c3Tyi(H .J0`AZO2v!ᨭ0sg($ܱ/`ld ~ 1H5/(XC,A_{1J !J @Y^@T:Xh0@0.J"kuܒ 㲦`@1(6Ay(/|0kZ":x9;`] 1ᑏ@2y"ף,x.iÚ ix 1((v% h0 )3Hw5 P>1+Hp+[ <x.[B  !т!?6C Y K7 "# qʐc0*v+vH 肥#!"Ȁ0i;a @1p*/H 84 'X]%C‚[˰c` 392H9A9(Ǻ ð -?=@`( !2)j 2).療6 X/ xEh 8+R X2ƾxE[B>Ю0 *18cO0tC̐ FiI2=nD%(ljJ83t.Ѝ& C 8 L3183 7@#9X+<>x06K#X{9:$BdCZ%84`XŒM% Z(% S =#Y%pN N@`3  x +:+E,3`Li:c;@{K1-+0p6L‚+X%c2## 91X*B>H*]+8xF . 4*C!3Ȱ Pe X%@ -ѩ5@-H$Xr O-9_[7;+$=0CCI4̂/C3$E%@ D%:  ݼb"16%6: L )@.`,(:41%X,X* S 7HsVK*اX+ɾY5 ЂP %ֱ?c (;i$ X` (;dutkA/:@)D% $#(:yE%0H#(=Jy6 L(Đ?*971TC+̾`ăLd[ 1B89Lf;  Zِ䘕D>’({f4': (-8D vN1A%ț eQ (c/Lj?Hx#9ۉHؚ9>p H$` ]\O1<.(1S23' a1G (o $ꙅl"0B|W1h曂 HxA 868% y?'HQ*ء2 ؾ%ޓ|t#%[91 p%N )p (n5JI0L86@2 MD:w%(h>9(.&*p72+pO+D*D1#+H9"&dXC  >V5p$+(J$8 @0` .X6-3hI x=c cڽ`N-ĮR=P2QOy a1C%>TAEh9 ߌ \],%,;X0Sfc 0+#E%!ݠ HHY V5h+*==fkf>c83(:U  i#Āp'nc\5 yOVR]"X!. 1H\2e\1"?@U06o\;?f@;ik|{.2x%a%O0V! vZL(u /}2<#˫5@.Ur U 듮b@;)փiȀjN>1`5X(*h"(y0%[p,7,> !{tZL僐m.#shr$9`K`@EI{".Wcw i==Hjc8 X;؂:4d$H&[?`Y%.k2O>& I%Q&H`Lu6H.Pق-*vd.Uڈ.h ċ `ql~l#G>J-H h#?DpY5BBB[Q=Zl`j\^,ЖE*EݱhK~4IYK~k6=փ?c@#+K%:%`W?I~'>)u8$Rx%S%X 9xnes_q(h[2i'88^_>'Mb= _REBf!(*hK6ZPGxr1b4b%ʉbXh,0EI8|X XІ#&R`Fg:0c6Wlٳe%~\E  çJ'XƊ%lTj}&J̸$Ʌ-q tD>KXx EI^BnD #0,i %1sBI](S6 [sW3aŒ-[VlfR!ΛbAŀ/ndi#MXZFnY+u& mFo`4XH!L҇4D3դ! ,DH-O6u!QYG4q0OVLw]yduWzUt7PZaEm^!oLFB*єw9& yv" ~4aB~Q(K,Pmah(j,F@lO*!5M]L 1\wו"dl]vl!{a Wa0@Ka3 o b! mpFZjA x!,t|q.f"zGFHPd00PJuE LB),Xf@dw;YQߠo!ǙZ!!boqwHJAcI&1SkMWEm! 6k VJuC,,5@,$a,,(Kǐ]@q0% 7<!YB($!c-+?Diˎg`A^Da~pB#.pU&ƫw`*J#.Dq Ib[ 0U ZBb$af YT tHgEUAX?Q[ m%*>DA  #&*KMNwQb LX3 U5 0Xk4"AτP7 I0 n %nX[PpϝiXX6(H#r`\ PI ,p-8@<҃tʼn,@hKToB`{!# 80' hcD}ߵ@X(d D©_" FAB&!xTR` ǐO LS$EqAX 1i=-Ȁё|I![-I}Bw"$}5bŇ>lojm blP-PA! ; `0 W8=@4K`?!,Uh Isb'I\GKe(&QXt3aa;?6jjI@R_*DQB8$6 q,jYb l i@@[@$X, Zu\,A^,AWF6.A@&@j!͠ }yh6@ QEp}ґA5 lAE@ 1 >Ç80!L(P` dXbE@: .DFm΀5Q6Mt A&=xXS5) "PQl!THx&)r4/pHT}V3A`41*l ?X%$ 8 $Alޭ.h#@˂() fE uu3KohK Ő76.l 1i̠|He/ ؿlk,NtV) |ALxAB!  󜇫:< o#>_<_;ocaml-book-1.0/en/html/book-ora025.gif0000644000000000000000000000652107452056111014265 0ustar GIF89aLUUU@@@!,LI8ͻ`(dihlp,tmx|0|rl:ШtJZجvzLuznA~Ͽ+rvc-uJEeIŸ+cٜsE,+H_;@o qB2zX:q€\2rdȱ͛^ÈE*q Lg@9Hk]AJJOÆsZzA3:E!hӪ]˶g)fa^kjv j {YŐq2FɳĹϠCMӨS^ͺue.\յ۸sͻwi͘0~ɷȓ+_.8$PfN ,fmK-OS99iRc/G':9I^{XIWwg XU R 2"ဟP} ሉlؠ;}ȝ$h(y,8⋓u'([00"S8#CH aTYܑ<(z5UBIQ^J Njp^&{vc%~^m9՛X0bW`_Mو^ڗ睋v%i)%0֙اaQZ騨Ns*rD!jPi:+k0ۣ?擢^v~+ގhm)m&cmo4-e.R-J":6pOu{H"bTĩ. BHq"WP#<ÏVlr?+#qL  /lHμ6Qυ͚Q2b4FgKt%/k;!-1@/[5Yû]/56RW.Fzuܮ/ՊȪ7~y{7 8}8 7W7Qg.>G甇8;%Ӊ^;4d>jiﬓ ?i&?Ou#yKrybD;}M\k}GO7Lّ=;b$(Gir,<&q=<وDǛGwMPN^3U)!JH602[G&O# Q 2,p1%H@bbl0x♡#x %cLj#"B0=\H2 8u6pG GZ5z8,KH0kI@R'~HHP @`q|庄(-|kqRz hy*[ ȃ1Iy,md&/[6hJ$M]e MKSh5ˉd֑t:iN ސO\ uk]׍fu|\cvS;*] kZs9FV5R/{Xj\ZKˆVMzږLr=dڄs-ikUug=n;:,;Pl 6 ݴJԭk;j},p~vh>"{m wab&/݌;8*rL_N k*E./<+6ƄZچ8#)9#Ns] ׬4Kƿ]d y(yo(mw\Xʭxelh2_ d^ό l~Ɨ83cgxwNsYve@s63m%~nUGSxq\sEL77Ȓ]a7IIN 6r+mfV c`\k[UK-xؿ.v^\̫n f6+`@= XS,,0`lJ{n Ù0;ў{P{S@W=9QOޭ@Y`x?'!n8(.=^דx4 w;^ӓ_}Cy7R>^OO_k?n1op[dɗ++dL ;*=%wez݂?ISy}zk&WG}y74#~|wgywm~v(ry &f 3|xg!8jp'hQw )(k+wvshlW{Xx؃lj(6w8pxp(poňonXn˨m(mlшl8lkhkjHjݸiihhhhghggfxf(fee(edd8dccIcbyb b a yaa`y` `_9_^^I^!]#]%Y]' ])\+)\-[/9sH1qmH35ir; =r?C9EI7Yp9ɓGIpOOpGaהy8Wȕ}(Hh(NyK'TvyhYPZxٗ~S[XY f1s٘uGNVY,TsY:EGtIƙVD iuYH 󚪹KMPNV& MӛV:GDI%"SyQI0ؙyYѝu@y虞깞i* 9Yy;ocaml-book-1.0/en/html/book-ora144.html0000644000000000000000000004476007453055400014476 0ustar Subtyping and Inclusion Polymorphism Previous Contents Next

Subtyping and Inclusion Polymorphism

Subtyping makes it possible for an object of some type to be considered and used as an object of another type. An object type ot2 could be a subtype of ot1 if:
  1. it includes all of the methods of ot1,
  2. each method of ot2 that is a method of ot1 is a subtype of the ot1 method.
The subtype relation is only meaningful between objects: it can only be expressed between objects. Furthermore, the subtype relation must always be explicit. It is possible to indicate either that a type is a subtype of another, or that an object has to be considered as an object of a super type.

Syntax


(name : sub_type :> super_type)
(name :> super_type)

Example

Thus we can indicate that an instance of colored_point can be used as an instance of point:

# let pc = new colored_point (4,5) "white";;
val pc : colored_point = <obj>
# let p1 = (pc : colored_point :> point);;
val p1 : point = <obj>
# let p2 = (pc :> point);;
val p2 : point = <obj>


Although known as a point, p1 is nevertheless a colored point, and sending the method to_string will trigger the method relevant for colored points:

# p1#to_string();;
- : string = "( 4, 5) with color white"


This way, it is possible to build lists containing both points and colored points:

# let l = [new point (1,2) ; p1] ;;
val l : point list = [<obj>; <obj>]
# List.iter (fun x -> x#print(); print_newline()) l;;
( 1, 2)
( 4, 5) with color white
- : unit = ()


Of course, the actions that can be performed on the objects of such a list are restricted to those allowed for points.

# p1#get_color () ;;
Characters 1-3:
This expression has type point
It has no method get_color


The combination of delayed binding and subtyping provides a new form of polymorphism: inclusion polymorphism. This is the ability to handle values of any type having a subtype relation with the expected type. Although static typing information guarantees that sending a message will always find the corresponding method, the behavior of the method depends on the actual receiving object.

Subtyping is not Inheritance

Unlike mainstream object-oriented languages such as C++, Java, and SmallTalk, subtyping and inheritance are different concepts in Objective CAML. There are two main reasons for this.
  1. Instances of the class c2 may have a type that is a subtype of the object type c1 even if the class c2 does not inherit from the class c1. Indeed, the class colored_point can be defined independently from the class point, provided the type of its instances are constrained to the object type point.
  2. Class c2 may inherit from the class c1 but have instances whose type is not a subtype of the object type c1. This is illustrated in the following example, which uses the ability to define an abstract method that takes an as yet undetermined instance as an argument of the class being defined. In our example, this is method eq of class equal.

# class virtual equal () =
object(self:'a)
method virtual eq : 'a -> bool
end;;
class virtual equal : unit -> object ('a) method virtual eq : 'a -> bool end
# class c1 (x0:int) =
object(self)
inherit equal ()
val x = x0
method get_x = x
method eq o = (self#get_x = o#get_x)
end;;
class c1 :
int ->
object ('a) val x : int method eq : 'a -> bool method get_x : int end
# class c2 (x0:int) (y0:int) =
object(self)
inherit equal ()
inherit c1 x0
val y = y0
method get_y = y
method eq o = (self#get_x = o#get_x) && (self#get_y = o#get_y)
end;;
class c2 :
int ->
int ->
object ('a)
val x : int
val y : int
method eq : 'a -> bool
method get_x : int
method get_y : int
end


We cannot force the type of an instance of c2 to be the type of instances of c1:

# let a = ((new c2 0 0) :> c1) ;;
Characters 11-21:
This expression cannot be coerced to type
c1 = < eq : c1 -> bool; get_x : int >;
it has type c2 = < eq : c2 -> bool; get_x : int; get_y : int >
but is here used with type < eq : c1 -> bool; get_x : int; get_y : int >
Type c2 = < eq : c2 -> bool; get_x : int; get_y : int >
is not compatible with type c1 = < eq : c1 -> bool; get_x : int >
Only the first object type has a method get_y


Types c1 and c2 are incompatible because the type of eq in c2 is not a subtype of the type of eq in c1. To see why this is true, let o1 be an instance of c1. If o21 were an instance of c2 subtyped to c1, then since o21 and o1 would both be of type c1 the type of eq in c2 would be a subtype of the type of eq in c1 and the expression o21#eq(o1) would be correctly typed. But at run-time, since o21 is an instance of class c2, the method eq of c2 would be triggered. But this method would try to send the message get_y to o1, which does not have such a method; our type system would have failed!

For our type system to fulfill its role, the subtyping relation must be defined less navely. We do this in the next paragraph.

Formalization

Subtyping between objects.
Let t=<m1:t1; ... mn: tn> and t'=<m1:s1 ; ... ; mn:sn; mn+1:sn+1; etc...> we shall say that t' is a subtype of t, denoted by t' t, if and only if si ti for i {1,...,n}.

Function call.
If f : t s, and if a:t' and t' t then (f a) is well typed, and has type s.

Intuitively, a function f expecting an argument of type t may safely receive `an argument of a subtype t' of t.

Subtyping of functional types.
Type t' s' is a subtype of t s, denoted by t' s' t s, if and only if
s' s and t t'
The relation s' s is called covariance, and the relation t t' is called contravariance. Although surprising at first, this relation between functional types can easily be justified in the context of object-oriented programs with dynamic binding.

Let us assume that two classes c1 and c2 both have a method m. Method m has type t1 s1 in c1, and type t2 s2 in c2. For the sake of readability, let us denote by m(1) the method m of c1 and m(2) that of c2. Finally, let us assume c2 c1, i.e. t2 s2 t1 s1, and let us look at a simple example of the covariance and contravariance relations.

Let g : s1 a, and h (o:c1) (x:t1) = g(o#m(x))

Covariance:
function h expects an object of type c1 as its first argument. Since c2 c1, it is legal to pass it an object of type c2. Then the method invoked by o#m(x) is m(2), which returns a value of type s2. Since this value is passed to g which expects an argument of type s1, clearly we must have s2 s1.
Contravariance:
for its second argument, h requires a value of type t1. If, as above, we give h a first argument of type c2, then method m(2) is invoked. Since it expects an argument of type t2, t1 t2.

Inclusion Polymorphism

By ``polymorphism'' we mean the ability to apply a function to arguments of any ``shape'' (type), or to send a message to objects of various shapes.

In the context of the functional/imperative kernel of the language, we have already seen parameterized polymorphism, which enables you to apply a function to arguments of arbitrary type. The polymorphic parameters of the function have types containing type variables. A polymorphic function will execute the same code for various types of parameters. To this end, it will not depend on the structure of these arguments.

The subtyping relation, used in conjunction with delayed binding, introduces a new kind of polymorphism for methods: inclusion polymorphism. It lets the same message be sent to instances of different types, provided they have been constrained to the same subtype. Let us construct a list of points where some of them are in fact colored points treated as points. Sending the same message to all of them triggers the execution of different methods, depending on the class of the receiving instance. This is called inclusion polymorphism because it allows messages from class c, to be sent to any instance of class sc that is a subtype of c (sc :> c) that has been constrained to c. Thus we obtain a polymorphic message passing for all classes of the tree of subtypes of c. Contrary to parameterized polymorphism, the code which is executed may be different for these instances.

Thanks to parameterized classes, both forms of polymorphism can be used together.

Equality between Objects

Now we can explain the somewhat surprising behavior of structural equality between objects which was presented on page ??. Two objects are structurally equal when they are physically the same.

# let p1 = new point (1,2);;
val p1 : point = <obj>
# p1 = new point (1,2);;
- : bool = false
# p1 = p1;;
- : bool = true


This comes from the subtyping relation. Indeed we can try to compare an instance o2 of a class sc that is a subtype of c, constrained to c, with an instance of o1 from class c. If the fields which are common to these two instances are equal, then these objects might be considered as equal. This is wrong from a structural point of view because o2 could have additional fields. Therefore Objective CAML considers that two objects are structurally different when they are physically different.


# let pc1 = new colored_point (1,2) "red";;
val pc1 : colored_point = <obj>
# let q = (pc1 :> point);;
val q : point = <obj>
# p1 = q;;
- : bool = false
This restrictive view of equality guarantees that an answer true is not wrong, but an answer false guarantees nothing.


Previous Contents Next ocaml-book-1.0/en/html/book-ora041.html0000644000000000000000000014144507453055377014505 0ustar Exercises Previous Contents Next

Exercises

Binary Trees

We represent binary trees in the form of vectors. If a tree a has height h, then the length of the vector will be 2(h+1)-1. If a node has position i, then the left subtree of this node lies in the interval of indices [i+1 , i+1+2h], and its right subtree lies in the interval [i+1+2h+1 , 2(h+1)-1]. This representation is useful when the tree is almost completely filled. The type 'a of labels for nodes in the tree is assumed to contain a special value indicating that the node does not exist. Thus, we represent labeled trees by the by vectors of type 'a array.

  1. Write a function , taking as input a binary tree of type 'a bin_tree

    # let fill_array tree tab empty =
    let rec aux i p = function
    Empty -> tab.(i) <- empty
    | Node (l,e,r) ->
    tab.(i) <- e ;
    aux (i+1) (p/2) l ;
    aux (i+p) (p/2) r
    in aux 0 (((Array.length tab)+1)/2) tree ;;
    val fill_array : 'a bin_tree -> 'a array -> 'a -> unit = <fun>

    # type 'a bin_tree =
    Empty
    | Node of 'a bin_tree * 'a * 'a bin_tree ;;
    type 'a bin_tree = | Empty | Node of 'a bin_tree * 'a * 'a bin_tree
    (defined on page ??) and an array (which one assumes to be large enough). The function stores the labels contained in the tree in the array, located according to the discipline described above.

  2. Write a function to create a leaf (tree of height 0).

    # let leaf empty = [| empty |] ;;
    val leaf : 'a -> 'a array = <fun>


  3. Write a function to construct a new tree from a label and two other trees.

    # let node elt left right =
    let ll = Array.length left and lr = Array.length right in
    let l = max ll lr in
    let res = Array.create (2*l+1) elt in
    Array.blit left 0 res 1 ll ;
    Array.blit right 0 res (ll+1) lr ;
    res ;;
    val node : 'a -> 'a array -> 'a array -> 'a array = <fun>


  4. Write a conversion function from the type 'a bin_tree to an array.

    # let rec make_array empty = function
    Empty -> leaf empty
    | Node (l,e,r) -> node e (make_array empty l) (make_array empty r) ;;
    val make_array : 'a -> 'a bin_tree -> 'a array = <fun>


  5. Define an infix traversal function for these trees.

    # let infix tab empty f =
    let rec aux i p =
    if tab.(i)<>empty then ( aux (i+1) (p/2) ; f tab.(i) ; aux (i+p) (p/2) )
    in aux 0 (((Array.length tab)+1)/2) ;;
    val infix : 'a array -> 'a -> ('a -> 'b) -> unit = <fun>


  6. Use it to display the tree.

    # let print_tab_int tab empty =
    infix tab empty (fun x -> print_int x ; print_string " - ") ;;
    val print_tab_int : int array -> int -> unit = <fun>


  7. What can you say about prefix traversal of these trees? Prefix traversal of the tree corresponds to left-to-right traversal of the array.

    # let prefix tab empty f =
    for i=0 to (Array.length tab)-1 do if tab.(i)<>empty then f tab.(i) done ;;
    val prefix : 'a array -> 'a -> ('a -> unit) -> unit = <fun>

Spelling Corrector

The exercise uses the lexical tree , from the exercise of chapter 2, page ??, to build a spelling corrector.

  1. Construct a dictionary from a file in ASCII in which each line contains one word. For this, one will write a function which takes a file name as argument and returns the corresponding dictionary.

    # type noeud_lex = Lettre of char * bool * arbre_lex
    and arbre_lex = noeud_lex list;;
    type noeud_lex = | Lettre of char * bool * arbre_lex
    type arbre_lex = noeud_lex list
    # type mot = string;;
    type mot = string
    # let rec existe m d =
    let aux sm i n =
    match d with
    [] -> false
    | (Lettre (c,b,l))::q ->
    if c = sm.[i] then
    if n = 1 then b
    else existe (String.sub sm (i+1) (n-1)) l
    else existe sm q
    in aux m 0 (String.length m);;
    val existe : string -> arbre_lex -> bool = <fun>

    # let rec ajoute m d =
    let aux sm i n =
    if n = 0 then d else
    match d with
    [] -> [Lettre (sm.[i], n = 1, ajoute (String.sub sm (i+1) (n-1)) [])]
    | (Lettre(c,b,l))::q ->
    if c = sm.[i] then
    if n = 1 then (Lettre(c,true,l))::q
    else Lettre(c,b,ajoute (String.sub sm (i+1) (n-1)) l)::q
    else (Lettre(c,b,l))::(ajoute sm q)
    in aux m 0 (String.length m);;
    val ajoute : string -> arbre_lex -> arbre_lex = <fun>

    # let rec verifie l d = match l with
    [] -> []
    | t::q -> if existe t d then t::(verifie q d)
    else verifie q d
    ;;
    val verifie : string list -> arbre_lex -> string list = <fun>

    # let string_of_char c = String.make 1 c;;
    val string_of_char : char -> string = <fun>

    # let rec filter p l = match l with
    [] -> []
    | t::q -> if p t then t::(filter p q)
    else filter p q;;
    val filter : ('a -> bool) -> 'a list -> 'a list = <fun>


    # let rec selecte n d =
    match d with
    [] -> []
    | (Lettre(c,b,l))::q ->
    if n = 1 then
    filter (function x -> x <> "!")
    (List.map (function (Lettre(c,b,_)) -> if b then string_of_char c else "!") d)
    else
    let r = selecte (n-1) l
    and r2 = selecte n q in
    let pr = List.map (function s -> (string_of_char c)^s) r
    in pr@r2;;
    val selecte : int -> arbre_lex -> string list = <fun>

    # let lire_fichier nom_fichier =
    let dico = ref []
    and canal = open_in nom_fichier in
    try
    while true do dico := ajoute (input_line canal) !dico done ;
    failwith "cas impossible"
    with
    End_of_file -> close_in canal ; !dico
    | x -> close_in canal ; raise x ;;
    val lire_fichier : string -> arbre_lex = <fun>


  2. Write a function words that takes a character string and constructs the list of words in this string. The word separators are space, tab, apostrophe, and quotation marks.

    # let mots s =
    let est_sep = function ' '|'\t'|'\''|'"' -> true | _ -> false in
    let res = ref [] and p = ref ((String.length s)-1) in
    let n = ref !p in
    while !p>=0 && est_sep s.[!p] do decr p done ;
    n := !p ;
    while (!n>=0) do
    while !n>=0 && not (est_sep s.[!n]) do decr n done ;
    res := String.sub s ( !n +1) (!p - !n) :: !res ;
    while !n>=0 && est_sep s.[!n] do decr n done ;
    p := !n
    done ;
    !res ;;
    val mots : string -> string list = <fun>


  3. Write a function verify that takes a dictionary and a list of words, and returns the list of words that do not occur in the dictionary.

    # let rec verifie dico = function
    [] -> []
    | m::l -> if existe m dico then verifie dico l else m::(verifie dico l) ;;
    val verifie : arbre_lex -> string list -> string list = <fun>


  4. Write a function occurrences that takes a list of words and returns a list of pairs associating each word with the number of its occurrences.

    # let rec ajoute x = function
    [] -> [(x,1)]
    | ((y,n) as p)::l -> if x=y then (y,n+1)::l else p::(ajoute x l) ;;
    val ajoute : 'a -> ('a * int) list -> ('a * int) list = <fun>

    # let rec ajoute_liste ld = function
    [] -> ld
    | n::l -> let res = ajoute_liste ld l in ajoute n res ;;
    val ajoute_liste : ('a * int) list -> 'a list -> ('a * int) list = <fun>

    # let occurences l = ajoute_liste [] l ;;
    val occurences : 'a list -> ('a * int) list = <fun>


  5. Write a function spellcheck that takes a dictionary and the name of a file containing the text to analyze. It should return the list of incorrect words, together with their number of occurrences.

    # let orthographe dico nom =
    let f = open_in nom and res = ref [] in
    try
    while true do
    let s = input_line f in
    let ls = mots s in
    let lv = verifie dico ls in
    res := ajoute_liste !res lv
    done ;
    failwith "cas impossible"
    with
    End_of_file -> close_in f ; !res
    | x -> close_in f ; raise x ;;
    val orthographe : arbre_lex -> string -> (string * int) list = <fun>

Set of Prime Numbers

We would like now to construct the infinite set of prime numbers (without calculating it completely) using lazy data structures.

  1. Define the predicate divisible which takes an integer and an initial list of prime numbers, and determines whether the number is divisible by one of the integers on the list. On sait que si un nombre x possde un diviseur suprieur x alors il en possde infrieur x. Nous nous contentons donc de ne tester que les lments de la liste qui sont infrieurs la racine carre de l'argument.

    # let rec est_divisible x = function
    [] -> false
    | n::l -> (x mod n)=0 || ( (n*n<=x) && (est_divisible x l)) ;;
    val est_divisible : int -> int list -> bool = <fun>


  2. Given an initial list of prime numbers, write the function next that returns the smallest number not on the list. On calcule le dernier lment de la liste.

    # let rec dernier = function
    [] -> failwith "liste vide"
    | [x] -> x
    | _::l -> dernier l ;;
    val dernier : 'a list -> 'a = <fun>
    On cherche le premier nombre premier partir d'un certain entier en les testant de deux en deux.

    # let rec plus_petit_premier l n =
    if est_divisible n l then plus_petit_premier l (n+2) else n ;;
    val plus_petit_premier : int list -> int -> int = <fun>
    Et on assemble.

    # let suivant = function
    [] -> 2
    | [2] -> 3
    | l -> let pg = dernier l in plus_petit_premier l (pg+2) ;;
    val suivant : int list -> int = <fun>


  3. Define the value setprime representing the set of prime numbers, in the style of the type 'a enum on page ??. It will be useful for this set to retain the integers already found to be prime.

    # type 'a ens = {mutable i:'a ; f : 'a -> 'a } ;;
    type 'a ens = { mutable i: 'a; f: 'a -> 'a }
    # let next e = let x = e.i in e.i <- (e.f e.i) ; x ;;
    val next : 'a ens -> 'a = <fun>

    # let ensprem =
    let prec = ref [2] in
    let fonct _ = let n = suivant !prec in prec := !prec @ [n] ; n
    in { i = 2 ; f = fonct } ;;
    val ensprem : int ens = {i=2; f=<fun>}

Previous Contents Next ocaml-book-1.0/en/html/book-ora022.html0000644000000000000000000000700507453055377014475 0ustar To learn more Previous Contents Next

To learn more

The computation model for functional languages is l-calculus, which was invented by Alonzo Church in 1932. Church's goal was to define a notion of effective computability through the medium of l-definability. Later, it became apparent that the notion thus introduced was equivalent to the notions of computability in the sense of Turing (Turing machine) and Gdel-Herbrand (recursive functions). This concidence leads one to think that there exists a universal notion of computability, independent of particular formalisms: this is Church's thesis. In this calculus, the only two constructions are abstraction and application. Data structures (integers, booleans, pairs, ...) can be coded by l-termes.

Functional languages, of which the first representative was Lisp, implement this model and extend it mainly with more efficient data structures. For the sake of efficiency, the first functional languages implemented physical modifications of memory, which among other things forced the evaluation strategy to be immediate, or strict, evaluation. In this strategy, the arguments of functions are evaluated before being passed to the function. It is in fact later, for other languages such as Miranda, Haskell, or LML, that the strategy of delayed (lazy, or call-by-need) evaluation was implemented for pure functional languages.

Static typing, with type inference, was promoted by the ML family at the start of the 80's. The web page

Link


http://www.pps.jussieu.fr/~cousinea/Caml/caml_history.html
presents a historical overview of the ML language. Its computation model is typed l-calculus, a subset of l-calculus. It guarantees that no type error will occur during program execution. Nevertheless ``completely correct'' programs can be rejected by ML's type system. These cases seldom arise and these programs can always be rewritten in such a way as to conform to the type system.

The two most-used functional languages are Lisp and ML, representatives of impure functional languages. To deepen the functional approach to programming, the books [ASS96] and [CM98] each present a general programming course using the languages Scheme (a dialect of Lisp) and Caml-Light, respectively.




Previous Contents Next ocaml-book-1.0/en/html/book-ora012.gif0000644000000000000000000000205207452056110014253 0ustar GIF89aj111bbbPPP@@@! ,j0I8ͻ dihl Cmx|p ȤrIZOtzz6R<>8ι@RxAw? i~>rq"z o Qd=^z e5`fGweU[sj|SNʨ?M55hɦ˩i"ෛ_@ۋDb'A3BBไ s/ߵIRD1!F0B٢G\ɲ˗0cʜIŀɳϟ/o^D ѣHmOgҧPk SΨXZ `rէLسH\O+u)N kʽڥ7U8r}\6Ƀ Cz.e~Zt残v;궭>;l9֚~,b%y' yR膥״~jZquC9u<@Abgȯr'gK_A]b6Ǡz uCi4U;K)Xf=EJW a h|auy"!{1@ 2Ea5E&%I"1ݔGXY]8be)&$&hc&O_Z&hIĜtVx&s}g k4蝉ulm(^Zuiib8nUIJsg뭶e典]ci†aN,@F[P%.%-.ƭ &4kk.DLTx{n;ʾ L-3o[n@åoeRK册@|-$^} r)o3'd2wAR"pK1ۼ9l׾s;l2-)\m`mvW}P!p7tmx;ocaml-book-1.0/en/html/book-ora124.html0000644000000000000000000054117207453055400014473 0ustar Constructing a Graphical Interface Previous Contents Next

Constructing a Graphical Interface

The implementation of a graphical interface for a program is a tedious job if the tools at your disposal are not powerful enough, as this is the case with the Graphics library. The user-friendliness of a program derives in part from its interface. To ease the task of creating a graphical interface we will start by creating a new library called Awi which sits on top of Graphics and then we will use it as a simple module to help us construct the interface for an application.

This graphical interface manipulates components. A component is a region of the main window which can be displayed in a certain graphical context and can handle events that are sent to it. There are basically two kinds of components: simple components, such as a confirmation button or a text entry field, and containers which allow other components to be placed within them. A component can only be attached to a single container. Thus the interface of an application is built as a tree whose root corresponds to the main container (the graphics window), the nodes are also containers and the leaves are simple components or empty containers. This treelike structure helps us to propagate events arising from user interaction. If a container receives an event it checks whether one of its children can handle it, if so then it sends the event to that child, otherwise it deals with the event using its own handler.

The component is the essential element in this library. We define it as a record which contains details of size, a graphic context, the parent and child components along with functions for display and for handling events. Containers include a function for displaying their components. To define the component type, we build the types for the graphics context, for events and for initialization options. A graphical context is used to contain the details of ``graphical styles'' such as the colors of the background and foreground, the size of the characters, the current location of the component and the fonts that have been chosen. Then must we define the kinds of events which can be sent to the component. These are more varied than those in the Graphics library on which they are based. We include a simple option mechanism which helps us to configure graphics contexts or components. One implementation difficulty arises in positioning components within a container.

The general event handling loop receives physical events from the input function of the Graphics library, decides whether other events should be generated as a result of these physical events, and then sends them to the root container. We shall consider the following components: text display, buttons, list boxes, input regions and enriched components. Next we will show how the components are assembled to construct graphical interfaces, illustrating this with a program to convert between Francs and Euros. The various components of this application communicate with each other over a shared piece of state.

Graphics Context, Events and Options

Let's start by defining the base types along with the functions to initialize and modify graphics contexts, events and options. There is also an option type to help us parametrize the functions which create graphical objects.

Graphics Context

The graphics context allows us to keep track of the foreground and background colors, the font, its size, the current cursor position, and line width. This results in the following type.


type g_context = {
mutable bcol : Graphics.color;
mutable fcol : Graphics.color;
mutable font : string;
mutable font_size : int;
mutable lw : int;
mutable x : int;
mutable y : int };;


The make_default_context function creates a new graphics context containing default values 1.


# let default_font = "fixed"
let default_font_size = 12
let make_default_context () =
{ bcol = Graphics.white; fcol = Graphics.black;
font = default_font;
font_size = default_font_size;
lw = 1;
x = 0; y = 0;};;
val default_font : string = "fixed"
val default_font_size : int = 12
val make_default_context : unit -> g_context = <fun>


Access functions for the individual fields allow us to retrieve their values without knowing the implementation of the type itself.


# let get_gc_bcol gc = gc.bcol
let get_gc_fcol gc = gc.fcol
let get_gc_font gc = gc.font
let get_gc_font_size gc = gc.font_size
let get_gc_lw gc = gc.lw
let get_gc_cur gc = (gc.x,gc.y);;
val get_gc_bcol : g_context -> Graphics.color = <fun>
val get_gc_fcol : g_context -> Graphics.color = <fun>
val get_gc_font : g_context -> string = <fun>
val get_gc_font_size : g_context -> int = <fun>
val get_gc_lw : g_context -> int = <fun>
val get_gc_cur : g_context -> int * int = <fun>


The functions to modify those fields work on the same principle.


# let set_gc_bcol gc c = gc.bcol <- c
let set_gc_fcol gc c = gc.fcol <- c
let set_gc_font gc f = gc.font <- f
let set_gc_font_size gc s = gc.font_size <- s
let set_gc_lw gc i = gc.lw <- i
let set_gc_cur gc (a,b) = gc.x<- a; gc.y<-b;;
val set_gc_bcol : g_context -> Graphics.color -> unit = <fun>
val set_gc_fcol : g_context -> Graphics.color -> unit = <fun>
val set_gc_font : g_context -> string -> unit = <fun>
val set_gc_font_size : g_context -> int -> unit = <fun>
val set_gc_lw : g_context -> int -> unit = <fun>
val set_gc_cur : g_context -> int * int -> unit = <fun>


We can thus create new contexts, and read and write various fields of a value of the g_context type.

The use_gc function applies the data of a graphic context to the graphical window.


# let use_gc gc =
Graphics.set_color (get_gc_fcol gc);
Graphics.set_font (get_gc_font gc);
Graphics.set_text_size (get_gc_font_size gc);
Graphics.set_line_width (get_gc_lw gc);
let (a,b) = get_gc_cur gc in Graphics.moveto a b;;
val use_gc : g_context -> unit = <fun>


Some data, such as the background color, are not directly used by the Graphics library and do not appear in the use_gc function.

Events

The Graphics library only contains a limited number of interaction events: mouse click, mouse movement and key press. We want to enrich the kind of event that arises from interaction by integrating events arising at the component level. To this end we define the type rich_event:


# type rich_event =
MouseDown | MouseUp | MouseDrag | MouseMove
| MouseEnter | MouseExit | Exposure
| GotFocus | LostFocus | KeyPress | KeyRelease;;


To create such events it is necessary to keep a history of previous events. The MouseDown and MouseMove events correspond to mouse events (clicking and moving) which are created by Graphics. Other mouse events are created by virtue of either the previous event MouseUp, or the last component which handled a physical event MouseExit. The Exposure event corresponds to a request to redisplay a component. The concept of focus expresses that a given component is interested in a certain kind of event. Typically the input of text to a component which has grabbed the focus means that this component alone will handle KeyPress and KeyRelease events. A MouseDown event on a text input component hands over the input focus to it and takes it away from the component which had it before.

These new events are created by the event handling loop described on page ??.

Options

A graphical interface needs rules for describing the creation options for graphical objects (components, graphics contexts). If we wish to create a graphics context with a certain color it is currently necessary to construct it with the default values and then to call the two functions to modify the color fields in that context. In the case of more complex graphic objects this soon becomes tedious. Since we want to extend these options as we build up the components of the library, we need an ``extensible'' sum type. The only one provided by Objective CAML is the exn type used for exceptions. Because usingexn for handling options would affect the clarity of our programs we will only use this type for real exceptions. Instead, we will simulate an extensible sum type using pseudo constructors represented by character strings. We define the type opt_val for the values of these options. An option is a tuple whose first element is the name of the option and the second its value. The lopt type encompasses a list of such options.


# type opt_val = Copt of Graphics.color | Sopt of string
| Iopt of int | Bopt of bool;;
# type lopt = (string * opt_val) list ;;


The decoding functions take as input a list of options, an option name and a default value. If the name belongs to the list then the associated value is returned, if not then we get the default value. We show here only the decoding functions for integers and booleans, the others work on the same principle.


# exception OptErr;;
exception OptErr
# let theInt lo name default =
try
match List.assoc name lo with
Iopt i -> i
| _ -> raise OptErr
with Not_found -> default;;
val theInt : ('a * opt_val) list -> 'a -> int -> int = <fun>
# let theBool lo name default =
try
match List.assoc name lo with
Bopt b -> b
| _ -> raise OptErr
with Not_found -> default;;
val theBool : ('a * opt_val) list -> 'a -> bool -> bool = <fun>


We can now write a function to create a graphics context using a list of options in the following manner:


# let set_gc gc lopt =
set_gc_bcol gc (theColor lopt "Background" (get_gc_bcol gc));
set_gc_fcol gc (theColor lopt "Foreground" (get_gc_fcol gc));
set_gc_font gc (theString lopt "Font" (get_gc_font gc));
set_gc_font_size gc (theInt lopt "FontSize" (get_gc_font_size gc));
set_gc_lw gc (theInt lopt "LineWidth" (get_gc_lw gc));;
val set_gc : g_context -> (string * opt_val) list -> unit = <fun>


This allows us to ignore the order in which the options are passed in.


# let dc = make_default_context () in
set_gc dc [ "Foreground", Copt Graphics.blue;
"Background", Copt Graphics.yellow];
dc;;
- : g_context =
{bcol=16776960; fcol=255; font="fixed"; font_size=12; lw=1; x=0; y=0}


This results in a fairly flexible system which unfortunately partially evades the type system. The name of an option is of the type string and nothing prevents the construction of a nonexistant name. The result is simply that the value is ignored.

Components and Containers

The component is the essential building block of this library. We want to be able to create components and then easily assemble them to construct interfaces. They must be able to display themselves, to recognize an event destined for them, and to handle that event. Containers must be able to receive events from other components or to hand them on. We assume that a component can only be added to one container.

Construction of Components

A value of type component has a size (w and h), an absolute position in the main window (x and y), a graphics context used when it is displayed (gc), a flag to show whether it is a container (container), a parent - if it is itself attached to a container (parent), a list of child components (children) and four functions to handle positioning of components. These control how children are positioned within a component (layout), how the component is displayed (display), whether any given point is considered to be within the area of the component (mem) and finally a function for event handling (listener) which returns true if the event was handled and false otherwise. The parameter of the listener is of type (type rich_status) and contains the name of the event the lowlevel event information coming from the Graphics module, information on the keyboard focus and the general focus, as well as the last component to have handled an event. So we arrive at the following mutually recursive declarations:

# type component =
{ mutable info : string;
mutable x : int; mutable y : int;
mutable w :int ; mutable h : int;
mutable gc : g_context;
mutable container : bool;
mutable parent : component list;
mutable children : component list;
mutable layout_options : lopt;
mutable layout : component -> lopt -> unit;
mutable display : unit -> unit;
mutable mem : int * int -> bool;
mutable listener : rich_status -> bool }
and rich_status =
{ re : rich_event;
stat : Graphics.status;
mutable key_focus : component;
mutable gen_focus : component;
mutable last : component};;


We access the data fields of a component with the following functions.

# let get_gc c = c.gc;;
val get_gc : component -> g_context = <fun>
# let is_container c = c.container;;
val is_container : component -> bool = <fun>


The following three functions define the default behavior of a component. The function to test whether a given mouse position applies to a given component (in_rect) checks that the coordinate is within the rectangle defined by the coordinates of the component. The default display function (display_rect) fills the rectangle of the component with the background color found in the graphic context of that component. The default layout function (direct_layout) places components relatively within their containers. Valid options are "PosX" and "PosY", corresponding to the coordinates relative to the container.

# let in_rect c (xp,yp) =
(xp >= c.x) && (xp < c.x + c.w) && (yp >= c.y) && (yp < c.y + c.h) ;;
val in_rect : component -> int * int -> bool = <fun>
# let display_rect c () =
let gc = get_gc c in
Graphics.set_color (get_gc_bcol gc);
Graphics.fill_rect c.x c.y c.w c.h ;;
val display_rect : component -> unit -> unit = <fun>
# let direct_layout c c1 lopt =
let px = theInt lopt "PosX" 0
and py = theInt lopt "PosY" 0 in
c1.x <- c.x + px; c1.y <- c.y + py ;;
val direct_layout :
component -> component -> (string * opt_val) list -> unit = <fun>


It is now possible to define a component using the function create_component which takes width and height as parameters and uses the three preceding functions.

# let create_component iw ih =
let dc =
{info="Anonymous";
x=0; y=0; w=iw; h=ih;
gc = make_default_context() ;
container = false;
parent = []; children = [];
layout_options = [];
layout = (fun a b -> ());
display = (fun () -> ());
mem = (fun s -> false);
listener = (fun s -> false);}
in
dc.layout <- direct_layout dc;
dc.mem <- in_rect dc;
dc.display <- display_rect dc;
dc ;;
val create_component : int -> int -> component = <fun>


We then define the following empty component:

# let empty_component = create_component 0 0 ;;
This is used as a default value when we construct values which need to contain at least one component (for example a value of type rich_status).

Adding Child Components

The difficult part of adding a component to a container is how to position the component within the container. The layout field contains this positioning function. It takes a component (a child) and a list of options and calculates the new coordinates of the child within the container. Different options can be used according to the positioning function. We describe several layout functions when we talk about about the panel component (see below, page ??). Here we simply describe the mechanism for propagating the display function through the tree of components, coordinate changes, and propagating events. The propagation of actions makes intensive use of the List.iter function, which applies a function to all the elements of a list.

The function change_coord applies a relative change to the coordinates of a component and those of all its children.

# let rec change_coord c (dx,dy) =
c.x <- c.x + dx; c.y <- c.y + dy;
List.iter (fun s -> change_coord s (dx,dy) ) c.children;;
val change_coord : component -> int * int -> unit = <fun>


The add_component function checks that the conditions for adding a component have been met and then joins the parent (c) and the child (c1). The list of positioning options is retained in the child component, which allows us to reuse them when the positioning function of the parent changes. The list of options passed to this function are those used by the positioning function. There are three conditions which need to be prohibited: the child component is already a parent, the parent is not a container or the child is too large for parent


# let add_component c c1 lopt =
if c1.parent <> [] then failwith "add_component: already a parent"
else
if not (is_container c ) then
failwith "add_component: not a container"
else
if (c1.x + c1.w > c.w) || (c1.y + c1.h > c.h)
then failwith "add_component: bad position"
else
c.layout c1 lopt;
c1.layout_options <- lopt;
List.iter (fun s -> change_coord s (c1.x,c1.y)) c1.children;
c.children <- c1::c.children;
c1.parent <- [c] ;;
val add_component : component -> component -> lopt -> unit = <fun>


The removal of a component from some level in the tree, implemented by the following function, entails both a change to the link between the parent and the child and also a change to the coordinates of the child and all its own children:

# let remove_component c c1 =
c.children <- List.filter ((!=) c1) c.children;
c1.parent <- List.filter ((!=) c) c1.parent;
List.iter (fun s -> change_coord s (- c1.x, - c1.y)) c1.children;
c1.x <- 0; c1.y <- 0;;
val remove_component : component -> component -> unit = <fun>


A change to the positioning function of a container depends on whether it has any children. If it does not the change is immediate. Otherwise we must first remove the children of the container, modify the container's positioning function and then add the components back in with the same options used when they were originally added.

# let set_layout f c =
if c.children = [] then c.layout <- f
else
let ls = c.children in
List.iter (remove_component c) ls;
c.layout <- f;
List.iter (fun s -> add_component c s s.layout_options) ls;;
val set_layout : (component -> lopt -> unit) -> component -> unit = <fun>
This is why we kept the list of positioning options. If the list of options is not recognized by the new function it uses the defaults.

When a component is displayed, the display event must be propagated to its children. The container is displayed behind its children. The order of display of the children is unimportant because they never overlap.

# let rec display c =
c.display ();
List.iter (fun cx -> display cx ) c.children;;
val display : component -> unit = <fun>


Event Handling

The handling of physical events (mouse click, key press, mouse movement) uses the Graphics.wait_next_event function (see page ??) which returns a physical status (of type Graphics.status) following any user interaction. This physical status is used to calculate a rich status (of type rich_status) containing the event type (of type rich_event), the physical status, the components possessing the keyboard focus and the general focus along with the last component which successfully handled such an event. The general focus is a component which accepts all events.

Next we describe the functions for the manipulating of rich events, the propagation of this status information to components for them to be handled, the creation of the information and the main event-handling loop.

Functions used on Status

The following functions read the values of the mouse position and the focus. Functions on focus need a further parameter: the component which is capturing or losing that focus.


# let get_event e = e.re;;
# let get_mouse_x e = e.stat.Graphics.mouse_x;;
# let get_mouse_y e = e.stat.Graphics.mouse_y;;
# let get_key e = e.stat.Graphics.key;;

# let has_key_focus e c = e.key_focus == c;;
# let take_key_focus e c = e.key_focus <- c;;
# let lose_key_focus e c = e.key_focus <- empty_component;;
# let has_gen_focus e c = e.gen_focus == c;;
# let take_gen_focus e c = e.gen_focus <- c;;
# let lose_gen_focus e c = e.gen_focus <- empty_component;;


Propagation of Events

A rich event is sent to a component to be handled. Analogous to the display mechanism discussed earlier, child components have priority over their parents for handling simple mouse movement. If a component receives status information associated with an event, it looks to see if it has a child which can handle it. If so, the child returns true otherwise false. If no child can handle the event, the parent component tries to use the function in its own listener field.

Status information coming from keyboard activity is propagated differently. The parent component looks to see if it possesses the keyboard focus, and if so it handles the event, otherwise it propagates to its children.

Some events are produced as a result of handling an initial event. For example, if one component captures the focus, then this means another has lost it. Such events are handled immediately by the target component. This is the same with the entry and exit events caused when the mouse is moved between different components.

The send_event function takes a value of type rich_status and a component. It returns a boolean indicating whether the event was handled or not.

# let rec send_event rs c =
match get_event rs with
MouseDown | MouseUp | MouseDrag | MouseMove ->
if c.mem(get_mouse_x rs, get_mouse_y rs) then
if List.exists (fun sun -> send_event rs sun) c.children then true
else ( if c.listener rs then (rs.last <-c; true) else false )
else false
| KeyPress | KeyRelease ->
if has_key_focus rs c then
( if c.listener rs then (rs.last<-c; true)
else false )
else List.exists (fun sun -> send_event rs sun) c.children
| _ -> c.listener rs;;
val send_event : rich_status -> component -> bool = <fun>


Note that the hierarchical structure of the components is really a tree and not a cyclic graph. This guarantees that the recursion in the send_event function cannot cause an infinite loop.

Event Creation

We differentiate between two kinds of events: those produced by a physical action (such as a mouse click) and those which arise from some action linked with the previous history of the system (such as the movement of the mouse cursor out of the screen area occupied by a component). As a result we define two functions for creating rich events.

The function which deals with the former kind constructs a rich event out of two sets of physical status information:

# let compute_rich_event s0 s1 =
if s0.Graphics.button <> s1.Graphics.button then
begin
if s0.Graphics.button then MouseDown else MouseUp
end
else if s1.Graphics.keypressed then KeyPress
else if (s0.Graphics.mouse_x <> s1.Graphics.mouse_x ) ||
(s0.Graphics.mouse_y <> s1.Graphics.mouse_y ) then
begin
if s1.Graphics.button then MouseDrag else MouseMove
end
else raise Not_found;;
val compute_rich_event : Graphics.status -> Graphics.status -> rich_event =
<fun>


The function creating the latter kind of event uses the last two rich events:

# let send_new_events res0 res1 =
if res0.key_focus <> res1.key_focus then
begin
ignore(send_event {res1 with re = LostFocus} res0.key_focus);
ignore(send_event {res1 with re = GotFocus} res1.key_focus)
end;
if (res0.last <> res1.last) &&
(( res1.re = MouseMove) || (res1.re = MouseDrag)) then
begin
ignore(send_event {res1 with re = MouseExit} res0.last);
ignore(send_event {res1 with re = MouseEnter} res1.last )
end;;
val send_new_events : rich_status -> rich_status -> unit = <fun>


We define an initial value for the rich_event type. This is used to initialize the history of the event loop.

# let initial_re =
{ re = Exposure;
stat = { Graphics.mouse_x=0; Graphics.mouse_y=0;
Graphics.key = ' ';
Graphics.button = false;
Graphics.keypressed = false };
key_focus = empty_component;
gen_focus = empty_component;
last = empty_component } ;;


Event Loop

The event loop manages the sequence of interactions with a component, usually the ancestor component for all the components of the interface. It is supplied with two booleans indicating whether the interface should be redisplayed after every physical event has been handled (b_disp) and whether to handle mouse movement (b_motion). The final argument (c), is the root of the component tree.

# let loop b_disp b_motion c =
let res0 = ref initial_re in
try
display c;
while true do
let lev = [Graphics.Button_down; Graphics.Button_up;
Graphics.Key_pressed] in
let flev = if b_motion then (Graphics.Mouse_motion) :: lev
else lev in
let s = Graphics.wait_next_event flev
in
let res1 = {!res0 with stat = s} in
try
let res2 = {res1 with
re = compute_rich_event !res0.stat res1.stat} in
ignore(send_event res2 c);
send_new_events !res0 res2;
res0 := res2;
if b_disp then display c
with Not_found -> ()
done
with e -> raise e;;
val loop : bool -> bool -> component -> unit = <fun>
The only way out of this loop is when one of the handling routines raises an exception.

Test Functions

We define the following two functions to create by hand status information corresponding to mouse and keyboard events.

# let make_click e x y =
{re = e;
stat = {Graphics.mouse_x=x; Graphics.mouse_y=y;
Graphics.key = ' '; Graphics.button = false;
Graphics.keypressed = false};
key_focus = empty_component;
gen_focus = empty_component;
last = empty_component}

let make_key e ch c =
{re = e;
stat = {Graphics.mouse_x=0; Graphics.mouse_y=0;
Graphics.key = c; Graphics.button = false;
Graphics.keypressed = true};
key_focus = empty_component;
gen_focus = empty_component;
last = empty_component};;
val make_click : rich_event -> int -> int -> rich_status = <fun>
val make_key : rich_event -> 'a -> char -> rich_status = <fun>


We can now simulate the sending of a mouse event to a component for test purposes.

Defining Components

The various mechanisms for display, coordinate change and, propagating event are now in place. It remains for us to define some components which are both useful and easy to use. We can classify components into the following three categories:
  • simple components which do not handle events, such as text to be displayed;
  • simple components which handle events, such as text entry fields;
  • containers and their various layout strategies.
Values are passed between components, or between a component and the application by modification of shared data. The sharing is implemented by closures which contain in their environment the data to be modified. Moreover, as the behavior of the component can change as a result of event handling, components also contain an internal state in the closures of their handling functions. For example the handling function for an input field has access to text while it is being written. To this end we implement components in the following manner:
  • define a type to represent the internal state of the component;
  • declare functions for the manipulation of this state;
  • implement the functions for display, testing whether a coordinate is within the component and handling events;
  • implement the function to create the component, thereby associating those closures with fields in the component;
  • test the component by simulating the arrival of events.
Creation functions take a list of options to configure the graphics context. The calculation of the size of a component when it is created needs to make use of graphics context of the graphical window in order to determine the width of the text to be displayed.

We describe the implementation of the following components:
  • simple text (label);
  • simple container (panel);
  • simple button (button);
  • choice among a sequence of strings (choice);
  • text entry field (textfield);
  • rich component (border).

The Label Component

The simplest component, called a label, displays a string of characters on the screen. It does not handle events. We will start by describing the display function and then the creation function.

Display must take account of the foreground and background colors and the character font. It is the job of the display_init function to erase the graphical region of the component, select the foreground color and position the cursor. The function display_label displays the string passed as a parameter immediately after the call to display_init.

# let display_init c =
Graphics.set_color (get_gc_bcol (get_gc c)); display_rect c ();
let gc= get_gc c in
use_gc gc;
let (a,b) = get_gc_cur gc in
Graphics.moveto (c.x+a) (c.y+b)
let display_label s c () =
display_init c; Graphics.draw_string s;;
val display_init : component -> unit = <fun>
val display_label : string -> component -> unit -> unit = <fun>


As this component is very simple it is not necessary to create any internal state. Only the function display_label knows the string to be displayed, which is passed by the creation function.

# let create_label s lopt =
let gc = make_default_context () in set_gc gc lopt; use_gc gc;
let (w,h) = Graphics.text_size s in
let u = create_component w h in
u.mem <- (fun x -> false); u.display <- display_label s u;
u.info <- "Label"; u.gc <- gc;
u;;
val create_label : string -> (string * opt_val) list -> component = <fun>


If we wish to change the colors of this component, we need to manipulate its graphic context directly.

The display of label l1 below is depicted in figure 13.1.

# let courier_bold_24 = Sopt "*courier-bold-r-normal-*24*"
and courier_bold_18 = Sopt "*courier-bold-r-normal-*18*";;
# let l1 = create_label "Login: " ["Font", courier_bold_24;
"Background", Copt gray1];;




Figure 13.1: Displaying a label.


The panel Component, Containers and Layout

A panel is a graphical area which can be a container. The function which creates a panel is very simple. It augments the general function for creating components with a boolean indicating whether it is a container. The functions for testing location within the panel and for display are those assigned by default in the create_component function.

# let create_panel b w h lopt =
let u = create_component w h in
u.container <- b;
u.info <- if b then "Panel container" else "Panel";
let gc = make_default_context () in set_gc gc lopt; u.gc <- gc;
u;;
val create_panel :
bool -> int -> int -> (string * opt_val) list -> component = <fun>


The tricky part with containers lies in the positioning of their child components. We define two new layout functions: center_layout and grid_layout. The first, center_layout places a component at the center of a container:

# let center_layout c c1 lopt =
c1.x <- c.x + ((c.w -c1.w) /2); c1.y <- c.y + ((c.h -c1.h) /2);;
val center_layout : component -> component -> 'a -> unit = <fun>


The second, grid_layout divides a container into a grid where each box has the same size. The layout options in this case are "Col" for the column number and "Row" for the row number.


# let grid_layout (a, b) c c1 lopt =
let px = theInt lopt "Col" 0
and py = theInt lopt "Row" 0 in
if (px >= 0) && (px < a) && ( py >=0) && (py < b) then
let lw = c.w /a
and lh = c.h /b in
if (c1.w > lw) || (c1.h > lh) then
failwith "grid_placement: too big component"
else
c1.x <- c.x + px * lw + (lw - c1.w)/2;
c1.y <- c.y + py * lh + (lh - c1.h)/2;
else failwith "grid_placement: bad position";;
val grid_layout :
int * int -> component -> component -> (string * opt_val) list -> unit =
<fun>
It is clearly possible to define more. One can also customize a container by changing its layout function (set_layout). Figure 13.2 shows a panel, declared as a container, in which two labels have been added and which corresponds to the following program:



Figure 13.2: A panel component.



# let l2 = create_label "Passwd: " ["Font", courier_bold_24;
"Background", Copt gray1] ;;
# let p1 = create_panel true 150 80 ["Background", Copt gray2] ;;
# set_layout (grid_layout (1,2) p1) p1;;
# add_component p1 l1 ["Row", Iopt 1];;
# add_component p1 l2 ["Row", Iopt 0];;


Since the components need at least one parent so that they can be integrated into the interface, and since the Graphics library only supports one window, we must define a principle window which is a container.

# let open_main_window w h =
Graphics.close_graph();
Graphics.open_graph (" "^(string_of_int w)^"x"^(string_of_int h));
let u = create_component w h in
u.container <- true;
u.info <- "Main Window";
u;;
val open_main_window : int -> int -> component = <fun>


The Button Component

A button is a component which displays a text in its graphical region and reacts to mouse clicks which occur there. To support this behavior it retains a piece of state, a value of type button_state, which contains the text and the mouse handling function.

# type button_state =
{ txt : string; mutable action : button_state -> unit } ;;


The function create_bs creates this state. The set_bs_action function changes the handling function and the function get_bs_text retrieves the text of a button.

# let create_bs s = {txt = s; action = fun x -> ()}
let set_bs_action bs f = bs.action <- f
let get_bs_text bs = bs.txt;;
val create_bs : string -> button_state = <fun>
val set_bs_action : button_state -> (button_state -> unit) -> unit = <fun>
val get_bs_text : button_state -> string = <fun>


The display function is similar to that used by labels with the exception that the text derives this time from the state information belonging to the button. By default the listening function activates the action function when a mouse button is pressed.


# let display_button c bs () =
display_init c; Graphics.draw_string (get_bs_text bs)
let listener_button c bs e = match get_event e with
MouseDown -> bs.action bs; c.display (); true
| _ -> false;;
val display_button : component -> button_state -> unit -> unit = <fun>
val listener_button : component -> button_state -> rich_status -> bool =
<fun>


We now have all we need to define the creation function for simple buttons:

# let create_button s lopt =
let bs = create_bs s in
let gc = make_default_context () in
set_gc gc lopt; use_gc gc;
let w,h = Graphics.text_size (get_bs_text bs) in
let u = create_component w h in
u.display <- display_button u bs;
u.listener <- listener_button u bs;
u.info <- "Button";
u.gc <- gc;
u,bs;;
val create_button :
string -> (string * opt_val) list -> component * button_state = <fun>
This returns a tuple of which the first element is the button which has been created and the second is the internal state of the button. The latter value is particularly useful if we want to change the action function of the button since the button state is not accessible via the button function.

Figure 13.3 shows a panel to which a button has been added. We have associated an action function which displays the string contained by the button on the standard output.



Figure 13.3: Button display and reaction to a mouseclick.



# let b,bs = create_button "Validation" ["Font", courier_bold_24;
"Background", Copt gray1];;
# let p2 = create_panel true 150 60 ["Background", Copt gray2];;
# set_bs_action bs (fun bs -> print_string ( (get_bs_text bs)^ "...");
print_newline());;
# set_layout (center_layout p2) p2;;
# add_component p2 b [];;


In contrast to labels, a button component knows how to react to a mouse click. To test this feature we send the event ``mouse click'' to a central position on the panel p2, which is occupied by the button. This causes the action associated with the button to be carried out:

# send_event (make_click MouseDown 75 30) p2;;
Validation...
- : bool = true
and returns the value true showing that the event has indeed been handled.

The choice Component

The choice component allows us to select one of the choices offered using a mouse click. There is always a current choice. A mouse click on another choice causes the current choice to change and causes an action to be carried out. We use the same technique we used previously for simple buttons. We start by defining the state needed by a choice list:

# type choice_state =
{ mutable ind : int; values : string array; mutable sep : int;
mutable height : int; mutable action : choice_state -> unit } ;;
The index ind shows which string is to be highlighted in the list of values. The sep and height fields describe in pixels the distance between two choices and the height of a choice. The action function takes an argument of type choice_state as an input and does its job using the index.

We now define the function to create a set of status information and the function to change to way it is handled.

# let create_cs sa = {ind = 0; values = sa; sep = 2;
height = 1; action = fun x -> ()}
let set_cs_action cs f = cs.action <- f
let get_cs_text cs = cs.values.(cs.ind);;
val create_cs : string array -> choice_state = <fun>
val set_cs_action : choice_state -> (choice_state -> unit) -> unit = <fun>
val get_cs_text : choice_state -> string = <fun>


The display function shows the list of all the possible choices and accentuates the current choice in inverse video. The event handling function reacts to a release of the mouse button.


# let display_choice c cs () =
display_init c;
let (x,y) = Graphics.current_point()
and nb = Array.length cs.values in
for i = 0 to nb-1 do
Graphics.moveto x (y + i*(cs.height+ cs.sep));
Graphics.draw_string cs.values.(i)
done;
Graphics.set_color (get_gc_fcol (get_gc c));
Graphics.fill_rect x (y+ cs.ind*(cs.height+ cs.sep)) c.w cs.height;
Graphics.set_color (get_gc_bcol (get_gc c));
Graphics.moveto x (y + cs.ind*(cs.height + cs.sep));
Graphics.draw_string cs.values.(cs.ind) ;;
val display_choice : component -> choice_state -> unit -> unit = <fun>

# let listener_choice c cs e = match e.re with
MouseUp ->
let x = e.stat.Graphics.mouse_x
and y = e.stat.Graphics.mouse_y in
let cy = c.y in
let i = (y - cy) / ( cs.height + cs.sep) in
cs.ind <- i; c.display ();
cs.action cs; true
| _ -> false ;;
val listener_choice : component -> choice_state -> rich_status -> bool =
<fun>


To create a list of possible choices we take a list of strings and a list of options, and we return the component itself along with its internal state.


# let create_choice lc lopt =
let sa = (Array.of_list (List.rev lc)) in
let cs = create_cs sa in
let gc = make_default_context () in
set_gc gc lopt; use_gc gc;
let awh = Array.map (Graphics.text_size) cs.values in
let w = Array.fold_right (fun (x,y) -> max x) awh 0
and h = Array.fold_right (fun (x,y) -> max y) awh 0 in
let h1 = (h+cs.sep) * (Array.length sa) + cs.sep in
cs.height <- h;
let u = create_component w h1 in
u.display <- display_choice u cs;
u.listener <- listener_choice u cs ;
u.info <- "Choice "^ (string_of_int (Array.length cs.values));
u.gc <- gc;
u,cs;;
val create_choice :
string list -> (string * opt_val) list -> component * choice_state = <fun>


The sequence of three pictures in figure 13.4 shows a panel to which a list of choices has been added. To it we have bound an action function which displays the chosen string to the standard output. The pictures arise from mouse clicks simulated by the following program.


Figure 13.4: Displaying and selecting from a choice list.



# let c,cs = create_choice ["Helium"; "Gallium"; "Pentium"]
["Font", courier_bold_24;
"Background", Copt gray1];;
# let p3 = create_panel true 110 110 ["Background", Copt gray2];;
# set_cs_action cs (fun cs -> print_string ( (get_cs_text cs)^"...");
print_newline());;
# set_layout (center_layout p3) p3;;
# add_component p3 c [];;


Here also we can test the component straight away by sending several events. The following changes the selection, as is shown in the central picture in figure 13.4.

# send_event (make_click MouseUp 60 55 ) p3;;
Gallium...
- : bool = true


The sending of the following event selects the first element in the choice list

# send_event (make_click MouseUp 60 90 ) p3;;
Helium...
- : bool = true


The textfield Component

The text input field, or textfield, is an area which enables us to input a text string. The text can be aligned to the left or (typically for a calculator) the right. Furthermore a cursor shows where the next character will be entered. Here we need a more complex internal state. This includes the text which is being entered, the direction of the justification, a description of the cursor, a description of how the characters are displayed and the action function.

# type textfield_state =
{ txt : string;
dir : bool; mutable ind1 : int; mutable ind2 : int; len : int;
mutable visible_cursor : bool; mutable cursor : char;
mutable visible_echo : bool; mutable echo : char;
mutable action : textfield_state -> unit } ;;


To create this internal state we need the initial text, the number of characters available for the text input field and the justification of the text.

# let create_tfs txt size dir =
let l = String.length txt in
(if size < l then failwith "create_tfs");
let ind1 = if dir then 0 else size-1-l
and ind2 = if dir then l else size-1 in
let n_txt = (if dir then (txt^(String.make (size-l) ' '))
else ((String.make (size-l) ' ')^txt )) in
{txt = n_txt; dir=dir; ind1 = ind1; ind2 = ind2; len=size;
visible_cursor = false; cursor = ' '; visible_echo = true; echo = ' ';
action= fun x -> ()};;
val create_tfs : string -> int -> bool -> textfield_state = <fun>


The following functions allow us to access various fields, including the displayed text.

# let set_tfs_action tfs f = tfs.action <- f
let set_tfs_cursor b c tfs = tfs.visible_cursor <- b; tfs.cursor <- c
let set_tfs_echo b c tfs = tfs.visible_echo <- b; tfs.echo <- c
let get_tfs_text tfs =
if tfs.dir then String.sub tfs.txt tfs.ind1 (tfs.ind2 - tfs.ind1)
else String.sub tfs.txt (tfs.ind1+1) (tfs.ind2 - tfs.ind1);;


The set_tfs_text function changes the text within the internal state tfs of the component tf with the string txt.

# let set_tfs_text tf tfs txt =
let l = String.length txt in
if l > tfs.len then failwith "set_tfs_text";
String.blit (String.make tfs.len ' ') 0 tfs.txt 0 tfs.len;
if tfs.dir then (String.blit txt 0 tfs.txt 0 l;
tfs.ind2 <- l )
else ( String.blit txt 0 tfs.txt (tfs.len -l) l;
tfs.ind1 <- tfs.len-l-1 );
tf.display ();;
val set_tfs_text : component -> textfield_state -> string -> unit = <fun>


Display operations must take account of how the character is echoed and the visibility of the cursor. The display_textfield function calls the display_cursor function which shows where the cursor is.

# let display_cursor c tfs =
if tfs.visible_cursor then
( use_gc (get_gc c);
let (x,y) = Graphics.current_point() in
let (a,b) = Graphics.text_size " " in
let shift = a * (if tfs.dir then max (min (tfs.len-1) tfs.ind2) 0
else tfs.ind2) in
Graphics.moveto (c.x+x + shift) (c.y+y);
Graphics.draw_char tfs.cursor);;
val display_cursor : component -> textfield_state -> unit = <fun>
# let display_textfield c tfs () =
display_init c;
let s = String.make tfs.len ' '
and txt = get_tfs_text tfs in
let nl = String.length txt in
if (tfs.ind1 >= 0) && (not tfs.dir) then
Graphics.draw_string (String.sub s 0 (tfs.ind1+1) );
if tfs.visible_echo then (Graphics.draw_string (get_tfs_text tfs))
else Graphics.draw_string (String.make (String.length txt) tfs.echo);
if (nl > tfs.ind2) && (tfs.dir)
then Graphics.draw_string (String.sub s tfs.ind2 (nl-tfs.ind2));
display_cursor c tfs;;
val display_textfield : component -> textfield_state -> unit -> unit = <fun>


The event-listener function for this kind of component is more complex. According to the input direction (left or right justified) we may need to move the string which has already been input. Capture of focus is achieved by a mouse click in the input zone.

# let listener_text_field u tfs e =
match e.re with
MouseDown -> take_key_focus e u ; true
| KeyPress ->
( if Char.code (get_key e) >= 32 then
begin
( if tfs.dir then
( ( if tfs.ind2 >= tfs.len then (
String.blit tfs.txt 1 tfs.txt 0 (tfs.ind2-1);
tfs.ind2 <- tfs.ind2-1) );
tfs.txt.[tfs.ind2] <- get_key e;
tfs.ind2 <- tfs.ind2 +1 )
else
( String.blit tfs.txt 1 tfs.txt 0 (tfs.ind2);
tfs.txt.[tfs.ind2] <- get_key e;
if tfs.ind1 >= 0 then tfs.ind1 <- tfs.ind1 -1
);
)
end
else (
( match Char.code (get_key e) with
13 -> tfs.action tfs
| 9 -> lose_key_focus e u
| 8 -> if (tfs.dir && (tfs.ind2 > 0))
then tfs.ind2 <- tfs.ind2 -1
else if (not tfs.dir) && (tfs.ind1 < tfs.len -1)
then tfs.ind1 <- tfs.ind1+1
| _ -> ()
))); u.display(); true
| _ -> false;;
val listener_text_field :
component -> textfield_state -> rich_status -> bool = <fun>


The function which creates text entry fields repeats the same pattern we have seen in the previous components.

# let create_text_field txt size dir lopt =
let tfs = create_tfs txt size dir
and l = String.length txt in
let gc = make_default_context () in
set_gc gc lopt; use_gc gc;
let (w,h) = Graphics.text_size (tfs.txt) in
let u = create_component w h in
u.display <- display_textfield u tfs;
u.listener <- listener_text_field u tfs ;
u.info <- "TextField"; u.gc <- gc;
u,tfs;;
val create_text_field :
string ->
int -> bool -> (string * opt_val) list -> component * textfield_state =
<fun>


This function returns a tuple consisting of the component itself, and the internal state of that component. We can test the creation of the component in figure 13.5 as follows:

# let tf1,tfs1 = create_text_field "jack" 8 true ["Font", courier_bold_24];;
# let tf2,tfs2 = create_text_field "koala" 8 false ["Font", courier_bold_24];;
# set_tfs_cursor true '_' tfs1;;
# set_tfs_cursor true '_' tfs2;;
# set_tfs_echo false '*' tfs2;;
# let p4 = create_panel true 140 80 ["Background", Copt gray2];;
# set_layout (grid_layout (1,2) p4) p4;;
# add_component p4 tf1 ["Row", Iopt 1];;
# add_component p4 tf2 ["Row", Iopt 0];;




Figure 13.5: Text input component.


Enriched Components

Beyond the components described so far, it is also possible to construct new ones, for example components with bevelled edges such as those in the calculator on page ??. To create this effect we construct a panel larger than the component, fill it out in a certain way and add the required component to the center.

# type border_state =
{mutable relief : string; mutable line : bool;
mutable bg2 : Graphics.color; mutable size : int};;


The creation function takes a list of options and constructs an internal state.

# let create_border_state lopt =
{relief = theString lopt "Relief" "Flat";
line = theBool lopt "Outlined" false;
bg2 = theColor lopt "Background2" Graphics.black;
size = theInt lopt "Border_size" 2};;
val create_border_state : (string * opt_val) list -> border_state = <fun>


We define the profile of the border used in the boxes of figure 5.6 (page ??) by defining the options "Top", "Bot" and "Flat".

# let display_border bs c1 c () =
let x1 = c.x and y1 = c.y in
let x2 = x1+c.w-1 and y2 = y1+c.h-1 in
let ix1 = c1.x and iy1 = c1.y in
let ix2 = ix1+c1.w-1 and iy2 = iy1+c1.h-1 in
let border1 g = Graphics.set_color g;
Graphics.fill_poly [| (x1,y1);(ix1,iy1);(ix2,iy1);(x2,y1) |] ;
Graphics.fill_poly [| (x2,y1);(ix2,iy1);(ix2,iy2);(x2,y2) |]
in
let border2 g = Graphics.set_color g;
Graphics.fill_poly [| (x1,y2);(ix1,iy2);(ix2,iy2);(x2,y2) |] ;
Graphics.fill_poly [| (x1,y1);(ix1,iy1);(ix1,iy2);(x1,y2) |]
in
display_rect c ();
if bs.line then (Graphics.set_color (get_gc_fcol (get_gc c));
draw_rect x1 y1 c.w c.h);
let b1_col = get_gc_bcol ( get_gc c)
and b2_col = bs.bg2 in
match bs.relief with
"Top" -> (border1 b1_col; border2 b2_col)
| "Bot" -> (border1 b2_col; border2 b1_col)
| "Flat" -> (border1 b1_col; border2 b1_col)
| s -> failwith ("display_border: unknown relief: "^s)
;;
val display_border : border_state -> component -> component -> unit -> unit =
<fun>


The function which creates a border takes a component and a list of options, it constructs a panel containing that component.

# let create_border c lopt =
let bs = create_border_state lopt in
let p = create_panel true (c.w + 2 * bs.size)
(c.h + 2 * bs.size) lopt in
set_layout (center_layout p) p;
p.display <- display_border bs c p;
add_component p c []; p;;
val create_border : component -> (string * opt_val) list -> component = <fun>


Now we can test creating a component with a border on the label component and the text entry field tf1 defined by in our previous tests. The result is show in figure 13.6.

# remove_component p1 l1;;
# remove_component p4 tf1;;
# let b1 = create_border l1 [];;
# let b2 = create_border tf1 ["Relief", Sopt "Top";
"Background", Copt Graphics.red;
"Border_size", Iopt 4];;
# let p5 = create_panel true 140 80 ["Background", Copt gray2];;
# set_layout (grid_layout (1,2) p5) p5;;
# add_component p5 b1 ["Row", Iopt 1];;
# add_component p5 b2 ["Row", Iopt 0];;




Figure 13.6: An enriched component.


Setting up the Awi Library

The essential parts of our library have now been written. All declarations 2 of types and values which we have seen so far in this section can be grouped together in one file. This library consists of one single module. If the file is called awi.ml then we get a module called Awi. The link between the name of the file and that of the module is described in chapter 14.

Compiling this file will produce a compiled interface file awi.cmi and, depending on the compiler being used, the bytecode itself awi.cmo or else the native machine code awi.cmx. To use the bytecode compiler we enter the following command
ocamlc -c awi.ml
To use it at the interactive toplevel, we need to load the bytecode of our new library with the command #load "awi.cmo";; having also previously ensured that we have loaded the Graphics library. We can then start calling functions from the module to create and work with components.
# open Awi;;
# create_component;;
- : int -> int -> Awi.component = <fun>
The result type of this function is Awi.component, chapter 14 explains more about this.

Example: A Franc-Euro Converter

We will now build a currency converter between Francs and Euros using this new library. The actual job of conversion is trivial, but the construction of the interface will show how the components communicate with each other. While we are getting used to the new currency we need to convert in both directions. Here are the components we have chosen:
  • a list of two choices to describe the direction of the conversion;
  • two text entry fields for inputting values and displaying converted results;
  • a simple button to request that the calculation be performed;
  • two labels to show the meaning of each text entry field.
These different components are shown in figure 13.7.

Communication between the components is implemented by sharing state. For this purpose we define the type state_conv which hold the fields for francs (a), euros (b), the direction in which the conversion is to be performed (dir) and the conversion factors (fa and fb).

# type state_conv =
{ mutable a:float; mutable b:float; mutable dir : bool;
fa : float; fb : float } ;;


We define the initial state as follows:

# let e = 6.55957074
let fe = { a =0.0; b=0.0; dir = true; fa = e; fb = 1./. e};;


The conversion function returns a floating result following the direction of the conversion.

# let calculate fe =
if fe.dir then fe.b <- fe.a /. fe.fa else fe.a <- fe.b /. fe.fb;;
val calculate : state_conv -> unit = <fun>


A mouse click on the list of two choices changes the direction of the conversion. The text of the choice strings is "->" and "<-".

# let action_dir fe cs = match get_cs_text cs with
"->" -> fe.dir <- true
| "<-" -> fe.dir <- false
| _ -> failwith "action_dir";;
val action_dir : state_conv -> choice_state -> unit = <fun>


The action associated with the simple button causes the calculation to be performed and displays the result in one of the two text entry fields. For this to be possible we pass the two text entry fields as parameters to the action.

# let action_go fe tf_fr tf_eu tfs_fr tfs_eu x =
if fe.dir then
let r = float_of_string (get_tfs_text tfs_fr) in
fe.a <- r; calculate fe;
let sr = Printf.sprintf "%.2f" fe.b in
set_tfs_text tf_eu tfs_eu sr
else
let r = float_of_string (get_tfs_text tfs_eu) in
fe.b <- r; calculate fe;
let sr = Printf.sprintf "%.2f" fe.a in
set_tfs_text tf_fr tfs_fr sr;;
val action_go :
state_conv ->
component -> component -> textfield_state -> textfield_state -> 'a -> unit =
<fun>


It now remains to build the interface. The following function takes a width, a height and a conversion state and returns the main container with the three active components.


# let create_conv w h fe =
let gray1 = (Graphics.rgb 120 120 120) in
let m = open_main_window w h
and p = create_panel true (w-4) (h-4) []
and l1 = create_label "Francs" ["Font", courier_bold_24;
"Background", Copt gray1]
and l2 = create_label "Euros" ["Font", courier_bold_24;
"Background", Copt gray1]
and c,cs = create_choice ["->"; "<-"] ["Font", courier_bold_18]
and tf1,tfs1 = create_text_field "0" 10 false ["Font", courier_bold_18]
and tf2,tfs2 = create_text_field "0" 10 false ["Font", courier_bold_18]
and b,bs = create_button " Go " ["Font", courier_bold_24]
in
let gc = get_gc m in
set_gc_bcol gc gray1;
set_layout (grid_layout (3,2) m ) m;
let tb1 = create_border tf1 []
and tb2 = create_border tf2 []
and bc = create_border c []
and bb =
create_border b
["Border_size", Iopt 4; "Relief", Sopt "Bot";
"Background", Copt gray2; "Background2", Copt Graphics.black]
in
set_cs_action cs (action_dir fe);
set_bs_action bs (action_go fe tf1 tf2 tfs1 tfs2);
add_component m l1 ["Col",Iopt 0;"Row",Iopt 1];
add_component m l2 ["Col",Iopt 2;"Row",Iopt 1];
add_component m bc ["Col",Iopt 1;"Row",Iopt 1];
add_component m tb1 ["Col",Iopt 0;"Row",Iopt 0];
add_component m tb2 ["Col",Iopt 2;"Row",Iopt 0];
add_component m bb ["Col",Iopt 1;"Row",Iopt 0];
m,bs,tf1,tf2;;
val create_conv :
int ->
int -> state_conv -> component * button_state * component * component =
<fun>


The event handling loop is started on the container m constructed below. The resulting display is shown in figure 13.7.

# let (m,c,t1,t2) = create_conv 420 150 fe ;;
# display m ;;




Figure 13.7: Calculator window.


One click on the choice list changes both the displayed text and the direction of the conversion because all the event handling closures share the same state.

Where to go from here

Closures allow us to register handling methods with graphical components. It is however impossible to ``reopen'' these closures to extend an existing handler with additional behavior. We need to define a completely new handler. We discuss the possibilities for extending handlers in chapter 16 where we compare the functional and object-oriented paradigms.

In our application many of the structures declared have fields with identical names (for example txt). The last declaration masks all previous occurences. This means that it becomes difficult to use the field names directly and this is why we have declared a set of access functions for every type we have defined. Another possibility would be to cut our library up into several modules. From then on field names could be disambiguated by using the module names. Nonetheless, with the help of the access functions, we can already make full use of the library. Chapter 14 returns to the topic of type overlaying and introduces abstract data types. The use of overlaying can, among other things, increase robustness by preventing the modification of sensitive data fields, such as the parent child relationships between the components which should not allow the construction of a circular graph.

There are many possible ways to improve this library.

One criterion in our design for components was that it should be possible to write new ones. It is fairly easy to create components of an arbitrary shape by using new definitions of the mem and display functions. In this way one could create buttons which have an oval or tear-shaped form.

The few layout algorithms presented are not as helpful as they could be. One could add a grid layout whose squares are of variable size and width. Or maybe we want to place components alongside each other so long as there is enough room. Finally we should anticipate the possibility that a change to the size of a component may be propagated to its children.




Previous Contents Next ocaml-book-1.0/en/html/book-ora066.html0000644000000000000000000005104307453055400014471 0ustar Compilation Previous Contents Next

Compilation

The distribution of a language depends on the processor and the operating system. For each architecture, a distribution of Objective CAML contains the toplevel system, the bytecode compiler, and in most cases a native compiler.

Command Names

The figure 7.5 shows the command names of the different compilers in the various Objective CAML distributions. The first four commands are available for all distributions.


ocaml toplevel loop
ocamlrun bytecode interpreter
ocamlc bytecode batch compiler
ocamlopt native code batch compiler
ocamlc.opt optimized bytecode batch compiler
ocamlopt.opt optimized native code batch compiler
ocamlmktop new toplevel constructor

Figure 7.5: Commands for compiling.


The optimized compilers are themselves compiled with the Objective CAML native compiler. They compile faster but are otherwise identical to their unoptimized counterparts.

Compilation Unit

A compilation unit corresponds to the smallest piece of an Objective CAML program that can be compiled. For the interactive system, the unit of compilation corresponds to a phrase of the language. For the batch compiler, the unit of compilation is two files: the source file, and the interface file. The interface file is optional - if it does not exist, then all global declarations in the source file will be visible to other compilation units. The construction of interface files is described in the chapter on module programming (14). The two file types (source and interface) are differentiated by separate file extensions.

Naming Rules for File Extensions

Figure 7.6 presents the extensions of different files used for Objective CAML and C programs.


extension meaning
.ml source file
.mli interface file
.cmo object file (bytecode)
.cma library object file (bytecode)
.cmi compiled interface file
.cmx object file (native)
.cmxa library object file (native)
.c C source file
.o C object file (native)
.a C library object file (native)

Figure 7.6: File extensions.


The files example.ml and example.mli form a compilation unit. The compiled interface file (example.cmi) is used for both the bytecode and native code compiler. The C language related files are used when integrating C code with Objective CAML code. (12).

The Bytecode Compiler

The general form of the batch compiler commands are:

command options file_name

For example:
ocamlc -c example.ml
The command-line options for both the native and bytecode compilers follow typical Unix conventions. Each option is prefixed by the character -. File extensions are interpreted in the manner described by figure 7.6. In the above example, the file example.ml is considered an Objective CAML source file and is compiled. The compiler will produce the files example.cmo and example.cmi. The option -c informs the compiler to generate individual object files, which may be linked at a later time. Without this option, the compiler will produce an executable file named a.out.

The table in figure 7.7 describes the principal options of the bytecode compiler. The table in figure 7.8 indicates other possible options.

Principal options
-a construct a runtime library
-c compile without linking
-o name_of_executable specify the name of the executable
-linkall link with all libraries used
-i display all compiled global declarations
-pp command uses command as preprocessor
-unsafe turn off index checking
-v display the version of the compiler
-w list choose among the list the level of warning message (see fig. 7.9)
-impl file indicate that file is a Caml source (.ml)
-intf file indicate that file is a Caml interface (.mli)
-I directory add directory in the list of directories

Figure 7.7: Principal options of the bytecode compiler.



Other options
light process -thread (19, page ??)
linking -g, -noassert (10, page ??)
standalone executable -custom, -cclib, -ccopt, -cc (see page ??)
runtime -make-runtime , -use-runtime
C interface -output-obj (12, page ??)

Figure 7.8: Other options for the bytecode compiler.


To display the list of bytecode compiler options, use the option -help.

The different levels of warning message are described in figure 7.9. A message level is a switch (enable/disable) represented by a letter. An upper case letter activates the level and a lower case letter disables it.

Principal levels  
A/a enable/disable all messages
F/f partial application in a sequence
P/p for incomplete pattern matching
U/u for missing cases in pattern matching
X/x enable/disable all other messages
for hidden object M/m and V/v (see chapter 15)

Figure 7.9: Description of compilation warnings.


By default, the highest level (A) is chosen by the compiler.

Example usage of the bytecode compiler is given in figure 7.10.



Figure 7.10: Session with the bytecode compiler.


Native Compiler

The native compiler has behavior similar to the bytecode compiler, but produces different types of files. The compilation options are generally the same as those described in figures 7.7 and 7.8. It is necessary to take out the options related to runtime in figure 7.8. Options specific to the native compiler are given in figure 7.11. The different warning levels are same.

-compact optimize the produced code for space
-S keeps the assembly code in a file
-inline level set the aggressiveness of inlining

Figure 7.11: Options specific to the native compiler.


Inlining is an elaborated version of macro-expansion in the preprocessing stage. For functions whose arguments are fixed, inlining replaces each function call with the body of the function called. Several different calls produce several copies of the function body. Inlining avoids the overhead that comes with function call setup and return, at the expense of object code size. Principal inlining levels are:

  • 0 : The expansion will be done only when it will not increase the size of the object code.
  • 1 : This is the default value; it accepts a light increase on code size.
  • n>1 : Raise the tolerance for growth in the code. Higher values result in more inlining.

Toplevel Loop

The toplevel loop provides only two command line options.
  • -I directory: adds the indicated directory to the list of search paths for compiled source files.
  • -unsafe: instructs the compiler not to do bounds checking on array and string accesses.
The toplevel loop provides several directives which can be used to interactively modify its behavior. They are described in figure 7.12. All these directives begin with the character # and are terminated by ;;.

#quit ;; quit from the toplevel interaction
#directory directory ;; add the directory to the search path
#cd directory ;; change the working directory
#load object_file ;; load an object file (.cmo)
#use source_file ;; compile and load a source file
#print_depth depth ;; modify the depth of printing
#print_length width ;; modify the length of printing
#install_printer function ;; specify a printing function
#remove_printer function ;; remove a printing function
#trace function ;; trace the arguments of the function
#untrace function ;; stop tracing the function
#untrace_all ;; stop all tracing

Figure 7.12: Toplevel loop directives.


The directives dealing with directories respect the conventions of the operating system used.

The loading directives do not have exactly the same behavior. The directive #use reads the source file as if it was typed directly in the toplevel loop. The directive #load loads the file with the extension .cmo. In the later case, the global declarations of this file are not directly accessible. If the file example.ml contains the global declaration f, then once the bytecode is loaded (#load "example.cmo";;), it is assumed that the value of f could be accessed by Example.f, where the first letter of the file is capitalized. This notation comes from the module system of Objective CAML (see chapter 14, page ??).

The directives for the depth and width of printing are used to control the display of values. This is useful when it is necessary to display the contents of a value in detail.

The directives for printer redefinition are used to install or remove a user defined printing function for values of a specified type. In order to integrate these printer functions into the default printing procedure, it is necessary to use the Format library(8) for the definition.

The directives for tracing arguments and results of functions are particularly useful for debugging programs. They will be discussed in the chapter on program analysis (10).

Figure 7.13 shows a session in the toplevel loop.



Figure 7.13: Session with the toplevel loop.


Construction of a New Interactive System

The command ocamlmktop can be used to construct a new toplevel executable which has specific library modules loaded by default. For example, ocamlmktop is often used for pulling native object code libraries (typically written in C) into a new toplevel.

ocamlmktop options are a subset of those used by the bytecode compiler (ocamlc):
-cclib libname, -ccopt option, -custom, -I directory -o executable_name
The chapter on graphics programming (5, page ??) uses this command for constructing a toplevel system containing the Graphics library in the following manner:
ocamlmktop -custom -o mytoplevel graphics.cma -cclib \ 
           -I/usr/X11/lib -cclib -lX11
This command constructs an executable with the name mytoplevel, containing the bytecode library graphics.cma. This standalone executable (-custom, see the following section) will be linked to the library X11 (libX11.a) which in turn will be looked up in the path /usr/X11/lib.


Previous Contents Next ocaml-book-1.0/en/html/book-ora210.html0000644000000000000000000000330307453055401014455 0ustar Introduction Previous Contents Next

Introduction

Independently of the development of Objective CAML, several extensions of the language appeared. One of these, named O'Labl, was integrated with Objective CAML, starting with version 3.00.

This appendix describes briefly the new features offered in the current version of Objective CAML at the time of this writing, that is. Objective CAML 3.04. This version can be found on the CD-ROM accompanying this book. The new features include:
  • labels;
  • optional arguments;
  • polymorphic constructors;
  • the ocamlbrowser IDE;
  • the LablTk library.
The reader is referred to the Objective CAML reference manual for a more detailed description of these features.


Previous Contents Next ocaml-book-1.0/en/html/book-ora117.html0000644000000000000000000002175507453055400014475 0ustar Exception handling in C and in Objective CAML Previous Contents Next

Exception handling in C and in Objective CAML

Different languages have different mechanisms for raising and handling exceptions: C relies on setjmp and longjmp, while Objective CAML has built-in constructs for exceptions (try ... with, raise). Of course, these mechanisms are not compatible: they do not keep the same information when setting up a handler. It is extremely hard to safely implement the nesting of exception handlers of different kinds, while ensuring that an exception correctly ``jumps over'' handlers. For this reason, only Objective CAML exceptions can be raised and handled from C; setjmp and longjmp in C cannot be caught from Objective CAML, and must not be used to skip over Objective CAML code.

All functions and macros introduced in this section are defined in the header file fail.h.

Raising a predefined exception

From a C function, it is easy to raise one of the exceptions Failure, Invalid_argument or Not_found from the Pervasives module: just use the following functions.
failwith(s) : raise the exception Failure(s)
invalid_argument(s) : raise the exception Invalid_argument(s)
raise_not_found() : raise the exception Not_found
In the first two cases, s is a C string (char *) that ends up as the argument to the exception raised.

Raising a user-defined exception

A registration mechanism similar to that for closures enables user-defined exceptions to be raised from C. We must first register the exception using the Callback module's register_exception function. Then, from C, we retrieve the exception identifier using the caml_named_value function (see page ??). Finally, we raise the exception, using one of the following functions:
raise_constant(e) raise the exception e with no argument,
raise_with_arg(e,v) raise the exception e with the value v as argument,
raise_with_string(e,s) same, but the argument is taken from the C string s.

Here is an example C function that raises an Objective CAML exception:

#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/fail.h>

value divide (value v1,value v2)
{
CAMLparam2(v1,v2);
if (Long_val(v2) == 0)
raise_with_arg(*caml_named_value("divzero"),v1) ;
CAMLreturn Val_long(Long_val(v1)/Long_val(v2)) ;
}


And here is an Objective CAML transcript showing the use of that C function:


# external divide : int -> int -> int = "divide" ;;
external divide : int -> int -> int = "divide"
# exception Division_zero of int ;;
exception Division_zero of int
# Callback.register_exception "divzero" (Division_zero 0) ;;
- : unit = ()
# divide 20 4 ;;
- : int = 5
# divide 22 0 ;;
Uncaught exception: Division_zero(22)


Catching an exception

In a C function, we cannot catch an exception raised from another C function. However, we can catch Objective CAML exceptions arising from the application of an Objective CAML function (callback). This is achieved via the functions callback_exn, callback2_exn, callback3_exn and callbackN_exn, which are similar to the standard callback functions, except that if the callback raises an exception, this exception is caught and returned as the result of the callback. The result value of the callback_exn functions must be tested with Is_exception_result(v); this predicate returns ``true'' if the result value represents an uncaught exception, and ``false'' otherwise. The macro Extract_exception(v) returns the exception value contained in an exceptional result value.

The C function divide_print below calls the Objective CAML function divide using callback2_exn, and checks whether the result is an exception. If so, it prints a message and raises the exception again; otherwise it prints the result.

#include <stdio.h>
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/callback.h>
#include <caml/fail.h>

value divide_print (value v1,value v2)
{
CAMLparam2(v1,v2) ;
CAMLlocal3(div,dbz,res) ;
div = * caml_named_value("divide") ;
dbz = * caml_named_value("div_by_0") ;
res = callback2_exn (div,v1,v2) ;
if (Is_exception_result(res))
{
value exn=Extract_exception(res);
if (Field(exn,0)==dbz) printf("division by 0\n") ;
else printf("other exception\n");
fflush(stdout);
if (Wosize_val(exn)==1) raise_constant(Field(exn,0)) ;
else raise_with_arg(Field(exn,0),Field(exn,1)) ;
}
printf("result = %d\n",Long_val(res)) ;
fflush(stdout) ;
CAMLreturn Val_unit ;
}

# Callback.register "divide" (/) ;;
- : unit = ()
# Callback.register_exception "div_by_0" Division_by_zero ;;
- : unit = ()
# external divide_print : int -> int -> unit = "divide_print" ;;
external divide_print : int -> int -> unit = "divide_print"
# divide_print 42 3 ;;
result = 14
- : unit = ()
# divide_print 21 0 ;;
division by 0
Uncaught exception: Division_by_zero


As the examples above show, it is possible to raise an exception from C and catch it in Objective CAML, and also to raise an exception from Objective CAML and catch it in C. However, a C program cannot by itself raise and catch an Objective CAML exception.


Previous Contents Next ocaml-book-1.0/en/html/book-ora083.gif0000644000000000000000000000351607452056112014273 0ustar GIF89aUUU999rrr!,X0I8ͻR$0hltm\C @a4 E`TpШ41095MI>MhO7#V$U ~DRN| Jd󟺅a[I6Rh/ݑwI|%-[μ0 zueߡ:_Ǯ(6~{Wӿ>i1A1. քOP RkK%lՅx]x‡b <yx+R A#-?izARCwGҤKG}I9,`|%<+҅2 ZF!`YD] I1f+5%Ş?bBě` gOcjP  jFl W6$F*щ:Vʪt*Wm"koBk&Wl?Ks6lTڐ]B̈́'&C^`v;;q4ᚻ4U;~ ^Cދ/.ī`VHp|j&b¡204Bb>Na1u?~L+qo$2.+qL'߼‘AڜcgT.@[&L]&]bXZZ2=zM ,_A"Q`fޡWV~1torSeU5 uTNw (5OmxU}M#^> Z5kVM))dOC`} bVyF[oRw6;zCJ5}xyH &^v)Y~(-$Ix&sYQ@0g&4RH/ Q(|!\(CkѰ'wvI>a!F<">^lܨB//(Vahr-"(FkgC5rc:ำoqAϐx- {QY8ݼS2E^aLCq+]/d& R>ƴ$('AI&(Bo>9Ոr7lKkMY'=J=UOףtk]PQ7Jh|SIxre")iӒBw@%^Pci0LF5rl#5 RN$6i8Jc8jUe5TJn T q%89tMEK--gAt$ķZί`s#T"=;- 3Y9V2G 7/qSёIlL8EڼTr԰zʴM`T`,WCUFAx42ԡC(Z+PԨneHpyk Chapter outline Previous Contents Next

Chapter outline

This chapter introduces the tools that allow interoperability between Objective CAML and C by building executables containing code fragments written in both languages. These tools include functions to convert between the data representations of each language, allocation functions using the Objective CAML heap and garbage collector, and functions to raise Objective CAML exceptions from C.

The first section shows how to call C functions from Objective CAML and how to build executables and interactive toplevel interpreters including the C code implementing those functions. The second section explores the C representation of Objective CAML values. The third section explains how to create and modify Objective CAML values from C. It discusses the interactions between C allocations and the Objective CAML garbage collector, and presents the mechanisms ensuring safe allocation from C. The fourth section describes exception handling: how to raise exceptions and how to handle them. The fifth section reverses the roles: it shows how to include Objective CAML code in an application whose main program is written in C.

Note


This chapter assumes a working knowledge of the C language. Moreover, reading chapter 9 can be helpful in understanding the issues raised by automatic memory management.



Previous Contents Next ocaml-book-1.0/en/html/book-ora110.html0000644000000000000000000000475607453055400014470 0ustar To Learn More Previous Contents Next

To Learn More

The reference book on lexical analysis and parsing is known affectionately as the ``dragon book'', a reference to the book's cover illustration. Its real name is Compilers: principles, techniques and tools ([ASU86]). It covers all aspects of compiler design and implementation. It explains clearly the construction of automata matching a given context-free grammar and the techniques to minimize it. The tools lex and yacc are described in-depth in several books, a good reference being [LMB92]. The interesting features of ocamllex and ocamlyac with respect to their original versions are the integration of the Objective CAML language and, above all, the ability to write typed lexers and parsers. With regard to streams, the research report by Michel Mauny and Daniel de Rauglaudre [MdR92] gives a good description of the operational semantics of this extension. On the other hand, [CM98] shows how to build such an extension. For a better integration of grammars within the Objective CAML language, or to modify the grammars of the latter, we may also use the camlp4 tool found at:

Link


http://caml.inria.fr/camlp4/







Previous Contents Next ocaml-book-1.0/en/html/book-ora073.html0000644000000000000000000000310707453055400014465 0ustar Chapter Outline Previous Contents Next

Chapter Outline

This chapter describes the collection of libraries in the Objective CAML distribution. Some have been used in previous chapters, such as the Graphics library (see chapter 5), or the Array library. The first section shows the organization of the various libraries. The second section finishes describing the preloaded Pervasives module. The third section classifies the set of modules found in the standard library. The fourth section examines the high precision math libraries and the libraries for dynamically loading code.


Previous Contents Next ocaml-book-1.0/en/html/book-ora020.gif0000644000000000000000000000301507452056111014253 0ustar GIF89a!,ڋ<} `ʪ)LOqp HL& c*PKTj{'&z܊#LB C?>/] u'xvH"h (4DXxtIXPhTڕz yj$FYd%ZJ[E|ʸKL}m ZzS*R'gN=.M}/8sj3ZS^,k؏nqsAT+Gf`u w0ݛ6lq K2h^̸r1zWLڙhgtV1J6 "/]D=6jmz5kPoyG,2k׫<}p$WRcxP_KsɨP $[)dh:{:kC&:j5D͔0^q#``f^':Qc4=t$UV߳]GoQWK;:(w$,n\Rs7.eI9 >#.QqHm- hFsׅpDȀG#%IM<%XWG>6i-&K]4 N3atCfBvX(Z fțyW0\EN2eIvɢa%"k8bݗ _ǖP>iX/qdHӛ7^kjAڌZ嚳%[eΈ^~䋕XJVm@NQCL%*3CbSGR:fҰ:x3a ūq:$-~.rɝ>q+zt\">y=z%SDJe  U *0a Hh'g9c\;HrBtXqɸӴ{մqi;fߒ.2.7o*O,¸EJbDW;HI I)n2Bq#N$_SgA;}L IwRiј/mXF3jݥZY8M Fz&#!n%z3Bա.c6^Hw}G-K7kć̐vЉk;Z5T|^ø?i S} ގ3#LwoӀsCNh#lI]xvuat!1^܆$,JSC,6[HA$S`6(B*ȉԋ8\WxE†pt 6DpDt阢8:b OQHeT_Ԡ(.Zش󱑅 t9u sZ $dE1”tzs!%]9BLÏnHJF@$KʰT]XK+&$//^S /IGdP8&3'EW5yMifSt3iEp.A$g[9CtPdy.x:L'N|S f;ocaml-book-1.0/en/html/book-ora052.gif0000644000000000000000000000166007452056111014264 0ustar GIF89at!,t޼H扦  Lעk& DL*{ǥ 5 ԪӼj, yrjlN?[bgx'hxxHؗxHP aiI!9BɠY9zyC*Pz*{j:КZK 6,!| Yq[il} -8-' ]]ˊޝN~ (q^~'v4W߫t@vVtǭx @./nQtLk俎\sN6'i`=}'Jb TLtJ Rc:NW aY"{0ȴeײ T,{L6i>6锩hdbZ\sµ+r%4#*LɊJ2Vtl_5-cAڌ2?vKԀ[ܷ&8fn.+˼.p.%KG ,!{6!kTlqg\_,r(*p;ocaml-book-1.0/en/html/book-ora077.html0000644000000000000000000005515007453055400014476 0ustar Other Libraries in the Distribution Previous Contents Next

Other Libraries in the Distribution

The other libraries provided with the Objective CAML language distribution relate to the following extensions:
  • graphics, with the portable Graphics module that was described in chapter 5;
  • exact math, containing many modules, and allowing the use of exact calculations on integers and rational numbers. Numbers are represented using Objective CAML integers whenever possible;
  • regular expression filtering, allowing easier string and text manipulations. The Str module will be described in chapter 11;
  • Unix system calls, with the Unix module allowing one to make unix system calls from Objective CAML. A large part of this library is nevertheless compatible with Windows. This bibliography will be used in chapters 18 and 20;
  • light-weight processes, comprising many modules that will largely be described and used in chapter 19;
  • access to NDBD databases, works only in Unix and will not be described;
  • dynamic loading of bytecode, implemented by the Dynlink module.
We will describe the big integer and dynamic loading libraries by using them.

Exact Math

The big numbers library provides exact math functions using integers and rational numbers. Values of type int and float have two limitations: calculations on integers are done modulo the greatest positive integer, which can cause unperceived overflow errors; the results of floating point calculations are rounded, which by propagation can lead to errors. The library presented here mitigates these defects.

This library is written partly in C. For this reason, you have to build an interactive loop that includes this code using the command:
ocamlmktop -custom -o top nums.cma -cclib -lnums
The library contains many modules. The two most important ones are Num for all the operations and Arith_status for controlling calculation options. The general type num is a variant type gathering three basic types:

type num = Int of int
| Big_int of big_int
| Ratio of ratio
The types big_int and ratio are abstract.

The operations on values of type num are followed by the symbol /. For example the addition of two num variables is written +/ and will be of type num -> num -> num. It will be the same for comparisons. Here is the first example that calculates the factorial:

# let rec fact_num n =
if Num.(<=/) n (Num.Int 0) then (Num.Int 1)
else Num.( */ ) n (fact_num ( Num.(-/) n (Num.Int 1)));;
val fact_num : Num.num -> Num.num = <fun>
# let r = fact_num (Num.Int 100);;
val r : Num.num = Num.Big_int <abstr>
# let n = Num.string_of_num r in (String.sub n 0 50) ^ "..." ;;
- : string = "93326215443944152681699238856266700490715968264381..."


Opening the Num module makes the code of fact_num easier to read:

# open Num ;;
# let rec fact_num n =
if n <=/ (Int 0) then (Int 1)
else n */ (fact_num ( n -/ (Int 1))) ;;
val fact_num : Num.num -> Num.num = <fun>


Calculations using rational numbers are also exact. If we want to calculate the number e by following the following definition:
e = lim
 
m ->



1 +
1

m



m



 
We should write a function that calculates this limit up to a certain m.

# let calc_e m =
let a = Num.(+/) (Num.Int 1) ( Num.(//) (Num.Int 1) m) in
Num.( **/ ) a m;;
val calc_e : Num.num -> Num.num = <fun>
# let r = calc_e (Num.Int 100);;
val r : Num.num = Ratio <abstr>
# let n = Num.string_of_num r in (String.sub n 0 50) ^ "..." ;;
- : string = "27048138294215260932671947108075308336779383827810..."


The Arith_status module allows us to control some calculations such as the normalization of rational numbers, approximation for printing, and processing null denominators. The arith_status function prints the state of these indicators.

# Arith_status.arith_status();;

Normalization during computation --> OFF
(returned by get_normalize_ratio ())
(modifiable with set_normalize_ratio <your choice>)

Normalization when printing --> ON
(returned by get_normalize_ratio_when_printing ())
(modifiable with set_normalize_ratio_when_printing <your choice>)

Floating point approximation when printing rational numbers --> OFF
(returned by get_approx_printing ())
(modifiable with set_approx_printing <your choice>)

Error when a rational denominator is null --> ON
(returned by get_error_when_null_denominator ())
(modifiable with set_error_when_null_denominator <your choice>)
- : unit = ()


They can be modified according to the needs of a calculation. For example, if we want to print an approximate value for a rational number, we can obtain, for the preceding calculation:

# Arith_status.set_approx_printing true;;
- : unit = ()
# Num.string_of_num (calc_e (Num.Int 100));;
- : string = "0.270481382942e1"


Calculations with big numbers take longer than those with integers and the values occupy more memory. Nevertheless, this library tries to use the most economical representations whenever possible. In any event, the ability to avoid the propagation of rounding errors and to do calculations on big numbers justifies the loss of efficiency.

Dynamic Loading of Code

The Dynlink module offers the ability to dynamically load programs in the form of bytecode. The dynamic loading of code provides the following advantages:
  • reduces the size of a program's code. If certain modules are not used, they are not loaded.
  • allows the choice at execution time of which module to load. According to certain conditions at execution time you choose to load one module rather than another.
  • allows the modification of the behavior of a module during execution. Here again, under some conditions the program can load a new module and hide the old code.
The interactive loop of Objective CAML already uses such a mechanism. It is convenient to let the programmer have access to it as well.

During the loading of an object file (with the .cmo extension), the various expressions are evaluated. The main program, that initiated the dynamic loading of the code does not have access to the names of declarations. Therefore it is up to the dynamically loaded module to update a table of functions used by the main program.

Warning


The dynamic loading of code only works for object files in bytecode.


Description of the Module

For dynamic loading of a bytecode file f.cmo, we need to know the access path to the file and the names of the modules that it uses. By default, dynamically loaded bytecode files do not have access to the paths and modules of the libraries in the distribution. Thus we have to add the path and the name of the required modules to the dynamic loading of the module.

init : unit -> unit
    initialize dynamic loading
add_interfaces : string list -> string list -> unit
    add the names of modules and paths for loading
loadfile : string -> unit
    load a bytecode file
clear_avalaible_units : unit -> unit
    empty the names of loadable modules and paths
add_avalaible_units : (string * Digest.t) list -> unit
    add the name of a module and a checksum for loading without needing the interface file
allow_unsafe_modules : bool -> unit
    allow the loading of files containing external declarations
loadfile_private : string -> unit
    the loaded module is not accessible to modules loaded later
The checksum of an interface .cmi can be obtained from the extract_crc command found in the catalog of libraries in the distribution.

Figure 8.10: Functions of the Dynlink module.


Many errors can occur during a request to load a module. Not only must the file exist with the right interface in one of the paths, but the bytecode must also be correct and loadable. These errors are gathered in the type error used as an argument to the Error exception and to the error function of type error -> string that allows the conversion of an error into a clear description.

Example

To write a small program that allows us to illustrate dynamic loading of bytecode, we provide three modules:
  • F that contains the definition of a reference to a function f;
  • Mod1 and Mod2 that modify in different ways the function referenced by F.f.
The F module is defined in the file f.ml:

let g () =
print_string "I am the 'f' function by default\n" ; flush stdout ;;
let f = ref g ;;


The Mod1 module is defined in the file mod1.ml:

print_string "The 'Mod1' module modifies the value of 'F.f'\n" ; flush stdout ;;
let g () =
print_string "I am the 'f' function of module 'Mod1'\n" ;
flush stdout ;;
F.f := g ;;


The Mod2 module is defined in the file mod2.ml:

print_string "The 'Mod2' module modifies the value of 'F.f'\n" ; flush stdout ;;
let g () =
print_string "I am the 'f' function of module 'Mod2'\n" ;
flush stdout ;;
F.f := g ;;


Finally we define in the file main.ml, a main program that calls the original function referenced by F.f, loads the Mod1 module, calls F.f again, then loads the Mod2 module and calls the F.f function one last time:

let main () =
try
Dynlink.init () ;
Dynlink.add_interfaces [ "Pervasives"; "F" ; "Mod1" ; "Mod2" ]
[ Sys.getcwd() ; "/usr/local/lib/ocaml/" ] ;
!(F.f) () ;
Dynlink.loadfile "mod1.cmo" ; !(F.f) () ;
Dynlink.loadfile "mod2.cmo" ; !(F.f) ()
with
Dynlink.Error e -> print_endline (Dynlink.error_message e) ; exit 1 ;;

main () ;;
The main program must, in addition to initializing the dynamic loading, declare by a call to Dynlink.add_interfaces the interface used.

We compile all of these modules:
$ ocamlc -c f.ml
$ ocamlc -o main dynlink.cma f.cmo main.ml
$ ocamlc -c f.cmo mod1.ml
$ ocamlc -c f.cmo mod2.ml
If we execute program main, we obtain:
$ main
I am the 'f' function by default
The 'Mod1' module modifies the value of 'F.f'
I am the 'f' function of module 'Mod1'
The 'Mod2' module modifies the value of 'F.f'
I am the 'f' function of module 'Mod2'
Upon the dynamic loading of a module, its code is executed. This is demonstrated in our example, with the outputs beginning with The 'Mod.... The possible side effects that it contains are therefore reflected at the level of the program that caused the code to be loaded. This is why the different calls to F.f call different functions.

The Dynlink library offers the basic mechanism for dynamically loading bytecode. The programmer still has to manage tables such that the loading will really be effective.






Previous Contents Next ocaml-book-1.0/en/html/book-ora044.gif0000644000000000000000000000363407452056111014270 0ustar GIF89aUUUrrr!,H0I8ͻ`(di( lp,tmx| Bh!\Al:SP JL5f8L37ngn <Uu~~Re}$wG#ymVdz|ųǛɇ̈́ьϓ'g{ը r"ﺒ CU? D3vʭ ,R)0TIn~ƖXn%/oDb4psp]gd,$;aL ^rF4-vmtq$>:܊&hO^o?`H <P50N| Z0~4Rݰ *dIm4(τC Wx06/Q2Zgќ wh s̰gDA!x21cN$Hx8?M'.2$L^ (F*hF66.?ycָ4'Uqm+юG!'ȒQ(u(IC") <2ďyR 64r&#PR)tJU%!A21$8"vPط'xOe=aJ݊@ݧ?c#`(q֋W)1.̎m$߂YQs؞V>ei'oj<0|A(yt⹶ +à>iͻJ&XoBx/Xu0|,\243:v*4x㳈7~K$X&sdz|;RSD$=*ՑTMP"5;W5WjAbuNVnu]mkZ*I\tT‰#?y]KMb:@W9SؔK9N)l,s?Df˥SfTuRa NLy"YKUAaUBzDh4Zs5UNVaH^&燉(ue^1ϭkiE~3ͮ9YD#&K F+CB9[oMp;ocaml-book-1.0/en/html/book-ora015.gif0000644000000000000000000000316007452056110014257 0ustar GIF89a!,޼zHቦʶkI! ;K̦ )ʁb9Z։gs7 m;g͛M?s8sˆx2iX(Ti鄉9JHEzid3zzc;d+C+z۲EV# X|J<ʫ|,=2]Me~^{>\>.oIm^zy߼jn"C;9W|بbljD Hģ*P#ˁ Yp9  edW ̒Aљb(MF)-ӕI~(=iUgQS1m qꊫ-Ůۊ+,gv )6Y )\xs˦*{qI0ĸǢ"[xaf5Z2fbQ̪Isڗfbym0f-wq_ gv~yr5K벊[W_}F=Wp-O}[{￱/M_yI7z]G_ٙUդ WzE!|aZ"FWBLUHtz)N Ҡ1p0⁣V4b! iߋG)i$2H^R"5NEeGfR]i ]>{ס`.aXԍI虛. y7}zehmbG\%Ǚo|6ߣ(qSzjf!OJ`u~~JaXjjxZ,T1֥R;jʛzm> nPfb윖[-'.1h+pZBoi .,{|f pm|[ Ho{h$ :r)M22nZBs]+GmKF M4e;Ð4 #"uTP4:1+iw]cɫu$Mo=K4 -{ #85, yko&5䊿#@P@9(?6p8zA] ~y1xIr>{ y.L|W~ɏOKS=տrmwDBQЅٳ]`89㡈C @D+4-b@<Fhg 0(*ZlS.ł,L$#@!ocdFjocڒŬ2Y=ebLgQf o#1\6\œI>P_z$Oz>mzr] 4GMЉ-^EnUPFnVm#\#drg3GJ¥VUh0ҕfz)j`j t2NJ+'@z2ɂ1mJ{4_ž/y\޼ x^cB! |U7/ b׽µIqwUa[!BqpARe?MτC!v@$iK{Ʈ45~2<"@~. 290t hnOr(Aq}2(/v0Kt.x+d=7>^.ˢGI4O[$C?c'.!2$ҋ%YҒb̜'Nh'L,&NL&:fzi=hR,3 "i,*K ͆Q2OԂ3QI#%}];Æbud"`צϊd&X;iMylEiY筎ߣZ?2N2uD3[J6FL3JEQp?iȅF{Jkƽn] R7$mdM%p,gW/^m;[z`IFɋf7ribFuVk"p~aG6J+5 *C}E5 i)XM[Z?L fhU؉~BtFb8?vlVTh 9Wv8S9ZL&r䜬aH O2qrf7WV%ڜݽs]>Òzs] Z͂kh?#:WZugD4뜨)յ6=4f/h+a;Whxiom^h1g MtN9<2ZY5B̮#j=VѦvCO*5>v% W .MarcYoalc*Kk9f12BuCYJjǷmR P}7ǩ@1o:W/*o\dc#2lwj8h֕{4J~n޶Z uP.9cڋF"x64,hJ9BQ%d"}驅:ä?p 7Tt>xt#->L4K=yЏҐ[J4UgȈu)ESxuu6ܸu1剡:6(l8vi u"WF͸lx2rGx$H^L)XVRk (3H}3PCjS9`>oqSG(`id}{Ho`vx+wy8')yq%yHF"qR'o5 1p)fpCJl(,CYCYvG THgg%6IA;);g Y:?FUXpbVc j@V|`ՎEܷ,*א1yZf74 ɉY/xxUȇ菃U%焩&Dx(XP儓7v;;)#;X{㨍\\h\Ù\Yt(踜~vǕ؜91(Ee"؝_ FFu[[C֞ᩝR7Ue: z [[Awe*u uz5^j%w4w#h/2*&4680;Jق0M r+$Njfڨ9RʤYâEzi fz ;hlڦn A2tZvzxz|rLzJeMPٌ_YI[xs3G:" Ҩ@|>odMXzrTiR:;"*^̘z8 ةJ5&j+s:_}"Id|ԙS=ʺԬ&!Zz'XY0Y'OzHZ'wu0wtwJyt9 Vʰ )Zo ";$T(*,%ñ0 ]/K57۲:98+.xb.#5IñD+ 3˴s/_"!TsDavKV+,Uiek%ZV{@՛ 6oTcKy{zڶr[k2nTYa T2q/S}Wt0x Y@CeٹE})dk;dNu2VkbT&')x54iǼͻ,X#~Xz*[KFr;k|+s2}"w[HQ{r{[EY⋽/)m lUeYdR{!"3oboغ\5~ejJ¾Si"ܚ ,3"¹nJ!8":C %E"6EO,Msۧ6{wV^\bR1{ƿU2 np<k0Wq|xzsġȂ<Ȅ\Ȇ|ȈȊȌȎȂL᳔\ ɖ|ɘL ;ocaml-book-1.0/en/html/book-ora071.gif0000644000000000000000000002320007452056112014260 0ustar GIF89at*}}}{{{yyywwwuuusssWWW333pppnnnllljjjĊ!!Q,tQ*ma)`(_&^%]$\#["!Y X (zXȰ!C+#Jءŋhq# CBɒR\r˗0ajI  㠧O"@BhH"´Ӧ J*UիX.ʵ+ `Ê ٳhӢM۷p"Kݻvw/6@?+^ǐL2e3kܣϞMz4ӨSk;b˞M6ms{w .$Q+WysQP<*ɗY }`Պ_6m_?{=L0#wlbڀfh%xk fۃۄF܅܆9 $ljp ,GbvXmywz@ސyח|G~ h 6`\Vᗺe(fqr%)"l0FƜtIՎM@)hDG"1ɘAiQ a v`ØPg~fm .)5Z+Ʈ f 'o+l\v kh' z2A*Qʙajq\jcaJbmꬲ檯/ ,pm\Q m @ #E찄"puбs9+r{Ek& +2 kiy+  {.%b!;/n"v\:+,mkj!%_@&{Ϭ .P'.'0N= tB] 2Aݜ&Rob][w/-ٴ;;Q v|̄U/F 8Ox/~f߇>&,BM4k z!Ɖ>:7~:걪N+r`G0ىv˝(:wi @º ,{ >}|=8A UBvV E#C '!sCˇ[rm7!]~8_ 5ЀC= -2 hL61 @:1BfN( `Ͱbx1Bp![#,@N0IIV]"boH$*kmIVTņi1b]HKލfK4s0<082if"@P$PN  x~l"FY7V*[WN\e-o9F]沗>c0~.l@:=h28uD jvi TȤt8]8Er\eJw3#=h3Sf? ОLP ѢFTHhJ:B |$I?3nP) XWt1L%X29 NCQԾ&}j Kv Mlb IJ౐, | (ᲘU6YHhGKҚHHjWֲlgKᶸͭnw"OK\FQr2:ХQVrP5$iZpL3bҀ|Kͯ~L턂;'L [ΰ7{ GL(NW0gL8αw@L"HN&;PL*[Xβ.{`L2hN6pL:xs>πMBЈNF;ѐ'MJ[ϊ7N{ӠG LԨNWVЦ~gMZ֪^MlbSZׇof;Ŏv#lBζ-nMr[Nn@9z?݉H6~[V7a?xv ˽p{/|N0~'3QmmF'Wr[\Ƹ4~cNAܾ8As%oʁ>;EtoX:#ֺ>=28g R}[7N/ki{קnBv/xƻҊo|? ~/^yPWЇMotMz3z+o^weƽ%{ ݯ=|No~}{[M+Gѿx򎖹_~M<{?Z_g~Wrwz޷vrnҧ~L7Vv} |}g%֗gx$xz7x"jxWp0|-i,Xy7':~8hi7n׃>hDBy:HȄTjNS'8WHhAG{ǃ=bhaG[l}_kFx)y+hWv|g\~;szI7vOO׀(~_ZQ}X~Lj։~r`7nhׅt{Kh5Vwȁv~8X}J8_KW_¸wǘ|QWo!vJ֍}&Ry X{wxX&wu3VRp!X\LJ鸌g_숐XTwU yjsH i'ǏR85׍1 j?69y3iA)ȋKiMɄոn8Y▔xVY4,ْ @)BHei\ٕrh~ zIz)tYsIir/)i喷w_%=t q} LٙI⨙9)W+[ | وG8I)fyt h핛y`ɜ]i9oi$H霴ɝȹC7ȚiI )ٞ؏ًw-ٝʙ9Xgy9y9iYGZ}zu*#J .&(zp 91j:zӉw)t8:Jڣ+TXMwky>uk<X xTڞ\^J[zj (ٚǦm@!Bʠهc:)qezxj a )ٚpz%Np CJqfsvʩ٤L6WlʫM:8WypJ- ʬf-ryJ}|J:7&&ɚʮꚪJ)'q :xs5YzVrOy˝+ʑ*ܷ*,;ڲ $k[i2ےމ س;˛U &#Ii[֦VɖFNqp[^h?ɗR[hegKhi;nDY`(^ق}gkwg{˶kZ}[{*[;5ȸۘS9Yv{V{hg:u)۹kӚ+[ś;KZ;h۹{  F֫hU}˺KsGƂ۽c|W|Fː󋿨zYhɽ^) iP k콵f;v8$%Lv+L**٭)Tfvi*{'FEL,,[l͞ʧUwF,&F{W׌Ͱη|nL<klL,n|ϲ |ۼ!˾l{ȝ'ȨL*FM`KξVeȝs j&*<5|k| ]Ҝ,h"=8nMtOӄUmZIlumS,l |d~?])!MKԸvJrJ ppE:+{6ؖ+ô6٠]{;\m_0 ٨ٍۧ^! | ?|ۿ[Բ+|FQ;ܪ mh} ݥ-ZǸܙ K;v=m+Nݾ6:bw>xg X-Ļt M>"u. ˿5;Ҽ3n;~ιMϯkI^ޥrA \ommjg_FSo]iU[e=__U ysBG ōi ^N.yMg5}(ۡԣ&0 ]mԟ=뱳5ӈs^k,um^Þf}sބyȱNIňt؅pz׋)-c emlj%-RsM˛ nsJ޲՗(bF^ۓߪ .7\9jծO?չlWLj#ߪ,!nCTM$ɯ.C(\Jj37jJ NnZ|R?L\\]ib?Woۊj5j}%8v@)9IYIɹi*:jhz:i*K[{,+lKx̜(}\[]݉Y=U.Nm]i*[K <[|Ϥ +]"S ҆S-o*{ʠA".#hM>!=6E[J7Ar":i(|S䰞"#Ks*e-2f™4S5hE&J1e_KPGc Jmڝf&555^]uR prm\p L92c˅(S99KΖ4[c]u5ٓ`϶]eܪUVH!v{ϭo#fs警K~m=|-S2?$vO}y].H[ӏ]7 Aw DՃ `er!'x9M%w%=b%4\7$1JWK$Y@'dM FdU$Gf%U'厛i d]^嚦E`jxXfX:Ycs(眍y)$4KJ4.Ah R*ajG觎ԋ'CZ*Ya\G**~T4bY&5үVk$eбn*PhzLQbjI2ʬ j[Y4K.;jlRo{o9ѹ'uo$p< Iۯ$0f*%rpq#%abQA.p3%+ A'qgI[,<{3;OM5=-vtW՛)ZTDK]h[ں5wy#҂9o]$? w]ݮd4`t.7Sk6Qs}I2ݍMBpsc_)z7bg[堿(,>3EzAe aE{CkS÷"0D'd=ן'?N(MnEy_Vԧu-xC33vYN=:#W!oW BƐZb@xjUA"b59L o ""fL`ʒضuɉ_1E*2ŊbƯ[n" (F.qt [8oOesڵ88&aHtiT Fedg"5iG.Љ H3-z~>i^rF4V2' KXC$پYK[ ~<8ƛXVy*RnLmf$U 5.U@L9)/uTJY]8#C$U-@5~e 'ކ z T=NR2'z~rtKR ZInY+C@SQy/l$iHM͌.ZFS}T(63jUVC%*W͸ճfdj[/Ua*]ž2K{j_}*fŮmI_$(P [ 6՛9Q 2=6Ll(KW&s^gBZ3,f*Zz;L>ո&pEq; o7eu*j$\m([djdEɰʜ M~"NcպFGrċF!8&EtuIYki!l˟wUQ4+/WR09 ʕ.0%fK պa PJUqb; b/zxk<Gt8t}x!2&xe+/lbB ͈ifV1=%lNwW֋eDwHҜs'NoYςrK8C!9!Zu,E3:<֕ cHoH5FB## NPfP-Qs[=h8:iR׊;`MlUvHe=򱶷#kcCU9&ek(ק-idʂmD?[w淫Fzχ8z]o0' \jGxMieǷ]t#H\s!]ҡ[>2i\r\#uEi"iT\tr6oeQ4ɭpI1'3H!5;k*36`w; #bAC֛^E O, ]Sw |gaxj${yW3ܣMuo7;¤Elg7.ĭв/7~;xꨯ/b40YP=QZ7c$;"*?K#}{'w$W~ QUyXPE"Ȁ"V $vǂ]A3!ZVExuoQ2xQHX egS3rWr(;YW~Y [c_Sv8X^7.o^wc_RO Wz@Uk4+uv-rG5(e3g_LiiVJK`6rTOwu/؈NP'J?BxEb6fwhwDWfh~w$u،TKx.8ILES,MyX/ZI\,S~z8~^Yf %9D9uŨy# 1YH؎(tIx錳L|9ȍ6x] 4In^qfS2yٙd}ؕI Yyb2Jw 9 I|4㉝虡蚈'V7I^(9e|) ΉKeE:ݩ⹝}uO)k}nyWډRĹI*uujj ʠ *Q(~)&zpٟ*Ykj J]#R 1Z+h|(9M : +ǣY(G"`I Uy:H @ZȨQeTc ]?dʦPqE:>ajmjG?tZw i]DzFf_ VJ JJ|꩓ɩ~ZjO-jEꐬ46:#Jgjǘ碴Ċ %j* ʬ2zq:2 * ʭ:( J抮jʮb Jڣꯥۮ'*[k +  *{˱ !+#K%k')+˲-/ 1+3K5k79;˳=? A+CKEkGIK˴MހQ+SKSQPY[˵]_ a+cKekgik˶mW[Ppukwy{˷}^Kc+dk˸y[a;cKk{O빟 +;ocaml-book-1.0/en/html/index.html0000644000000000000000000011321507453055377013650 0ustar <TITLE>Developing applications with Objective Caml













Developing Applications With


Emmanuel CHAILLOUX - Pascal MANOURY - Bruno PAGANO




Preface

Introduction

Chapter 1   How to obtain Objective CAML

Part I
Language Core



Chapter 2   Functional programming

Chapter 3   Imperative Programming

Chapter 4   Functional and Imperative Styles

Chapter 5   The Graphics Interface

Chapter 6   Applications

Part II
Development Tools



Chapter 7   Compilation and Portability

Chapter 8   Libraries

Chapter 9   Garbage Collection

Chapter 10   Program Analysis Tools

Chapter 11   Tools for lexical analysis and parsing

Chapter 12   Interoperability with C

Chapter 13   Applications

Part III
Application Structure



Chapter 14   Programming with Modules

Chapter 15   Object-Oriented Programming

Chapter 16   Comparison of the Models of Organisation

Chapter 17   Applications

Part IV
Concurrency and distribution



Chapter 18   Communication and Processes

Chapter 19   Concurrent Programming

Chapter 20   Distributed Programming

Chapter 21   Applications

Chapter 22   Developing applications with Objective CAML

Conclusion

Part V
Appendices



Appendix A   Cyclic Types

Appendix B   Objective CAML 3.04

References

Index









O'Reilly News




This document was translated from LATEX by HEVEA and HACHA.
ocaml-book-1.0/en/html/book-ora119.html0000644000000000000000000011433707453055400014476 0ustar Exercises Previous Contents Next

Exercises

Polymorphic Printing Function

We wish to define a printing function print with type 'a -> unit able to print any Objective CAML value. To this end, we extend and improve the inspect function.

  1. In C, write the function print_ws which prints Objective CAML as follows:
    • immediate values: as C integers;
    • strings: between quotes;
    • floats: as usual;
    • arrays of floats: between [| |]
    • closures: as < code, env >
    • everything else: as a tuple, between ( )
    The function should handle structured types recursively. A polymorphic printing function in C:
    #include <stdio.h>
    #include <caml/mlvalues.h>
    #include <caml/memory.h>
    
    value print_ws (value v) {
      CAMLparam1(v);
      int size,i ;
      if (Is_long(v)) printf("%d", Long_val(v)); 
      else {
        size=Wosize_val(v);
        switch (Tag_val(v)) 
          {
          case String_tag :
     printf("\"%s\"", String_val(v));  
     break;
          case Double_tag:  
     printf("%g", Double_val(v));
     break;
          case Double_array_tag : 
     printf ("[|"); 
            if (size>0) printf("%g", Double_field(v,0));
     for (i=1;i<(size/Double_wosize);i++)  printf("; %g", Double_field(v,i));
     printf("|]");
     break;
          case Abstract_tag :
          case Custom_tag : 
     printf("<abstract>"); 
     break;
          case Closure_tag : 
     printf("<%d, ",Code_val(v)) ;
     if (size>1) print_ws(Field(v,1)) ;
     for (i=2;i<size;i++) {
       printf("; ") ;
       print_ws(Field(v,i));
     }
     printf(">");
     break;
          default:  
     if (Tag_val(v)>=No_scan_tag) printf("?"); 
     else {
       printf("(");
       if (size>0) print_ws(Field(v,0));
       for (i=1;i<size;i++) {
         printf(", ");
         print_ws(Field(v,i));
       }
       printf(")");
     }
          }
      }
      fflush(stdout);
      return Val_unit;
    }
    
    called from Objective CAML:

    # external print_ws : 'a -> unit = "print_ws" ;;
    external print_ws : 'a -> unit = "print_ws"


  2. To avoid looping on circular values, and to display sharing properly, modify this function to keep track of the addresses of heap blocks it has already seen. If an address appears several times, name it when it is first printed (v = name), and just print the name when this address is encountered again.

    # type address ;;
    type address
    1. Define a data structure to record the addresses, determine when they occur several times, and associate a name with each address.

      # let (gensym,init_gensym) =
      let i = ref 0 in
      (function () -> incr i ; "val_" ^ (string_of_int !i))
      , (function () -> i:=0) ;;
      val gensym : unit -> string = <fun>
      val init_gensym : unit -> unit = <fun>

      # type occurrence =
      Once
      | Several_times
      | Already_named of string ;;
      type occurrence = | Once | Several_times | Already_named of string

      # let table = ref ([] : (address * occurrence) list) ;;
      val table : (address * occurrence) list ref = {contents=[]}

      # let record addr =
      try
      match List.assq addr !table with
      Once -> table := (addr, Several_times) :: !table;
      true
      | _ -> true
      with
      Not_found -> table := (addr, Once) :: !table; false ;;
      val record : address -> bool = <fun>

      # let multiple_occ addr =
      match List.assq addr !table with
      Once -> false
      | _ -> true ;;
      val multiple_occ : address -> bool = <fun>

      # let already_named addr =
      match List.assq addr !table with
      Once -> failwith "already_named"
      | Several_times -> table := (addr, Already_named (gensym ())) :: !table;
      false
      | Already_named _ -> true ;;
      val already_named : address -> bool = <fun>

      # let name_of addr =
      match List.assq addr !table with
      Already_named s -> s
      | _ -> raise Not_found ;;
      val name_of : address -> string = <fun>
    2. Traverse the value once first to determine all the addresses it contains and record them in the data structure. La partie Objective CAML:

      # Callback.register "add" ajoute ;;
      - : unit = ()
      # Callback.register "multiple?" multiple_occ ;;
      - : unit = ()
      # Callback.register "named?" already_named ;;
      - : unit = ()
      # Callback.register "name" name_of ;;
      - : unit = ()

      # external explore_value : 'a -> unit = "explore" ;;
      external explore_value : 'a -> unit = "explore"
      The C part:
      #include <caml/callback.h>
      
      value explore (value v) {
        CAMLparam1(v);
        int size,i;
        if (Is_long(v)) return Val_unit;
        if (Bool_val(callback(*caml_named_value("add"), v))) return Val_unit;
        size=Wosize_val(v);
        switch (Tag_val(v)) 
          {
            case String_tag :
            case Double_tag:  
            case Double_array_tag : 
            case Abstract_tag :
            case Final_tag : 
              break;
            case Closure_tag : 
              for (i=1;i<size;i++) explore(Field(v,i));
              break;
            default:  
              if (Tag_val(v)>=No_scan_tag) break ;
              for (i=0;i<size;i++) explore(Field(v,i));
          }
        return Val_unit;
      }
      
    3. The second traversal prints the value while naming addresses at their first occurrences.

      # external print_rec : 'a -> unit = "print_gen" ;;
      external print_rec : 'a -> unit = "print_gen"
      value print_gen (value v)
      {
        CAMLparam1(v);
        int size,i ;
        if (Is_long(v))  return print_ws(v) ;
        if (Bool_val(callback(*caml_named_value("multiple?"),v))) {
          if (Bool_val(callback(*caml_named_value("named?"),v))) {
            printf("%s",String_val(callback(*caml_named_value("name"),v))) ;
            return Val_unit ;
          }
          printf("%s = { ",String_val(callback(*caml_named_value("name"),v))) ;
        }
        size=Wosize_val(v);
        switch (Tag_val(v)) 
          {
            case String_tag :
            case Double_tag:  
            case Double_array_tag : 
            case Abstract_tag :
            case Final_tag : 
       print_ws(v);
       break;
            case Closure_tag : 
       printf("<%d, ",Code_val(v)) ;
       if (size>1) print_gen(Field(v,1)) ;
       for (i=2;i<size;i++) {
         printf("; ") ;
         print_gen(Field(v,i));
       }
       printf(">");
       break;
            default:  
       if (Tag_val(v)>=No_scan_tag) printf("?"); 
       else {
         printf("(");
         if (size>0) print_gen(Field(v,0));
         for (i=1;i<size;i++) {
           printf(", ");
           print_gen(Field(v,i));
         }
         printf(")");
       }
          }
        if (Bool_val(callback(*caml_named_value("multiple?"),v)))  printf(" }") ; 
        fflush(stdout);
        return Val_unit;
      }
      
    4. Define the function print combining both traversals.

      external print_rec : 'a -> unit = "print_gen" ;;
      let print v =
      table := [] ;
      init_gensym () ;
      explore_value v ;
      print_rec v ;
      table := [] ;;

Matrix Product

  1. Define an abstract type float_matrix for matrices of floating-point numbers.

    # type float_matrix ;;
    type float_matrix


  2. Define a C type for these matrices.
    typedef struct { void (*finalization_function)(value) ;
                     int size_x , size_y ;
                     double * mat ;
                   } Matrix ;
    


  3. Write a C function to convert values of type float array array to values of type float_matrix.
    static void finalize_matrix(value mat) {
      free(((Matrix *) mat)->mat);
    }
    
    static value alloc_matrix(int size_x,int size_y) {
      int byte_size ;
      Matrix * res ;
      vres=alloc(sizeof(Matrix)/sizeof(value),
                 finalize_matrix, size_x*size_y, 1000000) ;
      res=(Matrix *) vres ;
      res->size_x = size_x ;
      res->size_y = size_y ;
      res->mat = malloc(size_x*size_y*sizeof(double)) ;
      return vres;
    }
    
    value conversion_to_C (value faa) {
      CAMLparam1(faa) ;
      CAMLlocal2(vres,vect) ;
      Matrix * res ;
      double * tab;
      int size_x, size_y, i, j ;
    
      /* size of the array of arrays */
      size_x = Wosize_val(faa) ;
      /* size of each array */
      if (size_x>0) size_y = Wosize_val(Field(faa,0))/2 ;
    
      /* allocate the value of type float_matrix */
      vres=alloc_matrix(size_x,size_y);
      res=(Matrix *) vres ;
      res->size_x = size_x ;
      res->size_y = size_y ;
      tab = res->mat;
      for (i=0;i<size_x;i++) {
        vect = Field(faa,i) ;
        if (Wosize_val(vect) != size_y)
          failwith("non-rectangular float array array") ;
        for (j=0;j<size_y;j++) *tab++ = Double_field(vect,j) ;
        }
      }
      CAMLreturn vres ;
    }
    


  4. Write a C function performing the reverse conversion.
    value conversion_to_Caml (value matrix) {
      CAMLparam1(matrix) ;
      CAMLlocal2(res,aux) ;
      Matrix* mat = (Matrix *) matrix ;
      float * tab = mat->mat ;
      int i,j ;
      res=alloc(mat->size_x,0);
      for (i=0;i<mat->size_x;i++) {
        aux = alloc(mat->size_y*Double_wosize,Double_array_tag) ;
        Store_field(res,i,aux);
        for (j=0;j<mat->size_y;j++) Store_double_field(aux,j,*tab++) ;
      }
      CAMLreturn res ;
    }
    


  5. Add the C functions computing the sum and the product of these matrices.
    value matrix_sum (value arg1,value arg2) {
      CAMLparam2(arg1,arg2) ;
      CAMLlocal1(vres) ;
      Matrix *m1=(Matrix*) arg1, *m2=(Matrix*) arg2, *res;
      int size,i;
      if (m1->size_x != m2->size_x || m1->size_y != m2->size_y)
        failwith("illegal matrix addition");
      vres=alloc_matrix(m1->size_x,m1->size_y);
      res =(Matrix*) vres;
      tab=vres->mat;
      size=m1->size_x*m1->size_y;
      for (i=0;i<size;i++) tab[i]=m1->mat[i]+m2->mat[i] ;
      CAMLreturn vres;
    }
    
    value matrix_product (value arg1,value arg2) {
      CAMLparam2(arg1,arg2) ;
      CAMLlocal1(vres) ;
      Matrix *m1=(Matrix*) arg1, *m2=(Matrix*) arg2, *res;
      int i,j,k;
      if (m1->size_y != m2->size_x) failwith("illegal matrix product");
      vres=alloc_matrix(m1->size_x,m2->size_y);
      for (i=0;i<res->size_x;i++) 
        for (j=0;j<res->size_y;j++) {
          double acc=0 ;
          for (k=0;k<m1->size_y;k++) 
     acc += m1->mat[i*m1->size_x+k] * m1->mat[k*m2->size_x+j] ;
          tab[i*m1->size_x+j]=acc ;
        }
      CAMLreturn vres;
    }
    


  6. Interface them with Objective CAML and use them.

    # external to_matrix : float array array -> float_matrix = "conversion_to_C" ;;
    # external of_matrix : float_matrix -> float array array = "conversion_to_Caml";;
    # external sum : float_matrix -> float_matrix -> float_matrix = "matrix_add" ;;
    # external prod : float_matrix -> float_matrix -> float_matrix = "matrix_product" ;;

Counting Words: Main Program in C

The Unix command wc counts the number of characters, words and lines in a file. The goal of this exercise is to implement this command, while counting repeated words only once.
  1. Write the program wc in C. This program will simply count words, lines and characters in the file whose name is passed on the command line. Le fichier wc.c :
    #include <stdio.h>
    
    void read_file (char *path) {
      FILE *fd=fopen(path,"r");
      int chr=0;
      char buffer[80], *buff;
      int num_chr=0, nun_words=0, num_lines=0;
    
      if (fd == NULL) exit(2) ;
      buff=buffer ; *buff=0 ;
      while ((chr=getc(fd))!=EOF) {
        num_chr++ ;
        if (chr=='\n') num_lines++ ;
        if (chr==' ' || chr=='\n' ||(buff-buffer)>=80) {
          if (buff!=buffer) { num_words++; *buff=0; buff=buffer; }
        }
        else *(buff++)=chr; 
      }
      printf(" %d - %d - %d : %s\n",num_lines,num_words,num_chr,path);
    }
    
    int main (int argc,char **argv)
    {
      if (argc>1) read_file(argv[1]);
      return 0;
    }
    


  2. Write in Objective CAML a function add_word that uses a hash table to record how many times the function was invoked with the same character string as argument.

    # let table = Hashtbl.create 17 ;;
    val table : ('_a, '_b) Hashtbl.t = <abstr>

    # let add_word (w:string) =
    try let p = Hashtbl.find table w in incr p
    with Not_found -> Hashtbl.add table w (ref 1) ;;
    val add_word : string -> unit = <fun>


  3. Write two functions num_repeated_words and num_unique_words counting respectively the number of word repetitions and the number of unique words, as determined from the hash table built by add_word.

    # let num_repeated_words () =
    let i = ref 0 in
    Hashtbl.iter (fun _ n -> if !n>1 then incr i) table ;
    !i ;;
    val num_repeated_words : unit -> int = <fun>

    # let num_unique_words () =
    let i = ref 0 in
    Hashtbl.iter (fun _ _ -> incr i) table ;
    !i ;;
    val num_unique_words : unit -> int = <fun>


  4. Register the three previous functions so that they can be called from a C program.

    # Callback.register "add word" add_word ;
    Callback.register "rep words" num_repeated_words ;
    Callback.register "uni words" num_unique_words ;


  5. Rewrite the main function of the wc program so that it prints the number of unique words instead of the number of words. We must include the following header files:
    #include <caml/mlvalues.h>
    #include <caml/callback.h>
    
    void read_file (char *path) {
      FILE *fd = fopen(path,"r") ;
      int chr=0 ;
      char buffer[80],*buff ;
      int num_chr=0, num_words, num_lines=0;
    
      if (fd == NULL) exit(2) ;
      buff=buffer; *buff=0;
      while ((chr=getc(fd))!=EOF) {
        num_chr++ ;
        if (chr=='\n') num_lines++ ;
        if (chr==' ' || chr=='\n' ||(buff-buffer)>=80) {
          if (buff!=buffer) { 
     *buff=0;
     buff=buffer;
     callback(*caml_named_value("add word"),copy_string(buffer));
          }
        }
        else *(buff++)=chr; 
      }
      num_words=Int_val(callback(*caml_named_value("uni words"),Val_unit)); 
      printf(" %d - %d - %d : %s\n",num_lines,num_words,num_chr,path);
    }
    


  6. Write the main function and the commands required to compile this program as an Objective CAML program.
    int main (int argc,char **argv)
    {
      caml_main(argv);
      if (argc>1) read_file(argv[1]);
      return 0;
    }
    
    Compiling to bytecode:
    $ cc -c -I /usr/local/lib/ocaml/ wc.c
    $ ocamlc -custom words.ml wc.o
    
    Compiling to native code:
    $ cc -c -I /usr/local/lib/ocaml/ wc.c
    $ ocamlopt words.ml wc.o
    


  7. Write the main function and the commands required to compile this program as a C program.
    int main (int argc,char **argv)
    {
      caml_startup(argv);
      if (argc>1) read_file(argv[1]);
      return 0;
    }
    
    Compiling to bytecode:
    $ ocamlc -output-obj words.ml -o words.o
    $ gcc -c -I /usr/local/lib/ocaml/ wc.c
    $ gcc words.o wc.o -L /usr/local/lib/ocaml/ -lcamlrun -lcurses
    
    Compiling to native code:
    $ ocamlopt -output-obj words.ml -o wordsnat.o 
    $ gcc -c -I /usr/local/lib/ocaml/ wc.c
    $ gcc wordsnat.o wc.o -L /usr/local/lib/ocaml/ -lasmrun 
    

Previous Contents Next ocaml-book-1.0/en/html/book-ora024.html0000644000000000000000000000664507453055377014510 0ustar Introduction Previous Contents Next

Introduction

In contrast to functional programming, in which you calculate a value by applying a function to its arguments without caring how the operations are carried out, imperative programming is closer to the machine representation, as it introduces memory state which the execution of the program's actions will modify. We call these actions of programs instructions, and an imperative program is a list, or sequence, of instructions. The execution of each operation can alter the memory state. We consider input-output actions to be modifications of memory, video memory, or files.

This style of programming is directly inspired by assembly programming. You find it in the earliest general-purpose programming languages (Fortran, C, Pascal, etc.). In Objective CAML the following elements of the language fit into this model:

  • modifiable data structures, such as arrays, or records with mutable fields;
  • input-output operations;
  • control structures such as loops and exceptions.
Certain algorithms are easier to write in this programming style. Take for instance the computation of the product of two matrices. Even though it is certainly possible to translate it into a purely functional version, in which lists replace vectors, this is neither natural nor efficient compared to an imperative version.

The motivation for the integration of imperative elements into a functional language is to be able to write certain algorithms in this style when it is appropriate. The two principal disadvantages, compared to the purely functional style, are:

  • complicating the type system of the language, and rejecting certain programs which would otherwise be considered correct;

  • having to keep track of the memory representation and of the order of calculations.
Nevertheless, with a few guidelines in writing programs, the choice between several programming styles offers the greatest flexibility for writing algorithms, which is the principal objective of any programming language. Besides, a program written in a style which is close to the algorithm used will be simpler, and hence will have a better chance of being correct (or at least, rapidly correctable).

For these reasons, the Objective CAML language has some types of data structures whose values are physically modifiable, structures for controlling the execution of programs, and an I/O library in an imperative style.


Previous Contents Next ocaml-book-1.0/en/html/book-ora002.html0000644000000000000000000001062207453055377014472 0ustar Objectives Previous Contents Next

Objectives

Objective CAML is a programming language. One might ask why yet another language is needed. Indeed there are already numerous existing languages with new ones constantly appearing. Beyond their differences, the conception and genesis of each one of them proceeds from a shared motivation: the desire to abstract.
To abstract from the machine
In the first place, a programming language permits one to neglect the ``mechanical'' aspect of the computer; it even lets one forget the microprocessor model or the operating system on which the program will be executed.
To abstract from the operational model
The notion of function which most languages possess in one form or another is borrowed from mathematics and not from electronics. In a general way, languages substitute formal models for purely computational viewpoints. Thus they gain expressivity.
To abstract errors
This has to do with the attempt to guarantee execution safety; a program shouldn't terminate abruptly or become inconsistent in case of an error. One of the means of attaining this is strong static typing of programs and having an exception mechanism in place.
To abstract components (i)
Programming languages make it possible to subdivide an application into different software components which are more or less independent and autonomous. Modularity permits higher-level structuring of the whole of a complex application.
To abstract components (ii)
The existence of programming units has opened up the possibility of their reuse in contexts other than the ones for which they were developed. Object-oriented languages constitute another approach to reusability permitting rapid prototyping.
Objective CAML is a recent language which takes its place in the history of programming languages as a distant descendant of Lisp, having been able to draw on the lessons of its cousins while incorporating the principal characteristics of other languages. It is developed at INRIA1 and is supported by long experience with the conception of the languages in the ML family. Objective CAML is a general-purpose language for the expression of symbolic and numeric algorithms. It is object-oriented and has a parameterized module system. It supports the development of concurrent and distributed applications. It has excellent execution safety thanks to its static typing, its exception mechanism and its garbage collector. It is high-performance while still being portable. Finally, a rich development environment is available.

Objective CAML has never been the subject of a presentation to the ``general public''. This is the task to which the authors have set themselves, giving this exposition three objectives:
  1. To describe in depth the Objective CAML language, its libraries and its development environment.
  2. To show and explain what are the concepts hidden behind the programming styles which can be used with Objective CAML.
  3. To illustrate through numerous examples how Objective CAML can serve as the development language for various classes of applications.
The authors' goal is to provide insight into how to choose a programming style and structure a program, consistent with a given problem, so that it is maintainable and its components are reusable.


Previous Contents Next ocaml-book-1.0/en/html/book-ora012.html0000644000000000000000000001676507453055377014511 0ustar Presenting part I Previous Contents Next

Presenting part I

The first part of this book is a complete introduction to the core of the Objective CAML language, in particular the expression evaluation mechanism, static typing and the data memory model.

An expression is the description of a computation. Evaluation of an expression returns a value at the end of the computation. The execution of an Objective CAML program corresponds to the computation of an expression. Functions, program execution control structures, even conditions or loops, are themselves also expressions.

Static typing guarantees that the computation of an expression cannot cause a run-time type error. In fact, application of a function to some arguments (or actual parameters) isn't accepted unless they all have types compatible with the formal parameters indicated in the definition of the function. Furthermore, the Objective CAML language has type infererence: the compiler automatically determines the most general type of an expression.

Finally a minimal knowledge of the representation of data is indispensable to the programmer in order to master the effects of physical modifications to the data.

Outline

Chapter 2 contains a complete presentation of the purely functional part of the language and the constraints due to static typing. The notion of expression evaluation is illustrated there at length. The following control structures are detailed: conditional, function application and pattern matching. The differences between the type and the domain of a function are discussed in order to introduce the exception mechanism. This feature of the language goes beyond the functional context and allows management of computational breakdowns.

Chapter 3 exhibits the imperative style. The constructions there are closer to classic languages. Associative control structures such as sequence and iteration are presented there, as well as mutable data structures. The interaction between physical modifications and sharing of data is then detailed. Type inference is described there in the context of these new constructions.

Chapter 4 compares the two preceding styles and especially presents different mixed styles. This mixture supports in particular the construction of lazy data structures, including mutable ones.

Chapter 5 demonstrates the use of the Graphics library included in the language distribution. The basic notions of graphics programming are exhibited there and immediately put into practice. There's even something about GUI construction thanks to the minimal event control provided by this library.

These first four chapters are illustrated by a complete example, the implementation
of a calculator, which evolves from chapter to chapter.

Chapter 6 presents three complete applications: a little database, a mini-BASIC interpreter and the game Minesweeper. The first two examples are constructed mainly in a functional style, while the third is done in an imperative style.

The rudiments of syntax

Before beginning we indicate the first elements of the syntax of the language. A program is a sequence of phrases in the language. A phrase is a complete, directly executable syntactic element (an expression, a declaration). A phrase is terminated with a double semi-colon (;;). There are three different types of declarations which are each marked with a different keyword:
value declaration : let
exception declaration : exception
type declaration : type
All the examples given in this part are to be input into the interactive toplevel of the language.

Here's a first (little) Objective CAML program, to be entered into the toplevel, whose prompt is the pound character (#), in which a function fact computing the factorial of a natural number, and its application to a natural number 8, are defined.

# let rec fact n = if n < 2 then 1 else n * fact(n-1) ;;
val fact : int -> int = <fun>
# fact 8 ;;
- : int = 40320
This program consists of two phrases. The first is the declaration of a function value and the second is an expression. One sees that the toplevel prints out three pieces of information which are: the name being declared, or a dash (-) in the case of an expression; the inferred type; and the return value. In the case of a function value, the system prints <fun>.

The following example demonstrates the manipulation of functions as values in the language. There we first of all define the function succ which calculates the successor of an integer, then the function compose which composes two functions. The latter will be applied to fact and succ.


# let succ x = x+1 ;;
val succ : int -> int = <fun>
# let compose f g x = f(g x) ;;
val compose : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun>
# compose fact succ 8 ;;
- : int = 362880
This last call carries out the computation fact(succ 8) and returns the expected result. Let us note that the functions fact and succ are passed as parameters to compose in the same way as the natural number 8.




Previous Contents Next ocaml-book-1.0/en/html/book-ora178.html0000644000000000000000000005631107453055400014500 0ustar Example: Post Office Previous Contents Next

Example: Post Office

We present, to end this chapter, a slightly more complete example of a concurrent program: modelling a common queue at a number of counters at a post office.

As always in concurrent programming the problems are posed metaphorically, but replace the counters of the post office by a collection of printers and you have the solution to a genuine problem in computing.

Here the policy of service that we propose; it is well tried and tested, rather than original: each client takes a number when he arrives; when a clerk has finished serving a client, he calls for a number. When his number is called, the client goes to the corresponding counter.

Organization of development.
We distinguish in our development resources, and agents. The former are: the number dispenser, the number announcer, and the windows. The latter are: the clerks and the clients. The resources are modeled by objects which manage their own mutual exclusion mechanisms. The agents are modelled by functions executed by a thread. When an agent wishes to modify or examine the state of an object, it does not itself have to know about or manipulate mutexes, which allows a simplified organization for access to sensitive data, and avoids oversights in the coding of the agents.

The Components

The Dispenser.
The number dispenser contains two fields: a counter and a mutex. The only method provided by the distributor is the taking of a new number.


# class dispenser () =
object
val mutable n = 0
val m = Mutex.create()
method take () = let r = Mutex.lock m ; n <- n+1 ; n
in Mutex.unlock m ; r
end ;;
class dispenser :
unit ->
object val m : Mutex.t val mutable n : int method take : unit -> int end
The mutex prevents two clients from taking a number at the same time. Note the way in which we use an intermediate variable (r) to guarantee that the number calculated in the critical section is the same as the one return by the method call.

The Announcer.
The announcer contains three fields: an integer (the client number being called); a mutex and a condition variable. The two methods are: (wait) which reads the number, and (call), which modifies it.


# class announcer () =
object
val mutable nclient = 0
val m = Mutex.create()
val c = Condition.create()

method wait n =
Mutex.lock m;
while n > nclient do Condition.wait c m done;
Mutex.unlock m;

method call () =
let r = Mutex.lock m ;
nclient <- nclient+1 ;
nclient
in Condition.broadcast c ;
Mutex.unlock m ;
r
end ;;
The condition variable is used to put the clients to sleep, waiting for their number. They are all woken up when the method call is invoked. Reading or writing access to the called number is protected by the mutex.

The window.
The window consists of five fields: a fixed window number (variable ncounter); the number of the client being waited for (variable nclient); a boolean (variable available); a mutex, and a condition variable.

It offers eight methods, of which two are private: two simple access methods (methods get_ncounter and get_nclient): a group of three methods simulating the waiting period of the clerk between two clients (private method wait and public methods await_arrival, await_departure); a group of three methods simulate the occupation of the window (private method set_available and methods arrive, depart).


# class counter (i:int) =
object(self)
val ncounter = i
val mutable nclient = 0
val mutable available = true
val m = Mutex.create()
val c = Condition.create()

method get_ncounter = ncounter
method get_nclient = nclient

method private wait f =
Mutex.lock m ;
while f () do Condition.wait c m done ;
Mutex.unlock m

method wait_arrival n = nclient <- n ; self#wait (fun () -> available)
method wait_departure () = self#wait (fun () -> not available)

method private set_available b =
Mutex.lock m ;
available <- b ;
Condition.signal c ;
Mutex.unlock m
method arrive () = self#set_available false
method leave () = self#set_available true

end ;;


A post office.
We collect these three resources in a record type:


# type office = { d : dispenser ; a : announcer ; cs : counter array } ;;


Clients and Clerks

The behaviour of the system as a whole will depend on the three following parameters:

# let service_delay = 1.7 ;;
# let arrival_delay = 1.7 ;;
# let counter_delay = 0.5 ;;


Each represents the maximum value of the range from which each effective value will be randomly chosen. The first parameter models the time taken to serve a client; the second, the delay between the arrival of clients in the post office; the last, the time it takes a clerk to call a new client after the last one has left.

The Clerk.
The work of a clerk consists of looping indefinitely over the following sequence:
  1. Call for a number.
  2. Wait for the arrival of a client holding the called number.
  3. Wait for the departure of the client occupying his counter.
Adding some output, we get the function:

# let clerk ((a:announcer), (c:counter)) =
while true do
let n = a#call ()
in Printf.printf "Counter %d calls %d\n" c#get_ncounter n ;
c#wait_arrival n ;
c#wait_departure () ;
Thread.delay (Random.float counter_delay)
done ;;
val clerk : announcer * counter -> unit = <fun>


The Client.
A client executes the following sequence:
  1. Take a waiting number.
  2. Wait until his number is called.
  3. Go to the window having called for the number to obtain service.
The only slightly complex activity of the client is to find the counter where they are expected.

We give, for this, the auxiliary function:

# let find_counter n cs =
let i = ref 0 in while cs.(!i)#get_ncounter <> n do incr i done ; !i ;;
val find_counter : 'a -> < get_ncounter : 'a; .. > array -> int = <fun>


Adding some output, the principal function of the client is:

# let client o =
let n = o.d#take()
in Printf.printf "Arrival of client %d\n" n ; flush stdout ;
o.a#wait n ;
let ic = find_counter n o.cs
in o.cs.(ic)#arrive () ;
Printf.printf "Client %d occupies window %d\n" n ic ;
flush stdout ;
Thread.delay (Random.float service_delay) ;
o.cs.(ic)#leave () ;
Printf.printf "Client %d leaves\n" n ; flush stdout ;;
val client : office -> unit = <fun>


The System

The main programme of the application creates a post office and its clerks (each clerk is a process) then launches a process which creates an infinite stream of clients (each client is also a process).


# let main () =
let o =
{ d = new dispenser();
a = new announcer();
cs = (let cs0 = Array.create 5 (new counter 0) in
for i=0 to 4 do cs0.(i) <- new counter i done;
cs0)
}
in for i=0 to 4 do ignore (Thread.create clerk (o.a, o.cs.(i))) done ;
let create_clients o = while true do
ignore (Thread.create client o) ;
Thread.delay (Random.float arrival_delay)
done
in ignore (Thread.create create_clients o) ;
Thread.sleep () ;;
val main : unit -> unit = <fun>
The last instruction puts the process associated with the program to sleep in order to pass control immediately to the other active processes of the application.








Previous Contents Next ocaml-book-1.0/en/html/book-ora181.html0000644000000000000000000000460507453055400014471 0ustar To Learn More Previous Contents Next

To Learn More

The first requirements for concurrent algorithms arose from systems programming. For this application, the imperative model of shared memory is the most widely used. For example, the relation of mutual exclusion and semaphores are used to manage shared resources. The different low-level mechanisms of managing processes accessing shared memory are described in [Ari90].

Nonetheless, the possibility of expressing concurrent algorithms in one's favorite languages makes it possible to investigate this kind of algorithm, as presented in [And91]. It may be noted that while the concepts of such algorithms can simplify the solution of certain problems, the production of the corresponding programs is quite hard work.

The model of synchronous communication presented by CML, and followed by the Event module, is fully described in [Rep99]. The online version is at the following address:

Link


http://cm.bell-labs.com/cm/cs/who/jhr/index.html
An interesting example is the threaded graphical library EXene, implemented in CML under X-Windows. The preceding link contains a pointer to this library.






Previous Contents Next ocaml-book-1.0/en/html/book-ora040.html0000644000000000000000000004605707453055377014507 0ustar Streams of Data Previous Contents Next

Streams of Data

Streams are (potentially infinite) sequences containing elements of the same kind. The evaluation of a part of a stream is done on demand, whenever it is needed by the current computation. A stream is therefore a lazy data structure.

The stream type is an abstract data type; one does not need to know how it is implemented. We manipulate objects of this type using constructor functions and destructor (or selector) functions. For the convenience of the user, Objective CAML has simple syntactic constructs to construct streams and to access their elements.

Warning


Streams are an extension of the language, not part of the stable core of Objective CAML.


Construction

The syntactic sugar to construct streams is inspired by that for lists and arrays. The empty stream is written:

# [< >] ;;
- : 'a Stream.t = <abstr>


One may construct a stream by enumerating its elements, preceding each one with an with a single quote (character '):

# [< '0; '2; '4 >] ;;
- : int Stream.t = <abstr>


Expressions not preceded by an apostrophe are considered to be sub-streams:

# [< '0; [< '1; '2; '3 >]; '4 >] ;;
- : int Stream.t = <abstr>
# let s1 = [< '1; '2; '3 >] in [< s1; '4 >] ;;
- : int Stream.t = <abstr>
# let concat_stream a b = [< a ; b >] ;;
val concat_stream : 'a Stream.t -> 'a Stream.t -> 'a Stream.t = <fun>
# concat_stream [< '"if"; '"c";'"then";'"1" >] [< '"else";'"2" >] ;;
- : string Stream.t = <abstr>


The Stream module also provides other construction functions. For instance, the functions of_channel and of_string return a stream containing a sequence of characters, received from an input stream or a string.

# Stream.of_channel ;;
- : in_channel -> char Stream.t = <fun>
# Stream.of_string ;;
- : string -> char Stream.t = <fun>


The deferred computation of streams makes it possible to manipulate infinite data structures in a way similar to the type 'a enum defined on page ??. We define the stream of natural numbers by its first element and a function calculating the stream of elements to follow:

# let rec nat_stream n = [< 'n ; nat_stream (n+1) >] ;;
val nat_stream : int -> int Stream.t = <fun>
# let nat = nat_stream 0 ;;
val nat : int Stream.t = <abstr>


Destruction and Matching of Streams

The primitive next permits us to evaluate, retrieve, and remove the first element of a stream, all at once:

# for i=0 to 10 do
print_int (Stream.next nat) ;
print_string " "
done ;;
0 1 2 3 4 5 6 7 8 9 10 - : unit = ()
# Stream.next nat ;;
- : int = 11
When the stream is exhausted, an exception is raised.

# Stream.next [< >] ;;
Uncaught exception: Stream.Failure


To manipulate streams, Objective CAML offers a special-purpose matching construct called destructive matching. The value matched is calculated and removed from the stream. There is no notion of exhaustive match for streams, and, since the data type is lazy and potentially infinite, one may match less than the whole stream. The syntax for matching is:

Syntax


match expr with parser [< 'p1 ...>] -> expr1 | ...


The function next could be written:

# let next s = match s with parser [< 'x >] -> x ;;
val next : 'a Stream.t -> 'a = <fun>
# next nat;;
- : int = 12
Note that the enumeration of natural numbers picks up where we left it previously.

As with function abstraction, there is a syntactic form matching a function parameter of type Stream.t.

Syntax


parser p -> ...
The function next can thus be rewritten:

# let next = parser [<'x>] -> x ;;
val next : 'a Stream.t -> 'a = <fun>
# next nat ;;
- : int = 13


It is possible to match the empty stream, but take care: the stream pattern [<>] matches every stream. In fact, a stream s is always equal to the stream [< [<>]; s >]. For this reason, one must reverse the usual order of matching:

# let rec it_stream f s =
match s with parser
[< 'x ; ss >] -> f x ; it_stream f ss
| [<>] -> () ;;
val it_stream : ('a -> 'b) -> 'a Stream.t -> unit = <fun>
# let print_int1 n = print_int n ; print_string" " ;;
val print_int1 : int -> unit = <fun>
# it_stream print_int1 [<'1; '2; '3>] ;;
1 2 3 - : unit = ()
Since matching is destructive, one can equivalently write:

# let rec it_stream f s =
match s with parser
[< 'x >] -> f x ; it_stream f s
| [<>] -> () ;;
val it_stream : ('a -> 'b) -> 'a Stream.t -> unit = <fun>
# it_stream print_int1 [<'1; '2; '3>] ;;
1 2 3 - : unit = ()


Although streams are lazy, they want to be helpful, and never refuse to furnish a first element; when it has been supplied once it is lost. This has consequences for matching. The following function is an attempt (destined to fail) to display pairs from a stream of integers, except possibly for the last element.

# let print_int2 n1
n2 =
print_string "(" ; print_int n1 ; print_string "," ;
print_int n2 ; print_string ")" ;;
val print_int2 : int -> int -> unit = <fun>
# let rec print_stream s =
match s with parser
[< 'x; 'y >] -> print_int2 x y; print_stream s
| [< 'z >] -> print_int1 z; print_stream s
| [<>] -> print_newline() ;;
val print_stream : int Stream.t -> unit = <fun>
# print_stream [<'1; '2; '3>];;
(1,2)Uncaught exception: Stream.Error("")


The first two two members of the stream were displayed properly, but during the evaluation of the recursive call (print_stream [<3>]), the first pattern found a value for x, which was thereby consumed. There remained nothing more for y. This was what caused the error. In fact, the second pattern is useless, because if the stream is not empty, then first pattern always begins evaluation.

To obtain the desired result, we must sequentialize the matching:

# let rec print_stream s =
match s with parser
[< 'x >]
-> (match s with parser
[< 'y >] -> print_int2 x y; print_stream s
| [<>] -> print_int1 x; print_stream s)
| [<>] -> print_newline() ;;
val print_stream : int Stream.t -> unit = <fun>
# print_stream [<'1; '2; '3>];;
(1,2)3
- : unit = ()


If matching fails on the first element of a pattern however, then we again have the familiar behavior of matching:

# let rec print_stream s =
match s with parser
[< '1; 'y >] -> print_int2 1 y; print_stream s
| [< 'z >] -> print_int1 z; print_stream s
| [<>] -> print_newline() ;;
val print_stream : int Stream.t -> unit = <fun>
# print_stream [<'1; '2; '3>] ;;
(1,2)3
- : unit = ()


The Limits of Matching

Because it is destructive, matching streams differs from matching on sum types. We will now illustrate how radically different it can be.

We can quite naturally write a function to compute the sum of the elements of a stream:

# let rec sum s =
match s with parser
[< 'n; ss >] -> n+(sum ss)
| [<>] -> 0 ;;
val sum : int Stream.t -> int = <fun>
# sum [<'1; '2; '3; '4>] ;;
- : int = 10
However, we can just as easily consume the stream from the inside, naming the partial result:

# let rec sum s =
match s with parser
[< 'n; r = sum >] -> n+r
| [<>] -> 0 ;;
val sum : int Stream.t -> int = <fun>
# sum [<'1; '2; '3; '4>] ;;
- : int = 10


We will examine some other important uses of streams in chapter 11, which is devoted to lexical and syntactic analysis. In particular, we will see how consuming a stream from the inside may be profitably used.








Previous Contents Next ocaml-book-1.0/en/html/book-ora080.gif0000644000000000000000000001025007452056112014261 0ustar GIF89a$UUU999rrr!,$h0I8ͻ`(dihzk,tm3Ap)`ĤrOlZX hsKٴz]2KbVwleQ ~xMu }|^=F *&.@CJC*H9úISΡ`5(piM1=h,W掝7lآ`ɏ]bus/p @;ډDS@L#ם qEBH & [ͳ_%)s>9y~Ǝ,vscEߪ˪8qУ)0\p wqSqg8Vvr|5fq2#yMa^t!b.=2m:$8S.u])&:(W(hy$Rixց i$JN"M T6P[fH`)diT^KjnƙrIvz~Z &6jFV fz vs^#Q.fjNc^:knzkkiĆRlg&6;ѱ FBs-Cf{G_zsk0N ۖn5dT7 1aU& ǽםfQNH80r)t}MߐJYfqEƅÊ7{t&BzwWYדNj" p2{KNwTЋmv`\ &L: Sg5'v1uui>mŴ_(|{g3r}XT ]^2ӡ yq:(}dthvte<n!Ϫ\:㥼1lʹP(Άl@<C@c>RDXd}T6C'?ogHV< @T1z A% vze:R6( kRF%X\8NNZWRbr Quw+sZ:E afOp& +7$d[VPaT1pd d&46k liQ 1ְo/kMH0]H95^#l EeSB +1yVMs{4AXg ßARBG2N8ArqO&3~*PwE/vl5F{HX"j_^I]ZT<@9svh3b0Mg(GxS81ТR B*RWD'JъL $E5z:}T0ڍ )GR"TM-U7mF>t>Lnf BLASuA \TyL2^f(2q*>8U\d13nؒBG&7n%48/εrK~S̬]j`plV՛E8wW(v;赊vk(zóxŦl$i*i;?7k,`c|(@TJ:j:m$o[hoN~f=mFoE|gI6p~٥ {Ż۳4MUC~_eTV@,6}'- v{Ia#}ф)AdJˇbpB1GxL oTLWoBJG27!rΤEe0@\pE Kbr; eSS2As UQ295+r&PGAk͊sB||fv毷>ؔlf5㊖ĭ"TيPn.',U RJg`d:Xҋudt\st6dQDW%, /Z6]R;jvl mUv6cN~3[~kb粯>0C$yן0c"K[#ŗǷ) uw*vD]04?e|P 4:hG@ac^'8#H`'$h\i:EJzn/$h=u-LW.x( S5d#&،1؀~( ؍c&ы3#Vs8.b `叨 ~FPyTtSpTbŐ`GceNh~ڢA)I|H6&WX5Ez?Byua[2[Kqc:V{}X'4͔lNH'B{mW*Y[ͱvͷnYdnTk騎=6 g3zfyq py򒕪t\@s:?4@qN _ypSXxHv?ȊydIi)bؙVXs̈~F*{h>9>Vuv陞P W]_(reD.h (IBSyH隘"#gxa@GIqTLUF3s"Ӟ  *%λS+[۽޻ +K%TK}[+훧 B{ы6{(b[ukf>QlM qKchy\),l^| |o܎\¦>Mn*%Bk`1|%8<, ;ocaml-book-1.0/en/html/book-ora148.html0000644000000000000000000000324007453055400014466 0ustar Summary Previous Contents Next

Summary

This chapter described the object extension of the language Objective CAML. The class organization is an alternative to modules that, thanks to inheritance and delayed binding, allows object modeling of an application, as well as reusability and adaptability of programs. This extension is integrated with the type system of Objective CAML and adds the notion of subtype, which allows instances to be used as a subtype in any place where a value of this type is expected. By combining subtyping and delayed binding, we obtain inclusion polymorphism, which, for instance, allows us to build homogeneous lists from the point of view of types, albeit non-homogeneous with regard to behavior.


Previous Contents Next ocaml-book-1.0/en/html/book-ora205.html0000644000000000000000000000152107453055401014461 0ustar Notes
1
Bootstrapping is the compilation of a compiler by the compiler itself. Arrival at a fixed point, that is to say the compiler and the generated executable are identical, is a good test of compiler correctness.
ocaml-book-1.0/en/html/book-ora072.html0000644000000000000000000000561507453055400014472 0ustar Introduction Previous Contents Next

Introduction

Every language comes with collections of programs that are reusable by the programmer, called libraries. The quality and diversity of these programs are often some of the criteria one uses to assess the ease of use of a language. You could separate libraries into two categories: those that offer types and functions that are often useful but could be written in the language, and those that offer functionality that cannot be defined in the language. The first group saves the programmer the effort of redefining utilities such as stacks, lists, etc. The second group extends the possible uses of the language by incorporating new functionality into it.

The Objective CAML language distribution comes with many precompiled libraries. For the curious reader, the uncompiled version of these libraries comes packaged with the source code distribution for the language.

In Objective CAML, all the libraries are organized into modules that are also compilation units. Each one contains declarations of globals and types, exceptions and values that can be used in programs. In this chapter we are not interested in how to create new modules; we just want to use the existing ones. Chapter 14 will revisit the concepts of the module and the compilation unit while describing the module language of Objective CAML, including parameterized modules. Regarding the creation of libraries that incorporate code that is not written in Objective CAML, chapter 12 will describe how to integrate Objective CAML programs with code written in C.

The Objective CAML distribution contains a preloaded library (the Pervasives module), a collection of basic modules called the standard library, and many other libraries adding functionality to the language. Some of the libraries are briefly shown in this chapter while others are described in later chapters.


Previous Contents Next ocaml-book-1.0/en/html/book-ora047.html0000644000000000000000000000652207453055377014507 0ustar Basic notions Previous Contents Next

Basic notions

Graphics programming is tightly bound to the technological evolution of hardware, in particular to that of screens and graphics cards. In order to render images in sufficient quality, it is necessary that the drawing be refreshed (redrawn) at regular and short intervals, somewhat like in a cinema. There are basically two techniques for drawing on the screen: the first makes use of a list of visible segments where only the useful part of the drawing is drawn, the second displays all points of the screen (bitmap screen). It is the last technique which is used on ordinary computers.

Bitmap screens can be seen as rectangles of accessible, in other terms, displayable points. These points are called pixels, a word derived from picture element. They are the basic elements for constructing images. The height and width of the main bitmap is the resolution of the screen. The size of this bitmap therefore depends on the size of each pixel. In monochrome (black/white) displays, a pixel can be encoded in one bit. For screens that allow gray scales or for color displays, the size of a pixel depends on the number of different colors and shades that a pixel may take. In a bitmap of 320x640 pixels with 256 colors per pixel, it is therefore necessary to encode a pixel in 8 bits, which requires video memory of: 480 * 640 bytes = 307200 bytes ~ 300KB. This resolution is still used by certain MS-DOS programs.

The basic operations on bitmaps which one can find in the Graphics library are:
  • coloration of pixels,
  • drawing of pixels,
  • drawing of forms: rectangles, ellipses,
  • filling of closed forms: rectangles, ellipses, polygons,
  • displaying text: as bitmap or as vector,
  • manipulation or displacement of parts of the image.
All these operations take place at a reference point, the one of the bitmap. A certain number of characteristics of these graphical operations like the width of strokes, the joints of lines, the choice of the character font, the style and the motive of filling define what we call a graphical context. A graphical operation always happens in a particular graphical context, and its result depends on it. The graphical context of the Graphics library does not contain anything except for the current point, the current color, the current font and the size of the image.


Previous Contents Next ocaml-book-1.0/en/html/book-ora196.html0000644000000000000000000017445107453055401014507 0ustar HTTP Servlets Previous Contents Next

HTTP Servlets

A servlet is a ``module'' that can be integrated into a server application to respond to client requests. Although a servlet need not use a specific protocol, we will use the HTTP protocol for communication (see figure 21.1). In practice, the term servlet refers to an HTTP servlet.

The classic method of constructing dynamic HTML pages on a server is to use CGI (Common Gateway Interface) commands. These take as argument a URL which can contain data coming from an HTML form. The execution then produces a new HTML page which is sent to the client. The following links describe the HTTP and CGI protocols.

Link


http://www.cis.ohio-state.edu/cgi-bin/rfc/rfc1945.html

Link


http://hoohoo.ncsa.uiuc.edu/docs/cgi/overview.html
It is a slightly heavyweight mechanism because it launches a new program for each request.

HTTP servlets are launched just once, and can can decode arguments in CGI format to execute a request. Servlets can take advantage of the Web browser's capabilities to construct a graphical interface for an application.



Figure 21.1: communication between a browser and an Objective CAMLserver


In this section we will define a server for the HTTP protocol. We will not handle the entire specification of the protocol, but instead will limit ourselves to those functions necessary for the implementation of a server that mimics the behavior of a CGI application.

At an earlier time, we defined a generic server module Gsd. Now we will give the code to create an application of this generic server for processing part of the HTTP protocol.

HTTP and CGI Formats

We want to obtain a server that imitates the behavior of a CGI application. One of the first tasks is to decode the format of HTTP requests with CGI extensions for argument passing.

The clients of this server can be browsers such as Netscape or Internet Explorer.

Receiving Requests

Requests in the HTTP protocol have essentially three components: a method, a URL and some data. The data must follow a particular format.

In this section we will construct a collection of functions for reading, decomposing and decoding the components of a request. These functions can raise the exception:

# exception Http_error of string ;;
exception Http_error of string


Decoding
The function decode, which uses the helper function rep_xcode, attempts to restore the characters which have been encoded by the HTTP client: spaces (which have been replaced by +), and certain reserved characters which have been replaced by their hexadecimal code.


# let rec rep_xcode s i =
let xs = "0x00" in
String.blit s (i+1) xs 2 2;
String.set s i (char_of_int (int_of_string xs));
String.blit s (i+3) s (i+1) ((String.length s)-(i+3));
String.set s ((String.length s)-2) '\000';
Printf.printf"rep_xcode1(%s)\n" s ;;
val rep_xcode : string -> int -> unit = <fun>

# exception End_of_decode of string ;;
exception End_of_decode of string

# let decode s =
try
for i=0 to pred(String.length s) do
match s.[i] with
'+' -> s.[i] <- ' '
| '%' -> rep_xcode s i
| '\000' -> raise (End_of_decode (String.sub s 0 i))
| _ -> ()
done;
s
with
End_of_decode s -> s ;;
val decode : string -> string = <fun>


String manipulation functions
The module String_plus contains some functions for taking apart character strings:
  • prefix and suffix, which extract the substrings to either side of an index;
  • split, which returns the list of substrings determined by a separator character;
  • unsplit, which concatenates a list of strings, inserting separator characters between them.

# module String_plus =
struct
let prefix s n =
try String.sub s 0 n
with Invalid_argument("String.sub") -> s

let suffix s i =
try String.sub s i ((String.length s)-i)
with Invalid_argument("String.sub") -> ""

let rec split c s =
try
let i = String.index s c in
let s1, s2 = prefix s i, suffix s (i+1) in
s1::(split c s2)
with
Not_found -> [s]

let unsplit c ss =
let f s1 s2 = match s2 with "" -> s1 | _ -> s1^(Char.escaped c)^s2 in
List.fold_right f ss ""
end ;;


Decomposing data from a form
Requests typically arise from an HTML page containing a form. The contents of the form are transmitted as a character string containing the names and values associated with the fields of the form. The function get_field_pair transforms such a string into an association list.

# let get_field_pair s =
match String_plus.split '=' s with
[n;v] -> n,v
| _ -> raise (Http_error ("Bad field format : "^s)) ;;
val get_field_pair : string -> string * string = <fun>

# let get_form_content s =
let ss = String_plus.split '&' s in
List.map get_field_pair ss ;;
val get_form_content : string -> (string * string) list = <fun>


Reading and decomposing
The function get_query extracts the method and the URL from a request and stores them in an array of character strings. One can thus use a standard CGI application which retrieves its arguments from the array of command-line arguments. The function get_query uses the auxiliary function get. We arbitrarily limit requests to a maximum size of 2555 characters.

# let get =
let buff_size = 2555 in
let buff = String.create buff_size in
(fun ic -> String.sub buff 0 (input ic buff 0 buff_size)) ;;
val get : in_channel -> string = <fun>

# let query_string http_frame =
try
let i0 = String.index http_frame ' ' in
let q0 = String_plus.prefix http_frame i0 in
match q0 with
"GET"
-> begin
let i1 = succ i0 in
let i2 = String.index_from http_frame i1 ' ' in
let q = String.sub http_frame i1 (i2-i1) in
try
let i = String.index q '?' in
let q1 = String_plus.prefix q i in
let q = String_plus.suffix q (succ i) in
Array.of_list (q0::q1::(String_plus.split ' ' (decode q)))
with
Not_found -> [|q0;q|]
end
| _ -> raise (Http_error ("Unsupported method: "^q0))
with e -> raise (Http_error ("Unknown request: "^http_frame)) ;;
val query_string : string -> string array = <fun>

# let get_query_string ic =
let http_frame = get ic in
query_string http_frame;;
val get_query_string : in_channel -> string array = <fun>


The Server

To obtain a CGI pseudo-server, able to process only the GET method, we write the class http_servlet, whose argument fun_serv is a function for processing HTTP requests such as might have been written for a CGI application.

# module Text_Server = Server (struct type t = string
let to_string x = x
let of_string x = x
end);;

# module P_Text_Server (P : PROTOCOL) =
struct
module Internal_Server = Server (P)

class http_servlet n np fun_serv =
object(self)
inherit [P.t] Internal_Server.server n np

method receive_h fd =
let ic = Unix.in_channel_of_descr fd in
input_line ic

method process fd =
let oc = Unix.out_channel_of_descr fd in (
try
let request = self#receive_h fd in
let args = query_string request in
fun_serv oc args;
with
Http_error s -> Printf.fprintf oc "HTTP error : %s <BR>" s
| _ -> Printf.fprintf oc "Unknown error <BR>" );
flush oc;
Unix.shutdown fd Unix.SHUTDOWN_ALL
end
end;;


As we do not expect the servlet to communicate using Objective CAML's special internal values, we choose the type string as the protocol type. The functions of_string and to_string do nothing.

# module Simple_http_server =
P_Text_Server (struct type t = string
let of_string x = x
let to_string x = x
end);;
Finally, we write the primary function to launch the service and construct an instance of the class http_servlet.

# let cgi_like_server port_num fun_serv =
let sv = new Simple_http_server.http_servlet port_num 3 fun_serv
in sv#start;;
val cgi_like_server : int -> (out_channel -> string array -> unit) -> unit =
<fun>


Testing the Servlet

It is always useful during development to be able to test the parts that are already built. For this purpose, we build a small HTTP server which sends the file specified in the HTTP request as is. The function simple_serv sends the file whose name follows the GET request (the second element of the argument array). The function also displays all of the arguments passed in the request.

# let send_file oc f =
let ic = open_in_bin f in
try
while true do
output_byte oc (input_byte ic)
done
with End_of_file -> close_in ic;;
val send_file : out_channel -> string -> unit = <fun>

# let simple_serv oc args =
try
Array.iter (fun x -> print_string (x^" ")) args;
print_newline();
send_file oc args.(1)
with _ -> Printf.printf "error\n";;
val simple_serv : out_channel -> string array -> unit = <fun>

# let run n = cgi_like_server n simple_serv;;
val run : int -> unit = <fun>


The command run 4003 launches this servlet on port 4003. In addition, we launch a browser to issue a request to load the page baro.html on port 4003. The figure 21.2 shows the display of the contents of this page in the browser.



Figure 21.2: HTTP request to an Objective CAML servlet


The browser has sent the request GET /baro.html to load the page, and then the request GET /canard.gif to load the image.

HTML Servlet Interface

We will use a CGI-style server to build an HTML-based interface to the database of chapter 6 (see page ??).

The menu of the function main will now be displayed in a form on an HTML page, providing the same selections. The responses to requests are also HTML pages, generated dynamically by the servlet. The dynamic page construction makes use of the utilities defined below.

Application Protocol

Our application will use several elements from several protocols:
  1. Requests are transmitted from a Web browser to our application server in the HTTP request format.
  2. The data items within a request are encoded in the format used by CGI applications.
  3. The response to the request is presented as an HTML page.
  4. Finally, the nature of the request is specified in a format specific to the application.
We wish to respond to three kinds of request: queries for the list of mail addresses, queries for the list of email addresses, and queries for the state of received fees between two given dates. We give these query types respectively the names:
mail_addr, email_addr and fees_state. In the last case, we will also transmit two character strings containing the desired dates. These two dates correspond to the values of the fields start and end on an HTML form.

When a client first connects, the following page is sent. The names of the requests are encoded within it in the form of HTML anchors.
<HTML>
<TITLE> association </TITLE>
<BODY>
<HR>
<H1 ALIGN=CENTER>Association</H1>
<P>
<HR>
<UL>
<LI>List of
<A HREF="http://freres-gras.ufr-info-p6.jussieu.fr:12345/mail_addr">
mail addresses
</A>
<LI>List of
<A HREF="http://freres-gras.ufr-info-p6.jussieu.fr:12345/email_addr">
email addresses
</A>
<LI>State of received fees<BR>
<FORM 
 method="GET" 
 action="http://freres-gras.ufr-info-p6.jussieu.fr:12345/fees_state">
Start date : <INPUT type="text" name="start" value="">
End date : <INPUT type="text" name="end" value="">
<INPUT name="action" type="submit" value="Send">
</FORM>
</UL>
<HR>
</BODY>
</HTML>
We assume that this page is contained in the file assoc.html.

HTML Primitives

The HTML utility functions are grouped together into a single class called print. It has a field specifying the output channel. Thus, it can be used just as well in a CGI application (where the output channel is the standard output) as in an application using the HTTP server defined in the previous section (where the output channel is a network socket).

The proposed methods essentially allow us to encapsulate text within HTML tags. This text is either passed directly as an argument to the method in the form of a character string, or produced by a function. For example, the principal method page takes as its first argument a string corresponding to the header of the page1, and as its second argument a function that prints out the contents of the page. The method page produces the tags corresponding to the HTML protocol.

The names of the methods match the names of the corresponding HTML tags, with additional options added in some cases.

# class print (oc0:out_channel) =
object(self)
val oc = oc0
method flush () = flush oc
method str =
Printf.fprintf oc "%s"
method page header (body:unit -> unit) =
Printf.fprintf oc "<HTML><HEAD><TITLE>%s</TITLE></HEAD>\n<BODY>" header;
body();
Printf.fprintf oc "</BODY>\n</HTML>\n"
method p () =
Printf.fprintf oc "\n<P>\n"
method br () =
Printf.fprintf oc "<BR>\n"
method hr () =
Printf.fprintf oc "<HR>\n"
method hr () =
Printf.fprintf oc "\n<HR>\n"
method h i s =
Printf.fprintf oc "<H%d>%s</H%d>" i s i
method h_center i s =
Printf.fprintf oc "<H%d ALIGN=\"CENTER\">%s</H%d>" i s i
method form url (form_content:unit -> unit) =
Printf.fprintf oc "<FORM method=\"post\" action=\"%s\">\n" url;
form_content ();
Printf.fprintf oc "</FORM>"
method input_text =
Printf.fprintf oc
"<INPUT type=\"text\" name=\"%s\" size=\"%d\" value=\"%s\">\n"
method input_hidden_text =
Printf.fprintf oc "<INPUT type=\"hidden\" name=\"%s\" value=\"%s\">\n"
method input_submit =
Printf.fprintf oc "<INPUT name=\"%s\" type=\"submit\" value=\"%s\">"
method input_radio =
Printf.fprintf oc "<INPUT type=\"radio\" name=\"%s\" value=\"%s\">\n"
method input_radio_checked =
Printf.fprintf oc
"<INPUT type=\"radio\" name=\"%s\" value=\"%s\" CHECKED>\n"
method option =
Printf.fprintf oc "<OPTION> %s\n"
method option_selected opt =
Printf.fprintf oc "<OPTION SELECTED> %s" opt
method select name options selected =
Printf.fprintf oc "<SELECT name=\"%s\">\n" name;
List.iter
(fun s -> if s=selected then self#option_selected s else self#option s)
options;
Printf.fprintf oc "</SELECT>\n"
method options selected =
List.iter
(fun s -> if s=selected then self#option_selected s else self#option s)
end ;;
We will assume that these utilities are provided by the module Html_frame.

Dynamic Pages for Managing the Association Database

For each of the three kinds of request, the application must construct a page in response. For this purpose we use the utility module Html_frame given above. This means that the pages are not really constructed, but that their various components are emitted sequentially on the output channel.
We provide an additional (virtual) page to be returned in response to a request that is invalid or not understood.

Error page
The function print_error takes as arguments a function for emitting an HTML page (i.e., an instance of the class print) and a character string containing the error message.


# let print_error (print:Html_frame.print) s =
let print_body() =
print#str s; print#br()
in
print#page "Error" print_body ;;
val print_error : Html_frame.print -> string -> unit = <fun>


All of our functions for emitting responses to requests will take as their first argument a function for emitting an HTML page.

List of mail addresses
To obtain the page giving the response to a query for the list of mail addresses, we will format the list of character strings obtained by the function mail_addresses, which was defined as part of the database (see page ??). We will assume that this function, and all others directly involving requests to the database, have been defined in a module named Assoc.

To emit this list, we use a function for outputting simple lines:

# let print_lines (print:Html_frame.print) ls =
let print_line l = print#str l; print#br() in
List.iter print_line ls ;;
val print_lines : Html_frame.print -> string list -> unit = <fun>


The function for responding to a query for the list of mail addresses is:

# let print_mail_addresses print db =
print#page "Mail addresses"
(fun () -> print_lines print (Assoc.mail_addresses db))
;;
val print_mail_addresses : Html_frame.print -> Assoc.data_base -> unit =
<fun>


In addition to the parameter for emitting a page, the function print_mail_addresses takes the database as its second parameter.

List of email addresses
This function is built on the same principles as that giving the list of mail addresses, except that it calls the function email_addresses from the module Assoc:

# let print_email_addresses print db =
print#page "Email addresses"
(fun () -> print_lines print (Assoc.email_addresses db)) ;;
val print_email_addresses : Html_frame.print -> Assoc.data_base -> unit =
<fun>


State of received fees
The same principle also governs the definition of this function: retrieving the data corresponding to the request (which here is a pair), then emitting the corresponding character strings.

# let print_fees_state print db d1 d2 =
let ls, t = Assoc.fees_state db d1 d2 in
let page_body() =
print_lines print ls;
print#str ("Total : "^(string_of_float t));
print#br()
in
print#page "State of received fees" page_body ;;
val print_fees_state :
Html_frame.print -> Assoc.data_base -> string -> string -> unit = <fun>


Analysis of Requests and Response

We define two functions for producing responses based on an HTTP request. The first (print_get_answer) responds to a request presumed to be formulated using the GET method of the HTTP protocol. The second alters the production of the answer according to the actual method that the request used.

These two functions take as their second argument an array of character strings containing the elements of the HTTP request as analyzed by the function get_query_string (see page ??). The first element of the array contains the method, the second the name of the database request.
In the case of a query for the state of received fees, the start and end dates for the request are contained in the two fields of the form associated with the query. The data from the form are contained in the third field of the array, which must be decomposed by the function get_form_content (see page ??).


# let print_get_answer print q db =
match q.(1) with
| "/mail_addr" -> print_mail_addresses print db
| "/email_addr" -> print_email_addresses print db
| "/fees_state"
-> let nvs = get_form_content q.(2) in
let d1 = List.assoc "start" nvs
and d2 = List.assoc "end" nvs in
print_fees_state print db d1 d2
| _ -> print_error print ("Unknown request: "^q.(1)) ;;
val print_get_answer :
Html_frame.print -> string array -> Assoc.data_base -> unit = <fun>

# let print_answer print q db =
try
match q.(0) with
"GET" -> print_get_answer print q db
| _ -> print_error print ("Unsupported method: "^q.(0))
with
e
-> let s = Array.fold_right (^) q "" in
print_error print ("Something wrong with request: "^s) ;;
val print_answer :
Html_frame.print -> string array -> Assoc.data_base -> unit = <fun>


Main Entry Point and Application

The application is a standalone executable that takes the port number as a parameter. It reads in the database before launching the server. The main function is obtained from the function print_answer defined above and from the generic HTTP server function cgi_like_server defined in the previous section (see page ??). The latter function is located in the module Servlet.

# let get_port_num() =
if (Array.length Sys.argv) < 2 then 12345
else
try int_of_string Sys.argv.(1)
with _ -> 12345 ;;
val get_port_num : unit -> int = <fun>

# let main() =
let db = Assoc.read_base "assoc.dat" in
let assoc_answer oc q = print_answer (new Html_frame.print oc) q db in
Servlet.cgi_like_server (get_port_num()) assoc_answer ;;
val main : unit -> unit = <fun>


To obtain a complete application, we combine the definitions of the display functions into a file httpassoc.ml. The file ends with a call to the function main:
main() ;;
We can then produce an executable named assocd using the compilation command:
ocamlc -thread -custom -o assocd unix.cma threads.cma \
       gsd.cmo servlet.cmo html_frame.cmo string_plus.cmo assoc.cmo \
       httpassoc.ml -cclib -lunix -cclib -lthreads
All that's left is to launch the server, load the HTML page2 contained in the file assoc.html given at the beginning of this section (page ??), and click.

The figure 21.3 shows an example of the application in use.


Figure 21.3: HTTP request to an Objective CAML servlet


The browser establishes an initial connection with the servlet, which sends it the menu page. Once the entry fields are filled in, the user sends a new request which contains the data entered. The server decodes the request and calls on the association database to retrieve the desired information. The result is translated into HTML and sent to the client, which then displays this new page.

To Learn More

This application has numerous possible enhancements. First of all, the HTTP protocol used here is overly simple compared to the new versions, which add a header supplying the type and length of the page being sent. Likewise, the method POST, which allows modification of the server, is not supported.3

To be able to describe the type of a page to be returned, the servlet would have to support the MIME convention, which is used for describing documents such as those attached to email messages.

The transmission of images, such as in figure 21.2, makes it possible to construct interfaces for 2-player games (see chapter 17), where one associates links with drawings of positions to be played. Since the server knows which moves are legal, only the valid positions are associated with links.

The MIME extension also allows defining new types of data. One can thus support a private protocol for Objective CAML values by defining a new MIME type. These values will be understandable only by an Objective CAML program using the same private protocol. In this way, a request by a client for a remote Objective CAML value can be issued via HTTP. One can even pass a serialized closure as an argument within an HTTP request. This, once reconstructed on the server side, can be executed to provide the desired result.




Previous Contents Next ocaml-book-1.0/en/html/book-ora076.html0000644000000000000000000027644407453055400014510 0ustar Standard Library Previous Contents Next

Standard Library

The standard library contains a group of stable modules. These are operating system independent. There are currently 29 modules in the standard library containing 400 functions, 30 types of which half are abstract, 8 exceptions, 10 sub-modules, and 3 parameterized modules. Clearly we will not describe all of the declarations in all of these modules. Indeed, the reference manual [LRVD99] already does that quite well. Only those modules presenting a new concept or a real difficulty in use will be detailed.

The standard library can be divided into four distinct parts:

  • linear data structures (15 modules), some of which have already appeared in the first part;
  • input-output (4 modules), for the formatting of output, the persistence and creation of cryptographic keys;
  • parsing and lexical analysis (4 modules). They are described in chapter 11 (page ??);
  • system interface that permit communication and examination of parameters passed to a command, directory navigation and file access.
To these four groups we add a fifth containing some utilities for handling or creating structures such as functions for text processing or generating pseudo-random numbers, etc.

Utilities

The modules that we have named "utilities" concern:
  • characters: the Char module primarily contains conversion functions;
  • object cloning: OO will be presented in chapter 15 (page ??), on object oriented programming
  • lazy evaluation: Lazy is first presented on page ??;
  • random number generator: Random will be described below.

Generation of Random Numbers

The Random module is a pseudo-random number generator. It establishes a random number generation function starting with a number or a list of numbers called a seed. In order to ensure that the function does not always return the same list of numbers, the programmer must give it a different seed each time the generator is initialized.

From this seed the function generates a succession of seemingly random numbers. Nevertheless, an initialization with the same seed will create the same list. To correctly initialize the generator, you need to find some outside resource, like the date represented in milliseconds, or the length of time since the start of the program.

The functions of the module:
  • initialization: init of type int -> unit and full_init of type int array -> unit initialize the generator. The second function takes an array of seeds.
  • generate random numbers: bits of type unit -> int returns a positive integer, int of type int -> int returns a positive integer ranging from 0 to a limit given as a parameter, and float returns a float between 0. and a limit given as a parameter.

Linear Data Structures

The modules for linear data structures are:
  • simple modules: Array, String, List, Sort, Stack, Queue, Buffer, Hashtbl (that is also parameterized) and Weak;
  • parameterized modules: Hashtbl (of HashedType parameters), Map and Set (of OrderedType parameters).
The parameterized modules are built from the other modules, thus making them more generic. The construction of parameterized modules will be presented in chapter 14, page ??.

Simple Linear Data Structures

The name of the module describes the type of data structures manipulated by the module. If the type is abstract, that is to say, if the representation is hidden, the current convention is to name it t inside the module. These modules establish the following structures:
  • module Array: vectors;
  • module List: lists;
  • module String: character strings;
  • module Hashtbl: hash tables (abstract type);
  • module Buffer: extensible character strings (abstract type);
  • module Stack: stacks (abstract type);
  • module Queue: queues or FIFO (abstract type);
  • module Weak: vector of weak pointers (abstract type).
Let us mention one last module that implements linear data structures:
  • module Sort: sorting on lists and vectors, merging of lists.
Family of common functions
Each of these modules (with the exception of Sort), has functions for defining structures, creating/accessing elements (such as handler functions), and converting to other types. Only the List module is not physically modifiable. We will not give a complete description of all these functions. Instead, we will focus on families of functions that one finds in these modules. Then we will detail the List and Array modules that are the most commonly used structures in functional and imperative programming.

One finds more or less the following functionality in all these modules:
  • a length function that takes the value of a type and calculates an integer corresponding to its length;
  • a clear function that empties the linear structure, if it is modifiable;
  • a function to add an element, add in general, but sometimes named differently according to common practice, (for example, push for stacks);
  • a function to access the n-th element, often called get;
  • a function to remove an element (often the first) remove or take.
In the same way, in several modules the names of functions for traversal and processing are the same:
  • map: applies a function on all the elements of the structure and returns a new structure containing the results of these calls;
  • iter: like map, but drops successive results, and returns ().
For the structures with indexed elements we have:
  • fill: replaces (modifies in place) a part of the structure with a value;
  • blit: copies a part of one structure into another structure of the same type;
  • sub: copies a part of one structure into a newly created structure.

Modules List and Array

We describe the functions of the two libraries while placing an emphasis on the similarities and the particularities of each one. For the functions common to both modules, t designates either the 'a list or 'a array type. When a function belongs to one module, we will use the dot notation.

Common or analogous functionality
The first of them is the calculation of length.
List.length : 'a t -> int

Two functions permitting the concatenation of two structures or all the structures of a list.
List.append : 'a t -> 'a t -> 'a t
List.concat : 'a t list -> 'a t

Both modules have a function to access an element designated by its position in the structure.
List.nth : 'a list -> int -> 'a
Array.get : 'a array -> int -> 'a
The function to access an element at index i of a vector t, which is frequently used, has a syntactic shorthand: t.(i).

Two functions allow you to apply an operation to all the elements of a structure.
iter : ('a -> unit) -> 'a t -> unit
map : ('a -> 'b) -> 'a t -> 'b t

You can use iter to print the contents of a list or a vector.

# let print_content iter print_item xs =
iter (fun x -> print_string"("; print_item x; print_string")") xs;
print_newline() ;;
val print_content : (('a -> unit) -> 'b -> 'c) -> ('a -> 'd) -> 'b -> unit =
<fun>
# print_content List.iter print_int [1;2;3;4;5] ;;
(1)(2)(3)(4)(5)
- : unit = ()
# print_content Array.iter print_int [|1;2;3;4;5|] ;;
(1)(2)(3)(4)(5)
- : unit = ()


The map function builds a new structure containing the result of the application. For example, with vectors whose contents are modifiable:

# let a = [|1;2;3;4|] ;;
val a : int array = [|1; 2; 3; 4|]
# let b = Array.map succ a ;;
val b : int array = [|2; 3; 4; 5|]
# a, b;;
- : int array * int array = [|1; 2; 3; 4|], [|2; 3; 4; 5|]


Two iterators can be used to compose successive applications of a function on all elements of a structure.
fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
fold_right : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'b
You have to give these iterators a base case that supplies a default value when the structure is empty.

fold_left f r [v1; v2; ...; vn] = f ... ( f (f r v1) v2 ) ... vn
fold_right f [v1; v2; ...; vn] r = f v1 ( f v2 ... (f vn r) ... )

These functions allow you to easily transform binary operations into n-ary operations. When the operation is commutative and associative, left and right iteration are indistinguishable:

# List.fold_left (+) 0 [1;2;3;4] ;;
- : int = 10
# List.fold_right (+) [1;2;3;4] 0 ;;
- : int = 10
# List.fold_left List.append [0] [[1];[2];[3];[4]] ;;
- : int list = [0; 1; 2; 3; 4]
# List.fold_right List.append [[1];[2];[3];[4]] [0] ;;
- : int list = [1; 2; 3; 4; 0]


Notice that, for binary concatenation, an empty list is a neutral element to the left and to the right. We find thus, in this specific case, the equivalence of the two expressions:

# List.fold_left List.append [] [[1];[2];[3];[4]] ;;
- : int list = [1; 2; 3; 4]
# List.fold_right List.append [[1];[2];[3];[4]] [] ;;
- : int list = [1; 2; 3; 4]
We have, in fact, found the List.concat function.

Operations specific to lists.
It is useful to have the following list functions that are provided by the List module:
List.hd : 'a list -> 'a
    first element of the list
List.tl : 'a list -> 'a
    the list, without its first element
List.rev : 'a list -> 'a list
    reversal of a list
List.mem : 'a -> 'a list -> bool
    membership test
List.flatten : 'a list list -> 'a list
    flattens a list of lists
List.rev_append : 'a list -> 'a list -> 'a list
    is the same as append (rev l1) l2
The first two functions are partial. They are not defined on the empty list and raise a Failure exception. There is a variant of mem: memq that uses physical equality.

# let c = (1,2) ;;
val c : int * int = 1, 2
# let l = [c] ;;
val l : (int * int) list = [1, 2]
# List.memq (1,2) l ;;
- : bool = false
# List.memq c l ;;
- : bool = true


The List module provides two iterators that generalize boolean conjunction and disjunction (and / or): List.for_all and List.exists that are defined by iteration:

# let for_all f xs = List.fold_right (fun x -> fun b -> (f x) & b) xs true ;;
val for_all : ('a -> bool) -> 'a list -> bool = <fun>
# let exists f xs = List.fold_right (fun x -> fun b -> (f x) or b) xs false ;;
val exists : ('a -> bool) -> 'a list -> bool = <fun>


There are variants of the iterators in the List module that take two lists as arguments and traverse them in parallel (iter2, map2, etc.). If they are not the same size, the Invalid_argument exception is raised.

The elements of a list can be searched using the criteria provided by the following boolean functions:
List.find : ('a -> bool) -> 'a list -> 'a
List.find_all : ('a -> bool) -> 'a list -> 'a list
The find_all function has an alias: filter.

A variant of the general search function is the partitioning of a list:
List.partition : ('a -> bool) -> 'a list -> 'a list * 'a list

The List module has two often necessary utility functions permitting the division and creation of lists of pairs:
List.split : ('a * 'b) list -> 'a list * 'b list
List.combine : 'a list -> 'b list -> ('a * 'b) list

Finally, a structure combining lists and pairs is often used: association lists. They are useful to store values associated to keys. These are lists of pairs such that the first entry is a key and the second is the information associated to the key. One has these data structures to deal with pairs:
List.assoc : 'a -> ('a * 'b) list -> 'b
    extract the information associated to a key
List.mem_assoc : 'a -> ('a * 'b) list -> bool
    test the existence of a key
List.remove_assoc : 'a -> ('a * 'b) list -> ('a * 'b) list
    deletion of an element corresponding to a key
Each of these functions has a variant using physical equality instead of structural equality: List.assq, List.mem_assq and List.remove_assq.

Handlers specific to Vectors.
The vectors that imperative programmers often use are physically modifiable structures. The Array module furnishes a function to change the value of an element:
Array.set : 'a array -> int -> 'a -> unit
Like get, the set function has a syntactic shortcut: t.(i) <- a.

There are three vector allocation functions:
Array.create : int -> 'a -> 'a array
    creates a vector of a given size whose elements are all initialized with the same value
Array.make : int -> 'a -> 'a array
    alias for create
Array.init : int -> (int -> 'a) -> 'a array
    creates a vector of a given size whose elements are each initialized with the result of the application of a function to the element's index

Since they are frequently used, the Array module has two functions for the creation of matrices (vectors of vectors):
Array.create_matrix : int -> int -> 'a -> 'a array array
Array.make_matrix : int -> int -> 'a -> 'a array array

The set function is generalized as a function modifying the values on an interval described by a starting index and a length:
Array.fill : 'a array -> int -> int -> 'a -> unit

One can copy a whole vector or extract a sub-vector (described by a starting index and a length) to obtain a new structure:
Array.copy : 'a array -> 'a array
Array.sub : 'a array -> int -> int -> 'a array

The copy or extraction can also be done towards another vector:
Array.blit : 'a array -> int -> 'a array -> int -> int -> unit
The first argument is the index into the first vector, the second is the index into the second vector and the third is the number of values copied. The three functions blit, sub and fill raise the Invalid_argument exception.

The privileged use of indices in the vector manipulation functions leads to the definition of two specific iterators:
Array.iteri : (int -> 'a -> unit) -> 'a array -> unit
Array.mapi : (int -> 'a -> 'b) -> 'a array -> 'b array

They apply a function whose first argument is the index of the affected element.

# let f i a = (string_of_int i) ^ ":" ^ (string_of_int a) in
Array.mapi f [| 4; 3; 2; 1; 0 |] ;;
- : string array = [|"0:4"; "1:3"; "2:2"; "3:1"; "4:0"|]


Although the Array module does not have a function to modify the contents of all the elements in a vector, this effect can be easily obtained using iteri:

# let iter_and_set f t =
Array.iteri (fun i -> fun x -> t.(i) <- f x) t ;;
val iter_and_set : ('a -> 'a) -> 'a array -> unit = <fun>
# let v = [|0;1;2;3;4|] ;;
val v : int array = [|0; 1; 2; 3; 4|]
# iter_and_set succ v ;;
- : unit = ()
# v ;;
- : int array = [|1; 2; 3; 4; 5|]


Finally, the Array module provides two list conversion functions:
Array.of_list : 'a list -> 'a array
Array.to_list : 'a array -> 'a list

Input-output

The standard library has four input-output modules:
  • module Printf: for the formatting of output;
  • Format: pretty-printing facility to format text within ``pretty-printing boxes''. The pretty-printer breaks lines at specified break hints, and indents lines according to the box structure.
  • module Marshal: implements a mechanism for persistent values;
  • module Digest: for creating unique keys.
The description of the Marshal module will be given later in the chapter when we begin to discuss persistent data structures (see page ??).

Module Printf

The Printf module formats text using the rules of the printf function in the C language library. The display format is represented as a character string that will be decoded according to the conventions of printf in C, that is to say, by specializing the % character. This character followed by a letter indicates the type of the argument at this position. The following format "(x=%d, y=%d)" indicates that it should put two integers in place of the %d in the output string.

Specification of formats.
A format defines the parameters for a printed string. Those, of basic types: int, float, char and string, will be converted to strings and will replace their occurrence in the printed string. The values 77 and 43 provided to the format "(x=%d, y=%d)" will generate the complete printed string "(x=77, y=43)". The principal letters indicating the type of conversion to carry out are given in figure 8.1.


Type Letter Result
integer d or i signed decimal
  u unsigned decimal
  x unsigned hexadecimal, lower case form
  X same, with upper case letters
character c character
string s string
float f decimal
  e or E scientific notation
  g or G same
boolean b true or false
special a or t functional parameter
    of type (out_channel -> 'a -> unit) -> 'a -> unit
    or out_channel -> unit

Figure 8.1: Conversion conventions.


The format also allows one to specify the justification of the conversion, which allows for the alignment of the printed values. One can indicate the size in conversion characters. For this one places between the % character and the type of conversion an integer number as in %10d that indicates a conversion to be padded on the right to ten characters. If the size of the result of the conversion exceeds this limit, the limit will be discarded. A negative number indicates left justification. For conversions of floating point numbers, it is helpful to be able to specify the printed precision. One places a decimal point followed by a number to indicate the number of characters after the decimal point as in %.5f that indicates five characters to the right of the decimal point.

There are two specific format letters: a and t that indicate a functional argument. Typically, a print function defined by the user. This is specific to Objective CAML.

Functions in the module
The types of the five functions in this module are given in figure 8.2.


fprintf : out_channel -> ('a, out_channel, unit) format -> 'a
printf : ('a, out_channel, unit) format -> 'a
eprintf : ('a, out_channel, unit) format -> 'a
sprintf : ('a, unit, string) format -> 'a
bprintf : Buffer.t -> ('a, Buffer.t, string) format -> 'a

Figure 8.2: Printf formatting functions.


The fprintf function takes a channel, a format and arguments of types described in the format. The printf and eprintf functions are specializations on standard output and standard error. Finally, sprintf and bprintf do not print the result of the conversion, but instead return the corresponding string.

Here are some simple examples of the utilization of formats.

# Printf.printf "(x=%d, y=%d)" 34 78 ;;
(x=34, y=78)- : unit = ()
# Printf.printf "name = %s, age = %d" "Patricia" 18 ;;
name = Patricia, age = 18- : unit = ()
# let s = Printf.sprintf "%10.5f\n%10.5f\n" (-.12.24) (2.30000008) ;;
val s : string = " -12.24000\n 2.30000\n"
# print_string s ;;
-12.24000
2.30000
- : unit = ()


The following example builds a print function from a matrix of floats using a given format.

# let print_mat m =
Printf.printf "\n" ;
for i=0 to (Array.length m)-1 do
for j=0 to (Array.length m.(0))-1 do
Printf.printf "%10.3f" m.(i).(j)
done ;
Printf.printf "\n"
done ;;
val print_mat : float array array -> unit = <fun>
# print_mat (Array.create 4 [| 1.2; -.44.22; 35.2 |]) ;;

1.200 -44.220 35.200
1.200 -44.220 35.200
1.200 -44.220 35.200
1.200 -44.220 35.200
- : unit = ()


Note on the format type.
The description of a format adopts the syntax of character strings, but it is not a value of type string. The decoding of a format, according to the preceding conventions, builds a value of type format where the 'a parameter is instantiated either with unit if the format does not mention a parameter, or by a functional type corresponding to a function able to receive as many arguments as are mentioned and returning a value of type unit.

One can illustrate this process by partially applying the printf function to a format:

# let p3 =
Printf.printf "begin\n%d is val1\n%s is val2\n%f is val3\n" ;;
begin
val p3 : int -> string -> float -> unit = <fun>
One obtains thus a function that takes three arguments. Note that the word begin had already been printed. Another format would have given another type of function:

# let p2 =
Printf.printf "begin\n%f is val1\n%s is val2\n";;
begin
val p2 : float -> string -> unit = <fun>
In providing arguments one by one to p3, one progressively obtains the output.

# let p31 = p3 45 ;;
45 is val1
val p31 : string -> float -> unit = <fun>
# let p32 = p31 "hello" ;;
hello is val2
val p32 : float -> unit = <fun>
# let p33 = p32 3.14 ;;
3.140000 is val3
val p33 : unit = ()
# p33 ;;
- : unit = ()
From the last obtained value, nothing is printed: it is the value () of type unit.

One cannot build a format using values of type string:

# let f d =
Printf.printf (d^d);;
Characters 27-30:
This expression has type string but is here used with type
('a, out_channel, unit) format


The compiler cannot know the value of the string passed as an argument. It thus cannot know the type that instantiates the 'a parameter of type format.

On the other hand, strings are physically modifiable values, it would thus be possible to replace, for example, the %d part with another letter, thus dynamically changing the print format. This conflicts with the static generation of the conversion function.

Digest Module

A hash function converts a character string of unspecified size into a character string of fixed length, most often smaller. Hashing functions return a fingerprint (digest) of their entry.

Such functions are used for the construction of hash tables, as in the Hashtbl module, permitting one to rapidly test if an element is a member of such a table by directly accessing the fingerprint. For example the function f_mod_n, that generates the modulo n sum of the ASCII codes of the characters in a string, is a hashing function. If one creates an n by n table to arrange the strings, from the fingerprint one obtains direct access. Nevertheless two strings can return the same fingerprint. In the case of collisions, one adds to the hash table an extension to store these elements. If there are too many collisions, then access to the hash table is not very effective. If the fingerprint has a length of n bits, then the probability of collision between two different strings is 1/2n.

A non-reversible hash function has a very weak probability of collision. It is thus difficult, given a fingerprint, to construct a string with this fingerprint. The preceding function f_mod_n is not, based on the evidence, such a function. One way hash functions permit the authentification of a string, that it is for some text sent over the Internet, a file, etc.

The Digest module uses the MD5 algorithm, short for Message Digest 5. It returns a 128 bit fingerprint. Although the algorithm is public, it is impossible (today) to carry out a reconstruction from a fingerprint. This module defines the Digest.t type as an abbreviation of the string type. The figure 8.3 details the main functions of this module.


string : string -> t
    returns the fingerprint of a string
file : string -> t
    returns the fingerprint of a file

Figure 8.3: Functions of the Digest module.


We use the string function in the following example on a small string and on a large one built from the first. The fingerprint is always of fixed length.

# let s = "The small cat is dead...";;
val s : string = "The small cat is dead..."
# Digest.string s;;
- : Digest.t = "xr6\127\171(\134=\238`\252F\028\t\210$"

# let r = ref s in
for i=1 to 100 do r:= s^ !r done;
Digest.string !r;;
- : Digest.t = "\232\197|C]\137\180{>\224QX\155\131D\225"


The creation of a fingerprint for a program allows one to guarantee the contents and thus avoids the use of a bad version. For example, when code is dynamically loaded (see page ??), a fingerprint is used to select the binary file to load.

# Digest.file "basic.ml" ;;
- : Digest.t = "\179\026\191\137\157Ly|^w7\183\164:\167q"


Persistence

Persistence is the conservation of a value outside the running execution of a program. This is the case when one writes a value in a file. This value is thus accessible to any program that has access to the file. Writing and reading persistent values requires the definition of a format for representing the coding of data. In effect, one must know how to go from a complex structure stored in memory, such as a binary tree, to a linear structure, a list of bytes, stored in a file. This is why the coding of persistent values is called linearization 1.

Realization and Difficulties of Linearization

The implementation of a mechanism for the linearization of data structures requires choices and presents difficulties that we describe below.
  • read-write of data structures. Since memory can always be viewed as a vector of words, one value can always correspond to the memory that it occupies, leaving us to preserve the useful part by then compacting the value.
  • share or copy. Must the linearization of a data structure conserve sharing? Typically a binary tree having two identical children (in the sense of physical equality) can indicate, for the second child, that it has already saved the first. This characteristic influences the size of the saved value and the time taken to do it. On the other hand, in the presence of physically modifiable values, this could change the behavior of this value after a recovery depending on whether or not sharing was conserved.
  • circular structures. In the case of a circular value, linearization without sharing is likely to loop. It will be necessary to conserve sharing.
  • functional values. Functional values, or closures, are composed of an environment part and a code part. The code part corresponds to the entry point (address) of the code to execute. What must thus be done with code? It is possible to uniquely store this address, but thus only the same program will find the correct meaning of this address. It is also possible to save the list of machine instructions of this function, but that would require having a mechanism to dynamically load code.
  • guaranteeing the type when reloading. This is the main difficulty of this mechanism. Static typing guarantees that typed values will not generate type errors at execution time. But this is not true except for values belonging to the program during the course of execution. What type can one give to a value outside the program, that was not seen by the type verifier? Just to verify that the re-read value has the monomorphic type generated by the compiler, the type would have to be transmitted at the moment the value was saved, then the type would have to be checked when the value was loaded. Additionally, a mechanism to manage the versions of types would be needed to be safe in case a type is redeclared in a program.

Marshal Module

The linearization mechanism in the Marshal module allows you to choose to keep or discard the sharing of values. It also allows for the use of closures, but in this case, only the pointer to the code is saved.

This module is mainly comprised of functions for linearization via a channel or a string, and functions for recovery via a channel or a string. The linearization functions are parameterizable. The following type declares two possible options:
type external_flag = 
  No_sharing
| Closures;;
The No_sharing constant constructor indicates that the sharing of values is not to be preserved, though the default is to keep sharing. The Closures constructor allows the use of closures while conserving its pointer to the code. Its absence will raise an exception if one tries to store a functional value.

Warning


The Closures constructor is inoperative in interactive mode. It can only be used in command line mode.


The reading and writing functions in this module are gathered in figure 8.4.


to_channel : out_channel -> 'a -> extern_flag list -> unit
to_string : 'a -> extern_flag list -> string
to_buffer : string -> int -> int -> 'a -> extern_flag list -> unit
from_channel : in_channel -> 'a
from_string : string -> int -> 'a

Figure 8.4: Functions of the Marshal module.


The to_channel function takes an output channel, a value, and a list of options and writes the value to the channel. The to_string function produces a string corresponding to the linearized value, whereas to_buffer accomplishes the same task by modifying part of a string passed as an argument. The from_channel function reads a linearized value from a channel and returns it. The from_string variant takes as input a string and the position of the first character to read in the string. Several linearized values can be stored in the same file or in the same string. For a file, they can be read sequentially. For a string, one must specify the right offset from the beginning of the string to decode the desired value.


# let s = Marshal.to_string [1;2;3;4] [] in String.sub s 0 10;;
- : string = "\132\149\166\190\000\000\000\t\000\000"


Warning


Using this module one loses the safety of static typing (see infra, page ??).


Loading a persistent object creates a value of indeterminate type:

# let x = Marshal.from_string (Marshal.to_string [1; 2; 3; 4] []) 0;;
val x : '_a = <poly>
This indetermination is denoted in Objective CAML by the weakly typed variable '_a. You should specify the expected type:

# let l =
let s = (Marshal.to_string [1; 2; 3; 4] []) in
(Marshal.from_string s 0 : int list) ;;
val l : int list = [1; 2; 3; 4]
We return to this topic on page ??.

Note


The output_value function of the preloaded library corresponds to calling to_channel with an empty list of options. The input_value function in the Pervasives module directly calls the from_channel function. These functions were kept for compatibility with old programs.


Example: Backup Screens

We want to save the bitmap, represented as a matrix of colors, of the whole screen. The save_screen function recovers the bitmap, converts it to a table of colors and saves it in a file whose name is passed as a parameter.

# let save_screen name =
let i = Graphics.get_image 0 0 (Graphics.size_x ())
(Graphics.size_y ()) in
let j = Graphics.dump_image i in
let oc = open_out name in
output_value oc j;
close_out oc;;
val save_screen : string -> unit = <fun>


The load_screen function does the reverse operation. It opens the file whose name is passed as a parameter, restores the value stored inside, converts this color matrix into a bitmap, then displays the bitmap.

# let load_screen name =
let ic = open_in name in
let image = ((input_value ic) : Graphics.color array array) in
close_in ic;
Graphics.close_graph();
Graphics.open_graph (" "^(string_of_int(Array.length image.(0)))
^"x"^(string_of_int(Array.length image)));
let image2 = Graphics.make_image image in
Graphics.draw_image image2 0 0; image2 ;;
val load_screen : string -> Graphics.image = <fun>


Warning


Abstract typed values cannot be made persistent.
It is for this reason that the preceding example does not use the abstract Graphics.image type, but instead uses the concrete color array array type. The abstraction of types is presented in chapter 14.

Sharing

The loss of sharing in a data structure can make the structure completely lose its intended behavior. Let us revisit the example of the symbol generator from page ??. For whatever reason, we want to save the functional values new_s and reset_s, and thereafter use the current value of their common counter. We thus write the following program:
 
# let reset_s,new_s =
let c = ref 0 in
( function () -> c := 0 ) ,
( function s -> c:=!c+1; s^(string_of_int !c) ) ;;

# let save =
Marshal.to_string (new_s,reset_s) [Marshal.Closures;Marshal.No_sharing] ;;

# let (new_s1,reset_s1) =
(Marshal.from_string save 0 : ((string -> string ) * (unit -> unit))) ;;

# (* 1 *)
Printf.printf "new_s : \%s\n" (new_s "X");
Printf.printf "new_s : \%s\n" (new_s "X");
(* 2 *)
Printf.printf "new_s1 : \%s\n" (new_s1 "X");
(* 3 *)
reset_s1();
Printf.printf "new_s1 (after reset_s1) : \%s\n" (new_s1 "X") ;;
Characters 148-154:
Unbound value new_s1


The first two outputs in (* 1 *) comply with our intent. The output obtained in (* 2 *) after re-reading the closures also appears correct (after X2 comes X3). But, in fact, the sharing of the c counter between the re-read functions new_s1 and reset_s1 is lost, as the output of X4 attests that one of them set the counter to zero. Each closure has a copy of the counter and the call to reset_s1 does not reset the new_s1 counter to zero. Thus we should not have used the No_sharing option during the linearization.

It is generally necessary to conserve sharing. Nevertheless in certain cases where execution speed is important, the absence of sharing speeds up the process of saving. The following example demonstrates a function that copies a matrix. In this case it might be preferable to break the sharing:

# let copy_mat_f (m : float array array) =
let s = Marshal.to_string m [Marshal.No_sharing] in
(Marshal.from_string s 0 : float array array);;
val copy_mat_f : float array array -> float array array = <fun>


One can also use it to create a matrix without sharing:

# let create_mat_f n m v =
let m = Array.create n (Array.create m v) in
copy_mat_f m;;
val create_mat_f : int -> int -> float -> float array array = <fun>
# let a = create_mat_f 3 4 3.14;;
val a : float array array =
[|[|3.14; 3.14; 3.14; 3.14|]; [|3.14; 3.14; 3.14; 3.14|];
[|3.14; 3.14; 3.14; 3.14|]|]
# a.(1).(2) <- 6.28;;
- : unit = ()
# a;;
- : float array array =
[|[|3.14; 3.14; 3.14; 3.14|]; [|3.14; 3.14; 6.28; 3.14|];
[|3.14; 3.14; 3.14; 3.14|]|]


Which is a more common behavior than that of Array.create, and resembles that of Array.create_matrix.

Size of Values

It may be useful to know the size of a persistent value. If sharing is conserved, this size also reflects the amount of memory occupied by a value. Although the encoding sometimes optimizes the size of atomic values2, knowing the size of their respective encodings permits us to compare different implementations of a data structure. In addition, for programs that will never stop themselves, like embedded systems or even network servers; watching the size of data structures can help detect memory leaks. The Marshal module has two functions to calculate the size of a constant. They are described in figure 8.5.

header_size : int
data_size : string -> int -> int
total_size : string -> int -> int

Figure 8.5: Size functions of Marshal.


The total size of a persistent value is the same as the size of its data structures plus the size of its header.

Below is a small example of the use of MD5 encoding to compare two representations of binary trees:

# let size x = Marshal.data_size (Marshal.to_string x []) 0;;
val size : 'a -> int = <fun>
# type 'a bintree1 = Empty1 | Node1 of 'a * 'a bintree1 * 'a bintree1 ;;
type 'a bintree1 = | Empty1 | Node1 of 'a * 'a bintree1 * 'a bintree1
# let s1 =
Node1(2, Node1(1, Node1(0, Empty1, Empty1), Empty1),
Node1(3, Empty1, Empty1)) ;;
val s1 : int bintree1 =
Node1
(2, Node1 (1, Node1 (0, Empty1, Empty1), Empty1),
Node1 (3, Empty1, Empty1))
# type 'a bintree2 =
Empty2 | Leaf2 of 'a | Node2 of 'a * 'a bintree2 * 'a bintree2 ;;
type 'a bintree2 =
| Empty2
| Leaf2 of 'a
| Node2 of 'a * 'a bintree2 * 'a bintree2
# let s2 =
Node2(2, Node2(1, Leaf2 0, Empty2), Leaf2 3) ;;
val s2 : int bintree2 = Node2 (2, Node2 (1, Leaf2 0, Empty2), Leaf2 3)
# let s1, s2 = size s1, size s2 ;;
val s1 : int = 13
val s2 : int = 9
The values given by the size function reflect well the intuition that one might have of the size of s1 and s2.

Typing Problem

The real problem with persistent values is that it is possible to break the type system of Objective CAML. The creation functions return a monomorphic type (unit or string). On the other hand unmarshalling functions return a polymorphic type 'a. From the point of view of types, you can do anything with a persistent value. Here is the usage that can be done with it (see chapter 2, page ??): create a function magic_copy of type 'a -> 'b.

# let magic_copy a =
let s = Marshal.to_string a [Marshal.Closures] in
Marshal.from_string s 0;;
val magic_copy : 'a -> 'b = <fun>


The use of such a function causes a brutal halt in the execution of the program.
#  (magic_copy 3 : float) +. 3.1;;
Segmentation fault
In interactive mode (under Linux), we even leave the toplevel (interactive) loop with a system error signal corresponding to a memory violation.

Interface with the System

The standard library has six system interface modules:
  • module Sys: for communication between the operating system and the program;
  • module Arg: to analyze parameters passed to the program from the command line;
  • module Filename: operations on file names
  • module Printexc: for the interception and printing of exceptions;
  • module Gc: to control the mechanism that automatically deallocates memory, described in chapter 9;
  • module Callback: to call Objective CAML functions from C, described in chapter 12.
The first four modules are described below.

Module Sys

This module provides quite useful functions for communication with the operating system, such as handling the signals received by a program. The values in figure 8.6 contain information about the system.


OS_type : string
    type of system
interactive : bool ref
    true if executing at the toplevel
word_size : string
    size of a word (32 or 64 bits)
max_string_length : int
    maximum size of a string
max_array_length : int
    maximum size of a vector
time : unit -> float
    gives the time in seconds since the start of the program

Figure 8.6: Information about the system.


Communication between the program and the system can go through the command line, the value of an environmental variable, or through running another program. These functions are described in figure 8.7.

argv : string array
    contains the vector of parameters
getenv : string -> string
    retrieves the value of a variable
command : string -> int
    executes the command passed as an argument

Figure 8.7: Communication with the system.


The functions of the figure 8.8 allow us to navigate in the file hierarchy.

file_exists : string -> bool
    returns true if the file exists
remove : string -> unit
    destroys a file
rename : string -> string -> unit
    renames a file
chdir : string -> unit
    change the current directory
getcwd : unit -> string
    returns the name of the current directory

Figure 8.8: File manipulation.


Finally, the management of signals will be described in the chapter on system programming (18).

Here is a small program that revisits the example of saving a graphics window as an array of colors. The main function verifies that it is not started from the interactive loop, then reads from the command line the names of files to display, then tests if they exist, then displays them (with the load_screen function). We wait for a key to be pressed between displaying two images.

# let main () =
if not (!Sys.interactive) then
for i = 0 to Array.length(Sys.argv) -1 do
let name = Sys.argv.(i) in
if Sys.file_exists name then
begin
ignore(load_screen name);
ignore(Graphics.read_key)
end
done;;
val main : unit -> unit = <fun>


Module Arg

The Arg module defines a small syntax for command line arguments. With this module, you can parse arguments and associate actions with them. The various elements of the command line are separated by one or more spaces. They are the values stored in the Sys.argv array. In the syntax provided by Arg, certain elements are distinguished by starting with the minus character (-). These are called command line keywords or switches. One can associate a specific action with a keyword or take as an argument a value of type string, int or float. The value of these arguments is initialized with the value found on the command line just after the keyword. In this case one can call a function that converts character strings into the expected type. The other elements on the command line are called anonymous arguments. One associates an action with them that takes their value as an argument. An undefined option causes the display of some short documentation on the command line. The documentation's contents are defined by the user.

The actions associated with keywords are encapsulated in the type:

type spec =
| Unit of (unit -> unit) (* Call the function with unit argument*)
| Set of bool ref (* Set the reference to true*)
| Clear of bool ref (* Set the reference to false*)
| String of (string -> unit) (* Call the function with a string
argument *)
| Int of (int -> unit) (* Call the function with an int
argument *)
| Float of (float -> unit) (* Call the function with a float
argument *)
| Rest of (string -> unit) (* Stop interpreting keywords and call the
function with each remaining argument*)


The command line parsing function is:

# Arg.parse ;;
- : (string * Arg.spec * string) list -> (string -> unit) -> string -> unit =
<fun>


Its first argument is a list of triples of the form (key, spec, doc) such that:
  • key is a character string corresponding to the keyword. It starts with the reserved character '_'.
  • spec is a value of type spec specifying the action associated with key.
  • doc is a character string describing the option key. It is displayed upon a syntax error.
The second argument is the function to process the anonymous command line arguments. The last argument is a character string displayed at the beginning of the command line documentation.

The Arg module also includes:
  • Bad: an exception taking as its argument a character string. It can be used by the processing functions.
  • usage: of type (string * Arg.spec * string) list -> string -> unit, this function displays the command line documentation. One preferably provides it with the same arguments as those of parse.
  • current: of type int ref that contains a reference to the current value of the index in the Sys.argv array. One can therefore modify this value if necessary.
By way of an example, we show a function read_args that initializes the configuration of the Minesweeper game seen in chapter 6, page ??. The possible options will be -col, -lin and -min. They will be followed by an integer indicating, respectively: the number of columns, the number of lines and the number of mines desired. These values must not be less than the default values, respectively 10, 10 and 15.

The processing functions are:

# let set_nbcols cf n = cf := {!cf with nbcols = n} ;;
# let set_nbrows cf n = cf := {!cf with nbrows = n} ;;
# let set_nbmines cf n = cf := {!cf with nbmines = n} ;;


All three are of type config ref -> int -> unit. The command line parsing function can be written:

# let read_args() =
let cf = ref default_config in
let speclist =
[("-col", Arg.Int (set_nbcols cf), "number of columns (>=10)");
("-lin", Arg.Int (set_nbrows cf), "number of lines (>=10)");
("-min", Arg.Int (set_nbmines cf), "number of mines (>=15)")]
in
let usage_msg = "usage : minesweep [-col n] [-lin n] [-min n]" in
Arg.parse speclist (fun s -> ()) usage_msg; !cf ;;
val read_args : unit -> config = <fun>


This function calculates a configuration that will be passed as arguments to open_wcf, the function that opens the main window when the game is started. Each option is, as its name indicates, optional. If it does not appear on the command line, the corresponding parameter keeps its default value. The order of the options is unimportant.

Module Filename

The Filename module has operating system independant functions to manipulate the names of files. In practice, the file and directory naming conventions differ greatly between Windows, Unix and MacOS.

Module Printexc

This very short module (three functions described in figure 8.9) provides a general exception handler. This is particularly useful for programs executed in command mode3 to be sure not to allow an exception to escape that would stop the program.

catch : ('a -> 'b) -> 'a -> 'b
    general exception handler
print : ('a -> 'b) -> 'a -> 'b
    print and re-raise the exception
to_string : exn -> string
    convert an exception to a string

Figure 8.9: Handling exceptions.


The catch function applies its first argument to its second. This launches the main function of the program. If an exception arrives at the level of catch, that is to say that if it is not handled inside the program, then catch will print its name and exit the program. The print function has the same behavior as catch but re-raises the exception after printing it. Finally the to_string function converts an exception into a character string. It is used by the two preceding functions. If we look again at the main function for displaying bitmaps, we might thus write an encapsulating function go in the following manner:

# let go () =
Printexc.catch main ();;
val go : unit -> unit = <fun>


This permits the normal termination of the program by printing the value of the uncaptured exception.


Previous Contents Next ocaml-book-1.0/en/html/book-ora204.html0000644000000000000000000001037707453055401014471 0ustar Future of Objective CAML development Previous Contents Next

Future of Objective CAML development

It is difficult for a new language to exist if it is not accompanied by the important development of an application (like Unix for C) or considerable commercial and industrial support (like SUN for JAVA). The intrinsic qualities of the language are rarely enough. Objective CAML has numerous qualities and some defects which we have described in the course of this chapter. For its part, Objective CAML is sustained by INRIA where it was conceived and implemented in the bosom of the CRISTAL project. Born of academic research, Objective CAML is used there as an experimental laboratory for testing new programming paradigms, and an implementation language. It is widely taught in various university programs and preparatory classes. Several thousand students each year learn the concepts of the language and practice it. In this way the Objective CAML language has an important place in the academic world. The teaching of computer science, in France, but also in the United States, creates numerous programmers in this language on a practical as well as a theoretical level.

On the other hand, in industry the movement is less dynamic. To our knowledge, there is not a single commercial application, developed in Objective CAML, sold to the general public and advertising its use of Objective CAML. The only example coming close is that of the SCOL language from Cryo-Networks. There is however a slight agitation in this direction. The first appeals for funding for Objective CAML application startups are appearing. Without hoping for a rapid snowball effect, it is significant that a demand exists for this type of language. And without hoping for a very short-term return on investment either, it is important to take notice of it.

It is now for the language and its development environment to show their relevance. To accompany this phenomenon, it is no doubt necessary to provide certain guarantees as to the evolution of the language. In this capacity, Objective CAML is only just now emerging and must make the choice to venture further out of academia. But this ``entry into the world'' will only take place if certain rules are followed:
  • guaranteeing the survival of developments by assuring upward compatibility in future versions of the language (the difficulty being stability of new elements (objects, etc.));
  • specifying the language in conjunction with real developers with a view to future standardization (which will permit the development of several implementations to guarantee the existence of several solutions);
  • conceiving a development environment containing a portable graphical interface, a CORBA bus, database interfaces, and especially a more congenial debugging environment.
Some of the points brought up, in particular standardization, can remain within the jurisdiction of academia. Others are only of advantage to industry. Thus everthing will depend on their degree of cooperation. There is a precedent demonstrating that a language can be ``free'' and still be commercially maintained, as this was the case for the gnat compiler of the ADA language and the ACT corporation.

Link


http://www.act-europe.fr





Previous Contents Next ocaml-book-1.0/en/html/book-ora002.gif0000644000000000000000000000470707452056110014263 0ustar GIF89aUUU!,ڋ޼HBʲ L HL*̦ËDԪMUٮ *[NR s*~Z/ǠWhxD@YC 2 0piz8jPV {VY7* KWi+<`L<,=RwţjZnq^Wwx.Mk \#UzOR 0G(iji'M aے F%v?;U]l?/,c7er %cR, ё Summary Previous Contents Next

Summary

This chapter gave an overview of the different Objective CAML libraries presented as a set of simple modules (or compilation units). The modules for output formatting (Printf), persistant values (Marshal), the system interface (Sys) and the handling of exceptions (module Printexc) were detailed. The modules concerning parsing, memory management, system and network programming and light-weight processes will be presented in the following chapters.


Previous Contents Next ocaml-book-1.0/en/html/book-ora008.gif0000644000000000000000000000252407452056110014264 0ustar GIF89a!,ڋޜ~ݨ!:^p2K 0:ȦSyjGd-z*-76v '<_u>XGxHfH%0WiH8&tǤ iS8:Ykp K)k)[+X4|zU: v +4nJ~ɘh*΍N߃O{ DvnJ[MG,@9QňCJ4e4J<˗DEn˙c8^ZB1D:ӶĄ:XZa-*_ "[sƮZ-ZZaV7]iit,] ,aAb8l#ܔ(Rcά.\ʗ?,,ZզW("))8͎}+4nv[1i)7<0[S9m㜓Bn:xkM;Q?G'ۼ,ЫO=fw߀g R z &ȟjʄ "hafaT*z(Yމ(܊,bhbP4xÃ(rc<(c@ސAi䋼%)kL.XR.ޓ-f2d%<>J7U?br%N_]gNMyFevy'}璛Qf_r[jyhh:*)V:VUg}RZ)iߥ5Ej觠 ãI|d꟰ƚSJuej~+:+JiY0l4N"}"z$>HкR nC0;ھ믦6?*'p6n{۹*7xe:q|y$gD`\3f3^L =|Zl=I{frRmn봽9!n}-{굏뎨yjfƶЮW 'ݨٍL'6M9FxuZ֎[cd.\GÕ6ޓȲOܚ 1hJ"[!}(`xւYm~T{nNC'Kt{Ht_EokONl3{#!l~ulq?QQ~_ ? sb䘡y<EщhvLFCNw;]HAleV#`NS:M S3ص敆(*xEDb Debugging Tools Previous Contents Next

Debugging Tools

There are two debugging tools. The first is a trace mechanism that can be used on the global functions in the toplevel loop. The second tool is a debugger that is not used in the normal toplevel loop. After a first program run it is possible to go back to breakpoints, and to inspect values or to restart certain functions with different arguments. This second tool only runs under Unix, because it duplicates the running process via a fork (see page ??).

Trace

The trace of a function is the list of the values of its parameters together with its result in the course of a program run.

The trace commands are directives in the toplevel loop. They allow to trace a function, stop its trace or to stop all active traces. These three directives are shown in the table below.
#trace name trace function name
#untrace name stop tracing function name
#untrace_all stop all traces

Here is a first example of the definition of a function f:

# let f x = x + 1;;
val f : int -> int = <fun>
# f 4;;
- : int = 5


Now we will trace this function, so that its arguments and its return value will be shown.

# #trace f;;
f is now traced.
# f 4;;
f <-- 4
f --> 5
- : int = 5
Passing of the argument 4 to f is shown, then the function f calculates the desired value and the result is returned and also shown. The arguments of a function call are indicated by a left arrow and the return value by an arrow to the right.

Functions of Several Arguments

Functions of several arguments (or functions returning a closure) are also traceable. Each argument passed is shown. To distinguish the different closures, the number of arguments already passed to the closures is marked with a *. Let the function verif_div take 4 numbers (a, b, q, r) corresponding to the integer division: a = bq + r.

# let verif_div a b q r =
a = b*q + r;;
val verif_div : int -> int -> int -> int -> bool = <fun>
# verif_div 11 5 2 1;;
- : bool = true


Its trace shows the passing of 4 arguments:

# #trace verif_div;;
verif_div is now traced.
# verif_div 11 5 2 1;;
verif_div <-- 11
verif_div --> <fun>
verif_div* <-- 5
verif_div* --> <fun>
verif_div** <-- 2
verif_div** --> <fun>
verif_div*** <-- 1
verif_div*** --> true
- : bool = true


Recursive Functions

The trace gives valuable information about recursive functions, e.g. poor stopping criteria are easily detected.

Let the function belongs_to which tests whether an integer belongs to a list of integers be defined in the following manner:

# let rec belongs_to (e : int) l = match l with
[] -> false
| t::q -> (e = t) || belongs_to e q ;;
val belongs_to : int -> int list -> bool = <fun>
# belongs_to 4 [3;5;7] ;;
- : bool = false
# belongs_to 4 [1; 2; 3; 4; 5; 6; 7; 8] ;;
- : bool = true


The trace of the function invocation belongs_to 4 [3;5;7] will show the four calls of this function and the results returned.

# #trace belongs_to ;;
belongs_to is now traced.
# belongs_to 4 [3;5;7] ;;
belongs_to <-- 4
belongs_to --> <fun>
belongs_to* <-- [3; 5; 7]
belongs_to <-- 4
belongs_to --> <fun>
belongs_to* <-- [5; 7]
belongs_to <-- 4
belongs_to --> <fun>
belongs_to* <-- [7]
belongs_to <-- 4
belongs_to --> <fun>
belongs_to* <-- []
belongs_to* --> false
belongs_to* --> false
belongs_to* --> false
belongs_to* --> false
- : bool = false


At each call of the function belongs_to the argument 4 and the list to search in are passed as arguments. When the list becomes empty, the functions return false as a return value which is passed along to each waiting recursive invocation.

The following example shows the section of the list when the element searched for appears:

# belongs_to 4 [1; 2; 3; 4; 5; 6; 7; 8] ;;
belongs_to <-- 4
belongs_to --> <fun>
belongs_to* <-- [1; 2; 3; 4; 5; 6; 7; 8]
belongs_to <-- 4
belongs_to --> <fun>
belongs_to* <-- [2; 3; 4; 5; 6; 7; 8]
belongs_to <-- 4
belongs_to --> <fun>
belongs_to* <-- [3; 4; 5; 6; 7; 8]
belongs_to <-- 4
belongs_to --> <fun>
belongs_to* <-- [4; 5; 6; 7; 8]
belongs_to* --> true
belongs_to* --> true
belongs_to* --> true
belongs_to* --> true
- : bool = true
As soon as 4 becomes head of the list, the functions return true which gets passed along to each waiting recursive invocation.

If the sequence of statements around || were changed, the function belongs_to would still return the right result but would always have to go over the complete list.

# let rec belongs_to (e : int) = function
[] -> false
| t::q -> belongs_to e q || (e = t) ;;
val belongs_to : int -> int list -> bool = <fun>
# #trace belongs_to ;;
belongs_to is now traced.
# belongs_to 3 [3;5;7] ;;
belongs_to <-- 3
belongs_to --> <fun>
belongs_to* <-- [3; 5; 7]
belongs_to <-- 3
belongs_to --> <fun>
belongs_to* <-- [5; 7]
belongs_to <-- 3
belongs_to --> <fun>
belongs_to* <-- [7]
belongs_to <-- 3
belongs_to --> <fun>
belongs_to* <-- []
belongs_to* --> false
belongs_to* --> false
belongs_to* --> false
belongs_to* --> true
- : bool = true
Even though 3 is the first element of the list, it is traversed completely. So, trace also provides a mechanism for the efficiency analysis of recursive functions.

Polymorphic Functions

The trace does not show the value corresponding to an argument of a parameterized type. If for example the function belongs_to can be written without an explicit type constraint:

# let rec belongs_to e l = match l with
[] -> false
| t::q -> (e = t) || belongs_to e q ;;
val belongs_to : 'a -> 'a list -> bool = <fun>
The type of the function belongs_to is now polymorphic, and the trace does no longer show the value of its arguments but replaces them with the indication (poly).

# #trace belongs_to ;;
belongs_to is now traced.
# belongs_to 3 [2;3;4] ;;
belongs_to <-- <poly>
belongs_to --> <fun>
belongs_to* <-- [<poly>; <poly>; <poly>]
belongs_to <-- <poly>
belongs_to --> <fun>
belongs_to* <-- [<poly>; <poly>]
belongs_to* --> true
belongs_to* --> true
- : bool = true


The Objective CAML toplevel loop can only show monomorphic types. Moreover, it only keeps the inferred types of global declarations. Therefore, after compilation of the expression belongs_to 3 [2;3;4], the toplevel loop in fact no longer possesses any further type information about the function belongs_to apart form the type 'a -> 'a list -> bool. The (monomorphic) types of 3 and [2;3;4] are lost, because the values do not keep any type information: this is static typing. This is the reason why the trace mechanism attributes the polymorphic types 'a and 'a list to the arguments of the function belongs_to and does not show their values.

It is this absence of typing information in values that entails the impossibility of constructing a generic print function of type 'a -> unit.

Local Functions

Local functions cannot be traced for the same reasons as above, relating again to static typing. Only global type declarations are kept in the environment of the toplevel loop. Still the following programming style is common:

# let belongs_to e l =
let rec bel_aux l = match l with
[] -> false
| t::q -> (e = t) || (bel_aux q)
in
bel_aux l;;
val belongs_to : 'a -> 'a list -> bool = <fun>
The global function only calls on the local function, which does the interesting part of the work.

Notes on Tracing

Tracing is actually the only multi-platform debugging tool. Its two weaknesses are the absence of tracing information for local functions and the inability to show the value of polymorphic parameters. This strongly restricts its usage, mainly during the first steps with the language.

Debug

ocamldebug, is a debugger in the usual sense of the word. It permits step-by-step execution, the insertion of breakpoints and the inspection and modification of values in the environment.

Single-stepping a program presupposes the knowledge of what comprises a program step. In imperative programming this is an easy enough notion: a step corresponds (more or less) to a single instruction of the language. But this definition does not make much sense in functional programming; one instead speaks of program events. These are applications, entries to functions, pattern matching, a conditional, a loop, an element of a sequence, etc.

Warning


This tool only runs under Unix.


Compiling with Debugging Mode

The -g compiler option produces a .cmo file that allows the generation of the necessary instructions for debugging. Only the bytecode compiler knows about this option. It is necessary to set this option during compilation of the files encompassing an application. Once the executable is produced, execution in debug mode can be accomplished with the following ocamldebug command:

ocamldebug [options] executable [arguments]

Take the following example file fact.ml which calculates the factorial function:

let fact n =
let rec fact_aux p q n =
if n = 0 then p
else fact_aux (p+q) p (n-1)
in
fact_aux 1 1 n;;


The main program in the file main.ml goes off on a long recursion after the call of Fact.fact on -1.

let x = ref 4;;
let go () =
x := -1;
Fact.fact !x;;
go();;


The two files are compiled with the -g option:
$ ocamlc -g -i -o fact.exe fact.ml main.ml
val fact : int -> int
val x : int ref
val go : unit -> int

Starting the Debugger

Once an executable is compiled with debug mode, it can be run in this mode.
$ ocamldebug fact.exe
        Objective Caml Debugger version 3.00

(ocd) 

Execution Control

Execution control is done via program events. It is possible to go forward and backwards by n program events, or to go forward or backwards to the next breakpoint (or the nth breakpoint). A breakpoint can be set on a function or a program event. The choice of language element is shown by line and column number or the number of characters. This locality may be relative to a module.

In the example below, a breakpoint is set at the fourth line of module Main:

(ocd) step 0
Loading program... done.
Time : 0
Beginning of program.
(ocd)  break @ Main 4
Breakpoint 1 at 5028 : file Main, line 4 column 3
The initialisations of the module are done before the actual program. This is the reason the breakpoint at line 4 occurs only after 5028 instructions.

We go forward or backwards in the execution either by program elements or by breakpoints. run and reverse run the program just to the next breakpoint. The first in the direction of program execution, the second in the backwards direction. The step command advanced by 1 or n program elements, entering into functions, next steps over them. backstep and previous respectively do the same in the backwards direction. finish finally completes the current functions invocations, whereas start returns to the program element before the function invocation.

To continue our example, we go forward to the breakpoint and then execute three program instructions:
(ocd) run
Time : 6 - pc : 4964 - module Main
Breakpoint : 1
4   <|b|>Fact.fact !x;;
(ocd) step
Time : 7 - pc : 4860 - module Fact
2   <|b|>let rec fact_aux p q n = 
(ocd) step
Time : 8 - pc : 4876 - module Fact
6 <|b|>fact_aux 1 1 n;;
(ocd) step
Time : 9 - pc : 4788 - module Fact
3     <|b|>if n = 0 then p

Inspection of Values

At a breakpoint, the values of variables in the activation record can be inspected. The print and display commands output the values associated with a variable according to the different depths.

We will print the value of n, then go back three steps to print the contents of x:
(ocd) print n
n : int = -1
(ocd) backstep 3    
Time : 6 - pc : 4964 - module Main
Breakpoint : 1
4   <|b|>Fact.fact !x;;
(ocd) print x
x : int ref = {contents=-1}
Access to the fields of a record or via the index of an array is accepted by the printing commands.
(ocd) print x.contents
1 : int = -1

Execution Stack

The execution stack permits a visualization of the entanglement of function invocations. The backtrace or bt command shows the stack of calls. The up and down commands select the next or preceding activation record. Finally, the frame command gives a description of the current record.


Previous Contents Next ocaml-book-1.0/en/html/book-ora211.html0000644000000000000000000010554507453055401014471 0ustar Language Extensions Previous Contents Next

Language Extensions

Objective CAML 3.04 brings three language extensions to Objective CAML: labels, optional arguments, and polymorphic constructors. These extensions preserve backward compatibility with the original language: a program written for version 2.04 keeps the same semantics in version 3.04.

Labels

A label is an annotation for the arguments of a function in its declaration and its application. It is presented as a separate identifier of the function parameter (formal or actual), enclosed between an initial symbol '~' and a final symbol ':'.

Labels can appear in the declarations of functions:

Syntax


let f ~label:p = exp
in the anonymous declarations with the keyword fun :

Syntax


fun ~label:p -> exp
and in the actual parameter of a function:

Syntax


( f ~label:exp )


Labels in types
The labels given to arguments of a functional expression appear in its type and annotate the types of the arguments to which they refer. (The '~' symbol in front of the label is omitted in types.)

# let add ~op1:x ~op2:y = x + y ;;
val add : op1:int -> op2:int -> int = <fun>

# let mk_triplet ~arg1:x ~arg2:y ~arg3:z = (x,y,z) ;;
val mk_triplet : arg1:'a -> arg2:'b -> arg3:'c -> 'a * 'b * 'c = <fun>


If one wishes to give the same identifier to the label and the variable, as in ~x:x, it is unnecessary to repeat the identifier; the shorter syntax ~x can be used instead.

Syntax


fun ~p -> exp

# let mk_triplet ~arg1 ~arg2 ~arg3 = (arg1,arg2,arg3) ;;
val mk_triplet : arg1:'a -> arg2:'b -> arg3:'c -> 'a * 'b * 'c = <fun>


It is not possible to define labels in a declaration of a function by pattern matching; consequently the keyword function cannot be used for a function with a label.


# let f = function ~arg:x -> x ;;
Toplevel input:
#
let f = function ~arg:x -> x ;;
^^^^^
Syntax error
# let f = fun ~arg:x -> x ;;
val f : arg:'a -> 'a = <fun>


Labels in function applications
When a function is defined with labeled parameters, applications of this function require that matching labels are provided on the function arguments.

# mk_triplet ~arg1:'1' ~arg2:2 ~arg3:3.0 ;;
- : char * int * float = '1', 2, 3
# mk_triplet '1' 2 3.0 ;;
- : char * int * float = '1', 2, 3


A consequence of this requirement is that the order of arguments having a label does not matter, since one can identify them by their label. Thus, labeled arguments to a function can be ``commuted'', that is, passed in an order different from the function definition.


# mk_triplet ~arg2:2 ~arg1:'1' ~arg3:3.0 ;;
- : char * int * float = '1', 2, 3


This feature is particularly useful for making a partial application on an argument that is not the first in the declaration.

# let triplet_0_0 = mk_triplet ~arg2:0 ~arg3:0 ;;
val triplet_0_0 : arg1:'a -> 'a * int * int = <fun>
# triplet_0_0 ~arg1:2 ;;
- : int * int * int = 2, 0, 0


Arguments that have no label, or that have the same label as another argument, do not commute. In such a case, the application uses the first argument that has the given label.


# let test ~arg1:_ ~arg2:_ _ ~arg2:_ _ = () ;;
val test : arg1:'a -> arg2:'b -> 'c -> arg2:'d -> 'e -> unit = <fun>

# test ~arg2:() ;; (* the first arg2: in the declaration *)
- : arg1:'a -> 'b -> arg2:'c -> 'd -> unit = <fun>

# test () ;; (* the first unlabeled argument in the declaration *)
- : arg1:'a -> arg2:'b -> arg2:'c -> 'd -> unit = <fun>


Legibility of code
Besides allowing re-ordering of function arguments, labels are also very useful to make the function interface more explicit. Consider for instance the String.sub standard library function.

# String.sub ;;
- : string -> int -> int -> string = <fun>
In the type of this function, nothing indicates that the first integer argument is a character position, while the second is the length of the string to be extracted. Objective CAML 3.04 provides a ``labelized'' version of this function, where the purpose of the different function arguments have been made explicit using labels.

# StringLabels.sub ;;
- : string -> pos:int -> len:int -> string = <fun>
Clearly, the function StringLabels.sub takes as arguments a string, the position of the first character, and the length of the string to be extracted.

Objective CAML 3.04 provides ``labelized'' versions of many standard library functions in the modules ArrayLabels, ListLabels, StringLabels, UnixLabels, and MoreLabels. Table B.1 gives the labeling conventions that were used.


label significance
pos: a position in a string or array
len: a length
buf: a string used as buffer
src: the source of an operation
dst: the destination of an operation
init: the initial value for an iterator
cmp: a comparison function
mode: an operation mode or a flag list

Figure B.1: Conventions for labels


Optional arguments

Objective CAML 3.04 allows the definition of functions with labeled optional arguments. Such arguments are defined with a default value (the value given to the parameter if the application does not give any other explicitly).

Syntax


fun ?name: ( p = exp1) -> exp2


As in the case of regular labels, the argument label can be omitted if it is identical to the argument identifier:

Syntax


fun ?( name = exp1) -> exp2


Optional arguments appear in the function type prefixed with the ? symbol.


# let sp_incr ?inc:(x=1) y = y := !y + x ;;
val sp_incr : ?inc:int -> int ref -> unit = <fun>
The function sp_incr behaves like the function incr from the Pervasives module.

# let v = ref 4 in sp_incr v ; v ;;
- : int ref = {contents = 5}
However, one can specify a different increment from the default.

# let v = ref 4 in sp_incr ~inc:3 v ; v ;;
- : int ref = {contents = 7}


A function is applied by giving the default value to all the optional parameters until the actual parameter is passed by the application. If the argument of the call is given without a label, it is considered as being the first non-optional argument of the function.

# let f ?(x1=0) ?(x2=0) x3 x4 = 1000*x1+100*x2+10*x3+x4 ;;
val f : ?x1:int -> ?x2:int -> int -> int -> int = <fun>
# f 3 ;;
- : int -> int = <fun>
# f 3 4 ;;
- : int = 34
# f ~x1:1 3 4 ;;
- : int = 1034
# f ~x2:2 3 4 ;;
- : int = 234


An optional argument can be given without a default value, in this case it is considered in the body of the function as being of the type 'a option; None is its default value.

Syntax


fun ?name:p -> exp



# let print_integer ?file:opt_f n =
match opt_f with
None -> print_int n
| Some f -> let fic = open_out f in
output_string fic (string_of_int n) ;
output_string fic "\n" ;
close_out fic ;;
val print_integer : ?file:string -> int -> unit = <fun>
By default, the function print_integer displays its argument on standard output. If it receives a file name with the label file, it outputs its integer argument to that file instead.

Note


If the last parameter of a function is optional, it will have to be applied explicitly.

# let test ?x ?y n ?a ?b = n ;;
val test : ?x:'a -> ?y:'b -> 'c -> ?a:'d -> ?b:'e -> 'c = <fun>
# test 1 ;;
- : ?a:'_a -> ?b:'_b -> int = <fun>
# test 1 ~b:'x' ;;
- : ?a:'_a -> int = <fun>
# test 1 ~a:() ~b:'x' ;;
- : int = 1


Labels and objects

Labels can be used for the parameters of a method or an object's constructor.


# class point ?(x=0) ?(y=0) (col : Graphics.color) =
object
val pos = (x,y)
val color = col
method print ?dest:(file=stdout) () =
output_string file "point (" ;
output_string file (string_of_int (fst pos)) ;
output_string file "," ;
output_string file (string_of_int (snd pos)) ;
output_string file ")\n"
end ;;
class point :
?x:int ->
?y:int ->
Graphics.color ->
object
method print : ?dest:out_channel -> unit -> unit
val color : Graphics.color
val pos : int * int
end

# let obj1 = new point ~x:1 ~y:2 Graphics.white
in obj1#print () ;;
point (1,2)
- : unit = ()
# let obj2 = new point Graphics.black
in obj2#print () ;;
point (0,0)
- : unit = ()


Labels and optional arguments provide an alternative to method and constructor overloading often found in object-oriented languages, but missing from Objective CAML.

This emulation of overloading has some limitations. In particular, it is necessary that at least one of the arguments is not optional. A dummy argument of type unit can always be used.


# class number ?integer ?real () =
object
val mutable value = 0.0
method print = print_float value
initializer
match (integer,real) with
(None,None) | (Some _,Some _) -> failwith "incorrect number"
| (None,Some f) -> value <- f
| (Some n,None) -> value <- float_of_int n
end ;;
class number :
?integer:int ->
?real:float ->
unit -> object method print : unit val mutable value : float end

# let n1 = new number ~integer:1 () ;;
val n1 : number = <obj>
# let n2 = new number ~real:1.0 () ;;
val n2 : number = <obj>


Polymorphic variants

The variant types of Objective CAML have two principal limitations. First, it is not possible to extend a variant type with a new constructor. Also, a constructor can belong to only one type. Objective CAML 3.04 features an alternate kind of variant types, called polymorphic variants that do not have these two constraints.

Constructors for polymorphic variants are prefixed with a ` (backquote) character, to distinguish them from regular constructors. Apart from this, the syntactic constraints on polymorphic constructors are the same as for other constructors. In particular, the identifier used to build the constructor must begin with a capital letter.

Syntax


`Name
ou

Syntax


`Name type


A group of polymorphic variant constructors forms a type, but this type does not need to be declared before using the constructors.


# let x = `Integer 3 ;;
val x : [> `Integer of int] = `Integer 3


The type of x with the symbol [> indicates that the type contains at least the constructor `Integer int.


# let int_of = function
`Integer n -> n
| `Real r -> int_of_float r ;;
val int_of : [< `Integer of int | `Real of float] -> int = <fun>


Conversely, the symbol [< indicates that the argument of int_of belongs to the type that contains at most the constructors `Integer int and `Real float.

It is also possible to define a polymorphic variant type by enumerating its constructors:

Syntax


type t = [ `Name1 | `Name2 | ... | `Namen ]
or for parameterized types:

Syntax


type ('a,'b,...) t = [ `Name1 | `Name2 | ... | `Namen ]



# type value = [ `Integer of int | `Real of float ] ;;
type value = [ `Integer of int | `Real of float]


Constructors of polymorphic variants can take arguments of different types.

# let v1 = `Number 2
and v2 = `Number 2.0 ;;
val v1 : [> `Number of int] = `Number 2
val v2 : [> `Number of float] = `Number 2
However, v1 and v2 have different types.

# v1=v2 ;;
Toplevel input:
#
v1=v2 ;;
^^
This expression has type [> `Number of float] but is here used with type
[> `Number of int]


More generally, the constraints on the type of arguments for polymorphic variant constructors are accumulated in their type by the annotation &.


# let test_nul_integer = function `Number n -> n=0
and test_nul_real = function `Number r -> r=0.0 ;;
val test_nul_integer : [< `Number of int] -> bool = <fun>
val test_nul_real : [< `Number of float] -> bool = <fun>
# let test_nul x = (test_nul_integer x) || (test_nul_real x) ;;
val test_nul : [< `Number of float & int] -> bool = <fun>
The type of test_nul indicates that the only values accepted by this function are those with the constructor `Number and an argument which is at the same time of type int and of float. That is, the only acceptable values are of type 'a!

# let f () = test_nul (failwith "returns a value of type 'a") ;;
val f : unit -> bool = <fun>


The types of the polymorphic variant constructor are themselves likely to be polymorphic.

# let id = function `Ctor -> `Ctor ;;
val id : [< `Ctor] -> [> `Ctor] = <fun>
The type of the value returned from id is ``the group of constructors that contains at least `Ctor'' therefore it is a polymorphic type which can instantiate to a more precise type. In the same way, the argument of id is ``the group of constructors that contains no more than `Ctor'' which is also likely to be specified. Consequently, they follow the general polymorphic type mechanism of Objective CAML knowing that they are likely to be weakened.

# let v = id `Ctor ;;
val v : _[> `Ctor] = `Ctor
v, the result of the application is not polymorphic (as denoted by the character _ in the name of the type variable).

# id v ;;
- : _[> `Ctor] = `Ctor
v is monomorphic and its type is a sub-type of ``contains at least the constructor `Ctor''. Applying it with id will force its type to be a sub-type of``contains no more than the constructor `Ctor''. Logically, it must now have the type ``contains exactly `Ctor''. Let us check.

# v ;;
- : [ `Ctor] = `Ctor


As with object types, the types of polymorphic variant constructors can be open.

# let is_integer = function
`Integer (n : int) -> true
| _ -> false ;;
val is_integer : [> `Integer of int] -> bool = <fun>
# is_integer (`Integer 3) ;;
- : bool = true
# is_integer `Other ;;
- : bool = false
All the constructors are accepted, but the constructor `Integer must have an integer argument.

# is_integer (`Integer 3.0) ;;
Toplevel input:
#
is_integer (`Integer 3.0) ;;
^^^^^^^^^^^^
This expression has type [> `Integer of float] but is here used with type
[> `Integer of int]


As with object types, the type of a constructor can be cyclic.

# let rec long = function `Rec x -> 1 + (long x) ;;
val long : ([< `Rec of 'a] as 'a) -> int = <fun>


Finally, let us note that the type can be at the same time a sub-group and one of a group of constructors. Starting with a a simple example:

# let ex1 = function `C1 -> `C2 ;;
val ex1 : [< `C1] -> [> `C2] = <fun>
Now we identify the input and output types of the example by a second pattern.

# let ex2 = function `C1 -> `C2 | x -> x ;;
val ex2 : ([> `C2 | `C1] as 'a) -> 'a = <fun>
We thus obtain the open type which contains at least `C2 since the return type contains at least `C2.

# ex2 ( `C1 : [> `C1 ] ) ;; (* is a subtype of [<`C2|`C1| .. >`C2] *)
- : _[> `C2 | `C1] = `C2
# ex2 ( `C1 : [ `C1 ] ) ;; (* is not a subtype of [<`C2|`C1| .. >`C2] *)
Toplevel input:
# ex2 ( `C1 : [ `C1 ] ) ;; (* is not a subtype of [<`C2|`C1| .. >`C2] *)
^^^
This expression has type [ `C1] but is here used with type [> `C2 | `C1]



Previous Contents Next ocaml-book-1.0/en/html/book-ora166.html0000644000000000000000000004043007453055400014470 0ustar File Descriptors Previous Contents Next

File Descriptors

In chapter 3 we have seen functions from the standard module Pervasives. These functions allow us to access files via input / output channels. There is also a lower-level way to access files, using their descriptors.

A file descriptor is an abstract value of type Unix.file_descr, containing information necessary to use a file: a pointer to the file, the access rights, the access modes (read or write), the current position in the file, etc.



Three descriptors are predefined. They correspond to standard input, standard output, and standard error.


# ( Unix.stdin , Unix.stdout , Unix.stderr ) ;;
- : Unix.file_descr * Unix.file_descr * Unix.file_descr =
<abstr>, <abstr>, <abstr>


Be careful not to confuse them with the corresponding input / output channels:


# ( Pervasives.stdin , Pervasives.stdout , Pervasives.stderr ) ;;
- : in_channel * out_channel * out_channel = <abstr>, <abstr>, <abstr>


The conversion functions between channels and file descriptors are described at page ??.

File Access Rights.
Under Unix each file has an associated owner and group. The rights to read, write and execute are attached to each file according to three categories of users: the owner of a file, the members of the file's group1 and all other users.

The access rights of a file are represented by 9 bits divided into three groups of three bits each. The first group represents the rights of the owner, the second the rights of the members of the owner's group, and the last the rights of all other users. In each group of three bits, the first bit represents the right to read, the second bit the right to write and the third bit the right to execute. It is common to abbreviate these three rights by the letters r, w and x. The absence of the rights is represented in each case by a dash (-). For exampple, the right to read for all and the right to write only for the owner is written as rw-r--r--. This corresponds to the integer 420 (which is the binary number 0b110100100). Frequently the more comfortable octal notation 0o644 is used. These file access rights are not used under Windows.

REVIEWER'S QUESTION: IS THIS STILL TRUE UNDER WIN2K?

File Manipulation

Opening a file.
Opening a file associates the file to a file descriptor. Depending on the intended use of the file there are several modes to open a file. Each mode corresponds to a value of type open_flag described by figure 18.2.

O_RDONLY read only
O_WRONLY write only
O_RDWR reading and writing
O_NONBLOCK non-blocking opening
O_APPEND appending at the end of the file
O_CREAT create a new file if it does not exist
O_TRUNC set the file to 0 if it exists
O_EXCL chancel, if the file already exists

Figure 18.2: Values of type open_flag.


These modes can be combined. In consequence, the function openfile takes as argument a list of values of type open_flag.

# Unix.openfile ;;
- : string -> Unix.open_flag list -> Unix.file_perm -> Unix.file_descr =
<fun>
The first argument is the name of the file. The last is an integer2 coding the rights to attach to the file in the case of creation.

Here is an example of how to open a file for reading, or to create it with the rights rw-r--r-- if it does not exist:

# let file = Unix.openfile "test.dat" [Unix.O_RDWR; Unix.O_CREAT] 0o644 ;;
val file : Unix.file_descr = <abstr>


Closing a file.
The function Unix.close closes a file. It is applied to the descriptor of the file to close.

# Unix.close ;;
- : Unix.file_descr -> unit = <fun>
# Unix.close file ;;
- : unit = ()


Redirecting file descriptors.
It is possible to attach several file descriptors to one input / output. If there is only one file descriptor available and another one is desired we can use:

# Unix.dup ;;
- : Unix.file_descr -> Unix.file_descr = <fun>


If we have two file descriptors and we want to assign to the second the input / output of the first, we can use the function:

# Unix.dup2 ;;
- : Unix.file_descr -> Unix.file_descr -> unit = <fun>


For example, the error output can be directed to a file in the following way:

# let error_output = Unix.openfile "err.log" [Unix.O_WRONLY;Unix.O_CREAT] 0o644 ;;
val error_output : Unix.file_descr = <abstr>
# Unix.dup2 Unix.stderr error_output ;;
- : unit = ()
Data written to the standard error output will now be directed to the file err.log.

Input / Output on Files

The functions to read and to write to a file Unix.read and Unix.write use a character string as medium between the file and the Objective CAML program.

# Unix.read ;;
- : Unix.file_descr -> string -> int -> int -> int = <fun>
# Unix.write ;;
- : Unix.file_descr -> string -> int -> int -> int = <fun>


In addition to the file descriptor and the string the functions take two integers as arguments. One is the index of the first character and the other the number of characters to read or to write. The returned integer is the number of characters effectively read or written.

# let mode = [Unix.O_WRONLY;Unix.O_CREAT;Unix.O_TRUNC] in
let fl = Unix.openfile "file" mode 0o644 in
let str = "012345678901234565789" in
let n = Unix.write fl str 4 5
in Printf.printf "We wrote %s to the file\n" (String.sub str 4 n) ;
Unix.close fl ;;
We wrote 45678 to the file
- : unit = ()


Reading a file works the same way:

# let fl = Unix.openfile "file" [Unix.O_RDONLY] 0o644 in
let str = String.make 20 '.' in
let n = Unix.read fl str 2 10 in
Printf.printf "We read %d characters" n;
Printf.printf " and got the string %s\n" str;
Unix.close fl ;;
We read 5 characters and got the string ..45678.............
- : unit = ()


Access to a file always takes place at the current position of its descriptor. The current position can be modified by the function:

# Unix.lseek ;;
- : Unix.file_descr -> int -> Unix.seek_command -> int = <fun>


The first argument is the file descriptor. The second specifies the displacement as number of characters. The third argument is of type Unix.seek_command and indicates the origin of the displacement. The third argument may take one of three posssible values:
  • SEEK_SET: relative to the beginning of the file,
  • SEEK_CUR: relative to the current position,
  • SEEK_END: relative to the end of the file.
A function call with an erronous position will either raise an exception or return a value equal to 0.

Input / output channels.
The Unix module provides conversion functions between file descriptors and the input / output channels of module Pervasives:

# Unix.in_channel_of_descr ;;
- : Unix.file_descr -> in_channel = <fun>
# Unix.out_channel_of_descr ;;
- : Unix.file_descr -> out_channel = <fun>
# Unix.descr_of_in_channel ;;
- : in_channel -> Unix.file_descr = <fun>
# Unix.descr_of_out_channel ;;
- : out_channel -> Unix.file_descr = <fun>


It is necessary to indicate whether the input / output channels obtained by the conversion transfer binary data or character data.

# set_binary_mode_in ;;
- : in_channel -> bool -> unit = <fun>
# set_binary_mode_out ;;
- : out_channel -> bool -> unit = <fun>


In the following example we create a file by using the functions of module Unix. We read using the opening function of module Unix and the higher-level input function input_line.

# let mode = [Unix.O_WRONLY;Unix.O_CREAT;Unix.O_TRUNC] in
let f = Unix.openfile "file" mode 0o666 in
let s = "0123456789\n0123456789\n" in
let n = Unix.write f s 0 (String.length s)
in Unix.close f ;;
- : unit = ()
# let f = Unix.openfile "file" [Unix.O_RDONLY;Unix.O_NONBLOCK] 0 in
let c = Unix.in_channel_of_descr f in
let s = input_line c
in print_string s ;
close_in c ;;
0123456789- : unit = ()


Availability.
A program may have to work with multiple inputs and outputs. Data may not always be available on a given channel, and the program cannot afford to wait for one channel to be available while ignoring the others. The following function lets you determine which of a given list of inputs/outputs is available for use at a given time:

# Unix.select ;;
- : Unix.file_descr list ->
Unix.file_descr list ->
Unix.file_descr list ->
float ->
Unix.file_descr list * Unix.file_descr list * Unix.file_descr list
= <fun>
The first three arguments represent lists of respectively inputs, of outputs and error-outputs. The last argument indicates a delay in seconds. A negative value means the null delay. The results are the lists of available input, output and error-output.

Warning


select is not implemented under Windows



Previous Contents Next ocaml-book-1.0/en/html/book-ora067.html0000644000000000000000000000566007453055400014476 0ustar Standalone Executables Previous Contents Next

Standalone Executables

A standalone executable is a program that does not depend an Objective CAML installation to run. This facilitates the distribution of binary applications and robustness against runtime library changes across Objective CAML versions.

The Objective CAML native compiler produces standalone executables by default. But without the -custom option, the bytecode compiler produces an executable which requires the bytecode interpreter ocamlrun. Imagine the file example.ml is as follows:
let f x = x + 1;;
print_int (f 18);;
print_newline();;
Then the following command produces the (approximately 8k) file example.exe:
ocamlc -o example.exe example.ml
This file can be executed by the Objective CAML bytecode interpreter:
$ ocamlrun example.exe
19
The interpreter executes the Zinc machine instructions contained in the file example.exe.

Under Unix, the first line of the file example.exe contains the location of the interpreter, for example:
#!/usr/local/bin/ocamlrun
This means the file can be executed directly (without using ocamlrun. Like a shell-script, executing the file in turn runs the program specified on the first line, which is then used to interpret the remainder of the file. If ocamlrun can't be found, execution will fail and the error message Command not found will be displayed.

The same compilation with the option -custom produces a standalone executable with name exauto.exe:
ocamlc -custom -o exauto.exe example.ml
This time the file is about 85K, as it contains the Zinc interpreter as well as the program bytecode. This file can be executed directly or copied to another machine (using the same CPU/Operating System) for execution.


Previous Contents Next ocaml-book-1.0/en/html/book-ora087.html0000644000000000000000000001156007453055400014474 0ustar Memory Management by Objective CAML Previous Contents Next

Memory Management by Objective CAML

Objective CAML's garbage collector combines the various techniques described above. It works on two generations, the old and the new. It mainly uses a Stop&Copy on the new generation (a minor garbage collection) and an incremental Mark&Sweep on the old generation (major garbage collection).

A young object that survives a minor garbage collection is relocated to the old generation. The Stop&Copy uses the old generation as the to-space. When it is finished, the entire from-space is completely freed.

When we presented generational garbage collectors, we noted the difficulty presented by impure functional languages: an old-generation value may reference an object of the new generation. Here is a small example.

# let older = ref [1] ;;
val older : int list ref = {contents=[1]}
(* ... *)
# let newer = [2;5;8] in
older := newer ;;
- : unit = ()
The comment (* ... *) replaces a long sequence of code in which older passes into the older generation. The minor garbage collection must take account of certain old generation values. Therefore we must keep an up-to-date table of the references from the old generation to the new that becomes part of the set of roots for the minor garbage collection. This table of roots grows very little and becomes empty just after a minor garbage collection.

It is to be noted that the Mark&Sweep of the old generation is incremental, which means that a part of the major garbage collection happens during each minor garbage collection. The major garbage collection is a Mark&Sweep that follows the algorithm presented on page ??. The relevance of this incremental approach is the reduction of waiting time for a major garbage collection by advancing the marking phase with each minor garbage collection. When a major garbage collection is activated, the marking of the unprocessed regions is finished, and the reclamation phase is begun. Finally, as Mark&Sweep may fragment the old generation significantly, a compaction algorithm may be activated after a major garbage collection.

Putting this altogether, we arrive at the following stages:
  1. minor garbage collection: perform a Stop&Copy on the young generation; age the surviving objects by having them change zone; and then do part of the Mark&Sweep of the old generation.

    It fails if the zone change fails, in which case we go to step 2.
  2. end of the major garbage collection cycle.

    When this fails go on to step 3.
  3. another major garbage collection, to see if the objects counted as used during the incremental phases have become free.

    When this fails, go on to step 4.
  4. Compaction of the old generation in order to obtain maximal contiguous free space. If this last step does not succeed, there are no other possibilities, and the program itself fails.
The GC module allows activation of the various phases of the garbage collector.

A final detail of the memory management of Objective CAML is that the heap space is not allocated once and for all at the beginning of the program, but evolves with time (increasing or decreasing by a given size).


Previous Contents Next ocaml-book-1.0/en/html/book-ora008.html0000644000000000000000000000314507453055377014502 0ustar Downloading Previous Contents Next

Downloading

Objective CAML can be downloaded via web browser at the following address:

Link


http://caml.inria.fr/ocaml/distrib.html
There one can find binary distributions for Linux (Intel and PPC), for Windows (NT, 95, 98) and for MacOS (7, 8), as well as documentation, in English, in different formats (PDF, PostScript and HTML). The source code for the three systems is available for download as well. Once the desired distribution is copied to one's machine, it's time to install it. This procedure varies according to the operating system used.


Previous Contents Next ocaml-book-1.0/en/html/book-ora042.gif0000644000000000000000000001044107452056111014260 0ustar GIF89a UUU999rrr!, X0I8ͻ`(dihAp,ʠ8+s7@ "al:2`Z0p]3_xܥ:ZVi~͗T':eYS,+r} ?o86e7 A.wETJ*pq9qBV^p]:.?nSfwYEnnV RԺbL:5רn炈t^ a5x؛]탴 lZ\4> h$+"Qs1 ؒeY9͋ XNI`1_LcK*S4* GFSJGꊧi+#>d#iFO~r $ 7 A[ByUzUrg`;%z4`7>-kFs~&6۸gͻ Nȓ+_μУKNسkνËOӫ_ϾFDϿ( K h& 7ABa%L`jaRP:AR&PAkR.6!3E@+0O'(#l`!Ba0XFt6'B[OZF$:I!$:إOY0Ř Sil4g`J4$Voygd0Ţ%"<5Q) 鋵q4l^ւ( R<{Ȫ" "BA֒^AȡjPmƊ2 'ض1Zv tK@$HEbQ0Qk&у٨зfW.k'PN%DP-_̣Բ_Agh0h;cVZp␊eI4>vc3BgЖJ%]Zq^/ŠQ'[:6?OYO,e#ЅGU`fm A\R@96P%#T7iJQ}:%}eA`cs>i}\޴iƩUyQsHBUR@`j-wdsHdVDN kgWOSD(F1E8M+.ڇ/URꪹ& BX.ဌr.9XF˰&*JX⭔޼&f˚W^vtmQPLme6UKVWEINQUtSYTOݶM>6rEoS|LɎ(nZjh(@T|g}YnTzAA7{ӝJ;K`pJ+ c/׿nmJPkshRT=l&JmQb:v}n6I7}qv+mbˊ{UmxqJ|ĴW/-w }r|%7er1YXsߜ9?MsAOGXt#MWA8񉟧Ng5Nud: zNXp/^ߝF5 +|!/iyT~%N1/e nm($A؃? P NȄIxZYȅ%HnO؄^e؅PxQh:dl8kXfhjx`o2dGih8[x`؇HyH~t"Hȉ"H_qTZ艓Xka GȊ؉INXĈ8(vƌ؊8XȇAhȳ؍ȍDUpֈҘ؎@88XX}NԸ[R8_ t/‹Ǒ쨐 iX sב y YY^Y)ɑ#ُ-$)0Xu95y(&< ypwaB9DYFٍ+|0;ɔ:G9TYVI=\ )aIw(Z[irƖє]q [yxGr|g(#c|gٗiA(ph9e)OsI闓)K8){ ICnY9xZ9ș)ũIzٜY"EuvY'pYyؚЙِ/@99]ٞߙu)W)!I[)jJ쩠Bz Z}'jʠ /j%ڡ0t,ZG& 2Ѣ6zy4 l98ʣC:f{٘E1ZRyV:"mXʤMڣ.nE9cFil zXohyPtjzy~w1t (2 m:u0Yex́W!*)BaAi(|mb騩 Zڋg*9zUdCU/y呁&h:ؖ"ր-( UA?Ȫ誮ڮo'IjIʯ*+KyZ)Z馰9 ˚ VڌSX p7&{(*,۲.02;4[6{&7g"@ICk@[BRhoEQ?PF-IV>0 I6f;a HF+pKh(Uf`6p1'ٶ!O 2?y&vC5:+$Xg5 C&RHQ`Esa=0&FXvT|ITp˸@tL![|Tikq3bGO79%ea4 u=O_\2ke['l 3{? C]'ls!cI.e㼕FŹr}b#>V%q[{acy\]k>a%{RŰcU,Fröx` Ab6LynfK rm=E f:^eg4#;SbjQ9Ld ­UreV8{9̿běU$OXûD  L#'33Z,EK&R [>@RSRnk]ta/d a<}k{mjJ\NJȋ;ocaml-book-1.0/en/html/book-ora006.html0000644000000000000000000000245607453055377014504 0ustar Free software Previous Contents Next

Free software

The various programs used in this work are ``free'' software 1. They can be found either on the CD-ROM accompanying this work, or by downloading them from the Internet. This is the case for Objective CAML, developed at Inria.


Previous Contents Next ocaml-book-1.0/en/html/book-ora139.html0000644000000000000000000000407607453055400014476 0ustar Chapter Plan Previous Contents Next

Chapter Plan

This chapter describes Objective CAML's object extension. This extension does not change any of the features of the language that we already studied in the previous chapters. A few new reserved keywords are added for the object-oriented syntax.

The first section describes class declaration syntax, object instantiation, and message passing. The second section explains the various relations that may exist between classes. The third section clarifies the notion of object type and demonstrates the richness of the object extension, thanks to abstract classes, multiple inheritance, and generic parameterized classes. The fourth section explains the subtyping relation and shows its power through inclusion polymorphism. The fifth section deals with a functional style of object-oriented programming, where the internal state of the object is not modified, but a modified copy of the receiving object is returned. The sixth section clarifies other parts of the object-oriented extension, such as interfaces and local declarations in classes, which allow class variables to be created.


Previous Contents Next ocaml-book-1.0/en/html/book-ora070.html0000644000000000000000000000255507453055400014470 0ustar Summary Previous Contents Next

Summary

This chapter has shown the different ways to compile an Objective CAML program. The bytecode compiler is favorable for portable code, allowing for the system independent distribution of programs and libraries. This property is lost in the case of standalone bytecode executables. The native compiler trades producing efficient architecture dependent code for a loss of portability.


Previous Contents Next ocaml-book-1.0/en/html/book-ora056.html0000644000000000000000000000652407453055377014511 0ustar Introduction Previous Contents Next

Introduction

The reason to prefer one programming language over another lies in the ease of developing and maintaining robust applications. Therefore, we conclude the first part of this book, which dealt with a general presentation of the Objective CAML language, by demonstrating its use in a number of applications.

The first application implements a few functions which are used to write database queries. We emphasize the use of list manipulations and the functional programming style. The user has access to a set of functions with which it is easy to write and run queries using the Objective CAML language directly. This application shows the programmer how he can easily provide the user with most of the query tools that the user should need.

The second application is an interpreter for a tiny BASIC1. This kind of imperative language fueled the success of the first microcomputers. Twenty years later, they seem to be very easy to design. Although BASIC is an imperative language, the implementation of the interpreter uses the functional features of Objective CAML, especially for the evaluation of commands. Nevertheless, the lexer and parser for the language use a mutable structure.

The third application is a one-player game, Minesweeper, which is fairly well-known since it is bundled with the standard installation of Windows systems. The goal of the game is to uncover a bunch of hidden mines by repeatedly uncovering a square, which then indicates the number of mines around itself. The implementation uses the imperative features of the language, since the data structure used is a two-dimensional array which is modified after each turn of the game. This application uses the Graphics module to draw the game board and to interact with the player. However, the automatic uncovering of some squares will be written in a more functional style.
This latter application uses functions from the Graphics module described in chapter 5 (see page ??) as well as some functions from the Random and Sys modules (see chapter 8, pages ?? and ??).


Previous Contents Next ocaml-book-1.0/en/html/book-ora169.html0000644000000000000000000011462007453055400014476 0ustar Exercises Previous Contents Next

Exercises

The three proposed exercises manipulate file descriptors, processes, respectively pipes and signals. The first two exercises stem from Unix system programming. The Objective CAML code can be compared with the C code in the Unix or Linux distributions.

Counting Words: the wc Command

We want to (re)program the Unix wc command, which counts the number of lines, words or characters contained in a text file. Words are separated by a space character, a tab, or a carriage return. We do not count the separators.
  1. Write a first version (wc1) of the command, which only handles a single file. The name of the file is passed as an argument on the command line. On dfinit une liste de sparateurs ainsi que trois variables globales qui serviront aux diffrents comptages.

    # let seps = " \t" ;;
    val seps : string = " \t"
    # let nl = ref 0 ;;
    val nl : int ref = {contents=0}
    # let nw = ref 0 ;;
    val nw : int ref = {contents=0}
    # let nc = ref 0 ;;
    val nc : int ref = {contents=0}
    La fonction counts est charge du comptage d'une ligne.

    # let counts s =
    let was_sep = ref true in
    let n = String.length s in
    for i=0 to n-1 do
    let is_sep = String.contains seps s.[i] in
    if is_sep && (not !was_sep) then incr nw ;
    was_sep := is_sep
    done ;
    if not !was_sep then incr nw ;
    nc := !nc+n+1;
    incr nl ;;
    val counts : string -> unit = <fun>
    La fonction count itre la fonction de comptage d'une ligne sur l'ensemble des lignes d'un fichier.

    # let count f =
    nl := 0; nw := 0; nc := 0;
    let f_in = open_in f in
    try
    while true do
    counts (input_line f_in)
    done
    with
    End_of_file -> close_in f_in ;;
    val count : string -> unit = <fun>
    La fonction principale appelle la fonction de comptage sur le nom de fichier pass en argument et affiche les rsultats.

    # let print_count() =
    Printf.printf"\t%d" !nl ;
    Printf.printf"\t%d" !nw ;
    Printf.printf"\t%d\n" !nc ;;
    val print_count : unit -> unit = <fun>

    # let main() =
    try
    if Array.length Sys.argv < 2
    then print_string "wc1: missing file name\n"
    else ( count Sys.argv.(1) ; print_count() )
    with e -> Printf.printf "wc1: %s\n" (Printexc.to_string e) ;;
    val main : unit -> unit = <fun>
  2. Write a more elaborated version (wc2), which can handle the three options -l, -c, -w as well as several file names. The options indicate if we want to count the number of lines, characters or words. The output of each result shall be preceded by the name of the file. On reprend les variables globales et les fonctions counts et count de la question prcdente.

    On se donne trois variable globales donnant le statut des trois options possibles.

    # let l_opt = ref false
    and w_opt = ref false
    and c_opt = ref false ;;
    val l_opt : bool ref = {contents=false}
    val w_opt : bool ref = {contents=false}
    val c_opt : bool ref = {contents=false}
    On redfinit l'affichage en fonction du statut des options.

    # let print_count f =
    Printf.printf "%s:" f ;
    if !l_opt then Printf.printf"\t%d" !nl ;
    if !w_opt then Printf.printf"\t%d" !nw ;
    if !c_opt then Printf.printf"\t%d" !nc;
    print_newline() ;;
    val print_count : string -> unit = <fun>
    La ligne de commande est analyse pour mettre jour le statut des options ainsi que la liste des fichiers traiter.

    # let f_list = ref ([]:string list) ;;
    val f_list : string list ref = {contents=[]}

    # let read_args () =
    let usage_msg = "wc2 [-l] [-w] [-w] files..." in
    let add_f f = f_list := f::!f_list in
    let spec_list =
    [ ("-l", Arg.Set l_opt, "affichage nombre de lignes") ;
    ("-w", Arg.Set w_opt, "affichage nombre de mots") ;
    ("-c", Arg.Set c_opt, "affichage nombre de caractres") ] in
    Arg.parse spec_list add_f usage_msg ;;
    val read_args : unit -> unit = <fun>
    La fonction principale itre le comptage sur la liste des fichiers.

    # let main() =
    try
    read_args() ;
    List.iter (fun f -> count f; print_count f) !f_list
    with
    e -> Printf.printf "wc2: %s\n" (Printexc.to_string e) ;;
    val main : unit -> unit = <fun>

Pipes for Spell Checking

This exercise uses pipes to concatenate a suite of actions. Each action takes the result of the preceding action as argument. The communication is realized by pipes, connecting the output of one process to the input of the following, in the style of the Unix command line symbol | .

  1. Write a function pipe_two_progs of type string * string list -> string * string list -> unit such that pipe_two_progs (p1,[a1; ...; an]) (p2,[b1; ...; bp]) starts the programs p1 a1 ... an and p2 b1 ... bp, redirecting the standard output of p1 to the standard input of p2. ai and bi are the command line arguments of each program. Voici une faon trs `` unixienne '' d'implanter cette fonction.

    # let pipe_two_progs (p1, args1) (p2, args2) =
    let in2, out1 = Unix.pipe() in
    match Unix.fork() with
    0 ->
    Unix.close in2 ;
    Unix.close Unix.stdout ;
    ignore(Unix.dup out1) ;
    Unix.close out1 ;
    Unix.execvp p1 (Array.of_list args1)
    | _ ->
    Unix.close out1 ;
    Unix.close Unix.stdin ;
    ignore(Unix.dup in2) ;
    Unix.close in2 ;
    Unix.execvp p2 (Array.of_list args2) ;;
    val pipe_two_progs : string * string list -> string * string list -> unit =
    <fun>


  2. We revisit the spell checker function from the exercise on page ?? to write a first program. Modify it so that the list of faulty words is sent without treatment in the form of one line per word to the standard output.

    # let orthographe dico nom =
    let f = open_in nom in
    try
    while true do
    let s = input_line f in
    let ls = mots s in
    List.iter (Printf.printf"%s\n") (verifie dico ls)
    done ;
    failwith "cas impossible"
    with
    End_of_file -> close_in f
    | x -> close_in f ; raise x ;;
    val orthographe : arbre_lex -> string -> unit = <fun>


  3. The second program takes a sequence of character strings from its standard input and sorts it in lexicographical order. The function Sort.list can be used, which sorts a list in an order defined by a given predicate. The sorted list is written to the standard output.

    # let trie () =
    let l = ref [] in
    try
    while true do l := Sort.list (<) ((input_line stdin)::!l) done
    with
    End_of_file -> List.iter (Printf.printf"%s\n") !l ;;
    val trie : unit -> unit = <fun>


  4. Test the function pipe_two_progs with the two programs.

  5. Write a function pipe_n_progs to connect a list of programs.

    # pipe_two_progs ("orthographe",["";Sys.argv.(1)]) ("tri",[]) ;;
    Pour allger un peu l'criture on se donne deux fonctions de redirection de l'entre et de la sortie standard.

    # let dup_stdin in_descr =
    if in_descr<>Unix.stdin then Unix.dup2 in_descr Unix.stdin ;
    Unix.close in_descr ;;
    val dup_stdin : Unix.file_descr -> unit = <fun>

    # let dup_stdout out_descr =
    if out_descr<>Unix.stdout then Unix.dup2 out_descr Unix.stdout ;
    Unix.close out_descr ;;
    val dup_stdout : Unix.file_descr -> unit = <fun>
    Pour itrer le pipeline, on dfinit une fonction rcursive dont le premier argument donne le canal d'entre du premier processus chaner.

    # let rec pipe_n_progs_loop in_descr = function
    [p,args] ->
    dup_stdin in_descr ;
    Unix.execvp p (Array.of_list args)
    | (p,args)::ps ->
    let in2, out1 = Unix.pipe() in
    ( match Unix.fork() with
    0 ->
    Unix.close in2 ;
    dup_stdin in_descr ;
    dup_stdout out1 ;
    Unix.execvp p (Array.of_list args)
    | _ ->
    Unix.close out1 ;
    pipe_n_progs_loop in2 ps )
    | _ -> () ;;
    val pipe_n_progs_loop :
    Unix.file_descr -> (string * string list) list -> unit = <fun>

    # let pipe_n_progs ps = pipe_n_progs_loop Unix.stdin ps ;;
    val pipe_n_progs : (string * string list) list -> unit = <fun>


  6. Write a program to suppress multiple occurrences of elements in a list.

    # let rmdup () =
    let l = ref [] in
    try
    while true do
    let x = input_line stdin in
    if not (List.mem x !l) then l := x::!l
    done
    with End_of_file
    -> List.iter (Printf.printf"%s\n") !l ;;
    val rmdup : unit -> unit = <fun>


  7. Test the function pipe_n_progs with these three programs.

    # pipe_n_progs [ ("orthographe",["";Sys.argv.(1)]); ("tri",[]); ("rmdup",[]) ] ;;

Interactive Trace

In a complex calculation it may be useful to interact with the program to verify the progression. For this purpose we revisit the exercise on page ?? on the computation of prime numbers contained in an interval.
  1. Modify the program so that a global variable result always contains the last prime number found.

    # let result = ref 0 ;;
    val result : int ref = {contents=0}
    # let rec eras = function
    [] -> []
    | p::q ->
    result := p ;
    p :: (eras (List.filter (fun x -> x mod p <> 0) q)) ;;
    val eras : int list -> int list = <fun>


  2. Write a function sigint_handle which handles the signal sigint and writes the content of result to the output.

    # let sigint_handle (_ : int) =
    Printf.printf"Current prime number : %d\n" !result;
    flush stdout ;;
    val sigint_handle : int -> unit = <fun>


  3. Modify the default signal handling of sigint by associating with it the preceding function sigint_handle.

    # Sys.set_signal Sys.sigint (Sys.Signal_handle sigint_handle) ;;
    - : unit = ()


  4. Compile the program, then start the executable with an upper bound for the computation time. During the computation, send the signal sigint to the process, by the Unix kill command as well as by the key combination CTRL-C.
    $ ocamlc premiers.ml
    $ premiers 15000
    Current prime number : 2539
    Current prime number : 8263
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 ............
    

Previous Contents Next ocaml-book-1.0/en/html/book-ora105.html0000644000000000000000000007474307453055400014477 0ustar Lexicon Previous Contents Next

Lexicon

Lexical analysis is the first step in character string processing: it segments character strings into a sequence of words also known as lexical units or lexemes.

Module Genlex

This module provides a simple primitive allowing the analysis of a string of characters using several categories of predefined lexical units. These categories are distinguished by type:


# type token =
Kwd of string
| Ident of string
| Int of int
| Float of float
| String of string
| Char of char ;;


Hence, we will be able to recognize within a character string an integer (constructor Int) and to recover its value (constructor argument of type int). Recognizable strings and characters respect the usual conventions: a string is delimited by two (") characters and character literals by two (') characters. A float is represented by using either floating-point notation (for example 0.01) or exponent-mantissa notation (for example 1E-2).



Constructor Ident designates the category of identifiers. These are the names of variables or functions in programming languages, for example. They comprise all strings of letters and digits including underscore (_) or apostrophe ('). Such a string should not start with a digit. We also consider as identifiers (for this module at least) strings containing operator symbols, such as +, *, > or =. Finally, constructor Kwd defines the category of keywords containing distinguished identifiers or special characters (specified by the programmer when invoking the lexer).

The only variant of the token type controlled by parameters is that of keywords. The following primitive allows us to create a lexical analyser (lexer) taking as keywords the list passed as first argument to it.


# Genlex.make_lexer ;;
- : string list -> char Stream.t -> Genlex.token Stream.t = <fun>


The result of applying make_lexer to a list of keywords is a function taking as input a stream of characters and returning a stream of lexical units (of type token.)

Thus we can easily obtain a lexer for our BASIC interpreter. We declare the set of keywords:


# let keywords =
[ "REM"; "GOTO"; "LET"; "PRINT"; "INPUT"; "IF"; "THEN";
"-"; "!"; "+"; "-"; "*"; "/"; "%";
"="; "<"; ">"; "<="; ">="; "<>";
"&"; "|" ] ;;


With this definition in place, we define the lexer:


# let line_lexer l = Genlex.make_lexer keywords (Stream.of_string l) ;;
val line_lexer : string -> Genlex.token Stream.t = <fun>
# line_lexer "LET x = x + y * 3" ;;
- : Genlex.token Stream.t = <abstr>


Function line_lexer takes as input a string of characters and returns the corresponding stream of lexemes.

Use of Streams

We can carry out the lexical analysis ``by hand'' by directly manipulating streams.

The following example is a lexer for arithmetical expressions. Function lexer takes a character stream and returns a stream of lexical units of type lexeme Stream.t1. Spaces, tabs and newline characters are removed. To simplify, we do not consider variables or negative integers.


# let rec spaces s =
match s with parser
[<'' ' ; rest >] -> spaces rest
| [<''\t' ; rest >] -> spaces rest
| [<''\n' ; rest >] -> spaces rest
| [<>] -> ();;
val spaces : char Stream.t -> unit = <fun>
# let rec lexer s =
spaces s;
match s with parser
[< ''(' >] -> [< 'Lsymbol "(" ; lexer s >]
| [< '')' >] -> [< 'Lsymbol ")" ; lexer s >]
| [< ''+' >] -> [< 'Lsymbol "+" ; lexer s >]
| [< ''-' >] -> [< 'Lsymbol "-" ; lexer s >]
| [< ''*' >] -> [< 'Lsymbol "*" ; lexer s >]
| [< ''/' >] -> [< 'Lsymbol "/" ; lexer s >]
| [< ''0'..'9' as c;
i,v = lexint (Char.code c - Char.code('0')) >]
-> [<'Lint i ; lexer v>]
and lexint r s =
match s with parser
[< ''0'..'9' as c >]
-> let u = (Char.code c) - (Char.code '0') in lexint (10*r + u) s
| [<>] -> r,s
;;
val lexer : char Stream.t -> lexeme Stream.t = <fun>
val lexint : int -> char Stream.t -> int * char Stream.t = <fun>


Function lexint carries out the lexical analysis for the portion of a stream describing an integer constant. It is called by function lexer when lexer finds a digit on the input stream. Function lexint then consumes all consecutive digits to obtain the corresponding integer value.

Regular Expressions

2

Let's abstract a bit and consider the problem of lexical units from a more theoretical point of view.

From this point of view, a lexical unit is a word. A word is formed by concatening items in an alphabet. For our purposes, the alphabet we are considering is a subset of the ASCII characters. Theoretically, a word may contain no characters (the empty word3) or just a single character. The theoretical study of the assembly of lexical items (lexemes) from members of an alphabet has brought about a simple formalism known as regular expressions.

Definition
A regular expression defines a set of words. For example, a regular expression could specify the set of words that are valid identifiers. Regular expressions are specified by a few set-theoretic operations. Let M and N be two sets of words. Then we can specify:
  1. the union of M and N, denoted by M | N.

  2. the complement of M, denoted by ^M. This is the set of all words not in M.

  3. the concatenation of M and N. This is the set of all the words formed by placing a word from M before a word from N. We denote this set simply by MN.

  4. the set of words formed by a finite sequence of words in M, denoted M+.

  5. for syntactic convenience, we write M? to denote the set of words in M, with addition of the empty word.
Individual characters denote the singleton set of words containing just that character. Expression a | b | c thus describes the set containing three words: a, b ant c. We will use the more compact syntax [abc] to define such a set. As our alphabet is ordered (by the ASCII code order) we can also define intervals. For example, the set of digits can be written: [0-9]. We can use parentheses to group expressions.

If we want to use one of the operator characters as a character in a regular expression, it should be preceded by the escape character \. For example, (\*)* denotes the set of sequences of stars.

Example
Let's consider the alphabet comprising digits (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) the plus (+), minus (-) and dot (.) signs and letter E. We can define the set num of words denoting numbers. Let's call integers the set defined with [0-9]+. We define the set unum of unsigned numbers as:
integers?(.integers)?(E(\+|-)?integers)?

The set of signed numbers is thus defined as:
unum | -unum or with -?unum

Recognition
While regular expressions are a useful formalism in their own right, we usually wish to implement a program that determines whether a string of characters (or one of its substrings) is a member of the set of words described by a regular expression. For that we need to translate the formal definition of the set into a recognition and expression processing program. In the case of regular expressions such a translation can be automated. Such translation techniques are carried out by module Genlex in library Str (described in the next section) and by the ocamllex tools that we introduce in the following two sections.

The Str Library

This module contains an abstract data type regexp which represents regular expressions as well as a function regexp which takes a string describing a regular expression, more or less following the syntax described above, and returns its abstract representation.

This module contains, as well, a number of functions which exploit regular expressions and manipulate strings. The syntax of regular expressions for library Str is given in figure 11.1.

. any character except \n
* zero or more occurences of the preceding expression
+ one or more occurences of the preceding expression
? zero or one occurences of the preceding expression
[..] set of characters (example [abc])
  intervals, denoted by - (example [0-9])
  set complements, denoted by ^ (example [^A-Z])
^ start of line (not to be mistaken with the use of ^ as a set complement)
$ end of line
| alternative
(..) grouping of a complex expression (we can later refer to such an expression by an integer index -- see below)
i an integer constant, referring to the string matched by the i-th complex expression
\ escape character (used when matching a reserved character in regular expressions)

Figure 11.1: Regular expressions.


Example
We want to write a function translating dates in anglo-saxon format into French dates within a data file. We suppose that the file is organised into lines of data fields and the components of an anglo-saxon date are separated by dots. Let's define a function which takes as argument a string (i.e. a line from the file), isolates the date, decomposes and translates it, then replaces the original with the translation.

# let french_date_of d =
match d with
[mm; dd; yy] -> dd^"/"^mm^"/"^yy
| _ -> failwith "Bad date format" ;;
val french_date_of : string list -> string = <fun>

# let english_date_format = Str.regexp "[0-9]+\.[0-9]+\.[0-9]+" ;;
val english_date_format : Str.regexp = <abstr>

# let trans_date l =
try
let i=Str.search_forward english_date_format l 0 in
let d1 = Str.matched_string l in
let d2 = french_date_of (Str.split (Str.regexp "\.") d1) in
Str.global_replace english_date_format d2 l
with Not_found -> l ;;
val trans_date : string -> string = <fun>

# trans_date "..............06.13.99............" ;;
- : string = "..............13/06/99............"


The ocamllex Tool

The ocamllex tool is a lexical analyzer generator built for Objective CAML after the model of the lex tool for the C language. It generates a source Objective CAML file from a file describing the lexical elements to be recognized in the form of regular expressions. The programmer can augment each lexical element description with a processing action known as a semantic action. The generated code manipulates an abstract type lexbuf defined in module Lexing. The programmer can use this module to control processing of lexical buffers.

Usually the lexical description files are given the extension .mll. Later, to obtain a Objective CAML source from a lex_file.mll you type the command
ocamllex lex_file.mll
A file lex_file.ml is generated containing the code for the corresponding analyzer. This file can then be compiled with other modules of an Objective CAML application. For each set of lexical analysis rules there is a corresponding function taking as input a lexical buffer (of type Lexing.lexbuf) and returning the value defined by the semantic actions. Consequently, all actions in the same rule must produce values of the same type.

The general format for an ocamllex file is

{
header
}
let ident = regexp
...
rule ruleset1 = parse
regexp { action }
| ...
| regexp { action }
and ruleset2 = parse
...
and ...
{
trailer-and-end
}

Both section ``header'' and ``trailer-and-end'' are optional. They contain Objective CAML code defining types, functions, etc. needed for processing. The code in the last section can use the lexical analysis functions that will be generated by the middle section. The declaration list preceding the rule definition allows the user to give names to some regular expressions. They can later be invoked by name in the definition of rules.

Example
Let's revisit our BASIC example. We will want to refine the type of lexical units returned. We will once again define function lexer (as we did on page ??) with the same type of output (lexeme), but taking as input a buffer of type Lexing.lexbuf.


{
let string_chars s =
String.sub s 1 ((String.length s)-2) ;;
}

let op_ar = ['-' '+' '*' '%' '/']
let op_bool = ['!' '&' '|']
let rel = ['=' '<' '>']

rule lexer = parse
[' '] { lexer lexbuf }

| op_ar { Lsymbol (Lexing.lexeme lexbuf) }
| op_bool { Lsymbol (Lexing.lexeme lexbuf) }

| "<=" { Lsymbol (Lexing.lexeme lexbuf) }
| ">=" { Lsymbol (Lexing.lexeme lexbuf) }
| "<>" { Lsymbol (Lexing.lexeme lexbuf) }
| rel { Lsymbol (Lexing.lexeme lexbuf) }

| "REM" { Lsymbol (Lexing.lexeme lexbuf) }
| "LET" { Lsymbol (Lexing.lexeme lexbuf) }
| "PRINT" { Lsymbol (Lexing.lexeme lexbuf) }
| "INPUT" { Lsymbol (Lexing.lexeme lexbuf) }
| "IF" { Lsymbol (Lexing.lexeme lexbuf) }
| "THEN" { Lsymbol (Lexing.lexeme lexbuf) }

| '-'? ['0'-'9']+ { Lint (int_of_string (Lexing.lexeme lexbuf)) }
| ['A'-'z']+ { Lident (Lexing.lexeme lexbuf) }
| '"' [^ '"']* '"' { Lstring (string_chars (Lexing.lexeme lexbuf)) }


The translation of this file by ocamllex returns function lexer of type Lexing.lexbuf -> lexeme. We will see later how to use such a function in conjunction with syntactic analysis (see page ??).


Previous Contents Next ocaml-book-1.0/en/html/book-ora213.html0000644000000000000000000000574507453055401014474 0ustar OCamlBrowser Previous Contents Next

OCamlBrowser

OcamlBrowser is a code browser for Objective CAML, providing a LablTk-based graphical user interface. It integrates a ``navigator'' allowing to browse various modules, to look at their contents (names of values and types), and to edit them.

When launching OCamlBrowser by the command ocamlbrowser, the list of all the compiled modules available (see figure B.2) is displayed. One can add more modules by specifying a path to find them. From the menu File, one can launch a toplevel interactive loop or an editor in a new window.




Figure B.2: OCamlBrowser : the main window


When one of the modules is clicked on, a new window opens to display its contents (see figure B.3). By selecting a value, its type appears in bottom of the window.




Figure B.3: OCamlBrowser : module contents


In the main window, one can search on the name of a function. The result appears in a new window. The figure B.4 shows the result of a search on the word create.




Figure B.4: OCamlBrowser : search for create


There are other possibilities that we let the user discover.




Previous Contents Next ocaml-book-1.0/en/html/book-ora031.html0000644000000000000000000017676607453055377014522 0ustar Exercises Previous Contents Next

Exercises

Doubly Linked Lists

Functional programming lends itself well to the manipulation of non-cyclic data structures, such as lists for example. For cyclic structures, on the other hand, there are real implementation difficulties. Here we propose to define doubly linked lists, i.e., where each element of a list knows its predecessor and its successor.
  1. Define a parameterized type for doubly linked lists, using at least one record with mutable fields. A cell is a record containing a value, the list of the following cells, and the list of the preceding cells.

    # type 'a cell = {
    info : 'a ;
    mutable prev : 'a dlist ;
    mutable next : 'a dlist }


    A list is either empty or a cell.

    and 'a dlist = Empty | Cell of 'a cell ;;
    type 'a cell = { info: 'a; mutable prev: 'a dlist; mutable next: 'a dlist }
    type 'a dlist = | Empty | Cell of 'a cell


  2. Write the functions add and remove which add and remove an element of a doubly linked list. To add a cell containing x to the list, insert it between the cell indicated by the current list and its preceding cell.

    # let add x = function
    Empty -> Cell { info=x ; prev=Empty ; next=Empty }
    | Cell c as l ->
    let new_cell = { info=x ; prev=c.prev ; next=l } in
    let new_dlist = Cell new_cell in
    c.prev <- new_dlist ;
    ( match new_cell.prev with
    Empty -> ()
    | Cell pl -> pl.next <- new_dlist ) ;
    new_dlist ;;
    val add : 'a -> 'a dlist -> 'a dlist = <fun>
    To remove the cell indicated by the current list, simply readjust any remaining non-empty cells.

    # let remove_cell = function
    Empty -> failwith "Already empty"
    | Cell c -> match (c.prev , c.next) with
    Empty , Empty -> Empty
    | Cell c1 as l , Empty -> c1.next <- Empty ; l
    | Empty , ((Cell c2) as l) -> c2.prev <- Empty ; l
    | Cell c1 as l1 , (Cell c2 as l2) -> c1.next <- l2; c2.prev <- l1; l1 ;;
    val remove_cell : 'a dlist -> 'a dlist = <fun>


    To remove all the cells containing x, traverse the list in both directions and use remove_cell on any instance of x.

    # let rec remove x l =
    let rec remove_left = function
    Empty -> ()
    | Cell c as l -> let pl = c.prev in
    if c.info = x then ignore (remove_cell l) ;
    remove_left pl
    and remove_right = function
    Empty -> ()
    | Cell c as l -> let nl = c.next in
    if c.info = x then ignore (remove_cell l) ;
    remove_right nl
    in match l with
    Empty -> Empty
    | Cell c as l -> if c.info = x then remove x (remove_cell l)
    else (remove_left c.prev ; remove_right c.next ; l) ;;
    val remove : 'a -> 'a dlist -> 'a dlist = <fun>

Solving linear systems

This exercise has to do with matrix algebra. It solves a system of equations by Gaussian elimination (i.e., pivoting). We write the system of equations A   X = Y with A, a square matrix of dimension n, Y, a vector of constants of dimension n and X, a vector of unknowns of the same dimension.

This method consists of transforming the system A   X = Y into an equivalent system C   X = Z such that the matrix C is upper triangular. We diagonalize C to obtain the solution.
  1. Define a type vect, a type mat, and a type syst .

    # type vect = float array ;;
    type vect = float array
    # type mat = vect array ;;
    type mat = vect array
    # type syst = { m:mat ; v:vect } ;;
    type syst = { m: mat; v: vect }


  2. Write utility functions for manipulating vectors: to display a system on screen, to add two vectors, to multiply a vector by a scalar. To print floats, limiting the output to five characters.

    # let my_print_float s =
    let x = string_of_float s
    in let y = match String.length x with
    5 -> x
    | n when n<5 -> (String.make (5-n) ' ') ^ x
    | n -> String.sub x 0 5
    in print_string y ;;
    val my_print_float : float -> unit = <fun>


    To print a system.

    # let print_syst s =
    let l = Array.length s.m
    in for i=0 to l-1 do
    print_string " | " ;
    for j=0 to l-1 do
    my_print_float s.m.(i).(j) ;
    print_string " "
    done ;
    if i=l/2 then print_string " | * | x"
    else print_string " | | x" ;
    print_int (i+1) ;
    if i=l/2 then print_string " | = | "
    else print_string " | | " ;
    my_print_float s.v.(i) ;
    print_string " |" ;
    print_newline ()
    done ;;
    val print_syst : syst -> unit = <fun>

    # let add_vect v1 v2 =
    let l = Array.length v1
    in let res = Array.create l 0.0
    in for i=0 to l-1 do res.(i) <- v1.(i) +. v2.(i) done ;
    res ;;
    val add_vect : float array -> float array -> float array = <fun>

    # let mult_scal_vect x v =
    let l = Array.length v
    in let res = Array.create l 0.0
    in for i=0 to l-1 do res.(i) <- v.(i) *. x done ;
    res ;;
    val mult_scal_vect : float -> float array -> float array = <fun>


  3. Write utility functions for matrix computations: multiplication of two matrices, product of a matrix with a vector.

    # let mult_mat m1 m2 =
    let l1 = Array.length m1 and l2 = Array.length m2.(0)
    and l3 = Array.length m2
    in let res = Array.create_matrix l1 l2 0.0
    in for i=0 to l1-1 do
    for j=0 to l2-1 do
    for k=0 to l3-1 do
    res.(i).(j) <- res.(i).(j) +. m1.(i).(k) *. m2.(k).(j)
    done done done ;
    res ;;
    val mult_mat : float array array -> float array array -> float array array =
    <fun>

    # let mult_mat_vect m v =
    let l1 = Array.length m and l2 = Array.length v
    in let res = Array.create l1 0.0
    in for i=0 to l1-1 do
    for j=0 to l2-1 do
    res.(i) <- res.(i) +. m.(i).(j) *. v.(j)
    done done ;
    res ;;
    val mult_mat_vect : float array array -> float array -> float array = <fun>


  4. Write utility functions for manipulating systems: division of a row of a system by a pivot, (Aii), swapping two rows.

    # let div_syst s i =
    let p = s.m.(i).(i)
    in s.m.(i).(i) <- 1.0 ;
    for j=i+1 to (Array.length s.m.(0)) - 1 do
    s.m.(i).(j) <- s.m.(i).(j) /. p
    done ;
    s.v.(i) <- s.v.(i) /. p ;;
    val div_syst : syst -> int -> unit = <fun>

    # let permut_syst s i j =
    let aux1 = s.m.(i) and aux2 = s.v.(i)
    in s.m.(i) <- s.m.(j) ;
    s.v.(i) <- s.v.(j) ;
    s.m.(j) <- aux1 ;
    s.v.(j) <- aux2 ;;
    val permut_syst : syst -> int -> int -> unit = <fun>


  5. Write a function to diagonalize a system. From this, obtain a function solving a linear system.

    # exception Not_linear ;;
    exception Not_linear

    # let triangulize s =
    if s.m=[| |] || s.v=[| |] then raise Not_linear ;
    let l = Array.length s.m
    in if l<>Array.length s.m.(0) || l<>Array.length s.v then raise Not_linear ;
    for i=0 to l-1 do
    if s.m.(i).(i)=0.0 then
    begin
    let j = ref (i+1)
    in while !j<l && s.m.(!j).(i)=0.0 do incr j done ;
    if !j=l then raise Not_linear ;
    permut_syst s i !j
    end ;
    div_syst s i ;
    for j=i+1 to l-1 do
    s.v.(j) <- s.v.(j) -. s.m.(j).(i) *. s.v.(i) ;
    s.m.(j) <- add_vect s.m.(j) (mult_scal_vect (-. s.m.(j).(i)) s.m.(i))
    done
    done ;;
    val triangulize : syst -> unit = <fun>

    # let solve s =
    triangulize s ;
    let l = Array.length s.v
    in let res = Array.copy s.v
    in for i = l-1 downto 0 do
    let x = ref res.(i)
    in for j=i+1 to l-1 do x := !x -. s.m.(i).(j) *. res.(j) done ;
    res.(i) <- !x
    done ;
    res ;;
    val solve : syst -> float array = <fun>


  6. Test your functions on the following systems:

    AX =



    10 7 8 7
    7 5 6 5
    8 6 10 9
    7 5 9 10




    *



    x1
    x2
    x3
    x4




    =



    32
    23
    33
    31




    = Y

    AX =



    10 7 8 7
    7 5 6 5
    8 6 10 9
    7 5 9 10




    *



    x1
    x2
    x3
    x4




    =



    32.1
    22.9
    33.1
    30.9




    = Y

    AX =



    10 7 8.1 7.2
    7.08 5.04 6 5
    8 5.98 9.89 9
    6.99 4.99 9 9.98




    *



    x1
    x2
    x3
    x4




    =



    32
    23
    33
    31




    = Y

    # let ax1 = { m = [| [| 10.0 ; 7.0 ; 8.0 ; 7.0 |]
    ; [| 7.0 ; 5.0 ; 6.0 ; 5.0 |]
    ; [| 8.0 ; 6.0 ; 10.0 ; 9.0 |]
    ; [| 7.0 ; 5.0 ; 9.0 ; 10.0 |] |] ;
    v = [| 32.0 ; 23.0 ; 33.0 ; 31.0 |] } ;;
    val ax1 : syst =
    {m=[|[|10; 7; 8; 7|]; [|7; 5; 6; 5|]; [|8; 6; 10; ...|]; ...|]; v=...}

    # let r1 = solve ax1 ;;
    val r1 : float array = [|1; 1; 1; 1|]

    # let ax2 = { m = [| [| 10.0 ; 7.0 ; 8.0 ; 7.0 |]
    ; [| 7.0 ; 5.0 ; 6.0 ; 5.0 |]
    ; [| 8.0 ; 6.0 ; 10.0 ; 9.0 |]
    ; [| 7.0 ; 5.0 ; 9.0 ; 10.0 |] |] ;
    v = [| 32.1 ; 22.9 ; 33.1 ; 30.9 |] } ;;
    val ax2 : syst =
    {m=[|[|10; 7; 8; 7|]; [|7; 5; 6; 5|]; [|8; 6; 10; ...|]; ...|]; v=...}

    # let r2 = solve ax2 ;;
    val r2 : float array = [|9.2; -12.6; 4.5; -1.1|]

    # let ax3 = { m = [| [| 10.0 ; 7.0 ; 8.1 ; 7.2 |]
    ; [| 7.08 ; 5.04 ; 6.0 ; 5.0 |]
    ; [| 8.0 ; 5.98 ; 9.89 ; 9.0 |]
    ; [| 6.99 ; 4.99 ; 9.0 ; 9.98 |] |] ;
    v = [| 32.0 ; 23.0 ; 33.0 ; 31.0 |] } ;;
    val ax3 : syst =
    {m=
    [|[|10; 7; 8.1; 7.2|]; [|7.08; 5.04; 6; 5|]; [|8; 5.98; 9.89; ...|];
    ...|];
    v=...}

    # let r3 = solve ax3 ;;
    val r3 : float array = [|-80.9999999999; 137; -33.9999999999; 22|]


  7. What can you say about the results you got? One should never neglect the effect of round-off errors. A small error in the input data can cause a great error in the result when multiplied.

Previous Contents Next ocaml-book-1.0/en/html/book-ora030.html0000644000000000000000000004304407453055377014477 0ustar Calculator With Memory Previous Contents Next

Calculator With Memory

We now reuse the calculator example described in the preceding chapter, but this time we give it a user interface, which makes our program more usable as a desktop calculator. This loop allows entering operations directly and seeing results displayed without having to explicitly apply a transition function for each keypress.

We attach four new keys: C, which resets the display to zero, M, which memorizes a result, m, which recalls this memory and OFF, which turns off the calculator. This corresponds to the following type:

# type key = Plus | Minus | Times | Div | Equals | Digit of int
| Store | Recall | Clear | Off ;;


It is necessary to define a translation function from characters typed on the keyboard to values of type key. The exception Invalid_key handles the case of characters that do not represent any key of the calculator. The function code of module Char translates a character to its ASCII-code.

# exception Invalid_key ;;
exception Invalid_key
# let translation c = match c with
'+' -> Plus
| '-' -> Minus
| '*' -> Times
| '/' -> Div
| '=' -> Equals
| 'C' | 'c' -> Clear
| 'M' -> Store
| 'm' -> Recall
| 'o' | 'O' -> Off
| '0'..'9' as c -> Digit ((Char.code c) - (Char.code '0'))
| _ -> raise Invalid_key ;;
val translation : char -> key = <fun>


In imperative style, the translation function does not calculate a new state, but physically modifies the state of the calculator. Therefore, it is necessary to redefine the type state such that the fields are modifiable. Finally, we define the exception Key_off for treating the activation of the key OFF.

# type state = {
mutable lcd : int; (* last computation done *)
mutable lka : bool; (* last key activated *)
mutable loa : key; (* last operator activated *)
mutable vpr : int; (* value printed *)
mutable mem : int (* memory of calculator *)
};;



# exception Key_off ;;
exception Key_off
# let transition s key = match key with
Clear -> s.vpr <- 0
| Digit n -> s.vpr <- ( if s.lka then s.vpr*10+n else n );
s.lka <- true
| Store -> s.lka <- false ;
s.mem <- s.vpr
| Recall -> s.lka <- false ;
s.vpr <- s.mem
| Off -> raise Key_off
| _ -> let lcd = match s.loa with
Plus -> s.lcd + s.vpr
| Minus -> s.lcd - s.vpr
| Times -> s.lcd * s.vpr
| Div -> s.lcd / s.vpr
| Equals -> s.vpr
| _ -> failwith "transition: impossible match"
in
s.lcd <- lcd ;
s.lka <- false ;
s.loa <- key ;
s.vpr <- s.lcd;;
val transition : state -> key -> unit = <fun>


We define the function go, which starts the calculator. Its return value is (), because we are only concerned about effects produced by the execution on the environment (start/end, modification of state). Its argument is also the constant (), because the calculator is autonomous (it defines its own initial state) and interactive (the arguments of the computation are entered on the keyboard as required). The transitions are performed within an infinite loop (while true do) so we can quit with the exception Key_off.


# let go () =
let state = { lcd=0; lka=false; loa=Equals; vpr=0; mem=0 }
in try
while true do
try
let input = translation (input_char stdin)
in transition state input ;
print_newline () ;
print_string "result: " ;
print_int state.vpr ;
print_newline ()
with
Invalid_key -> () (* no effect *)
done
with
Key_off -> () ;;
val go : unit -> unit = <fun>


We note that the initial state must be either passed as a parameter or declared locally within the function go, because it needs to be initialized at every application of this function. If we had used a value initial_state as in the functional program, the calculator would start in the same state as the one it had when it was terminated. This would make it difficult to use two calculators in the same program.








Previous Contents Next ocaml-book-1.0/en/html/book-ora176.html0000644000000000000000000010535307453055400014477 0ustar Synchronization of Processes Previous Contents Next

Synchronization of Processes

In the setting of processes sharing a common zone of memory, the word ``concurrency'' carries its full meaning: the various processes involved are compete for access to the unique resource of the memory2. To the problem of division of resources, is added that of the lack of control of the alternation and of the execution times of the concurrent processes.

The system which manages the collection of processes can at any moment interrupt a calculation in progress. Thus when two processes cooperate, they must be able to guarantee the integrity of the manipulations of certain shared data. For this, a process should be able to remain owner of these data as long as it has not completed a calculation or any other operation (for example, an acquisition of data from a peripheral). To guarantee the exclusivity of access to the data to a single process, we set up a mechanism called mutual exclusion.

Critical Section and Mutual Exclusion

The mechanisms of mutual exclusion are implemented with the help of particular data structures called mutexes. The operations on mutexes are limited to their creation, their setting, and their disposal. A mutex is the smallest item of data shared by a collection of concurrent processes. Its manipulation is always exclusive. To the notion of exclusivity of manipulation of a mutex is added that of exclusivity of possession: only the process which has taken a mutex can free it; if other processes wish to use the mutex, then they must wait for it to be released by the process that is holding it.

Mutex Module

Module Mutex is used to create mutexes between processes related by mutual exclusion on an area of memory. We will illustrate their use with two small classic examples of concurrency.

The functions of creation, locking, and unlocking of mutexes are:

# Mutex.create ;;
- : unit -> Mutex.t = <fun>
# Mutex.lock ;;
- : Mutex.t -> unit = <fun>
# Mutex.unlock ;;
- : Mutex.t -> unit = <fun>


There exists a variant of mutex locking that is non-blocking:

# Mutex.try_lock;;
- : Mutex.t -> bool = <fun>
If the mutex is already locked, the function returns false. Otherwise, the function locks the mutex and returns true.

The Dining Philosophers

This little story, due to Dijkstra, illustrates a pure problem of resource allocation. It goes as follows:

``Five oriental philosophers divide their time between study and coming to the refectory to eat a bowl of rice. The room devoted to feeding the philosophers contains nothing but a single round table on which there is a large dish of rice (always full), five bowls, and five chopsticks.''




Figure 19.1: The Table of the Dining Philosophers


As we can see in the figure 19.1, a philosopher who takes his two chopsticks beside his bowl stops his neighbours from doing the same. When he puts down one of his chopsticks, his neighbour, famished, can grab it. If needs be, this latter should wait until the other chopstick is available. Here the chopsticks are the resources to be allocated.

To simplify things, we suppose that each philosopher habitually comes to the same place at the table. We model the five chopsticks as five mutexes stored in a vector b.

# let b =
let b0 = Array.create 5 (Mutex.create()) in
for i=1 to 4 do b0.(i) <- Mutex.create() done;
b0 ;;
val b : Mutex.t array = [|<abstr>; <abstr>; <abstr>; <abstr>; <abstr>|]


Eating and meditation are simulated by a suspension of processes.

# let meditation = Thread.delay
and eating = Thread.delay ;;
val meditation : float -> unit = <fun>
val eating : float -> unit = <fun>


We model a philosopher by a function which executes an infinite sequence of actions from Dijsktra's story. Taking a chopstick is simulated by the acquisition of a mutex, thus a single philosopher can hold a given chopstick at a time. We introduce a little time of reflection between taking and dropping of each of the two chopsticks while a number of output commands track the activity of the philosopher.


# let philosopher i =
let ii = (i+1) mod 5
in while true do
meditation 3. ;
Mutex.lock b.(i);
Printf.printf "Philosopher (%d) takes his left-hand chopstick" i ;
Printf.printf " and meditates a little while more\n";
meditation 0.2;
Mutex.lock b.(ii);
Printf.printf "Philosopher (%d) takes his right-hand chopstick\n" i;
eating 0.5;
Mutex.unlock b.(i);
Printf.printf "Philosopher (%d) puts down his left-hand chopstick" i;
Printf.printf " and goes back to meditating\n";
meditation 0.15;
Mutex.unlock b.(ii);
Printf.printf "Philosopher (%d) puts down his right-hand chopstick\n" i
done ;;
val philosopher : int -> unit = <fun>


We can test this little program by executing:

for i=0 to 4 do ignore (Thread.create philosopher i) done ;
while true do Thread.delay 5. done ;;


We suspend, in the infinite loop while, the main process in order to increase the chances of the philosopher processes to run. We use randomly chosen delays in the activity loop with the aim of creating some disparity in the parallel execution of the processes.

Problems of the nave solution.
A terrible thing can happen to our philosophers: they all arrive at the same time and seize the chopstick on their left. In this case we are in a situation of dead-lock. None of the philosophers can eat! We are in a situation of starvation.

To avoid this, the philosophers can put down a chopstick if they do not manage to take the second one. This is highly courteous, but still allows two philosophers to gang up against a third to stop him from eating, by not letting go of their chopsticks, except the ones that their other neighbour has given them. There exist numerous solutions to this problem. One of them is the object of the exercise on page ??.

Producers and Consumers I

The pair of producers-consumers is a classic example of concurrent programming. A group of processes, designated the producers, are in charge of storing data in a queue: a second group, the consumers, is in charge of removing it. Each intervening party excludes the others.

We implement this scheme using a queue shared between the producers and the consumers. To guarantee the proper operation of the system, the queue is manipulated in mutual exclusion in order to guarantee the integrity of the operations of addition and removal.

f is the shared queue, and m  is the mutex.

# let f = Queue.create () and m = Mutex.create () ;;
val f : '_a Queue.t = <abstr>
val m : Mutex.t = <abstr>


We divide the activity of a producer into two parts: creating a product (function produce) and storing a product (fonction store). Only the operation of storage needs the mutex.

# let produce i p d =
incr p ;
Thread.delay d ;
Printf.printf "Producer (%d) has produced %d\n" i !p ;
flush stdout ;;
val produce : int -> int ref -> float -> unit = <fun>

# let store i p =
Mutex.lock m ;
Queue.add (i,!p) f ;
Printf.printf "Producer (%d) has added its %dth product\n" i !p ;
flush stdout ;
Mutex.unlock m ;;
val store : int -> int ref -> unit = <fun>


The code of the producer is an endless loop of creation and storage. We introduce a random delay at the end of each iteration in order to desynchronize the execution.

# let producer i =
let p = ref 0 and d = Random.float 2.
in while true do
produce i p d ;
store i p ;
Thread.delay (Random.float 2.5)
done ;;
val producer : int -> unit = <fun>


The only operation of the consumer is the retrieval of an element of the queue, taking care that the product is actually there.

# let consumer i =
while true do
Mutex.lock m ;
( try
let ip, p = Queue.take f
in Printf.printf "The consumer(%d) " i ;
Printf.printf "has taken product (%d,%d)\n" ip p ;
flush stdout ;
with
Queue.Empty ->
Printf.printf "The consumer(%d) " i ;
print_string "has returned empty-handed\n" ) ;
Mutex.unlock m ;
Thread.delay (Random.float 2.5)
done ;;
val consumer : int -> unit = <fun>


The following test program creates four producers and four consumers.

for i = 0 to 3 do
ignore (Thread.create producer i);
ignore (Thread.create consumer i)
done ;
while true do Thread.delay 5. done ;;


Waiting and Synchronization

The relation of mutual exclusion is not ``fine'' enough to describe synchronization between processes. It is not rare that the work of a process depends on the completion of an action by another process, thus modifying a certain condition. It is therefore desirable that the processes should be able to communicate the fact that this condition might have changed, indicating to the waiting processes to test it again. The different processes are thus in a relation of mutual exclusion with communication.

In the preceding example, a consumer, rather than returning empty-handed, could wait until a producer came to resupply the stock. This last could signal to the waiting consumer that there is something to take. The model of waiting on a condition to take a mutex is known as semaphore.

Semaphores.
A semaphore is an integral variable s which can only take non negative values. Once s is initialised, the only operations allowed are: wait(s) and signal(s), written P(s) and V(s), respectively. They are defined thus, s corresponding to the number of resources of a given type.
  • wait(s): if s > 0 then s := s -1, otherwise the process, having called wait(s), is suspended.
  • signal(s): if a process has been suspended after a prior invocation of wait(s), then wake it up, otherwise s := s + 1.
A semaphore which only takes the values 0 or 1 is called a binary semaphore.

Condition Module

The functions of the module Condition implement the primitives of putting to sleep and waking up processes on a signal. A signal, in this case, is a variable shared by a collection of processes. Its type is abstract and the manipulation functions are:
create
: unit -> Condition.t which creates a new signal.
signal
: Condition.t -> unit which wakes up one of the processes waiting on a signal.

broadcast
: Condition.t -> unit which wakes up all of the processes waiting on a signal.

wait
: Condition.t -> Mutex.t -> unit which suspends the calling process on the signal passed as the first argument. The second argument is a mutex used to protect the manipulation of the signal. It is released, and then reset at each execution of the function.

Producers and Consumers (2)

We revisit the example of producers and consumeres by using the mechanism of condition variables to put to sleep a consumer arriving when the storehouse is empty.

To implement synchronization between waiting consumers and production, we declare:


# let c = Condition.create () ;;
val c : Condition.t = <abstr>


We modify the storage function of the producer by adding to it the sending of a signal:

# let store2 i p =
Mutex.lock m ;
Queue.add (i,!p) f ;
Printf.printf "Producer (%d) has added its %dth product\n" i !p ;
flush stdout ;
Condition.signal c ;
Mutex.unlock m ;;
val store2 : int -> int ref -> unit = <fun>
# let producer2 i =
let p = ref 0 in
let d = Random.float 2.
in while true do
produce i p d;
store2 i p;
Thread.delay (Random.float 2.5)
done ;;
val producer2 : int -> unit = <fun>


The activity of the consumer takes place in two phases: waiting until a product is available, then taking the product. The mutex is taken when the wait is finished and it is released when the consumer has taken its product. The wait takes place on the variable c.

# let wait2 i =
Mutex.lock m ;
while Queue.length f = 0 do
Printf.printf "Consumer (%d) is waiting\n" i ;
Condition.wait c m
done ;;
val wait2 : int -> unit = <fun>
# let take2 i =
let ip, p = Queue.take f in
Printf.printf "Consumer (%d) " i ;
Printf.printf "takes product (%d, %d)\n" ip p ;
flush stdout ;
Mutex.unlock m ;;
val take2 : int -> unit = <fun>
# let consumer2 i =
while true do
wait2 i;
take2 i;
Thread.delay (Random.float 2.5)
done ;;
val consumer2 : int -> unit = <fun>
We note that it is no longer necessary, once a consumer has begun to wait in the queue, to check for the existence of a product. Since the end of its wait corresponds to the locking of the mutex, it does not run the risk of having the new product stolen before it takes it.

Readers and Writers

Here is another classic example of concurrent processes in which the agents do not have the same behaviour with respect to the shared data.

A writer and some readers operate on some shared data. The action of the first may cause the data to be momentarily inconsistent, while the second group only have a passive action. The difficulty arises from the fact that we do not wish to prohibit multiple readers from examining the data simultaneously. One solution to this problem is to keep a counter of the number of readers in the processes of accessing the data. Writing is not allowed except if the number of readers is 0.

The data is symbolized by the integer data which takes the value 0 or 1. The value 0 indicates that the data is ready for reading:


# let data = ref 0 ;;
val data : int ref = {contents=0}


Operations on the counter n are protected by the mutex m:

# let n = ref 0 ;;
val n : int ref = {contents=0}
# let m = Mutex.create () ;;
val m : Mutex.t = <abstr>
# let cpt_incr () = Mutex.lock m ; incr n ; Mutex.unlock m ;;
val cpt_incr : unit -> unit = <fun>
# let cpt_decr () = Mutex.lock m ; decr n ; Mutex.unlock m ;;
val cpt_decr : unit -> unit = <fun>
# let cpt_signal () = Mutex.lock m ;
if !n=0 then Condition.signal c ;
Mutex.unlock m ;;
val cpt_signal : unit -> unit = <fun>


The readers update the counter and emit the signal c when no more readers are present. This is how they indicate to the writer that it may come into action.


# let c = Condition.create () ;;
val c : Condition.t = <abstr>
# let read i =
cpt_incr () ;
Printf.printf "Reader (%d) read (data=%d)\n" i !data ;
Thread.delay (Random.float 1.5) ;
Printf.printf "Reader (%d) has finished reading\n" i ;
cpt_decr () ;
cpt_signal () ;;
val read : int -> unit = <fun>

# let reader i = while true do read i ; Thread.delay (Random.float 1.5) done ;;
val reader : int -> unit = <fun>


The writer needs to block the counter to prevent the readers from accessing the shared data. But it can only do so if the counter is 0, otherwise it waits for the signal indicating that this is the case.

# let write () =
Mutex.lock m ;
while !n<>0 do Condition.wait c m done ;
print_string "The writer is writing\n" ; flush stdout ;
data := 1 ; Thread.delay (Random.float 1.) ; data := 0 ;
Mutex.unlock m ;;
val write : unit -> unit = <fun>

# let writer () =
while true do write () ; Thread.delay (Random.float 1.5) done ;;
val writer : unit -> unit = <fun>


We create a reader and six writers to test these functions.

ignore (Thread.create writer ());
for i=0 to 5 do ignore(Thread.create reader i) done;
while true do Thread.delay 5. done ;;


This solution guarantees that the writer and the readers cannot have access to the data at the same time. On the contrary, nothing guarantees that the writer could ever ``fufill his offic', there we are confronted again with a case of starvation.


Previous Contents Next ocaml-book-1.0/en/html/logocaml-petit.gif0000644000000000000000000001320107452056113015240 0ustar GIF89a||z~*:B$Zb,6&&4JR/jr466( :BLZbp*&~43BR\jf!4~B*2-BJFB$vr5$2>L'rn6>," zOrn7D:>&bft<>Z^l2:J $6:$&&BFTFN\&2,4<"<"*.% kn09NV,nv4=FG^ft pv2:8<<:4V^,D/VVC6nj-UV&@CF)+?V^l.:L&2D42&#" C2:,fn|>F-"*$>FX{v7>NVd*2BDBJL4*B!^b*4-nr3^bt&.*FJ';!Made with GIMP! ,|| H*\ȰÇ#JHŋ3jȱǏ 9"IB\Yp[^H1Pf48aA. X (OהQܪIV+2OH%"U*02` 9C6PIXxu۷P3.x%(L;u[boޡt&paˇ1qmÚ NϴQ[3NSgvVV9N`PmY0Χ&Qq( p[MuI'nowͧ+NWH6 G-l#~aomuT,$ T"U{Ej7!oeHMk *77HD~HCc9'a| AO7I(eEV |(&,( Y{b^!TIwQTb^'A#8 h(T(&KNБLaB%:'ECSb@C=O (RX c+UTbC!fGBp gAGo`.ĵ !a2fl 7p(w ܱѵ?D(CAX+ٴP xPF^l]cH+TF].c"P^oPD.!# [uxrm1cfF /U5{rTES蠅*bk-5fo4oCO% BBC%Cd9όsU/ \+"ެP(\ A$&kb <j,0oOʭ߁JxtC,A@ u<&z xq9`D Q㱉rˋ8󊳌S!&PH'`w*xp2$Љ' |d@Y"(/8p2a o"xT"5] X` &HdXGK9/e =YCe0aW3P;"(\X= ܨF p@e rXB/Ԋ4IDQ*Fq(aX+|xyi UU(@1X@THЖVu^"X\FEJS+ἸsE,@AŪViIm,=Z,b0n 8pA|ªU*|4vGx1H>H> P[Xa! Z73TjC.>&ɇ/-Td$s \D*b8u!(ojf Ç=`j.zzXoHHI!&!C ࠼ prY`x^1񕫨RFCtq7*`9#5Fn6ǨwA F0W'Ԋ,Q!ȄKwK[*v4+PGAZbPuO-fR٥}sA㷅`8xSwQE0ZMl˜хf1B ! `%ЈgeYpŚR;ٖ]9ZBac!j `ۤ,TZƀ" ȅ'Å)b Sܠ-Uk)Eb0^q=p⸅e@4N|B: NUn@LT/zq1ڛ  8]a1S^X\j& pH!8VP^G@HBNna>b ZoRn8ő1dr`;ME5F$ʄurET(X`aj!9bb2ƑjMj@ qyi uH ^ΏZ"@7-8 7vJ"w;]E;( #9D68-,T/0$ *oD 2k_}n! `l|wMwfF` y.Z0ސlp5P7j60 bf%x` |opG )0 @`B - n#j:>vpp\ d nB݇F(G B80 >4Xj7 IV0 qrp$ V 0Ơ -I@C `PphfWSvl v4K )M e lpY FK rt֕ c f7IuxzpsTŐB 0G 7`v|X;v"a3'7Sa`q++t(|nj : vf4p[0'(b4 iFVq T-`:`@pŀ Xjvpqf0 u$"@+ Q ;@Ep8!@b'5d`iCXY` xPR#H{qy!kudMTp]$@/,PӸ8J:`Z P݁G @1GIVY |@d@PT<@M˰A9um==vƄ`o#".pJ Y P{ P;AvFj U0ny*O3(/\ Xf7m:i-U PeT<4T%OA 8U)R@g-JQnQ $ pE A % QP 0 &^mX:06O50j c)Fp 0: J8$(lBxz|)0  g02 lo PBJues!#,X.I> TOR `Y q 7Ҟ_`L E>b?P3a#@pa0 . aT3$ѱPQ@CʦWBH KOkjnm0; Pd*0{!yw0GEɗq ׀o0<Р40X3 sܐkW^p:V6"d4 z 3g0 Cs%*pIb i305[E\ : K5pf`CX Ҫ; h-7|*t : Jaq@j T@ .`05 ltZ"a|@^b10ά,PePaǒ[ *) 0^".? 72̗o7{2rHF oŴQ ۾Hqh"0G* o0 }Gn:R !@\}%,B- m*Rrt]F' ^ `,w r (5w*Q " ]! P +I%`!S@ rw10c1 ^0bs#OQ ÀQɀ 5p -  0 ~W 5 0 0A` p z_0*]&`Ԁqu0 gW}g 0 rPX`܉Sp0oc0  0^~;ocaml-book-1.0/en/html/book-ora120.html0000644000000000000000000000307407453055400014461 0ustar Summary Previous Contents Next

Summary

This chapter introduced the interface between the Objective CAML language and the C language. This interface allows C functions to operate on Objective CAML values. Using abstract Objective CAML types, the converse is also possible. An important feature of this interface is the ability to use the Objective CAML garbage collector to perform automatic reclamation of values created in C. This interface supports the combination, in the same program, of components developed in the two languages. Finally, Objective CAML exceptions can be raised and (with some limitations) handled from C.


Previous Contents Next ocaml-book-1.0/en/html/book-ora058.gif0000644000000000000000000000223407452056112014271 0ustar GIF89aUUU!,t޼HTʶnK3~ DDLڎʦ|J(*bI-+812*5q 9~o m-܍|כ)J5s&VrԅiWH"VeA"^i8IgFɀT偙Xu!fzr○^! d^/>eI $i݃f!,8Չ)#|Y e :,;h@6g肑*ljza]Rr[caUTjj*Zg' h >},&C^ɶgw1^-ts2ȭ&yFQkV_xRd-E_@V#\WYl)k6nY0nk 0L4q gx Y[^f퓯جklo[mϞ{r!2\/Y_=+wՃ\{n}̂Xމ9uQ;\uYCquHa{4Fefy1^)#X&-fsxbyG@jc|C>q &93F*6h]'O[s1_7{vWSz4'iOId Aj6q5ߩJ_2oB=jO0{?~7q?A;ocaml-book-1.0/en/html/book-ora084.gif0000644000000000000000000001070007452056112014265 0ustar GIF89aWt֝0r,ݪD½3 L}TrTtt;LD;33fLLʈ=&njdU$vڝ,ڙ,$ WWWrLfflll333DΖ3’;[ [B}TLTdDLΌT{ʛj nnnҙ.],3Ƅʖ3[l d7 Ν3"""DəfŲ$Ҕƒ;n,!, H*\ȰÇ#JHbQ2jsǏB iI;RLY:bʜI8sϟ Ѣ*u঩Ӧ Jeժ jU׮Š5ٲҪ%۷pќK2xK߾(È Vimb˭z;V\nc|_ l$[A#rUtyHF pH`G+- c{1kk!\2*g \`F#Tmv+l!tm}|߀ny$x4xxD.9UG4RcEd0YVwyx!Ey,Vו@,m/oj#77O{Wog|Zk_<槯⣿~ӟïG ^׽9ދ`ط? .m A #> l`UBo wO> X< HLbh&:PH*ZX̢.z`D܅hL#H!4T+#B!:x̣GqۂTB kp`F:Sf!XL 8z[*.n kDZhF>0"B^Z$,bKr | g0UXr$ /8>Qr4 5d!hEH/ϙY:d$H'Kuo%*K"D޷Wt )Rusք )N񙳝Aˎbe.uQS-CPr d_j3q$A e _#V!RfC/ZRPEgxSj貺]3iYֶ..s]󺷾n-r<6e#&"`(!v5BB=km۩ `@;(T~涡N T_@T@ľp{D ~&u Ȃ1? H!  n(Q.&񅝃|^L H&ooZ{y50gNޏ#,mR|o'r+AJ@ڎrWK@ f <$ w§|,*QD@)( V8F1xfC &7j`^3 }œ|J@ QoyK?ĽO}I /(AmZ;O`HS?qBda t }|5w@@$ m }{#J@@oh=2G=G^#x="p =@*<-j\$0W ^g} g8aJ J| @Јp( `XZ`x  T" p˅ԣ #rFDž 08 }'~@C8w2 L'ESpp5؆KX@`v % 6~Ÿ5uk>h m,`v6\ X ٘m,0xk\g@HH(Ѕ(Ǎq1'vK&) 9swy\gֈ` )kXha@؊NSU9aE#ɕh v] hIؐok\X=xt 9  ( ] sjCI(p؅t Wr8y sboy ozy<]{y=ɘr*ؘ:bg邴\jY}^=cixWj7)jɝdٗ }_ө>ڈ䙑)d i9_Iii)`m(AT}IuѦkz l ڠjl:Vl϶*ml"Zb$Z<(Jd'5. <0Fi٢8>3ӣ>z;@K3DPG:IʖKMZOQ*SU*\FY[z]JĥFb:dZfzg;_kmoqjsJuwy{}ZL:ZF[]׵G FH<_4 :کjXI+5D߂L婪2ITHCZCP  jjGGRNo#$IWZЮ>z*P :3jdu%PI1jKJZ*ЭB :֫SPW1jT4{Lτ #^;(*CBԳ@B+dDDz%:0T[V{XZ\۵^`b;d[fa3j۶npr;t[v{x{li{;[}۶{;#uk;ocaml-book-1.0/en/html/book-ora146.html0000644000000000000000000004467507453055400014505 0ustar Other Aspects of the Object Extension Previous Contents Next

Other Aspects of the Object Extension

In this section we describe the declaration of ``object'' types and local declarations in classes. The latter can be used for class variables by making constructors that reference the local environment.

Interfaces

Class interfaces are generally infered by the type system, but they can also be defined by a type declaration. Only public methods appear in this type.

Syntax


class type name =
  object
    :
    val namei : typei
    :
    method namej : typej
    :
  end

Thus we can define the class point interface:

# class type interf_point =
object
method get_x : int
method get_y : int
method moveto : (int * int ) -> unit
method rmoveto : (int * int ) -> unit
method to_string : unit -> string
method distance : unit -> float
end ;;


This declaration is useful because the defined type can be used as a type constraint.

# let seg_length (p1:interf_point) (p2:interf_point) =
let x = float_of_int (p2#get_x - p1#get_x)
and y = float_of_int (p2#get_y - p1#get_y) in
sqrt ((x*.x) +. (y*.y)) ;;
val seg_length : interf_point -> interf_point -> float = <fun>


Interfaces can only mask fields of instance variables and private methods. They cannot mask abstract or public methods.

This is a restriction in their use, as shown by the following example:

# let p = ( new point_m1 (2,3) : interf_point);;
Characters 11-29:
This expression has type
point_m1 =
< distance : unit -> float; get_x : int; get_y : int;
moveto : int * int -> unit; rmoveto : int * int -> unit;
to_string : unit -> string; undo : unit -> unit >
but is here used with type
interf_point =
< distance : unit -> float; get_x : int; get_y : int;
moveto : int * int -> unit; rmoveto : int * int -> unit;
to_string : unit -> string >
Only the first object type has a method undo


Nevertheless, interfaces may use inheritance. Interfaces are especially useful in combination with modules: it is possible to build the signature of a module using object types, while only making available the description of class interfaces.

Local Declarations in Classes

A class declaration produces a type and a constructor. In order to make this chapter easier to read, we have been presenting constructors as functions without an environment. In fact, it is possible to define constructors which do not need initial values to create an instance: that means that they are no longer functional. Furthermore one can use local declarations in the class. Local variables captured by the constructor are shared and can be treated as class variables.

Constant Constructors

A class declaration does not need to use initial values passed to the constructor. For example, in the following class:

# class example1 =
object
method print () = ()
end ;;
class example1 : object method print : unit -> unit end
# let p = new example1 ;;
val p : example1 = <obj>
The instance constructor is constant. The allocation does not require an initial value for the instance variables. As a rule, it is better to use an initial value such as (), in order to preserve the functional nature of the constructor.

Local Declarations for Constructors

A local declaration can be written directly with abstraction.

# class example2 =
fun a ->
object
val mutable r = a
method get_r = r
method plus x = r <- r + x
end;;
class example2 :
int ->
object val mutable r : int method get_r : int method plus : int -> unit end


Here it is easier to see the functional nature of the constructor. The constructor is a closure which may have an environment that binds free variables to an environment of declarations. The syntax for class declarations allows local declarations in this functional expression.

Class Variables

Class variables are declarations which are known at class level and therefore shared by all instances of the class. Usually these class variables can be used outside of any instance creation. In Objective CAML, thanks to the functional nature of a constructor with a non-empty environment, we can make these values (particularly the modifiable ones) shared by all instances of a class.

We illustrate this facility with the following example, which allows us to keep a register of the number of instances of a class. To do this we define a parameterized abstract class 'a om.

# class virtual ['a] om =
object
method finalize () = ()
method virtual destroy : unit -> unit
method virtual to_string : unit -> string
method virtual all : 'a list
end;;


Then we declare class 'a lo, whose constructor contains local declarations for n, which associates a unique number with each instance, and for l, which contains the list of pairs (number, instance) of still active instances.

# class ['a] lo =
let l = ref []
and n = ref 0 in
fun s ->
object(self:'b )
inherit ['a] om
val mutable num = 0
val name = s
method to_string () = s
method print () = print_string s
method print_all () =
List.iter (function (a,b) ->
Printf.printf "(%d,%s) " a (b#to_string())) !l
method destroy () = self#finalize();
l:= List.filter (function (a,b) -> a <> num) !l; ()
method all = List.map snd !l
initializer incr n; num <- !n; l:= (num, (self :> 'a om) ) :: !l ; ()
end;;
class ['a] lo :
string ->
object
constraint 'a = 'a om
val name : string
val mutable num : int
method all : 'a list
method destroy : unit -> unit
method finalize : unit -> unit
method print : unit -> unit
method print_all : unit -> unit
method to_string : unit -> string
end


At each creation of an instance of class lo, the initializer increments the reference n and adds the pair (number, self) to the list l. Methods print and print_all display respectively the receiving instance and all the instances containing in l.


# let m1 = new lo "start";;
val m1 : ('a om as 'a) lo = <obj>
# let m2 = new lo "between";;
val m2 : ('a om as 'a) lo = <obj>
# let m3 = new lo "end";;
val m3 : ('a om as 'a) lo = <obj>
# m2#print_all();;
(3,end) (2,between) (1,start) - : unit = ()
# m2#all;;
- : ('a om as 'a) list = [<obj>; <obj>; <obj>]


Method destroy removes an instance from the list of instances, and calls method finalize to perform a last action on this instance before it disappears from the list. Method all returns all the instances of a class created with new.

# m2#destroy();;
- : unit = ()
# m1#print_all();;
(3,end) (1,start) - : unit = ()
# m3#all;;
- : ('a om as 'a) list = [<obj>; <obj>]


We should note that instances of subclasses are also kept in this list. Nothing prevents you from using the same technique by specializing some of these subclasses. On the other hand, the instances obtained by a copy (Oo.copy or {< >}) are not tracked.






Previous Contents Next ocaml-book-1.0/en/html/book-ora200.html0000644000000000000000000002275607453055401014471 0ustar Other development tools Previous Contents Next

Other development tools

We have restricted ourselves up to now to the Objective CAML distribution. Nevertheless the community of developers using this language is active, as demonstrated by the number of messages on the caml-list@inria.fr mailing list. Numerous tools, libraries, and extensions are used to facilitate development. In the following we detail the use of tools for editing, syntax extension, interfacing with other languages and parallel programming. We mention as well the numerous graphical interface libraries. Most of these contributions can be found on the ``Caml Hump'' site:

Link


http://caml.inria.fr/hump.html


Editing tools

There are several modes recognizing Objective CAML syntax for the emacs editor. These modes are used to automatically indent text in the course of entering it, making it more readable. This is an alternative to the interaction window under Windows. Since emacs runs under Windows, the Objective CAML toplevel can be launched within one of its windows.

Syntax extension

The lexical and syntactic analysis tools provided by the distribution are already quite complete, but they don't support extending the syntax of the language itself. The camlp4 tool (see the link on page ??) is used in place and instead of Objective CAML's syntactic analyzer. The latter's compilers have only to proceed to typing and code generation. This tool allows the user to extend the syntax of the language, or to change to the original syntax. Moreover it offers pretty-printing facilities for the generated syntax trees. In this way it becomes easy to write a new toplevel for any Objective CAML syntax extension, or even another language implemented in Objective CAML.

Interoperability with other languages

Chapter 12 detailed how to interface the Objective CAML language with C. A multi-language application takes advantage of the features of each one, all while making different codes sharing a single memory space live in harmony. Nevertheless encapsulating C functions to make them callable from Objective CAML requires some tedious work. To simplify it, the camlIDL tool (see the link on page ??) supplies an interface generator and tools for importing COM (Windows) components into Objective CAML. The interfaces are generated from an IDL interface description file.

Graphical interfaces

The Graphics library allows the development of drawings and simple interactions, but it can't be considered a graphical interface worthy of the name. Chapter 13 has shown how this library could be extended to construct graphical components responding to some interactions. Using Graphics as a base allows us to preserve the portability of the interface between different platforms (X-Windows, Windows, MacOS), but limits its use to the low level of events and graphics contexts.

Several projects attempt to fill this gap, unfortunately none succeeds in being complete, portable, documented, and simple to use. Here is a list (extracted from the ``Caml Hump'') of the main projects:
  • OlibRt: under this sweet name, it is a veritable toolbox, constructed under X-Windows, but not documented. Its distribution contains complete examples and in particular numerous games.

    Link


    http://cristal.inria.fr/~ddr/
  • camlTk is a complete and well documented interface to the Tk toolkit. Its weak point is its dependency on particular versions of Tcl/Tk, which makes it difficult to install. It was used to build the web browser mmm [Rou96] written in Objective CAML.

    Link


    http://caml.inria.fr/~rouaix/camltk-readme.html
  • The Xlib library has been rewritten in Objective CAML. Efuns, a mini-clone of emacs, was developed using it. Xlib is not really a toolbox, and is not portable to graphical systems other than X-Windows.

  • mlGtk is an interface built on Gtk. It is in development and has no documentation. Its interest lies in being portable under Unix and Windows (because Gtk is) in a simpler fashion than Tk. Besides it uses Objective CAML's object-oriented layer---which doesn't happen without sometimes posing some problems.
  • LabTk is an interface to Tcl/Tk, for Unix, using extensions to Objective CAML which will be integrated into the next version (see appendix B). It includes its own Tcl/Tk distribution which installs easily.
Despite the efforts of the community, there is a real lack of tools for constructing portable interfaces. It may be hoped that LabTk becomes portable to different systems.

Parallel programming and distribution

Threads and sockets already offer basic mechanisms for concurrent and distributed programming. Interfacing with the C language allows the use of classic parallel programming libraries. The only thing missing is an interface with CORBA for invoking methods of remote objects. On the other hand, there are numerous libraries and language extensions which use different models of parallelism.

Libraries

The two main parallel programming libraries, MPI (Message Passing Interface) and PVM (Parallel Virtual Machine), are interfaced with Objective CAML. Documentation, links, and sources for these libraries can be found on the site

Link


http://www.netlib.org
The ``Caml Hump'' contains the various HTTP addresses from which the versions interfaced with Objective CAML can be downloaded.

Extensions

Numerous parallel extensions of Caml-Light or Objective CAML have been developed:
  • Caml-Flight ([FC95]) is a SPMD (Simple Program Multiple Data) extension of the Caml-Light language. A program executes a copy of itself on a fixed number of processes. Communications are explicit, there is only one communication operation get which can only be executed from within the synchronization operation sync.

    Link


    http://www.univ-orleans.fr/SCIENCES/LIFO/Members/ghains/caml-flight.html


  • BSML [BLH00] is an extension by BSP operations. The language preserves compositionality and allows precise predictions of performance if the number of processors is fixed.

    Link


    http://www.univ-orleans.fr/SCIENCES/LIFO/Members/loulergu/bsml.html


  • OCAMLP3 [DDLP98] is a parallel programming environment based on the skeleton model of the P3L language. The various predefined skeletons can overlap. Programs may be tested either in sequential mode or parallel mode, thus supporting reuse of Objective CAML's own tools.

    Link


    http://www.di.unipi.it/~susanna/projects.html


  • JoCAML [CL99] is based on the join-calculus model which supports high-level operations for concurrency, communication, and synchronization in the presence of distributed objects and mobile code, all while preserving automatic memory management.

    Link


    http://pauillac.inria.fr/jocaml/


  • Lucid Synchrone ([CP95]) is a language dedicated to the implemenation of reactive systems. It combines the functionality of Objective CAML and the features of data-flow synchronous languages.

    Link


    http://www-spi.lip6.fr/~pouzet/lucid-synchrone/

Previous Contents Next ocaml-book-1.0/en/html/book-ora069.gif0000644000000000000000000000474007452056112014277 0ustar GIF89a UUU99UU!, I8ͻ`(dyhltMǢ&(#[vK$RVj3;bU Tcos۷MB,fzo|laIq{&`is@pU*O)1!G(Ϡ˩uٻ_RzZټĸLFLڰbj׮"CW< VpX]I*Dm%A1GE#py$Dž*Bz9qє$1) =xtᅍSPr6ZlTiH솩DBˮFiъvMM Д"XVYawf+WU\BޭoEY5WϷ(,o;T'k|3316Z򼴧2,`VXz$s;7J[e\s.{Kwuv%}xn`7_<_>zzTosJ^ܕq٧t(R)dDIb(*y#vPFib`Ĕi ] f c" jv)bIgw_yy`w %tڈx R9) pyZ)iz:*sj*y*!Mzk}|&:,z;j>a K^m{lK#Yv>J.Rnھ+RH"/2;nu /Ky ;C=5a O,d1vQtE#F&SH+_\ o '`: 0u\VRY%6VfL"s똧g*4_gT>]mn!T[-ǾEYuQqfvl+(I'}e)YL 5W81yTCL IfU1J]^61NMZqLn\/Y:jj=IӆCMILZhQNZJ0UED04}=6mW0dc~!|3ti?P+r3nnc}v4h Y w;*z ͟ %Yp^|v."ÖwՌ|׼~嫃jaRxḞ;ԑ W0%!w.4\JLn<kЭ-b+^d!h.1[54 ljj;FakQRuyf@ #!&>ˑ$H"Ґ%!iL.rҤ* JI~2,%8)T2,)_ƯyRhd.)\eԥ/m GYr,& 3lfy3($%nz 8ljeR&9NqԦIvf'P~D<ς~@`І"4%jЇҗ E jdS 5FQjbTi?KϏft*'KC*͗R@™L$1DPu*3GiTdN ͜"M}j9gCxSj8UU`; XҏHQ^Vzk  QL*\)Bi7*ΜrժNu׃SDLR TASGZZFvg]{W֩uwz[>-9[Kۖ&C-h\R5ls}2wIU.dݱV'wwO"&eڍ7S}o^B赮|E׮M/;(E| oź!,P {-A7ApDa}XP%6T毂^2jcnvػ~ :&.Bd% ?vryQKQeI2Uf/_ʚ]-S}P1WkR5[Q7sW=lVU ,cv[bu:OU8E6Й3`7LIFl9ԿPJY2zsS5Ws-okP6jp{]]ة&lUf6iS[֖RmlsA6$-nc{Y>w*S)~-Bƻ޵ҭF{xo9V\W7x p7M~xa%>o5K{['p#md99S[7u^9vT.s2ʆ9Gkns]79SYYyyjoӧ9C=NLkZR5yaEz,MZFPtO1gCnv+-W"59IN_}mj4:S~'h3/]s^<=/z;ocaml-book-1.0/en/html/book-ora128.html0000644000000000000000000000534007453055400014467 0ustar Introduction Previous Contents Next

Introduction

Modular design and modular programming support the decomposition of a program into several software units, also called modules, which can be developed largely independently. A module can be compiled separately from the other modules comprising the program. Consequently, the developer of a program that uses a module does not need access to the source code of the module: the compiled code of the module is enough for building an executable program. However, the programmer must know the interface of the modules used, that is, which values, functions, types, exceptions, or even sub-modules are provided by the module, under which names, and with which types.

Explicitly writing down the interface of a module hides the details of its implementation from the programs that use this module. All these programs know about the module are the names and types of exported definitions; their exact implementations are not known. Thus, the maintainer of the module has considerable flexibility in evolving the module implementation: as long as the interface is unchanged and the semantics are preserved, users of the module will not notice the change in implementation. This can greatly facilitate the maintenance and evolution of large programs. Like local declarations, a module interface also supports hiding parts of the implementation that the module designer does not wish to publicize. An important application of this hiding mechanism is the implementation of abstract data types.

Finally, advanced module systems such as that of Objective CAML support the definition of parameterized modules, also called generics. These are modules that take other modules as parameters, thus increasing opportunities for code reuse.


Previous Contents Next ocaml-book-1.0/en/html/book-ora118.html0000644000000000000000000000541607453055400014472 0ustar Main program in C Previous Contents Next

Main program in C

Until now, the entry point of our programs was in Objective CAML; the program could then call C functions. Nothing prevents us from writing the entry point in C, and having the C code call Objective CAML functions when desired. To do this, the program must define the usual C main function. This function will then initialize the Objective CAML runtime system by calling the function caml_main(char **), which takes as an argument the array of command-line arguments that corresponds to the Sys.argv array in Objective CAML. Control is then passed to the Objective CAML code using callbacks (see page ??).

Linking Objective CAML code with C

The Objective CAML compiler can output C object files (with extension .o) instead of Objective CAML object files (with extension .cmo or .cmx). All we need to do is set the -output-obj compiler flag.
ocamlc -output-obj files.ml
ocamlopt -output-obj.cmxa files.ml
From the Objective CAML source files, an object file with default name camlprog.o is produced.

The final executable is obtained by linking, using the C compiler, and adding the library -lcamlrun if the Objective CAML code was compiled to bytecode, or the library -lasmrun if it was compiled to native code.
cc camlprog.o filesC.o -lcamlrun 
cc camlprog.o filesC.o -lasmrun
Calling Objective CAML functions from the C program is performed as described previously, via the callback functions. The only difference is that the initialization of the Objective CAML runtime system is performed via the function caml_startup instead of caml_main.






Previous Contents Next ocaml-book-1.0/en/html/book-ora013.gif0000644000000000000000000000152707452056110014262 0ustar GIF89akPPP!,kH0IXͻ`(di(@p,tmxpH,hl:2dF,mJ%`_xv/4y;ϳ|NE^ op2z{Ui|j.},y~ƿ˲ɶЫRը`ڞ؜ߔݯu U?9ǰÇ Gٵ3j|a%H$Et"?Bj F0~iגJ0 N6kaR%(O5?4e&w,zUhvZ:"X9{i١iRfu۪[5Ww]yuUjYXz"k-PKl \ń?+$޸v)82hgl4c-hgv=9ڥ|snϻ6`lq5?fbtՏ_.(f6=-?|~[|ñV%nH #dG܁%_Far 6zXٱ`%2Q5@bو!bfxa2x# x^M>G$CGy8$A!VNZ$]Fe}#e&i*& g.r静xfv&;ocaml-book-1.0/en/html/book-ora082.gif0000644000000000000000000000455007452056112014271 0ustar GIF89a>UUU999!,>H0I8ͻ`(`hlp tmJ/H lJΨ72lMr7 FҬn_nc~xW: J8郕8wɇfH(B $23;7w@ k1R6M$]AR56׎8TC189PC+yТ=h%`~ǓLe*E9C(HR52KLMʛ6gI JK'C gME"PRRH)>ԩfz1Vm/}ϘUvh憨NI`k~JůjZMq6c9-FԧNw#*ka*h븸nMD o梛 릻;i{^*pR(nGn[fh1\NVig5Q*8{2 Hu Ɠgr|mŦ%L7ӟ C-`}Ywj zW˚jf&1!GwlC}bzbu 8qiwɆߘsB͸s?'Œsrao/z6aNsgxkN{ðߎ;{v|nƟ{|0O|rsЛqk}3߃=VXEvү/ow/(~Vy8@9t  @r~0@ !}٠ZO(a809JRBr@+`l(@:A[<a81 &&0q#%BSj'?9qVH48%EE b"QEQtw3NġKV g@&W6Rڨ%#R1~<|*7QVT!w5*I=#G4yq$, `UfXd*%)IDQ2SdJ'f7Vƣ̒L&KV dlY ?oğ@?g?5i?C\^ȡ($j.:Ȣ5 Q~YPԤ 2ۇ~;iLSґԥ7i뒧R>t)(m TՔG jRS6өjTxBUMT27u5Σ_X?.Bsj֎UkGW ` {M,Ak 0v,\יF0PAh+3ʾUf(T\"wVJ!YR ǚgWvYQDqb ;E4Йb۠>W<(Dѝ5`v3\MF$NYbv%Q=2 %KT0EZKom9Yv`֔oLӗN^Fxۭp;/|YEZ]V'G+#Q=r`bFq*eNr.u_usDw[3*yXJ=eQjŚxJZ\_,Xv)z29c^[r|{;"yϛE+weЃ+t ,9EvЍF!8GC}taa\iyhf\minA.NV:vQiPcZoFȭkL:qBκkyN~tU=EZf況mi 9tVmP<'#6Lt;m9 ^vYNIn(mSϑ !9o+L ck}xɭ/ Index of language elements Contents Next

Index of language elements

  • & , 2
  • && , 2
  • !, 3
  • [<, B
  • [>, B
  • (), 2
  • **, 2
  • *., 2
  • *, 2, 2
  • +., 2
  • +, 2
  • -., 2
  • ->, 2
  • -, 2
  • /., 2
  • /, 2
  • ::, 2
  • :=, 3
  • :>, 15
  • :, 2, B
  • ;, 3
  • <-, 3, 3, 3
  • <=, 2
  • <>, 2
  • <, 2
  • ==, 2
  • =, 2
  • >=, 2
  • >}, 15
  • >, 2
  • ?, B
  • @, 2
  • [], 2
  • #, 15, 15
  • %, 8
  • ^, 2
  • _, 2
  • {<, 15
  • `, B
  • ||, 2
  •  , B
  • Arg (module), 8
  • Arith_status (module), 8
  • Array (module), 3, 8, 8, 8
  • accept, 20
  • acos, 2
  • add_available_units, 8
  • add_interfaces, 8
  • alarm, 18
  • alloc.h, 12
  • allow_unsafe_modules, 8
  • and (mot-cl), 2, 2
  • append, 8, 8
  • argv, 8
  • array (type), 3
  • as (mot-cl), 2, 15, A
  • asin, 2
  • assoc, 6, 8
  • assq, 8
  • atan, 2
  • Buffer (module), 8
  • background, 5
  • big_int (type du module Num), 8
  • bind, 20, 20
  • blit, 8
  • blit_image, 5
  • bool (type), 2
  • bprintf, 8
  • broadcast, 19
  • button_down, 5
  • Callback (module), 12
  • Condition (module), 19
  • catch, 8
  • ceil, 2
  • char (type), 2
  • char_of_int, 2
  • chdir, 8
  • check, 9
  • class (mot-cl), 15
  • clear_available_units, 8
  • clear_graph, 5
  • close, 18, 20, 20
  • close_graph, 5
  • close_in, 3
  • close_out, 3
  • close_process, 18
  • color (type), 5
  • combine, 6, 8
  • command, 8
  • compact, 9
  • concat, 8, 8
  • connect, 20, 20
  • constraint (mot-cl), 15
  • copy, 8, 15
  • cos, 2
  • create, 3, 8, 9, 19, 19, 19
  • create_image, 5
  • create_process, 18
  • current_point, 5
  • Delayed, 4
  • Digest (module), 8, 8
  • Dynlink (module), 8
  • delay, 19
  • descr_of_in_channel, 18
  • descr_of_out_channel, 18
  • do (mot-cl), 3
  • done (mot-cl), 3
  • downto (mot-cl), 3
  • draw_arc, 5
  • draw_circle, 5
  • draw_ellipse, 5
  • draw_image, 5
  • dump_image, 5
  • dup, 18
  • dup2, 18
  • End_of_file, 3
  • Event (module), 19
  • else (mot-cl), 2
  • end (mot-cl), 14, 15
  • eprintf, 8
  • error, 8
  • error (type du module Unix), 18
  • error_message, 18
  • establish_server, 20
  • event, 5
  • exception (mot-cl), 2
  • exists, 2, 8
  • exit, 19
  • exn (type du module Pervasives), 2
  • exp, 2
  • external (mot-cl), 12
  • Filename (module), 8
  • Format (module), 8
  • failwith, 2
  • false, 2
  • file, 8
  • file_exists, 8
  • fill, 8
  • fill_poly, 5
  • fill_rect, 5
  • filter, 8
  • find, 8
  • find_all, 8
  • flatten, 8
  • float (type), 2
  • float_of_string, 2
  • floor, 2
  • fold_left, 2, 8, 8
  • fold_right, 8, 8
  • for (mot-cl), 3
  • for_all, 2, 8
  • force, 4
  • foreground, 5
  • format (type), 8, 8
  • fprintf, 8
  • from_channel, 8
  • from_string, 8
  • fst, 2
  • full_major, 9
  • fun (mot-cl), 2
  • function (mot-cl), 2
  • functor (mot-cl), 14
  • Gc (module), 9
  • Genlex (module), 11
  • Graphics (module), 5
  • get, 8, 9, 9
  • get_image, 5
  • getcwd, 8
  • getenv, 8
  • gethostbyaddr, 20
  • gethostbyname, 20
  • gethostname, 20
  • getservbyname, 20
  • getservbyport, 20
  • global_replace, 11
  • Hashtbl (module), 8, 8
  • handle_error, 18
  • hd, 2, 8
  • host_entry (type du module Unix), 20
  • if (mot-cl), 2
  • ignore, 3
  • image, 5
  • in (mot-cl), 2
  • in_channel, 3
  • in_channel_of_descr, 18
  • inet_addr (type du module Unix), 20
  • inet_addr_of_string, 20
  • init, 6, 8
  • initializer (mot-cl), 15
  • input, 3
  • input_line, 3
  • int, 6
  • int (type), 2
  • int_of_char, 2
  • int_of_string, 2
  • interactive, 8
  • iter, 8, 8
  • iter2, 8
  • iteri, 8
  • key_pressed, 5
  • kill, 18, 19
  • Lazy (module), 4
  • Lexing (module), 11
  • List (module), 2, 8, 8, 8
  • labltk (commande), B
  • lazy (mot-cl), 4
  • length, 8, 8
  • let (mot-cl), 2, 2
  • lexbuf (type du module Lexing), 11
  • lineto, 5
  • list (type), 2
  • listen, 20, 20
  • loadfile, 8
  • loadfile_private, 8
  • lock, 19
  • log, 2
  • log10, 2
  • lseek, 18
  • Map (module), 14
  • Marshal (module), 8, 8
  • Match_Failure, 2
  • Mutex (module), 19
  • major, 9
  • make, 8
  • make_image, 5
  • make_lexer, 11
  • make_matrix, 8
  • map, 2, 2, 8, 8
  • map2, 8
  • mapi, 8
  • match (mot-cl), 2, 4
  • matched_string, 11
  • max_array_length, 8, 8
  • mem, 2, 8
  • mem_assoc, 8
  • mem_assq, 8
  • memory.h, 12
  • memq, 2, 8
  • method (mot-cl), 15
  • minor, 9
  • mkfifo, 18
  • mlvalues.h, 12
  • mod, 2
  • module (mot-cl), 14
  • module type (mot-cl), 14
  • mouse_pos, 5
  • moveto, 5
  • mutable (mot-cl), 3
  • None, 9
  • Num (module), 8
  • new (mot-cl), 15
  • next, 4
  • not, 2, 2
  • nth, 8
  • num (type du module Num), 8
  • OS_type, 8
  • object (mot-cl), 15
  • ocaml (commande), 7, 7
  • ocamlbrowser (commande), B
  • ocamlc (commande), 7, 7, 7
  • ocamlc.opt (commande), 7
  • ocamldebug (commande), 10
  • ocamldep (commande), 10
  • ocamllex (commande), 11, 11
  • ocamlmktop (commande), 5, 7, 7
  • ocamlopt (commande), 7
  • ocamlopt.opt (commande), 7
  • ocamlrun (commande), 7, 7
  • ocamlyacc (commande), 11, 11
  • of (mot-cl), 2
  • of_channel, 4
  • of_list, 6, 8
  • of_string, 4
  • open (mot-cl), 8, 14
  • open_connection, 20
  • open_flag (type du module Unix), 18
  • open_graph, 5
  • open_in, 3
  • open_out, 3
  • open_process, 18
  • openfile, 18
  • option (type du module Pervasives), 9
  • or, 2
  • out_channel, 3
  • out_channel_of_descr, 18
  • output, 3
  • Pervasives (module), 8
  • Printexc (module), 8
  • Printf (module), 8
  • parse, 8
  • parser (mot-cl), 4, 11
  • partition, 8
  • pipe, 18
  • plot, 5
  • point_color, 5
  • print, 8
  • print_newline, 3
  • print_stat, 9
  • print_string, 3
  • printf, 8
  • private (mot-cl), 15
  • process_status (type du module Unix), 18
  • Queue (module), 8
  • Random (module), 8
  • raise (mot-cl), 2
  • ratio (type du module Num), 8
  • read, 18
  • read_key, 5
  • read_line, 3
  • rec (mot-cl), 2
  • receive, 19
  • -rectypes, A
  • ref (type), 3
  • regexp, 11
  • register, 12
  • remove, 8
  • remove_assoc, 8
  • remove_assq, 8
  • rename, 8
  • rev, 8
  • rev_append, 8
  • rgb (type), 5
  • Set (module), 14
  • SOCK_STREAM, 20
  • Some, 9
  • Sort (module), 8
  • Stack (module), 8, 14
  • Stack_overflow (exception), 4
  • Str (module), 11
  • Stream (module), 4
  • String (module), 8
  • Sys (module), 8
  • Sys_error, 3
  • search_forward, 11
  • seek_command (type du module Unix), 18
  • send, 19
  • service_entry (type du module Unix), 20
  • set, 8, 9, 9
  • set_binary_mode_in, 18
  • set_binary_mode_out, 18
  • set_color, 5
  • set_font, 5
  • set_line, 5
  • set_signal, 18
  • set_text_size, 5
  • shutdown_connection, 20
  • sig (mot-cl), 14
  • sigalrm, 18
  • sigchld, 18
  • sigint, 18
  • signal, 18, 19
  • signal_behavior (type du module Unix), 18
  • sigusr1, 18
  • sigusr2, 18
  • sin, 2
  • sleep, 18
  • snd, 2
  • sockaddr (type du module Unix), 20
  • socket, 20
  • socket (type du module Unix), 20
  • socket_domain (type du module Unix), 20
  • socket_type (type du module Unix), 20
  • split, 8
  • sprintf, 8
  • sqrt, 2
  • stat, 9
  • status, 5
  • stderr, 3, 18
  • stdin, 3, 18
  • stdout, 3, 18
  • stream (type), 4
  • string, 8
  • string (type), 2
  • string_of_float, 2
  • string_of_inet_addr, 20
  • string_of_int, 2
  • struct (mot-cl), 14
  • sub, 8
  • sync, 19
  • Thread (module), 19
  • ThreadUnix (module), 20
  • tan, 2
  • then (mot-cl), 2
  • time, 6, 8
  • tl, 2, 8
  • to (mot-cl), 3
  • to_buffer, 8
  • to_channel, 8
  • to_list, 8
  • to_string, 8, 8
  • token (type du module Genlex), 11
  • #trace (directive), 10
  • true, 2
  • try (mot-cl), 2
  • try_lock, 19
  • type (mot-cl), 2
  • Unix (module), 18
  • Unix_error, 18
  • unit (type), 2
  • unlock, 19
  • #untrace (directive), 10
  • #untrace_all (directive), 10
  • Value, 4
  • val (mot-cl), 14, 15
  • val mutable (mot-cl), 15
  • value, 12, 12
  • virtual (mot-cl), 15
  • Weak (module), 8, 9
  • wait, 18, 19
  • wait_next_event, 5
  • wait_time_read, 20
  • wait_time_write, 20
  • waitpid, 18
  • when (mot-cl), 2
  • while (mot-cl), 3
  • with (mot-cl), 2, 2, 2, 4, 14
  • word_size, 8
  • write, 18

Contents Next ocaml-book-1.0/en/html/book-ora004.html0000644000000000000000000001350007453055377014472 0ustar Outline of the book Previous Contents Next

Outline of the book

The present work consists of four main parts, bracketed by two chapters and enhanced by two appendices, a bibliography, an index of language elements and an index of programming concepts.

Chapter 1 :
This chapter describes how to install version 2.04 of the Objective CAML language on the most current systems (Windows, Unix and MacOS).

Part I: Core of the language
The first part is a complete presentation of the basic elements of the Objective CAML language. Chapter 2 is a dive into the functional core of the language. Chapter 3 is a continuation of the previous one and describes the imperative part of the language. Chapter 4 compares the ``pure'' functional and imperative styles, then presents their joint use. Chapter 5 presents the graphics library. Chapter 6 exhibits three applications: management of a simple database, a mini-Basic interpreter and a well-known single-player game, minesweeper.
Part II: Development tools
The second part of the book describes the various tools for application development. Chapter 7 compares the various compilation modes, which are the interactive toplevel and command-line bytecode and native code compilers. Chapter 8 presents the principal libraries provided with the language distribution. Chapter 9 explains garbage collection mechanisms and details the one used by Objective CAML. Chapter 10 explains the use of tools for debugging and profiling programs. Chapter 11 addresses lexical and syntactic tools. Chapter 12 shows how to interface Objective CAML programs with C. Chapter 13 constructs a library and an application. This library offers tools for the construction of GUIs. The application is a search for least-cost paths within a graph, whose GUI uses the preceding library.
Part III: Organization of applications
The third part describes the two ways of organizing a program: with modules, and with objects. Chapter 14 is a presentation of simple and parameterized language modules. Chapter 15 introduces Objective CAML object-oriented extension. Chapter 16 compares these two types of organization and indicates the usefulness of mixing them to increase the extensibility of programs. Chapter 17 describes two substantial applications: two-player games which put to work several parameterized modules used for two different games, and a simulation of a robot world demonstrating interobject communication.
Part IV: Concurrence and distribution
The fourth part introduces concurrent and distributed programs while detailing communication between processes, lightweight or not, and on the Internet. Chapter 18 demonstrates the direct link between the language and the system libraries, in particular the notions of process and communication. Chapter 19 leads to the lack of determinism of concurrent programming while presenting Objective CAML's threads. Chapter 20 discusses interprocess communication via sockets in the distributed memory model. Chapter 21 presents first of all a toolbox for client-server applications. It is subsequently used to extend the robots of the previous part to the client-server model. Finally, we adapt some of the programs already encountered in the form of an HTTP server.
Chapter 22
This last chapter takes stock of application development in Objective CAML and presents the best-known applications of the ML language family.
Appendices
The first appendix explains the notion of cyclic types used in the typing of objects. The second appendix describes the language changes present in the new version 3.00. These have been integrated in all following versions of Objective CAML (3.xx).
Each chapter consists of a general presentation of the subject being introduced, a chapter outline, the various sections thereof, statements of exercises to carry out, a summary, and a final section entitled ``To learn more'' which indicates bibliographic references for the subject which has been introduced.








Previous Contents Next ocaml-book-1.0/en/html/book-ora060.html0000644000000000000000000000130107453055401014454 0ustar Notes
1
which means ``Beginner's All purpose Symbolic Instruction Code''.
ocaml-book-1.0/en/html/book-ora024.gif0000644000000000000000000003161107452056111014262 0ustar GIF89abej欥sqjbaYb]Y($ }y{}{{y{{u{Ž{q{sussqsݛjejjajbebbabb]bbYbY]YYYYYUY̽RURA;A9;9{979}{939y{131{ys{us{qsYabjeb  jabźŽsljshjՃՔՊJHA½JDAJ@AslsshsjljjhjRUYݬRPRRLRJLJJHJJDJYURADAA@A1011,1(,((((($( $ jhb{}{yŽsu{sq{RLJ!,' H*\ȰÇ#JHŋ3jȱǏ =h#M(7IK-_dHM96k&IK@S07<2aFSΡC{VtWG} +X1:Eҩ]*ד'(xeT)[*UreL:ФO%9sdXX_.9  ("LA(p`@`p![5ќYR&! xx>=p!̝^"||Pc 0Bh`@] #Qk":ckcPIU+g A)- cLhě $D"#! G '"EĊq %;LNoooA;w@o6l3A4д:\z \Ek$:lEdAq"P­߆ˁm`m#0 jAF0.0T.h@9g :s`Dβ`˾83aNxA&`<|lc`cgco/?|>\~T˯<Dx/~:{p`9>h1Ԁ0q W|\&p4 O@x4a +`L" f0 dy@Tر3!3O%BJJ$CHj >q3&``P*,0$%/ ˢFUJqiT\TOJzԔ|UEuAdeRƚk8nPJ8:ajX&%@$! h`JbXi8 Up&JXyDh0= G`%t@y'S (G-K`t=!Obz@3CHGj.jT!euX!ae0DQ:̢c}%+[ۺ"eUvWe P#Lk8eS ]$> 72nv @% V@% [4ǂ+ZQ(@W;LX,!e@%߲.`IIZi -ёR`)1Qp-#@^rȃZV?,`U tؚhCh4O Ӡ4JQ+ս6+0O/X}X]"W[`3#ƀQbF'&qJ 4:+ n j@I€bl0Q& XlC8D$Oԍb #QŅ)Fb*P>T#!@ +w|3PҜl>J]R`+9[A'{//:i+5|S& X[t*Љ@ +jb/P nuV5p+J *D'!  CD`0i"'B.0Ē#ߘ˂@ r2җVNo˗?,]9~Z-$`ӡzWӤ'S&pNWqk\C}_UFtlQl U5U3,6%0 ;b/LBCkT3\XV 0-P8Pp&Pve*`f U" PUjt&oDD.0@rs@s9gV)gsj+OƂpsGhs `WVtbre^DVbE,Csc+& nl4V (P]-!, Xg(~wXi?x*` 1C&b:K|"nK  ` ]@ V{K|G#֤ kH}+g} ҋkʢ,(4866/!;p81xh+ӷw}-*.} q$bAd~S ĉ03 IBZ}g@Bz-BJL۴NPD:಩QZ\۵^ ) P`Hhjl)+[Gm[v{x)kpV{y;@D˱t[۸o˲ X{D22 6˳{fC[y ?뺬۸)`W EIqT˻k|@+ɛk˼ [k0Y{+ͻ߫k+P+Cw勶;۽KY[ ut+ͻK)k 1p ;[{z 4)̴{۷]l˼ \;Ja{>\F<@:tf|P ) 407X) kNY囲5 aW,j);k<DZKu4[prL|3<,}\Ȏ YIP_Ȑɒ<ɔ\ɒ)K J< C@ D@(<ʤ\ʦ|ʨʪʬʮʰ˲<˦[IyqO\L<\|*`˘A [Ƞ 'O`= `Im> =+k o ,לÞm-ƆlJMZ í-ӢГ`6}K x= ڵùMCνѝ]M 䝺yLqޛ޶x^}$1Q]kqsnnεЮx+ɳg(~!5 ~&PSL{/KMnz}xp\oG<[Jóm>^(^)=`p?'Q= 5ߊ}SMF8&ј9_EaMF> B2?!rWol(x E>k_m^q g]v?~ "DOѐ@y#\/s羭"n*Cܢ?mӰ?_Iڽٷ?Ŀoߊ,厌؟  ^F@ DPB >QD-6Tb&McE=.DRJ-]^̸qG U<4RN=}JGRnTRM#2sR͎H^ŚUk˘"=N"Ve͞5(T$[TEW\BNr.]}Zz7ID{FݯCXd1Zpdʝ=oY#UfN F:i`Ҧ9e_>-[G2 kō3+*r]`L=i&.]{w}BԢ\tJ!u3I85@=(LKTR͈RTOeDSPW uPXG(0 3Mnا`-XUd MgKhF]7(?z2JjdpŕXg-wYhvuMoKMŝDm ]e_uw޴^ၑ 8/\u6`uU2McrfRXb_W:5dxmdOqTy>K]6^89hqWX脡ŜꅩaM{mվk&hFQfk[a~&~_(ʞOj^n[b{;rvW'&k'9_[u5a?Y'1*>PJ<%~/brY> qWoze={O3wuң<|䙧oѧ$%m0|"8^cmjO|+_=:߳سIl03yyc( q8kc4p<̡ C%>2Z\!(L Ր ,760X-n"r1yatc1~Q(!H~#> r!i$*[w`"2Ғ$R#X%M4#3N-o9`TJVҕ+E:NZ@Dq*I]p"0!KTUƤ2+BoJJjCe ʚլ7ypɂaæE/?Sa8EQHLU<+2OԳf]ܡ¦q*2]xFr "VB;ne2M3h<5*z M4iB@WÎuܝl Ú4Z(HC&bT( ӝ6=iB&QМ"dz*VӅ5}V5QE_VU/F#?&!4 к5xAjXUq=OʘBl=[ 1c߈O ~*^ᥐςͨKYn̙RE$z{cmbS٥VYi%V .gy^Hs4E}j=u ][˧Y(өԀ5"Lu mY'ιkS:ɵ-LX[t5|QbX]mc"_0 s{KTDz Ƌ)!IIm]JcSGc Dd emʓ̲,_#K3~U\9s*9ǩgfj{2|Q=N*`\Qnԟ-iHzm>9˃.wjЁ[l(d_;3p[~_:wvnA`"]wܞOgyǞژ=lx>/l[GXut'';ףt:Mz[D>7!?fk@9I;ZK-\iݙCЋłE8:+=C5n ȳ+)Ʈq6\D oà t1|Fu:Jy\Eٗ[AcƃhyǗ?fght)`EZ;[>u+q̡MdNd!2ÁԯłF]^LT*([eClHC=H38>DHtC>LłC4blBI¥DF =GkL@Ȭ,C@ aI  L %*;1KTdM<ӁśċDlJ,Lfgi|?ڬMKl4&#T? FSE,DFSuG[3?=8|s+%NEҝb4-VcVSaEUbU:i,T kI"OHm%,guAlEM EVVoUm0o]\Wde}HKB݂\OHWM}T|WtIqLm Ph'l~UXpXkdL,T3@؏Vuא׆Xu) M V`P. l͠ڡ%Z90xX3U}mٗ٨ڌĕWނ%̪ZYYlÃP+]رuڧ؇m> m 0q[ڷ۾:.0beQIP0 ^/b/F%2̍ݾ]v1_]*+>bt c2&Qc$4?edL52YnKv'd=@ۂ=3RZU^dN^国4WXcJ`fطh.d\Rֺe^% =W5TfA_GV^=[kI3jf{g9Nd|}cbyUz~cBխyuhnhehQRXi6&g?hBD7mMGb]Ng\etVSʸ )jɠSRSghmn鎌 LjC$^nFIv O4֣:V>G^j6eGfձ̈gb~^=k N";a:iiܷԈ_DV$XFkU xl)s-jdS%UV霾ֆ쉔0iY0qU)asvo KhJk7OjPkfge&j•޻֭Y8/Tn _ok.3iAMjo]q3 gwngq\.  r.#nmw  $e6[lN/`h%JXoFusrkUp.q!駶oy7pk4|yBg lg|gyǏ_h`N|zoKB7DEsH/wi7-B{/vA6~h*V^og0zty} |eF7{^~n~_uo5,hp &\p`Ç Q 0i-P| ES$Ȳ%D eYfL_ْE'iqRH0RNNSUR!ЯV1qEḼrznܻ2(*KK}J0bx .flWq]{j 'k }lK4A+z-=2ቆM'\iZ%kd,soݑi3o9rAWk,QRoQ;Ǔ/o<׳o}+_ gvI^k7Iv jKMR9&u깧g)_Q`TE(Dy(2ꩢ雟Vvp!.Y蜜j*뢡ACIQ:@)ZIy%앥rFi*j kF:Nj-B κUǥMqb~+D4$Y-.[|q4^WÍ p1:R6~+"D&K\q:M.SG )i97rꬌz {3,uTF=1źi<Q׵c4:mT)̓d08?dw@P['=xx^6ԘW^PYeI|~:ꩫgdENUt;;; ?<<^۬,CG5$2U>??Z׿??(pi}n 2phi9 vsR s! C(  H@ .|aҖ6!30(JPs(!, H%2IPV"-r^"(1fg>',@#F4"A:X):+E)Hs:R4&-Gԏ>ᖣ#h|zR4#DbQuG!-*P[Q(5+Pԣ#)[Vt3}+KI RJQ? U|+^9Rv **{׷4Z՚SX$hC+ђ;<-jSղ}';ocaml-book-1.0/en/html/book-ora167.html0000644000000000000000000010417407453055400014477 0ustar Processes Previous Contents Next

Processes

Unix associates a process with each execution of a program. In [CDM98] Card, Dumas and Mvel describe the difference between a program and a process: ``a program itself is not a process: a program is a passive entity (an executable file on a disc), while a process is an active entity with a counter specifying the next instruction to execute and a set of associated resources.''

Unix is a multi-task operating system: many processes may be executed at the same time. It is preemptive, which means that the execution of processes is entrusted to a particular process. A process is therefore not totally master of its resources. Especially a process can not determine the time of its execution. A process has to be created.

Each process has his own private memory space. Processes can communicate via files or communication channels. Thus the distributed memory model of parallelism is simulated on a single machine.

The system gives each process a unique identifier: the PID (Process IDentifier). Under Unix each process, except the initial process, is created by another process, which is called its parent.

The set of all active processes can be listed by the Unix command ps3:
$ ps -f
PID    PPID    CMD
1767   1763   csh
2797   1767   ps -f
The use of the option -f adds for each active process its identifier (PID), that of its parent (PPID) and the name of the started program (CMD). Here we have two processes, the command line interpreter csh and the command ps itself. It can be seen that ps has been started from the command line interpreter csh. The parent of its process is the process associated with the execution of csh.

Executing a Program

Execution Context

Three values are associated with an executing program, which is started from the command line:
  1. The command line used to start it. It is contained in the value Sys.argv.
  2. The environment variables of the command line interpreter. These can be accessed by the command Sys.getenv.
  3. An execution status until the program is terminated.
Command line.
The command line allows you to read arguments or options of a program call. The behavior of the program may depend from these values. Here is a small example. We write the following program into the file argv_ex.ml:


if Array.length Sys.argv = 1 then
Printf.printf "Hello world\n"
else if Array.length Sys.argv = 2 then
Printf.printf "Hello %s\n" Sys.argv.(1)
else Printf.printf "%s : too many arguments\n" Sys.argv.(0)


We compile it:
$ ocamlc -o argv_ex argv_ex.ml
And we execute it:
$ argv_ex
Hello world
$ argv_ex reader
Hello reader
$ argv_ex dear reader
./argv_ex : too many arguments
Environment variables.
Environment variables may contain values necessary for execution. The number and the names of these variables depend on the operating system and on the user configuration. The values of these variables can be accessed by the function getenv, which takes as argument the name of a variable in form of a character string:

# Sys.getenv "HOSTNAME";;
- : string = "zinc.pps.jussieu.fr"


Execution Status

The return value of a program is generally a fixed integer, indicating if the program did terminate with an error or not. The exact values may differ from one operating system to another. The programer can always explicitly stop his program and return the execution status value with the function call:

# Pervasives.exit ;;
- : int -> 'a = <fun>


Process Creation

A program is started by another process, which is called the current process. The executed program becomes a new process. There are three different relations between the two processes:
  • The two processes are independent from each other and can be executed concurrently.
  • The parent process is waiting for the child process to terminate.
  • The created process replaces the parent process, which terminates.
It is also possible to duplicate the current process to obtain two instances. The two instances of the process do not differ but in their PID. This is the famous fork which we will describe later.

Independent Processes

The Unix module offers a portable function to create a process.

# Unix.create_process ;;
- : string ->
string array ->
Unix.file_descr -> Unix.file_descr -> Unix.file_descr -> int
= <fun>
The first argument is the name of the program (it may be a path). The second is the array of arguments for the program. The last three arguments are the descriptors indicating the standard input, standard output and standard error output of the process. The return value is the PID of the created process.

There also exists a variant of this function which allows you to indicate the values of environment variables:

# Unix.create_process_env ;;
- : string ->
string array ->
string array ->
Unix.file_descr -> Unix.file_descr -> Unix.file_descr -> int
= <fun>
These two functions can be used under Unix and Windows.

GGH

Process Stacks

It is not always useful for a created process to be of concurrent nature. The parent process may have to wait for the created process to terminate. The two following functions take as argument the name of a command and execute it.

# Sys.command;;
- : string -> int = <fun>
# Unix.system;;
- : string -> Unix.process_status = <fun>
They differ in the type of the return code. The type process_status is explained in more detail on page ??. During the execution of the command the parent process is blocked.

Replacement of Current Processes

The replacement of current processes by freshly created processes allows you to limit the number of concurrently executed processes. The four following functions allow this:

# Unix.execv ;;
- : string -> string array -> unit = <fun>
# Unix.execve ;;
- : string -> string array -> string array -> unit = <fun>
# Unix.execvp ;;
- : string -> string array -> unit = <fun>
# Unix.execvpe ;;
- : string -> string array -> string array -> unit = <fun>
Their first argument is the name of the program. Using execvp or execvpe, this name may indicate a path in the file system. The second argument contains the program arguments. The last argument of the functions execve and execvpe additionally allows you to indicate the values of system variables.

Creation of Processes by Duplication

The original system call to create processes under Unix is:

# Unix.fork ;;
- : unit -> int = <fun>


The function fork starts a new process, not a new program. Its effect is to duplicate the calling process. The code of the new process is the same as that of its parent. Under Unix the same code can be shared by several processes, each process possessing its own execution context. Therefore we speak about reentrant code.

Let's look at the following small program (we use the function getpid which returns the PID of the process associated with the execution):
Printf.printf "before fork : %d\n" (Unix.getpid ())  ;;
flush stdout ;;
Unix.fork () ;;
Printf.printf "after fork : %d\n" (Unix.getpid ()) ;;
flush stdout ;;


We obtain the following output:
before fork : 10529
after fork : 10529
after fork : 10530


After the execution of fork, two processes execute the code. This leads to the output of two PID's ``after'' the fork. We note that one process has kept the PID of the beginning (the parent). The other one has a new PID (the child), which corresponds to the return value of the fork call. For the parent process the return value of fork is the PID of the child, while for the child, it is 0.

It is this difference in the return value of fork which allows in one program source to decide which code shall be executed by the child and which by the parent:
Printf.printf "before fork : %d\n" (Unix.getpid ())  ;;
flush stdout ;;
let pid = Unix.fork () ;;
if pid=0 then (* -- Code of the child *)
Printf.printf "I am the child: %d\n" (Unix.getpid ())
else (* -- Code of the father *)
Printf.printf "I am the father: %d of child: %d\n" (Unix.getpid ()) pid ;;
flush stdout ;;


Here is the trace of the execution of this program:
before fork : 10539
I am the father: 10539 of child: 10540
I am the child: 10540


It is also possible to use the return value for matching:
match Unix.fork () with
0 -> Printf.printf "I am the child: %d\n" (Unix.getpid ())
| pid -> Printf.printf "I am the father: %d of child: %d\n"
(Unix.getpid ()) pid ;;


The fertility of a process may be very big. Therefore the number of descendents of a process is limited by the configuration of the operating system. The following example creates two generations of processes with grandparent, parents, uncles and cousins.
let pid0 = Unix.getpid ();;
let print_generation1 pid ppid =
Printf.printf "I am %d, son of %d\n" pid ppid;
flush stdout ;;

let print_generation2 pid ppid pppid =
Printf.printf "I am %d, son of %d, grandson of %d\n"
pid ppid pppid;
flush stdout ;;

match Unix.fork() with
0 -> let pid01 = Unix.getpid ()
in ( match Unix.fork() with
0 -> print_generation2 (Unix.getpid ()) pid01 pid0
| _ -> print_generation1 pid01 pid0)
| _ -> match Unix.fork () with
0 -> ( let pid02 = Unix.getpid ()
in match Unix.fork() with
0 -> print_generation2 (Unix.getpid ()) pid02 pid0
| _ -> print_generation1 pid02 pid0 )
| _ -> Printf.printf "I am %d, father and grandfather\n" pid0 ;;



We obtain:
I am 10644, father and grandfather
I am 10645, son of 10644
I am 10648, son of 10645, grandson of 10644
I am 10646, son of 10644
I am 10651, son of 10646, grandson of 10644


Order and Moment of Execution

A sequence of process creations without synchronization may lead to surprising effects. This is illustrated by the following poem writing program la M. Jourdain4:
match Unix.fork () with
0 -> Printf.printf "fair Marquise " ; flush stdout
| _ -> match Unix.fork () with
0 -> Printf.printf "your beautiful eyes " ; flush stdout
| _ -> match Unix.fork () with
0 -> Printf.printf "make me die " ; flush stdout
| _ -> Printf.printf "of love\n" ; flush stdout ;;


It may produce the following result:
of love
fair Marquise your beautiful eyes make me die


We usually want our program to be able to assure the order of execution of its processes. More generally speaking, an application which makes use of several processes may have to synchronize them. Depending on the model of parallelism in use, the synchronization is realized by communication between the processes or by waiting conditions. This subject is presented more profoundly by the two following chapters. For the moment, we can improve our poem writing program in two ways:

  • Give the child the time to write its phrase before writing the own.
  • Wait for the termination of the child, which will then have written its phrase, before writing our own phrase.
Delays.
A process can suspend its activity by calling the function:

# Unix.sleep ;;
- : int -> unit = <fun>
The argument provides the number of seconds during which the process wants to suspend its activities.

Using this function, we write:
match Unix.fork () with
0 -> Printf.printf "fair Marquise " ; flush stdout
| _ -> Unix.sleep 1 ;
match Unix.fork () with
0 -> Printf.printf"your beautiful eyes "; flush stdout
| _ -> Unix.sleep 1 ;
match Unix.fork () with
0 -> Printf.printf"make me die "; flush stdout
| _ -> Unix.sleep 1 ; Printf.printf "of love\n" ; flush stdout ;;


And we can obtain:
fair Marquise your beautiful eyes make me die of love


Nevertheless, this method is not sure. In theory, it would be possible that the system gives enough time to one of the processes to sleep and to write its output at the same turn. Therefore we prefer the following method for assuring the execution order of our processes.

GGH

Waiting for the termination of the child.
A parent process may wait for his child to terminate through a call to the function:

# Unix.wait ;;
- : unit -> int * Unix.process_status = <fun>


The execution of the parent is suspended until one of its children terminates. If wait is called by a process not having any children, a Unix_error is thrown. We will discuss later the return value of wait. For the moment, we will just use the command to pronounce our poem:
match Unix.fork () with
0 -> Printf.printf "fair Marquise " ; flush stdout
| _ -> ignore (Unix.wait ()) ;
match Unix.fork () with
0 -> Printf.printf "your beautiful eyes " ; flush stdout
| _ -> ignore (Unix.wait ()) ;
match Unix.fork () with
0 -> Printf.printf "make me die " ; flush stdout
| _ -> ignore (Unix.wait ()) ;
Printf.printf "of love\n" ;
flush stdout


Indeed, we obtain:
fair Marquise your beautiful eyes make me die of love


Warning


fork is proprietary to the Unix system


Descendence, Death and Funerals of Processes

The function wait is useful not only to wait for the termination of a child. It also has the responsibility to complete the death of the child process.

Whenever a process is created, the system adds an entry in a table. The table serves to keep track of all processes. When a process terminates, the entry does not disappear automatically in the table. It is the responsibility of the parent to assure the deletion by the call of wait. If this is not done, the child process keeps an entry in the table. This is called a zombie process.

When the system is started, a first process called init is started. After the initialization of some parameters, the essential role of this ``forefather'' is to take care of orphan processes and to call the wait which deletes them from the process table after their termination.

Waiting for the Termination of a Given Process

There is a variation of the function wait, named waitpid. This command is supported on Unix and Windows:

# Unix.waitpid ;;
- : Unix.wait_flag list -> int -> int * Unix.process_status = <fun>
The first argument specifies the waiting modalities. The second indicates which process or which group of processes are treated.

After the termination of a process, two pieces of information can be accessed by its parent as a result of the function calls wait or waitpid: the number of the terminated process and its exit status. The status is represented by a value of type Unix.process_status. This type has three constructors. Each of them takes an integer as argument.
  • WEXITED n: the process has terminated normally with the return code n.
  • WSIGNALED n: the process has been killed by the signal n.
  • WSTOPPED n: the process has been stopped by the signal n.
The last value only makes sense for the function waitpid which can listen for such signals as indicated by its first argument. We will discuss signals and their treatment at page ??.

Managing of Waiting by Ancestors

In order to avoid having to care for the termination of child processes oneself, it is possible to delegate this responsibility to an ancestor process. ``Double fork'' allows a process not to take care of the funerals of all its child processes, but to delegate this responsibility to the init process. Here is the principle: a process P0 creates a process P1, which in turn creates a third process P2. Then P1 terminates. So P2 is orphan and will be adopted by init, which waits for its termination. The initial process P0 can execute a wait for P1 which will be of short duration. The idea is to delegate to the grandchild the work which otherwise would have been for the child.

The schema is the following:

# match Unix.fork() with (* P0 creates P1 *)
0 -> if Unix.fork() = 0 then exit 0 ; (* P1 creates P2 and terminates *)
Printf.printf "P2 did its work\n" ;
exit 0
| pid -> ignore (Unix.waitpid [] pid) ; (* P0 waits for P1 to terminate *)
Printf.printf "P0 can do other things without waiting\n" ;;
P2 did its work
P0 can do other things without waiting
- : unit = ()
We will apply this principle to handle requests sent to a server in chapter 20.


Previous Contents Next ocaml-book-1.0/en/html/book-ora021.html0000644000000000000000000000313007453055377014467 0ustar Summary Previous Contents Next

Summary

This chapter has demonstrated the main features of functional programming and parametric polymorphism, which are two essential features of the Objective CAML language. The syntax of the expressions in the functional core of the language as well as those of the types which have been described allowed us to develop our first programs. Moreover, the profound difference between the type of a function and its domain of definition was underlined. Introducing the exception mechanism allowed us to resolve this problem and already introduces a new programming style in which one specifies how computations should unfold.


Previous Contents Next ocaml-book-1.0/en/html/book-ora182.html0000644000000000000000000000174707453055401014477 0ustar Notes
1
In this case, the Objective CAML compilers should have been constructed to indicate that they used the library furnished by the platform, and not the one provided by the distribution.
2
In a more general sense, we can be in contention for other resources such as I/O peripherals
ocaml-book-1.0/en/html/book-ora049.html0000644000000000000000000002573107453055377014514 0ustar Animation Previous Contents Next

Animation

The animation of graphics on a screen reuses techniques of animated drawings. The major part of a drawing does not change, only the animated part must modify the color of its constituent pixels. One of the immediate problems we meet is the speed of animation. It can vary depending on the computational complexity and on the execution speed of the processor. Therefore, to be portable, an application containing animated graphics must take into account the speed of the processor. To get smooth rendering, it is advisable to display the animated object at the new position, followed by the erasure of the old one and taking special care with the intersection of the old and new regions.

Moving an object
We simplify the problem of moving an object by choosing objects of a simple shape, namely rectangles. The remaining difficulty is knowing how to redisplay the background of the screen once the object has been moved.

We try to make a rectangle move around in a closed space. The object moves at a certain speed in directions X and Y. When it encounters a border of the graphical window, it bounces back depending on the angle of impact. We assume a situation without overlapping of the new and old positions of the object. The function calc_pv computes the new position and the new velocity from an old position (x,y), the size of the object (sx,sy) and from the old speed (dx,dy), taking into account the borders of the window.

# let calc_pv (x,y) (sx,sy) (dx,dy) =
let nx1 = x+dx and ny1 = y + dy
and nx2 = x+sx+dx and ny2 = y+sy+dy
and ndx = ref dx and ndy = ref dy
in
( if (nx1 < 0) || (nx2 >= Graphics.size_x()) then ndx := -dx );
( if (ny1 < 0) || (ny2 >= Graphics.size_y()) then ndy := -dy );
((x+ !ndx, y+ !ndy), (!ndx, !ndy));;
val calc_pv :
int * int -> int * int -> int * int -> (int * int) * (int * int) = <fun>
The function move_rect moves the rectangle given by pos and size n times, the trajectory being indicated by its speed and by taking into account the borders of the space. The trace of movement which one can see in figure 5.7 is obtained by inversion of the corresponding bitmap of the displaced rectangle.

# let move_rect pos size speed n =
let (x, y) = pos and (sx,sy) = size in
let mem = ref (Graphics.get_image x y sx sy) in
let rec move_aux x y speed n =
if n = 0 then Graphics.moveto x y
else
let ((nx,ny),n_speed) = calc_pv (x,y) (sx,sy) speed
and old_mem = !mem in
mem := Graphics.get_image nx ny sx sy;
Graphics.set_color Graphics.blue;
Graphics.fill_rect nx ny sx sy;
Graphics.draw_image (inv_image old_mem) x y;
move_aux nx ny n_speed (n-1)
in move_aux x y speed n;;
val move_rect : int * int -> int * int -> int * int -> int -> unit = <fun>


The following code corresponds to the drawings in figure 5.7. The first is obtained on a uniformly red background, the second by moving the rectangle across the image of Jussieu.


# let anim_rect () =
Graphics.moveto 105 120;
Graphics.set_color Graphics.white;
Graphics.draw_string "Start";
move_rect (140,120) (8,8) (8,4) 150;
let (x,y) = Graphics.current_point() in
Graphics.moveto (x+13) y;
Graphics.set_color Graphics.white;
Graphics.draw_string "End";;
val anim_rect : unit -> unit = <fun>
# anim_rect();;
- : unit = ()




Figure 5.7: Moving an object.


The problem was simplified, because there was no intersection between two successive positions of the moved object. If this is not the case, it is necessary to write a function that computes this intersection, which can be more or less complicated depending on the form of the object. In the case of a square, the intersection of two squares yields a rectangle. This intersection has to be removed.


Previous Contents Next ocaml-book-1.0/en/html/book-ora051.gif0000644000000000000000000000275307452056111014267 0ustar GIF89awww!,*޼Hʶ jL ! flJKЩbS-C1355r'9.='8WhH)9IYiyi5 *:JZ*iں*;K +PKy ,n N~>l.؎~+ </ONp/\#x^=64(.[aUQ9Z1$]vR$D&A,%1O&T/4ٲdJ1]wTh;CU#Itd2U]̉H> Ψ/]"jצRvvTJSfzhޭmVWKx{g]{l֗b3VƊ>Nl^R lQΓ=_~*ZҞ1tuֈ?F&YuYۙA֣c) ͻ5Q5ڇ=L{gg7 !L[Nn|t%lL[ا~rņ 4f 6}%h*m!0tAHb:#bD<+c8'Hczcc>BˍD$$A&ɤK6 %/OFI%-S]ޑWr emm)J `F6)hηeI,sWNiz]⧂Uf:'jB * f]xYEh._&iQ鍤65J)vޥ *GRkq+:ˬڮgXT[mk˭RE.fzJWYk/޷*:خ;+0 Go^qj 02Rl9'/6er;o Kݚ1:8y Myi&9oxYK%ҩ<,_+o 7)kM)o׍%s|q.xۄ?U6xG/+|c] nb$rcl)X7 zo2cS=nZ/َ ngbc|BlO?+纻쯱2f0syt&̰V;/[+Wܷ/<_j汖l0Kג2 c;]4 ì@}+Jr; }B~( =XC g,M[?G &Q\RWsU4(2y2rX&3PgҼ5 hs7;ocaml-book-1.0/en/html/next_motif.gif0000644000000000000000000000047507452056124014507 0ustar GIF89app!# Imported from XPM image: next.xpm!,@63333B! 0 A0 0 0  0 `0 `0 A @ `0 `00000000000000000000000000000000000000000000  000000 0000000000000000000000000000` ;ocaml-book-1.0/en/html/book-ora059.gif0000644000000000000000000000742707452056112014303 0ustar GIF89aUUUrrr!,H0I8ͻ`(dIhlp,tmߵ:pTxȤl:%ǧt:F2*.n-%<<{"w]kFO _ |ÂbuZΌłٻ׷xsgJjbЦ@ $b A << 5h 6tj˜Oi:6j0,4UXo߽|cxxiiE01g.Wo^E˷@? ܷ]R``3=x4(na2aIX؈ b$s"&)آH ؃:61cd1gL6PbTVihdI%?~bf!En~ѥzs'mXu̞ d'Z)̉əW ݈cM.e!L %Ǔc8GI Z@z*حf'M@6|iE)E;-8Mh#nV-y.rLԮ[&ۦN!@b>ӱкXt[״.]4=[15-bԸ9IP+R2$X'2u]k( gbǦ|dUr: 3Aw0pg$tRA\V\O_z^¥5f'w1Mk 6Ww?-i˛jܐu h^쨪v$%}s!~~M "0qcl<:f*SUq &{kp39wݜ 5HFB!`ԕ-mBhC =%-vđ|?yAj7 sR;@Ps7I-|E,VaVZܢg1Fbqk\$0,zcC:zB1IBޠLd@F:rj "T`92gqs#\s'4?Zpte, TVrMS.u9LU pL)bHcf51@?ׄ"3ejMr.䦳@)q ʾIr$.HE4.#' 12P{Z=Qw#T'aLseJK:XFEYfJbuIOPnuj]hS5)iyYtŒ(5 bWpCJJ$/1͘I'6U<߻LZJKD[A&zz]CVZ.URI2}KZ+lK39J7Û@QMrFZ`V#!mi?Udub-b #o:1X~pu+ +DB{e`bHQΎxsԥ.F6QkYB^WNmO\bK/Լz(mWr;4k3 # h*6V fO#b"L&*/, Ό9+rx>'=S-,~Nxt]h\JY ,z).IG$\GrB̓2=y͹ M]eiVM{8b:F^a+M @LCL<6UxO*Vwhl!} ^3"Ӆv Is3uqɽY5Y;)RFLצ44VW%1\N5VaQ-Qh4 /4k*.T9W(; qز>M6s{:r.:pcZqoԛ]]k+_uXb9 [n3%qp&Q.JÒta`#97nMt~OO8s h"&+PTRDQT\qeph~O Mg6ft3w}ZhUrIm#(&I{9oC=<ɿܷNRݡz̕&kp0`}-qZZo^/+RmklXea& ~}9vUt%M93_Ɵ}}GzW$}(X<>lI7~ 8}ph~ xz@/50=T|}Y|Z"?(<`mYP})X301Fqn8zJ0REj|3Ehn4[G<6\OSG\FnJho儀CAsBe.]gr FtH W1uUݵt>C^ODv  f^ op!GK#7$?'ssksF(񵉾deps'CNS'0>18)؋QCǨD83<ga/kPXWyӌD3HoK&H}'dwP$v;g Pp'dx{OSVOxzxtG 9fnXUwXSu{,?!2vfSH9O2cnh Exercises Previous Contents Next

Exercises

Resolution of Linear Systems

This exercise revisits the resolution of linear systems presented as an exercise in the chapter on imperative programming (3).
  1. By using the Printf module, write a function print_system that aligns the columns of the system.

    # type vect = float array
    type mat = vect array
    type syst = { m:mat ; v:vect } ;;
    type vect = float array
    type mat = vect array
    type syst = { m: mat; v: vect }

    # let affiche_systeme s =
    let w = Array.length s.m.(0)
    and h = Array.length s.m in
    let c = h / 2 in
    for i = 0 to h - 1 do
    Printf.printf "| ";
    for j = 0 to w - 1 do
    Printf.printf " %8.2f " s.m.(i).(j)
    done;
    Printf.printf " |";
    if i = c then Printf.printf " * " else Printf.printf " ";
    Printf.printf "| x_%-2d |" i;
    if i = c then Printf.printf " = " else Printf.printf " ";
    Printf.printf "| %8.2f |\n" s.v.(i)
    done;
    Printf.printf "\n" ;;
    val affiche_systeme : syst -> unit = <fun>


  2. Test this function on the examples given on page ??.

    # let ax3 = { m = [| [| 10.0 ; 7.0 ; 8.1 ; 7.2 |]
    ; [| 7.08 ; 5.04 ; 6.0 ; 5.0 |]
    ; [| 8.0 ; 5.98 ; 9.89 ; 9.0 |]
    ; [| 6.99 ; 4.99 ; 9.0 ; 9.98 |] |] ;
    v = [| 32.0 ; 23.0 ; 33.0 ; 31.0 |] } ;;
    val ax3 : syst =
    {m=
    [|[|10; 7; 8.1; 7.2|]; [|7.08; 5.04; 6; 5|]; [|8; 5.98; 9.89; ...|];
    ...|];
    v=...}
    # affiche_systeme ax3 ;;
    | 10.00 7.00 8.10 7.20 | | x_0 | | 32.00 |
    | 7.08 5.04 6.00 5.00 | | x_1 | | 23.00 |
    | 8.00 5.98 9.89 9.00 | * | x_2 | = | 33.00 |
    | 6.99 4.99 9.00 9.98 | | x_3 | | 31.00 |

    - : unit = ()

Search for Prime Numbers

The Sieve of Eratosthenes is an easily programmed algorithm that searches for prime numbers in a range of integers, given that the lower limit is a prime number. The method is:
  1. Enumerate, in a list, all the values on the range.
  2. Remove from the list all the values that are multiples of the first element.
  3. Remove this first element from the list, and keep it as a prime.
  4. Restart at step 2 as long as the list is not empty.
Here are the steps to create a program that implements this algorithm:
  1. Write a function range that builds a range of integers represented in the form of a list. On reprend la fonction interval des exercices du chapitre prcdent.

    # let interval order next a b =
    let rec aux a =
    if not (order a b) then [a] else a :: aux (next a)
    in aux a ;;
    val interval : ('a -> 'b -> bool) -> ('a -> 'a) -> 'a -> 'b -> 'a list =
    <fun>


  2. Write a function eras that calculates the prime numbers on a range of integers starting with 2, according to the algorithm of the Sieve of Eratosthenes.

    # let rec eras l = match l with
    [] -> []
    | p::q -> p :: (eras (List.filter (fun x -> x mod p <> 0) q)) ;;
    val eras : int list -> int list = <fun>


    Write a function era_go that takes an integer and returns a list of all the prime numbers smaller than this integer.

    # let era_go n = eras (interval (<) (fun x -> x + 1) 2 n) ;;
    val era_go : int -> int list = <fun>


  3. We want to write an executable primes that one will launch by typing the command primes n, where n is an integer. This executable will print the prime numbers smaller than n. For this we must use the Sys module and check whether a parameter was passed. Fichier principal premiers.ml :

    # let usage () = Printf.printf "Usage: premiers n\n";;
    val usage : unit -> unit = <fun>

    # let main () =
    if Array.length (Sys.argv) < 2 then usage()
    else
    let n = int_of_string Sys.argv.(1) in
    if n < 2 then usage()
    else
    let r = era_go n in
    List.iter (fun x -> Printf.printf "%d " x) r;
    Printf.printf "\n" ;;
    val main : unit -> unit = <fun>

    main() ;;


    Construction de l'excutable :
    $ ocamlc -o premiers premiers.ml
    
    ou
    $ ocamlopt -o premiers premiers.ml
    
    Test de l'excutable :
    $ premiers 
    Usage: premiers n
    $ premiers 50
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 
    $ premiers 100
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 
    

Displaying Bitmaps

Bitmaps saved as color array array are bulky. Since 24 bits of color are rarely used, it is possible to encode a bitmap in less space. For this we will analyze the number of colors in a bitmap. If the number is small (for example less than 256) we can encode each pixel in 1 byte, representing the number of the color in the table of colors of this bitmap.

  1. Write a function analyze_colors exploring a value of type color array array and that returns a list of all the colors found in this image.

    # let analyse_couleurs c =
    let l = ref [] in
    for i = 0 to (Array.length c) - 1 do
    for j = 0 to (Array.length c.(0)) -1 do
    if not(List.mem c.(i).(j) !l) then l:= c.(i).(j) :: !l
    done ;
    done ;
    List.rev !l ;;
    val analyse_couleurs : 'a array array -> 'a list = <fun>


  2. From this list, construct a palette. We will take a vector of colors. The index in the table will correspond to the order of the color, and the contents are the color itself. Write the function find_index that returns the index of a value stored in the array.

    # let construire_table l = Array.of_list l ;;
    val construire_table : 'a list -> 'a array = <fun>

    # exception Find of int;;
    exception Find of int

    # let trouve_indice c t =
    let aux () =
    for i=0 to Array.length t do
    if c = t.(i) then raise (Find i)
    done ;
    raise Not_found
    in
    try aux () with Find i -> i ;;
    val trouve_indice : 'a -> 'a array -> int = <fun>


  3. From this table, write a conversion function, encode, that goes from a color array array to a string. Each pixel is thus represented by a character.

    # let encode caa t =
    if Array.length t > 255 then failwith "trop de couleurs (> 255)"
    else
    let h = Array.length caa
    and w = Array.length caa.(0) in
    let s = String.create (h * w) in
    let ns = ref 0 in
    for i = 0 to h-1 do
    for j = 0 to w-1 do
    let ci = trouve_indice caa.(i).(j) t in
    s.[!ns] <- char_of_int ci ;
    incr ns
    done
    done ;
    s ;;
    val encode : 'a array array -> 'a array -> string = <fun>


  4. Define a type image_tdc comprising a table that matches colors to a vector of strings, allowing the encoding of a bitmap (or color array) using a smaller method.

    # type image_tdc = { tdc : Graphics.color array; image : string;
    largeur : int; hauteur : int};;
    type image_tdc =
    { tdc: Graphics.color array;
    image: string;
    largeur: int;
    hauteur: int }


  5. Write the function to_image_tdc to convert a color array array to this type.

    # let to_image_tdc caa =
    let t = construire_table (analyse_couleurs caa) in
    let s = encode caa t in
    { tdc = t; image = s;
    largeur = Array.length caa.(0); hauteur = Array.length caa} ;;
    val to_image_tdc : Graphics.color array array -> image_tdc = <fun>


  6. Write the function save_image_tdc to save the values to a file.

    # let sauve_image_tdc im nom =
    let oc = open_out nom in
    Marshal.to_channel oc im [] ;;
    val sauve_image_tdc : 'a -> string -> unit = <fun>


  7. Compare the size of the file obtained with the saved version of an equivalent palette. Elle est plus petite, d'un facteur 4!!!

  8. Write the function from_image_tdc to do the reverse conversion.

    # let from_image_tdc im =
    let r = Array.create_matrix im.hauteur im.largeur Graphics.black in
    let ns = ref 0 in
    for i = 0 to im.hauteur -1 do
    for j = 0 to im.largeur -1 do
    r.(i).(j) <- im.tdc.(int_of_char im.image.[!ns]) ;
    incr ns
    done
    done ;
    r ;;
    val from_image_tdc : image_tdc -> Graphics.color array array = <fun>


  9. Use it to display an image saved in a file. The file will be in the form of a value of type bitmap_tdc.

    # let visu name =
    let ic = open_in name in
    let im = Marshal.from_channel ic in
    let caa = from_image_tdc im in
    let b = Graphics.make_image caa in
    let size = (string_of_int (Array.length caa.(0)))
    ^ "x" ^ (string_of_int (Array.length caa)) in
    Graphics.open_graph (" " ^ size) ;
    Graphics.draw_image b 0 0 ;
    b ;;
    val visu : string -> Graphics.image = <fun>

Previous Contents Next ocaml-book-1.0/en/html/book-ora152.html0000644000000000000000000000320207453055400014457 0ustar Plan of the Chapter Previous Contents Next

Plan of the Chapter

The first section compares the functional/modular model and the object model. This comparison brings out the particular features of each model, in order to show how many of them may be translated by hand into the other model. One can thus simulate inheritance with modules and use classes to implement simple modules. The limitations of each model are then reviewed. The second section is concerned with the problem of extensibility for data structures and methods, and proposes a solution which mixes the two models. The third section describes some other combinations of the two models by the use of abstract module types for objects.


Previous Contents Next ocaml-book-1.0/en/html/book-ora009.html0000644000000000000000000001767507453055377014520 0ustar Installation Previous Contents Next

Installation

Installing Objective CAML requires about 10MB of free space on one's hard disk drive. The software can easily be uninstalled without corrupting the system.

Installation under Windows

The file containing the binary distribution is called: ocaml-2.04-win.zip, indicating the version number (here 2.04) and the operating system.

Warning


Objective CAML only works under recent versions of Windows : Windows 95, 98 and NT. Don't try to install it under Windows 3.x or OS2/Warp.


  1. The file is in compressed (.zip) format; the first thing to do is decompress it. Use your favorite decompression software for this. You obtain in this way a file hierarchy whose root is named ocaml. You can place this directory at any location on your hard disk. It is denoted by <caml-dir> in what follows.
  2. This directory includes:

    • two subdirectories: bin for binaries and lib for libraries;
    • two ``text'' files: License.txt and Changes.txt containing the license to use the software and the changes relative to previous versions;
    • an application: OCamlWin corresponding to the main application;
    • a configuration file: Ocamlwin.ini which will need to be modified (see the following point);
    • two files of version notes: the first, Readme.gen, for this version and the second, Readme.win, for the version under Windows.
  3. If you have chosen a directory other than c:\ocaml as the root of your file hierarchy, then it is necessary to indicate this in the configuration file. Edit it with Wordpad and change the line defining CmdLine which is of the form:
    CmdLine=ocamlrun c:\ocaml\bin\ocaml.exe -I c:\ocaml\lib
    
    to
    CmdLine=ocamlrun <caml-dir>\bin\ocaml.exe -I <caml-dir>\lib
    
    You have to replace the names of the search paths for binaries and libraries with the name of the Objective CAML root directory. If we have chosen C:\Lang\ocaml as the root directory (<caml-dir>), the modification becomes:
    CmdLine=ocamlrun C:\Lang\ocaml\bin\ocaml.exe -I C:\Lang\ocaml\lib
    
  4. Copy the file OCamlWin.ini to the main system directory, that is, C:\windows or C:\win95 or C:\winnt according to the installation of your system.
Now it's time to test the OCamlWin application by double-clicking on it. You'll get the window in figure 1.1.




Figure 1.1: Objective CAML window under Windows.


The configuration of command-line executables, launched from a DOS window, is done by modifying the PATH variable and the Objective CAML library search path variable (CAMLLIB), as follows:
PATH=%PATH%;<caml-dir>\bin
set CAMLLIB=<caml-dir>\lib
where <caml-dir> is replaced by the path where Objective CAML is installed.

These two commands can be included in the autoexec.bat file which every good DOS has. To test the command-line executables, type the command ocaml in a DOS window. This executes the file:
<caml-dir>/bin/ocaml.exe
corresponding to the Objective CAML. text mode toplevel. To exit from this command, type #quit;;.

To install Objective CAML from source under Windows is not so easy, because it requires the use of commercial software, in particular the Microsoft C compiler. Refer to the file Readme.win of the binary distribution to get the details.

Installation under Linux

The Linux installation also has an easy-to-install binary distribution in the form of an rpm. package. Installation from source is described in section 1. The file to download is: ocaml-2.04-2.i386.rpm which will be used as follows with root privileges:
rpm -i ocaml-2.04-2.i386.rpm
which installs the executables in the /usr/bin directory and the libraries in the /usr/lib/ocaml directory.

To test the installation, type: ocamlc -v which prints the version of Objective CAML installed on the machine.
ocamlc -v
The Objective Caml compiler, version 2.04
Standard library directory: /usr/lib/ocaml
You can also execute the command ocaml which prints the header of the interactive toplevel.
        Objective Caml version 2.04

# 
The # character is the prompt in the interactive toplevel. This interactive toplevel can be exited by the #quit;; directive, or by typing CTRL-D. The two semi-colons indicate the end of an Objective CAML phrase.

Installation under MacOS

The MacOS distribution is also in the form of a self-extracting binary. The file to download is: ocaml-2.04-mac.sea.bin which is compressed. Use your favorite software to decompress it. Then all you have to do to install it is launch the self-extracting archive and follow the instructions printed in the dialog box to choose the location of the distribution. For the MacOS X server distribution, follow the installation from source under Unix.

Installation from source under Unix

Objective CAML can be installed on systems in the Unix family from the source distribution. Indeed it will be necessary to compile the Objective CAML system. To do this, one must either have a C compiler on one's Unix, machine, which is generally the case, or download one such as gcc which works on most Unix. systems. The Objective CAML distribution file containing the source is: ocaml-2.04.tar.gz. The file INSTALL describes, in a very clear way, the various stages of configuring, making, and then installing the binaries.

Installation of the HTML documentation

Objective CAML's English documentation is present also in the form of a hierarchy of HTML files which can be found in the docs directory of the CD-ROM.

This documentation is a reference manual. It is not easy reading for the beginner. Nevertheless it is quite useful as a description of the language, its tools, and its libraries. It will soon become indispensable for anyone who hopes to write a program of more than ten lines.


Previous Contents Next ocaml-book-1.0/en/html/book-ora084.html0000644000000000000000000000363107453055400014471 0ustar Program Memory Previous Contents Next

Program Memory

A machine code program is a sequence of instructions manipulating values in memory. Memory consists generally of the following elements:
  • processor registers (for direct and fast access),
  • the stack,
  • a data segment (static allocation region),
  • the heap (dynamic allocation region).
Only the stack and the dynamic allocation region can change in size during the execution of a program. Depending on the programming language used, some control over these classes of memory can be exercised. Whereas the program instructions (code) usually reside in static memory, dynamic linking (see page ??) makes use of dynamic memory.


Previous Contents Next ocaml-book-1.0/en/html/book-ora188.html0000644000000000000000000003005207453055400014473 0ustar Communication Protocols Previous Contents Next

Communication Protocols

The various client-server communications described in the previous section consisted of sending a string of characters ending in a carriage-return and receiving another. However simple, this communication pattern defines a protocol. If we wish to communicate more complex values, such as floats, matrices of floats, a tree of arithmetic expressions, a closure, or an object, we introduce the problem of encoding these values. Many solutions exist according to the nature of the communicating programs, which can be characterized by the implementation language, the machine architecture, and in certain cases, the operating system. Depending on the machine architecture, integers can be represented in many different ways (most significant bits on the left, on the right, use of tag bits, and size of a machine word). To communicate a value between different programs, it is necessary to have a common representation of values, referred to as the external representation2. More structured values, such as records, just as integers, must have an external representation. Nonetheless, there are problems when certain languages allow constructs, such as bit-fields in C, which do not exist in other languages. Passing functional objects or objects, which contain pieces of code, poses a new difficulty. Is the code byte-compatible between the sender and receiver, and does there exist a mechanism for dynamically loading the code? As a general rule, the problem is simplified by supposing that the code exists on both sides. It is not the code itself that is transmitted, but information that allows it to be retrieved. For an object, the instance variables are communicated along with the object's type, which allows retrieval of the object's methods. For a closure, the environment is sent along with the address of its code. This implies that the two communicating programs are actually the same executable.

A second difficulty arises from the complexity of linked exchanges and the necessity of synchronizing communications involving many programs.

We first present text protocols, later discussing acknowledgements and time limits between requests and responses. We also mention the difficulty of communicating internal values, in particular as it relates to interoperability between programs written in different languages.

Text Protocol

Text protocols, that is, communication in ASCII format, are the most common because they are the simplest to implement and the most portable. When a protocol becomes complicated, it may become difficult to implement. In this setting, we define a grammar to describe the communication format. This grammar may be rich, but it will be up to the communicating programs to handle the work of coding and interpreting the text strings sent and received.

As a general rule, a network application does not allow viewing the different layers of protocols in use. This is typified by the case of the HTTP protocol, which allows a browser to communicate with a Web site.

The HTTP Protocol

The term ``HTTP'' is seen frequently in advertising. It corresponds to the communication protocol used by Web applications. The protocol is completely described on the page of the W3 Consortium:

Link


http://www.w3.org
This protocol is used to send requests from browsers (Communicator, Internet Explorer, Opera, etc.) and to return the contents of requested pages. A request made by a browser contains the name of the protocol (HTTP), the name of the machine (www.ufr-info-p6.jussieu.fr), and the path of the requested page (/Public/Localisation/index.html). Together these components define a URL (Uniform Resource Locator):
http://www.ufr-info-p6.jussieu.fr/Public/Localisation/index.html
When such a URL is requested by a browser, a connection over a socket is established between the browser and the server running on the indicated server, by default on port 80. Then the browser sends a request in the HTTP format, like the following:
GET /index.html HTTP/1.0
The server responds in the protocol HTTP, with a header:
HTTP/1.1 200 OK
Date: Wed, 14 Jul 1999 22:07:48 GMT
Server: Apache/1.3.4 (Unix) PHP/3.0.6 AuthMySQL/2.20
Last-Modified: Thu, 10 Jun 1999 12:53:46 GMT
 
Accept-Ranges: bytes
Content-Length: 3663
Connection: close
Content-Type: text/html
This header indicates that the request has been accepted (code 200 OK), the kind of server, the modification date for the page, the length of the send page and the type of content which follows. Using the GET commmand in the protocol (HTTP/1.0), only the HTML page is transferred. The following connection with telnet allows us to see what is actually transmitted:
$ telnet www.ufr-info-p6.jussieu.fr 80
Trying 132.227.68.44...
Connected to triton.ufr-info-p6.jussieu.fr.
Escape character is '^]'.
GET


<!-- index.html -->
<HTML>
<HEAD>
<TITLE>Serveur de l'UFR d'Informatique de Pierre et Marie Curie</TITLE>
</HEAD>
<BODY>

<IMG SRC="/Icons/upmc.gif" ALT="logo-P6" ALIGN=LEFT HSPACE=30>
Unit&eacute; de Formation et de Recherche 922 - Informatique<BR>
Universit&eacute; Pierre et Marie Curie<BR>
4, place Jussieu<BR>
75252 PARIS Cedex 05, France<BR><P> 
....
</BODY>
</HTML>
<!-- index.html -->

Connection closed by foreign host.
The connection closes once the page has been copied. The base protocol is in text mode so that the language may be interpreted. Note that images are not transmitted with the page. It is up to the browser, when analyzing the syntax of the HTML page, to observe anchors and images (see the IMG tags in the transmitted page). At this time, the browser sends a new request for each image encountered in the HTML source; there is a new connection for each image. The images are displayed when they are received. For this reason, images are often displayed in parallel.

The HTTP protocol is simple enough, but it transports information in the HTML language, which is more complex.

Protocols with Acknowledgement and Time Limits

When a protocol is complex, it is useful that the receiver of a message indicate to the sender that it has received the message and that it is grammatically correct. The client blocks while waiting for a response before working on its tasks. If the part of the server handling this request has a difficulty interpreting the message, the server must indicate this fact to the client rather than ignoring the request. The HTTP protocol has a system of error codes. A correct request results in the code 200. A badly-formed request or a request for an unauthorized page results in an error code 4xx or 5xx according to the nature of the error. These error codes allow the client to know what to do and allow the server to record the details of such incidents in its log files.



When the server is in an inconsistent state, it can always accept a connection from a client, but risks never sending it a response over the socket. For avoiding these blocking waits, it is useful to fix a limit to the time for transmission of the response. After this time has elapsed, the client supposes that the server is no longer responding. Then the client can close this connection in order to go on to its other work. This is how WWW browsers work. When a request has no response after a certain time, the browser decides to indicate that to the user. Objective CAML has input-output with time limits. In the Thread library, the functions wait_time_read and wait_time_write suspend execution until a character can be read or written, within a certain time limit. As input, these function take a file descriptor and a time limit in seconds: Unix.file_descr -> float -> bool. If the time limit has passed, the function returns false, otherwise the I/O is processed.

Transmitting Values in their Internal Representation

The interest in transmission of internal values comes from simplifying the protocol. There is no longer any need to encode and decode data in a textual format. The inherent difficulty in sending and receiving values in their internal representation are the same as those encountered for persistent values (see the Marshal library, page ??). In effect, reading or writing a value in a file is equivalent to receiving the same value over a socket.

Functional Values

In the case of transmitting a closure between two Objective CAML programs, the code in the closure is not sent, only its environment and its code pointer (see figure 12.9 page ??). For this strategy to work, it is necessary that the server possess the same code in the same memory location. This implies that the same program is running on the server as on the client. Nothing, however, prevents the two programs from running different parts of the code at the same time. We adapt the matrix calculation service by sending a closure with an environment containing the data for calculation. When it is received, the server applies this closure to () and the calculation begins.

Interoperating with Different Languages

The interest in text protocols is that they are independent of implementation languages for clients and servers. In effect, the ASCII code is always known by programming languages. Therefore, it is up to the client and to the server to analyze syntactically the strings of characters transmitted. An example of such an open protocol is the simulation of soccer players called RoboCup.

Soccer Robots

A soccer team plays against another team. Each member of the team is a client of a referee server. The players on the same team cannot communicate directly with each other. They must send information through the server, which retransmits the dialog. The server shows a part of the field, according to the player's position. All these communications follow a text protocol. A Web page that describes the protocol, the server, and certain clients:

Link


http://www.robocup.org/
The server is written in C. The clients are written in different languages: C, C++, SmallTalk, Objective CAML, etc. Nothing prevents a team from fielding players written in different languages.

This protocol responds to the interoperability needs between programs in different implementation languages. It is relatively simple, but it requires a particular syntax analyzer for each family of languages.






Previous Contents Next ocaml-book-1.0/en/html/book-ora098.html0000644000000000000000000003010407453055400014471 0ustar Profiling Previous Contents Next

Profiling

This tool allows measuring a variety of metrics concerning program execution, including how many times a particular function or control structure (including conditionals, pattern matchers and loops) are executed. The results are recorded in a file. By examining this information, you may be able to locate either algorithmic errors or crucial locations for optimization.

In order for the profiler to do its work, it is necessary to compile the code using a special mode that adds profiling instructions. There are two profiling modes: one for the bytecode compiler, and the other for the native-code compiler. There are also two commands used to analyze the results. Analysis of native code will retrieve the time spent in each function.

Profiling an application therefore proceeds in three stages:
  1. compilation in profiling mode;
  2. program execution;
  3. presentation of measurements.

Compilation Commands

The commands to compile in profiling mode are the following:
  • ocamlcp -p options for the bytecode compiler;
  • ocamlopt -p options for the native-code compiler.
These compilers produce the same type of files as the usual commands (7). The different options are described in figure 10.1.

f function call
i branch of if
l while and for loops
m branches of match
t branches of try
a all options

Figure 10.1: Options of the profiling commands


These indicate which control structures must be taken into account. By default, the fm options are activated.

Program Execution

Bytecode Compiler

The execution of a program compiled in profiling mode will, if it terminates, produce a file named ocamlprof.dump which contains the information wanted.

We resume the example of the product of a list of integers. We write the following file f1.ml:

let rec interval a b =
if b < a then []
else a::(interval (a+1) b);;

exception Found_zero ;;

let mult_list l =
let rec mult_rec l = match l with
[] -> 1
| 0::_ -> raise Found_zero
| n::x -> n * (mult_rec x)
in
try mult_rec l with Found_zero -> 0
;;


and the file f2.ml which uses the functions of f1.ml:

let l1 = F1.interval 1 30;;
let l2 = F1.interval 31 60;;
let l3 = l1 @ (0::l2);;

print_int (F1.mult_list l1);;
print_newline();;

print_int (F1.mult_list l3);;
print_newline();;


The compilation of these files in profiling mode is shown in the following:
ocamlcp -i -p a -c f1.ml
val profile_f1_ : int array
val interval : int -> int -> int list
exception Found_zero
val mult_list : int list -> int
With the -p option, the compiler adds a new function (profile_f1_) for the initialization of the counters in module F1. It is the same for file f2.ml:
ocamlcp -i -p a -o f2.exe f1.cmo f2.ml
val profile_f2_ : int array
val l1 : int list
val l2 : int list
val l3 : int list

Native Compiler

The native code compilation gives the following result:
$ ocamlopt -i -p  -c f1.ml
val interval : int -> int -> int list
exception Found_zero
val mult_list : int list -> int
$ ocamlopt -i -p -o f2nat.exe f1.cmx f2.ml
Only the -p option without argument is used. The execution of f2nat.exe produces a file named gmon.out which is in a format that can be handled by the usual Unix commands (see page ??).

Presentation of the Results

Since the information gathered by the two profiling modes differs, their presentation follows suit. In the first (bytecode) mode comments on the number of passages through the control structures are added to the program text. In the second (native) mode, the time spent in its body and the number of calls is associated with each function.

Bytecode Compiler

The ocamlprof command gives the analysis of the measurement results. It uses the information contained in the file camlprof.dump. This command takes the source of the program on entry, then reads the measurements file and produces a new program text with the desired counts added as comments.

For our example this gives:
ocamlprof f1.ml

let rec interval a b = 
  (* 62 *) if b < a then (* 2 *) []
  else (* 60 *) a::(interval (a+1) b);;

exception Found_zero ;; 

let mult_list l = 
 (* 2 *) let rec mult_rec l = (* 62 *) match l with 
    [] -> (* 1 *) 1
  | 0::_ -> (* 1 *) raise Found_zero
  | n::x -> (* 60 *) n * (mult_rec x)
 in
  try mult_rec l with Found_zero -> (* 1 *) 0
;; 
These counters reflect the calculations done in F2 quite well. There are two calls of mult_list and 62 of the auxiliary function mult_rec. Examination of the different branches of the pattern matching show 60 passages through the common case, one through the pattern [] and the only match where the head is 0, raising an exception, which can be seen in the counter of the try statement.

The ocamlprof command accepts two options. The first -f file indicates the name of the file to contain the measurements. The second -F string specifies a string to add to the comments associated with the control structures treated.

Native Compilation

To get the time spent in the calls of the functions for multiplying the elements of a list, we write the following file f3.ml:

let l1 = F1.interval 1 30;;
let l2 = F1.interval 31 60;;
let l3 = l1 @ (0::l2);;

for i=0 to 100000 do
F1.mult_list l1;
F1.mult_list l3
done;;

print_int (F1.mult_list l1);;
print_newline();;

print_int (F1.mult_list l3);;
print_newline();;
This is the same file as f2.ml with a loop of 100000 iterations.

Execution of the program creates the file gmon.out. This is in a format readable by gprof, a command that can be found on Unix systems. The following call to gprof prints information about the time spent and the call graph. Since the output is rather long, we show only the first page which contains the name of the functions that are called at least once and the time spent in each.
$ gprof  f3nat.exe 
Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  us/call  us/call  name    
 92.31      0.36     0.36   200004     1.80     1.80  F1_mult_rec_45
  7.69      0.39     0.03   200004     0.15     1.95  F1_mult_list_43
  0.00      0.39     0.00     2690     0.00     0.00  oldify
  0.00      0.39     0.00      302     0.00     0.00  darken
  0.00      0.39     0.00      188     0.00     0.00  gc_message
  0.00      0.39     0.00      174     0.00     0.00  aligned_malloc
  0.00      0.39     0.00      173     0.00     0.00  alloc_shr
  0.00      0.39     0.00      173     0.00     0.00  fl_allocate
  0.00      0.39     0.00       34     0.00     0.00  caml_alloc3
  0.00      0.39     0.00       30     0.00     0.00  caml_call_gc
  0.00      0.39     0.00       30     0.00     0.00  garbage_collection
...
The main lesson is that almost all of the execution time is spent in the function F1_mult_rec_45, which corresponds to the function F1.mult_rec in file f1.ml. On the other hand we recognize a lot of other functions that are called. The first on the list are memory management functions in the runtime library (9).






Previous Contents Next ocaml-book-1.0/en/html/book-ora068.html0000644000000000000000000001023607453055400014472 0ustar Portability and Efficiency Previous Contents Next

Portability and Efficiency

One reason to compile to an abstract machine is to produce an executable independent of the architecture of the real machine where it runs. A native compiler will produce more efficient code, but the binary can only be executed on the architecture it was compiled for.

Standalone Files and Portability

To produce a standalone executable, the bytecode compiler links the bytecode object file example.cmo with the runtime library, the bytecode interpreter and some C code. It is assumed that there is a C compiler on the host system. The inclusion of machine code means that stand-alone bytecode executables are not portable to other systems or other architectures.

This is not the case for the non-standalone version. Since the Zinc machine is not included, the only things generated are the platform independent bytecode instructions. Bytecode programs will run on any platform that has the interpreter. Ocamlrun is part of the default Objective CAML distribution for Sparc running Solaris, Intel running Windows, etc. It is always preferable to use the same version of interpreter and compiler.

The portability of bytecode object files makes it possible to directly distribute Objective CAML libraries in bytecode form.

Efficiency of Execution

The bytecode compiler produces a sequence of instructions for the Zinc machine, which at the moment of the execution, will be interpreted by ocamlrun. Interpretation has a moderately negative linear effect on speed of execution. It is possible to view Zinc's bytecode interpretation as a big pattern matching machine (matching match ... with) where each instruction is a trigger and the computation branch modifies the stack and the counter (address of the next instruction).

Without testing all parts of the language, the following small example which computes Fibonacci numbers shows the difference in execution time between the bytecode compiler and the native compiler. Let the program fib.ml as follows:
let rec fib n = 
  if n < 2 then 1
  else (fib (n-1)) + (fib(n-2));;
and the following program main.ml as follows:
for i = 1 to 10 do 
  print_int (Fib.fib 30);
  print_newline()
done;;
Their compilation is as follows:
$ ocamlc -o fib.exe fib.ml main.ml
$ ocamlopt -o fibopt.exe fib.ml main.ml
These commands produce two executables: fib.exe and fibopt.exe. Using the Unix command time in Pentium 350 under Linux, we get the following data:
fib.exe (bytecode) fibopt.exe (native)
7 s 1 s
This corresponds to a factor 7 between the two versions of the same program. This program does not test all characteristics of the language. The difference depends heavily on the type of application, and is typically much smaller.






Previous Contents Next ocaml-book-1.0/en/html/book-ora126.html0000644000000000000000000000151107453055401014462 0ustar Notes
1
The name of the character font may vary according to the system being used.
2
except for those used in our test examples
ocaml-book-1.0/en/html/book-ora201.html0000644000000000000000000000740207453055401014461 0ustar Applications developed in Objective CAML Previous Contents Next

Applications developed in Objective CAML

A certain number of applications have been developed in Objective CAML. We will only speak of ``public'' applications, that is, those which anyone can use either freely or by buying them.

Like other functional languages, Objective CAML is a good compiler implementation language. The bootstrap1 of the ocaml compiler is a convincing example. As well, numerous language extensions have been contributed, as seen previously for parallel programming, but also for typing such as O'Labl (part of which is in the process of being integrated into Objective CAML, see appendix B) or for physical units. Links to these applications can be found on the ``Caml Hump''.

Objective CAML's second specialty concerns proof assistants. The major development in this area is the program Coq which accompanies the evolution of Caml almost since its origin. Historically, ML was conceived as the command language of the LCF (Logic for Computable Functions) system, before becoming independent of this application. It is thus natural to find it as the implementation language of an important theorem-proving program.

A third application domain concerns parallelism (see page ??) and communication of which a good example is the Ensemble system.

Link


http://www.cs.cornell.edu/Info/Projects/Ensemble/


A list, not exhaustive, of significant applications developed in Objective CAML is maintained on Inria's Caml site:

Link


http://caml.inria.fr/users_programs-eng.html


Let us mention in particular hevea which is a LATEX to HTML translator which we have used to create the HTML version of this book found on the accompanying CD-ROM.

Link


http://pauillac.inria.fr/~maranget/hevea/


While of importance, the applications we've just mentioned don't represent what, at the beginning of this chapter, we christened a ``beacon application''. Moreover, they don't explore a new specialized domain demonstrating the relevance of using Objective CAML. It is not clear that this example can be issued from academia. It is more likely that it will come from industry, whether in conjunction with language standardization (and so its formal specification), or for the needs of applications having to integrate different programming and program organization styles.


Previous Contents Next ocaml-book-1.0/en/html/book-ora063.gif0000644000000000000000000000266207452056112014272 0ustar GIF89a UUU!, H0I8ͻ@(dihl~p,0@m|kpH]r32Њ3JVX4}l733^773.Ov">_M1 OnLSd9  cs "A6!~ABl9~ZҾд#>Qֹ ;Hެ8FCIA0Gz"JHAE0%Wɮ*Nb6=pd8uJR I-Y ;3匷SN?{PKG&Ec3.JJꋧX1֮`hff-yn^Y%v:/N~P!0OÈ'^|cd,,a/cysΞt4iOLz&+a~-hmҴo{˭Uͼc .bqďU89s;Ο8:u?VkkËj}-c k* m ޽^ŏX~`gX)XMz q{\ (0fUX&Z )Ax-.AаE! $ 'GCp15#MQFT/K^ȑA@-fi 1ȲP_z~ftB dC-؃ERB&0䘇6RSJ%j9ډHҨ[ N0iϦ "u<ѐZN9n#p0jF#MҔЩkl$곴IEԙBnBLr̎m'N'ƊkKĤ. %#B1pVK^Spl11* p/|1D~Bh+kɣQ^h2lst :lż)]ֆIZ i:} e<:;uoqc:37zֳz꬯^\Tھw݉C(|@=mdOo__|7̽?{E~nõO?t?]" xݥ]k8,x;MP $jpa4&E4` t.2i+Kd(\9 4'< O0cAia ,ٱ8EEZtH|U(V QI:mCD] !zdk+XIvAmZC</+=`-"wc 2'svAh͇A/2\d,IuR W6 ;ocaml-book-1.0/en/html/book-ora071.html0000644000000000000000000001143707453055400014470 0ustar To Learn More Previous Contents Next

To Learn More

The techniques to compile for abstract machines were used in the first generation of SmallTalk, then in the functional languages LISP and ML. The argument that the use of abstract machines will hinder performance has put a shadow on this technique for a long time. Now, the JAVA language has shown that the opposite is true. An abstract machine provides several advantages. The first is to facilitate the porting of a compiler to different architectures. The part of the compiler related to portability has been well defined (the abstract machine interpreter and part of runtime library). Another benefit of this technique is portable code. It is possible to compile an application on one architecture and execute it on another. Finally, this technique simplifies compiler construction by adding specific instructions for the type of language to compile. In the case of functional languages, the abstract machines make it easy to create the closures (packing environment and code together) by adding the notion of execution environment to the abstract machine.

To compensate for the loss in efficiency caused by the use of the bytecode interpreter, one can expand the set of abstract machine instructions to include those of a real machine at runtime. This type of expansion has been found in the implementation of Lisp (llm3) and JAVA (JIT). The performance increases, but does not reach the level of a native C compiler.

One difficulty of functional language compilation comes from closures. They contain both the executable code and execution environment (see page ??).
The choice of implementation for the environment and the access of values in the environment has a significant influence on the performance of the code produced. An important function of the environment consists of obtaining access to values in constant time; the variables are viewed as indexes in an array containing their values. This requires the preprocessing of functional expressions. An example can be found in L. Cardelli's book - Functional Abstract Machine. Zinc uses this technique. Another crucial optimization is to avoid the construction of useless closures. Although all functions in ML can be viewed as functions with only one argument, it is necessary to not create intermediate closures in the case of application on several arguments. For example, when the function add is applied with two integers, it is not useful to create the first closure corresponding to the function of applying add to the first argument. It is necessary to note that the creation of a closure would allocate certain memory space for the environment and would require the recovery of that memory space in the future (9). Automatic memory recovery is the second major performance concern, along with environment.

Finally, bootstrapping allows us to write the majority of a compiler with the same language which it is going to compile. For this reason, like the chicken and the egg, it is necessary to define the minimal part of the language which can be expanded later. In fact, this property is hardly appreciable for classifying the languages and their implementations. This property is also used as a measure of the capability of a language to be used in the implementation of a compiler. A compiler is a large program, and bootstrapping is a good test of it's correctness and performance. The following are links to the references:

Link


http://caml.inria.fr/camlstone.txt
At that time, Caml was compiled over fifty machines, these were antecedent versions of Objective CAML. We can get an idea of how the present Objective CAML has been improved since then.






Previous Contents Next ocaml-book-1.0/en/html/book-ora088.gif0000644000000000000000000000560407452056124014303 0ustar GIF89ak֖!,kH0I8ͻ`(di©lp,ϴF|pH,Ȥrl:ШtJZoSٰxL.zm]Wή~W}q<;;JtHptGI^:HpIpGFmǩ!^ @#JHŋaCંyTrH 3ʜI͛jOt?my#Œ!gԒ o=jʵ'K}+k &6 Vkfv+k覫0@k,0/' 7G,'|IWO q ,l!_LP(%,L8?o,0K, t4-2B.' @T+Nc}Ykuc2@,r4CM7[c=4-˽wS`-vz{sMj3ܑCn9ѕ;m ۓϽ29Wo9yc醓^q箣.{oϾ; N'ꎋ.{<Т[n=_} /wᣏ{S4~F[Bj0"4GDP<̢|Bc?0_O\LjF tU"(hBi.c+1'80쐯H R},a Ga{st-iqPD%ʆx;҈ +Y&q"sIAr~]6*r"" #IS&F(JnxK sӤ1ei-gf__i2u3b=yunX11M(Aω-np|hP*RkgECGtft i#)*Ғⓤ&M)CAҖ:L.i`*Ӛ6hӞ" >)NJTz2=*R]:ԥTNM)TZҦRSFՀ6"%(01*s`d ?ɯfԬdQ撥+>jW0vjǶ cCIXV/fy@e5ֆRk3q-nse#;P{K{ZzdVr$o XNR<o&yKݜ «k}ࡐݞq ݒЕk."1L^Coҥcx ξtEa(\rDn˹U5&mޮhGrW涶ӽZvo&jzsز38{u6~=^ly\8b[HP0V&[֍8ȁ Y ;fs#/kglG6R&S\9y>ꚛ5Z)\@3@8tY`O~`gm }[Ͼ_TO~NiyHSOϿCv/`TuVHUw7Ho Tx؀A%>8':hl?'xhR5EE50(R&V-R*,)f-5;P:hRo?1!>8aE_B~aDŽ7 '#ㄉGh\{g+=]8ǃ(dg[nXvl2tXvxxz|؇~~8XxHxy;"W,xxPT$8X؉  ؊8Xx 8XxȘP~H ;ocaml-book-1.0/en/html/book-ora165.html0000644000000000000000000002475707453055400014505 0ustar The Unix Module Previous Contents Next

The Unix Module

This module contains the interfaces to the most important Unix library functions. Much of this module has been ported to Windows and can be used there. Whenever necessary we will indicate the restrictions in the use of the presented functions. A table resuming the restrictions is given by figure 18.1.

The Unix library belongs to the Objective CAML non-standard libraries which have to be bound by the -custom compiler command (see chapter 7, page ??). Depending on the desired form of the program, one of the following commands is used under Unix to produce bytecode, native code or an interaction loop:
$ ocamlc -custom unix.cma  fichiers.ml -cclib -lunix
$ ocamlopt unix.cma fichiers.ml -cclib -lunix
$ ocamlmktop -custom -o unixtop unix.cma -cclib -lunix 
The purpose of constructing an interaction loop (of which the name will be unixtop) is to support an incremental development style. Each function can be compiled quickly from its type declaration. It is also possible to execute functional tests.

Depending on the version of Unix in use, the system library may not be located at the default place. If necessary the access path of the libraries may be indicated with the option -ccopt (see chapter 7).

Under Windows the commands to compile become:
$ ocamlc -custom unix.cma  fichiers.ml %CAMLLIB%\libunix.lib wsock32.lib
$ ocamlopt unix.cma fichiers.ml %CAMLLIB%\libunix.lib wsock32.lib
$ ocamlmktop -custom -o unixtop.exe unix.cma %CAMLLIB%\libunix.lib wsock32.lib
The name of the obtained interaction loop is unixtop.exe.

Error Handling

Errors produced by system calls throw Unix_error exceptions, which can be handled by the Objective CAML program. Such errors contain three arguments: a value of type Unix.error which can be transformed into a character string by the function error_message, a string containing the name of the function producing the error and optionally, a string containing the argument of the function when the argument is of type string.

It is possible to define a generic calling function with error treatment:

# let wrap_unix funct arg =
try (funct arg) with
Unix.Unix_error (e,fm,argm) ->
Printf.printf "%s %s %s" (Unix.error_message e) fm argm ;;
val wrap_unix : ('a -> unit) -> 'a -> unit = <fun>
The function wrap_unix takes a function and its argument, and applies one to the other. If a Unix error occurs, an explaining message is printed. An equivalent function is defined in the Unix module:


# Unix.handle_unix_error ;;
- : ('a -> 'b) -> 'a -> 'b = <fun>


Portability of System Calls

Figure 18.1 indicates which of the communication and process handling functions presented in this chapter are accessible under Windows. The main shortcoming is the lack of the two functions fork and kill to create new processes and to send signals.


Fonction Unix Windows Comment
openfile  
close  
dup  
dup2  
read  
write  
lseek  
execv  
execve  
execvp  
execvpe  
fork   use create_process
getpid  
sleep  
wait    
waitpid only for a given
      number of processes
create_process  
create_process_env  
kill    
pipe  
mkfifo    
open_process   use the interpretation of
      /bin/sh commands
close_process    

Figure 18.1: Portability of the module Unix functions used in this chapter.


Furthermore, the function wait waiting for the end of a child process is not implemented, because fork is not.


Previous Contents Next ocaml-book-1.0/en/html/book-ora053.gif0000644000000000000000000000251407452056111014264 0ustar GIF89atwww!,tH0I8=`(dihl뾤tmxnz',H1l:˧tJUجz.xLΉznkj{N-+y3 qh rz/(%;.-œ̼1VϚٳ#\ܐ!־Vկ`3Dpxk" :R& j(n|ɇ&1&FrHTYqߣ&|gM-#%S`V TshI/FZnOꎊDSTXCtjMK2,ط^tyVnZfk$xNٸүZHيuDž/X@zBXʄJ,/~ jhΣ;F|:n+tjLBvjc~:lMk'e[f rԠ\yy': #[w*WOZo7dO;'F$UUueQ 3xWbj!-AmKH}XP"fi&fs^Z( .$ֆ76؍Di+wLdPFTViXf)BZv^bfjp2ftZRF̉* zzg&袌6裐.:3饘f馘NJi%p*ꨤV:ꪩ~eQj뭒ƪ4L*k&!!Ъ,V06-z ^nӭ/.=ۀ[.񚋭pB"?ԮKlˆ^pqY@%30!KK1|r'I/;s#28;1-#GA5<'4T+m53k]s{@nCW[59lu-tgxk޼6@Z<0~ =Ai.78EN*Lt>:R.0ܜn7 3ӎ|ڀ$6|G.3lS}s?@֗>qpMݺ12-z_# :DZMɤ@十ʠ7z GHb W0!^8̡OHU@ %X!Hl ?&:\*ZNWⅴv,HF>$;ocaml-book-1.0/en/html/book-ora111.html0000644000000000000000000000250407453055401014457 0ustar Notes
1
Type lexeme is defined on page ??
2
Note of translators: From an academic standpoint, the proper term would have been ``Rational Expressions''; we chose the term ``regular'' to follow the programmers' tradition.
3
By convention, the empty word is denoted by the greek character epsilon: e
4
We underline the portion of input processed at each stage and we point out the rule used.
ocaml-book-1.0/en/html/book-ora086.html0000644000000000000000000005325307453055400014500 0ustar Automatic Garbage Collection Previous Contents Next

Automatic Garbage Collection

We classify automatic memory reclamation algorithms into two classes:
  • reference counters: each allocated region knows how many references there are to it. When this number becomes zero, the region is freed.
  • sweep algorithms: starting from a set of roots, the collection of all accessible values is traversed in a way similar to the traversal of a directed graph.
Sweep algorithms are more commonly used in programming languages. In effect, reference counting garbage collectors increase the processing costs (through counter updating) even when there is no need to reclaim anything.

Reference Counting

Each allocated region (object) is given a counter. This counter indicates the number of pointers to the object. It is incremented each time a reference to the object is shared. It is decremented whenever a pointer to the object disappears. When the counter becomes zero, the object is garbage collected.

The advantage of such a system comes from the immediate freeing of regions that are no longer used. Aside from the systematic slowdown of computations, reference counting garbage collectors suffer from another disadvantage: they do not know how to process circular objects. Suppose that Objective CAML had such a mechanism. The following example constructs a temporary value l, a list of characters of where the last element points to the cell containing 'c'. This is clearly a circular value (figure 9.2).

# let rec l = 'c'::'a'::'m'::l in List.hd l ;;
- : char = 'c'


Figure 9.2: Memory representation of a circular list.


At the end of the calculation of this expression each element of the list l has a counter equal to one (even the first element, for the tail points to the head). This value is no longer accessible and yet cannot be reclaimed because its reference counter is not zero. In languages equipped with memory reclamation via reference counting---such as Python---and which allow the construction of circular values, it is necessary to add a memory sweep algorithm.

Sweep Algorithms

Sweep algorithms allow us to explore the graph of accessible values on the heap. This exploration uses a set of roots indicating the beginning of the traversal. These roots are exterior to the heap, stored most often in a stack. In the example in figure 9.1, we can suppose that the values of u and v are roots. The traversal starting from these roots constructs the graph of the values to save: the cells and pointers marked with heavy lines in figure 9.3.


Figure 9.3: Memory reclamation after a garbage collection.


The traversal of this graph necessitates knowing how to distinguish immediate values from pointers in the heap. If a root points to an integer, we must not consider this value to be the address of another cell. In functional languages, this distinction is made by using a few bits of each cell of the heap. We call these bits tag bits. This is why Objective CAML integers only use 31 bits. This option is described in Chapter 12, page ??. We describe other solutions to the problem of distinguishing between pointers and immediate values in this chapter, page ??.

The two most commonly used algorithms are Mark&Sweep, which constructs the list of the free cells in the heap, and Stop&Copy, which copies cells that are still alive to a second memory region.

The heap should be seen as a vector of memory boxes. The representation of the state of the heap for the example of figure 9.1 is illustrated in figure 9.4.


Figure 9.4: State of the heap.


We use the following characteristics to evaluate a sweep algorithm:
  • efficiency: does the time-complexity depend on the size of the heap or only on the number of the living cells?
  • reclamation factor: is all of the free memory usable?
  • compactness: is all of the free memory usable in a single block?
  • localization: are all of the different cells of a structured value close to one another?
  • memory needs: does the algorithm need to use part of the memory when it runs?
  • relocation: do values change location following a garbage collection?
Localization avoids changing memory pages when traversing a structured value. Compactness avoids fragmentation of the heap and allows allocations equal to the amount of available memory. The efficiency, reclamation factor, and supplementary memory needs are intimately linked to the time and space complexity of the algorithm.

Mark&Sweep

The idea of Mark&Sweep is to keep an up-to-date list of the free cells in the heap called the free list. If, at the time of an allocation request, the list is empty or no longer contains a free cell of a sufficient size, then a Mark&Sweep occurs.

It proceeds in two stages:
  1. the marking of the memory regions in use, starting from a set of roots (called the Mark phase); then
  2. reclamation of the unmarked memory regions by sequentially sweeping through the whole heap (called the Sweep phase).
One can illustrate the memory management of Mark&Sweep by using four ``colorings'' of the heap cells: white, gray1, black, and hached. The mark phase uses the gray; the sweep phase, the hached; and the allocation phase, the white.

The meaning of the gray and black used by marking is as follows:
  • gray: marked cells whose descendents are not yet marked;
  • black: marked cells whose descendents are also marked.
It is necessary to keep the collection of grayed cells in order to be sure that everything has been explored. At the end of the marking each cell is either white or black, with black cells being those that were reached from the roots. Figure 9.5 shows an intermediate marking stage for the example of figure 9.4: the root u has been swept, and the sweeping of v is about to begin.


Figure 9.5: Marking phase.


It's during the sweep phase that the free list is constructed. The sweep phase modifies the colorings as follows:
  • black becomes white, as the cell is alive;
  • white becomes hached, and the cell is added to the free list.
Figure 9.6 shows the evolution of the colors and the construction of the free list.


Figure 9.6: Sweep phase.


Characteristics of Mark&Sweep are that it:
  • depends on the size of the entire heap (Sweep phase);
  • reclaims all possible memory;
  • does not compact memory;
  • does not guarantee localization;
  • does not relocate data.
The marking phase is generally implemented by a recursive function, and therefore uses space on the execution stack. One can give a completely iterative version of Mark&Sweep that does not require a stack of indefinite size, but it turns out to be less efficient than the partially recursive version.

Finally, Mark&Sweep needs to know the size of values. The size is either encoded in the values themselves, or deduced from the memory address by splitting the heap into regions that allocate objects of a bounded size. The Mark&Sweep algorithm, implemented since the very first versions of Lisp, is still widely used. A part of the Objective CAML garbage collector uses this algorithm.

Stop&Copy

The principal idea of this garbage collector is to use a secondary memory in order to copy and compact the memory regions to be saved. The heap is divided into two parts: the useful part (called from-space), and the part being re-written (called to-space).



Figure 9.7: Beginning of Stop&Copy.


The algorithm is the following. Beginning from a set of roots, each useful part of the from-space is copied to the to-space; the new address of a relocated value is saved (most often in its old location) in order to update all of the other values that point to this value.



Figure 9.8: Rewriting from from-space into to-space.


The contents of the rewritten cells gives new roots. As long as there are unprocessed roots the algorithm continues.



Figure 9.9: New roots.


In the case of sharing, in other words, when attempting to relocate a value that has already been relocated, it suffices to use the new address.



Figure 9.10: Sharing.


At the end of garbage collection, all of the roots are updated to point to their new addresses. Finally, the roles of the two parts are reversed for the next garbage collection.



Figure 9.11: Reversing the two parts.


The principal characteristics of this garbage collector are the following:
  • it depends solely on the size of the objects to be kept;
  • only half of the memory is available;
  • it compacts memory;
  • it may localize values (using breadth-first traversal);
  • it does not use extra memory (only from-space+to-space);
  • the algorithm is not recursive;
  • it relocates values into the new part of memory;

Other Garbage Collectors

Many other techniques, often derived from the two preceding, have been used: either in particular applications, e.g., the manipulation of large matrices in symbolic calculations, or in a general way linked to compilation techniques. Generational garbage collectors allow optimizations based on the age of the values. Conservative garbage collectors are used where there is not an explicit differentiation between immediate values and pointers (for example, when one translates into C). Finally, incremental garbage collectors allow us to avoid a noticeable slow-down at the time of garbage collection activation.

Generational Garbage Collection

Functional programs are, in general, programs that allocate frequently. We notice that a very large number of values have a very short lifetime2. On the other hand, when a value has survived several garbage collections, it is quite likely to survive for a while longer. In order to avoid complete traversal of the heap---as in Mark&Sweep---during each memory reclamation, we would like to be able to traverse only the values that have survived one or more garbage collections. Most frequently, it is among the young values that we will recover the most space. In order to take advantage of this property, we give objects dates, either a time-stamp or the number of garbage collections survived. To optimize garbage collection, we use different algorithms according to the age of the values:
  • The garbage collections for young objects should be fast and traverse only the younger generations.
  • The garbage collections for old objects should be rare and do well at collecting free space from the entire memory.
As a value ages it should take part less and less in the most frequent garbage collections. The difficulty, therefore, is taking count of only the region of memory occupied by young objects. In a purely functional language, that is, a language without assignment, younger objects reference older objects, and on the other hand, older objects do not possess pointers to younger objects because they were created before the young objects existed. Therefore, these garbage collection techniques lend themselves well to functional languages, with the exception of those with delayed evaluation which can in fact evaluate the constituents of a structure after evaluating the structure itself. On the other hand, for functional languages with assignment it is always possible to modify part of an older object to refer to a younger object. The problem then is to save young memory regions referenced only by an older value. For this, it is necessary to keep an up-to-date table of references from old objects to young objects in order to have a correct garbage collection. We study the case of Objective CAML in the following section.

Conservative Garbage Collectors

To this point, all of the garbage collection techniques presume knowing how to tell a pointer from an immediate value. Note that in functional languages with parametric polymorphism values are uniformly represented, and in general occupy one word of memory3. This is what allows having generic code for polymorphic functions.

However, this restriction on the range for integers may not be acceptable. In this case, conservative garbage collectors make it possible to avoid marking immediate values such as integers. In this case, every value uses an entire memory word without any tag bits. In order to avoid traversing a memory region starting from a root actually containing an integer, we use an algorithm for discriminating between immediate values and pointers that relies on the following observations:
  • the addresses of the beginning and end of the heap are known so any value outside of these bounds is an immediate value;
  • allocated objects are aligned on a word address. Every value that does not correspond to such an alignment must also be an immediate value.
Thus each heap value that is valid from the point of view of being an address into the heap is considered to be a pointer and the garbage collector tries to keep this region, including those cases where the value is in fact an immediate value. These cases may become very rare by using specific memory pages according to the size of the objects. It is not possible to guarantee that the entire unused heap is collected. This is the principal defect of this technique. However, we remain certain that only unused regions are reclaimed.

In general, conservative garbage collectors are conservative, i.e., they do not relocate objects. Indeed, as the garbage collector considers some immediate values as pointers, it would be harmful to change their value. Nevertheless, some refinements can be introduced for building the sets of roots, which allow to relocate corresponding to clearly known roots.

Garbage collection techniques for ambiguous roots are often used when compiling a functional language into C, seen here as a portable assembler. They allow the use of immediate C values coded in a memory word.

Incremental Garbage Collection

One of the criticisms frequently made of garbage collection is that it stops the execution of a running program for a time that is perceptible to the user and is unbounded. The first is embarrassing in certain applications, for instance, rapid-action games where the halting of the game for a few seconds is too often prejudicial to the player, as the execution restarts without warning. The latter is a source of loss of control for applications which must process a certain number of events in a limited time. This is typically the case for embedded programs which control a physical device such as a vehicle or a machine tool. These applications, which are real-time in the sense that they must respond in a bounded time, most often avoid using garbage collectors.

Incremental garbage collectors must be able to be interrupted during any one of their processing phases and be able to restart while assuring the safety of memory reclamation. They give a sufficiently satisfactory method for dealing with the former case, and can be used in the latter case by enforcing a programming discipline that clearly isolates the software components that use garbage collection from those that do not.

Let us reconsider the Mark&Sweep example and see what adaptations are necessary in order to make it incremental. There are essentially two:
  1. how to be sure of having marked everything during the marking phase?
  2. how to allocate during either the marking phase or the reclamation phase?
If Mark&Sweep is interrupted in the Mark phase, it is necessary to assure that cells allocated between the interruption of marking and its restart are not unduly reclaimed by the Sweep that follows. For this, we mark cells allocated during the interruption in black or gray in anticipation.

If the Mark&Sweep is interrupted during the Sweep phase, it can continue as usual in re-coloring the allocated cells white. Indeed, as the Sweep phase sequentially traverses the heap, the cells allocated during the interruption are localized before the point where the sweep restarts, and they will not be re-examined before the next garbage collection cycle.

Figure 9.12 shows an allocation during the reclamation phase. The root w is created by:

# let w = 'f'::v;;
val w : char list = ['f'; 'z'; 'a'; 'm']


Figure 9.12: Allocation during reclamation.



Previous Contents Next ocaml-book-1.0/en/html/book-ora174.html0000644000000000000000000000333607453055400014473 0ustar Plan of the Chapter Previous Contents Next

Plan of the Chapter

The first section details the possible interactions between threads, and proceeds with describing module Thread, and showing how to execute many processes in the same application.

The second part deals with the synchronization between threads by mutual exclusion (Mutex module), and with waiting for conditions (Condition module). Two complete examples show the difficulties inherent to this module.

The third section explains the mode of communication by events provided by the Event module and the new possibilities which is provides.

The fourth section concludes the chapter with the implementation of a shared queue for the different counters at a post office.


Previous Contents Next