pax_global_header00006660000000000000000000000064133412031470014510gustar00rootroot0000000000000052 comment=1a2d225218213d850bdcd40ed87449c176690139 orpie-release-1.6.0/000077500000000000000000000000001334120314700142505ustar00rootroot00000000000000orpie-release-1.6.0/.gitignore000066400000000000000000000001711334120314700162370ustar00rootroot00000000000000*.swp _build .merlin orpie.install doc/*.aux doc/*.dvi doc/*.haux doc/*.htoc doc/*.log doc/*.ps doc/manual.tex doc/*.toc orpie-release-1.6.0/ChangeLog000066400000000000000000000202171334120314700160240ustar00rootroot00000000000000Orpie ChangeLog ------------------------------------------------------------------------ v1.6 2018-08-28 Version 1.6.0 release. This is a maintenance update intended to leverage OPAM, findlib, dune, and other bits of modern OCaml tooling. Hopefully this will be easier for most people to build and install. v1.5 2014-04-11 Version 1.5.2 release. Catch division by zero when invoking the modulo operator. 2010-03-06 Minor changes for compatibility with OCaml 3.11. Honor custom CFLAGS and LDFLAGS settings more carefully. 2007-09-17 ln() and log10() now return complex values for negative real arguments. 2007-09-13 Version 1.5.1 release. pow() now avoids using complex arithmetic in some cases, leading to more pleasant results for expressions like 2^3 - 7. Fixed "invalid argument" crash bug when typing an unprintable character during entry of units. Made a minor syntactical change for compatibility with OCaml 3.10.0. 2007-09-13 Version 1.5.0 release. 2007-07-01 Complete rewrite of units implementation, allowing user-defined units and physical constants. 2007-05-06 Updated included ocamlgsl bindings to 0.6.0. 2006-11-13 Square root of a negative value now returns a complex result. 2006-11-12 gcd(), lcd(), and mod() now accept real-valued arguments. Fix for crash in abbrev mode. 2004-09-09 Made a minor Makefile change to correct linking errors under Gentoo (maybe others). Numerous code cleanups. Calculator code has been better separated from interface code. 2004-09-07 Support --sysconfdir configure option. (Anyone packaging for the filesystem hierarchy standard will now need to use something like "./configure --prefix=/usr --sysconfdir=/etc".) 2004-09-03 Implemented entry of most fundamental physical constants. 2004-08-31 Fixed incorrect error message regarding deprecated extended_enter command. v1.4 2005-10-29 Version 1.4.3 release. Backported a bugfix for crash when executing uconvert() with only one element on the stack. 2005-09-21 Version 1.4.2 release. 2005-09-20 Updated build script for better support on FreeBSD and OS X. 2004-09-17 Version 1.4.1 release. 2004-09-15 Updated the build dependency tree, so parallel make should work properly again. 2004-09-09 Made a minor Makefile change to correct linking errors under Gentoo (maybe others). 2004-09-01 Made some minor documentation fixes. 2004-08-31 Fixed incorrect error message regarding deprecated extended_enter command. 2004-08-30 Version 1.4.0 release. Started work on a testing framework for the rpc_calc object. (I'd really appreciate help writing test cases--see calc_test.ml .) Implemented matrix trace. 2004-08-29 Integrated unit handling code. Real and complex scalars and matrices can are now dimensioned quantities. Unit conversion and base standardization operators were added. When entering external data, the integer base separator character was changed from '_' to '`'. 2004-08-27 Raising a negative real number to a noninteger power now produces a complex value rather than real-valued 'nan'. 2004-08-23 Deprecated "extended_" operation identifiers in favor of somewhat more clear "abbrev_" identifiers. Added methods to include (i.e. source) rcfiles within each other, remove key bindings, and remove operation abbreviations. 2004-07-19 Implemented automatic key bindings. Deprecated operation "function_rand" in favor of "command_rand" because it does not take an argument. v1.3 2004-07-22 Version 1.3.1 release. Ocaml 3.08 support: an include string is provided to the compiler when building curses bindings. Added a fix for a possible crash when drawing help panel and using a custom minimalist orpierc. 2004-07-18 User variables are now evaluated before being passed as arguments to var(). 2004-07-17 Version 1.3.0 release. Added permutation function and random number generator. Implemented common single-variable statistics functions, and an upper tail normal probability function. 2004-07-16 Included "register" variable shortcut macros in orpierc. Added a configuration option to conserve memory by turning off string caching. Added a background thread that precomputes string representations of stack elements. 2004-07-13 Implemented memoization of string representations for stack elements. 2004-07-08 Replaced general integer base conversion algorithm with fast (N*log(N) or better) divide-and- -conquer algorithms. 2004-07-04 Added binomial coefficient function. 2004-07-03 Added LCM function. 2004-06-27 Implemented a method for handling interruptible computations. Added exact factorial and GCD functions. v1.2 2004-06-16 Version 1.2.0 release. 2004-06-15 Fixed the build script to support parallel make. Replaced 'datafile', 'buffer', and 'input_buffer' orpierc variables with the 'datadir' variable. Fixed crash due to displaying 'about' screen in a very small terminal window. 2004-06-14 Implemented user-defined variables and autocompletion routines. 2004-06-13 mod() now has a check for >1 stack elements. 2004-04-22 Orpie no longer generates an error message if it is run without a preexisting calculator state file. v1.1 2004-04-17 Version 1.1.1 release. Added Chris Petersen's orpie.spec file for building RPMs. 2004-04-16 Version 1.1 release. 2004-04-14 Added a linear system solver that avoids computation of an inverse matrix. 2004-04-13 Created a parser and associated routines that enable Orpie to read data entered in an external editor. 2004-04-10 Added a check for inversion of ill-conditioned real matrices. Implemented Mutt-like rcfile macros. 2004-04-09 Added a configuration variable to enable hiding the help panel. v1.0 2004-04-07 Version 1.0.2 release. Added a manpage for orpie-curses-keys(1). 2004-04-05 Version 1.0.1 release. Minor changes to the build script, to assist in package creation (thanks to Uwe Steinmann for the patch). 2004-04-01 Version 1.0 release. orpie-release-1.6.0/LICENSE.md000066400000000000000000001041421334120314700156560ustar00rootroot00000000000000### GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. ### Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. ### TERMS AND CONDITIONS #### 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. #### 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. #### 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. #### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. #### 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. #### 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: - a) The work must carry prominent notices stating that you modified it, and giving a relevant date. - b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". - c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. - d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. #### 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: - a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. - b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. - c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. - d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. - e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. #### 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: - a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or - b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or - c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or - d) Limiting the use for publicity purposes of names of licensors or authors of the material; or - e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or - f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. #### 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. #### 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. #### 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. #### 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. #### 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. #### 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. #### 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. #### 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. #### 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. #### 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS ### How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands \`show w' and \`show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . orpie-release-1.6.0/Makefile000066400000000000000000000005751334120314700157170ustar00rootroot00000000000000 _build/install/default/bin/orpie: dune build _build/install/default/bin/orpie-curses-keys: dune build install: _build/install/default/bin/orpie _build/install/default/bin/orpie-curses-keys ORPIE_PREFIX=`scripts/compute_prefix eval` && \ mkdir -p "$(DESTDIR)/$$ORPIE_PREFIX" && \ dune install --prefix="$(DESTDIR)/$$ORPIE_PREFIX" clean: dune clean .PHONY: install clean orpie-release-1.6.0/README.adoc000066400000000000000000000016221334120314700160360ustar00rootroot00000000000000Orpie README ============ Orpie is a Curses-based RPN calculator. == Installation === Using OPAM The recommended method for installing Orpie is to use https://opam.ocaml.org/[OPAM]. +opam install orpie+ should get the job done. === Without using OPAM If you want to install manually, you will need the following OCaml packages installed: * OCaml 4.03+ * dune * camlp5 * ocamlfind * curses (registered with ocamlfind) * gsl (registered with ocamlfind) If you have satisfied all the dependencies, then use the Makefile to build: ---- # optionally set an installation prefix (default is /usr/local) $ export PREFIX=/usr # optionally set a staging directory (useful if you're creating a package) $ export DESTDIR=/tmp/orpie # build $ make # install build products (use 'sudo' if installing to a root-owned location) $ make install ---- == Usage See the 'doc' subdirectory for more extensive documentation. orpie-release-1.6.0/doc/000077500000000000000000000000001334120314700150155ustar00rootroot00000000000000orpie-release-1.6.0/doc/Makefile000066400000000000000000000021321334120314700164530ustar00rootroot00000000000000# Orpie documentation makefile all: manual.pdf manual.html post-build-cleanup orpie.1 orpierc.5 orpie-curses-keys.1 manual.ps: dvi dvips -tletterSize -Ppdf -G0 manual.dvi dvi: manual.tex latex manual.tex latex manual.tex manual.tex: manual.tex.in latex2man -CLATEX -L manual.tex.in manual.tex manual.pdf: manual.ps ps2pdf13 manual.ps manual.html: manual.tex hevea -fix manual.tex hevea -fix manual.tex manual.tex.stripped: manual.tex.in python remove-tt.py manual.tex.in manual.tex.stripped orpie.1: manual.tex.stripped latex2man -M manual.tex.stripped orpie.1 orpierc.5: manual.tex.stripped latex2man -CORPIERC -M manual.tex.stripped orpierc.5 orpie-curses-keys.1: orpie-curses-keys.tex latex2man -M orpie-curses-keys.tex orpie-curses-keys.1 post-build-cleanup: manual.pdf manual.html orpie.1 orpierc.5 orpie-curses-keys.1 rm -f *.aux *.log *.toc *.haux *.htoc *.dvi *.ps *.stripped manual.tex clean: rm -f manual.tex *.aux *.log *.toc *.haux *.htoc *.dvi *.ps *.pdf *.html *.stripped orpie.1 orpierc.5 orpie-curses-keys.1 # arch-tag: DO_NOT_CHANGE_a5d62ea3-3a73-4de2-a2b3-a70bb310823f orpie-release-1.6.0/doc/TODO000066400000000000000000000005371334120314700155120ustar00rootroot00000000000000Orpie To-Do List (roughly in order of priority) -------------------------------------------------------------------------------- Orpie is pretty much in maintenance mode at the moment. Please report bugs to . New features are unlikely to be implemented by the Orpie maintaner, but reasonable patches will certainly be accepted. orpie-release-1.6.0/doc/dune000066400000000000000000000002071334120314700156720ustar00rootroot00000000000000(install (section doc) (files manual.html manual.pdf)) (install (section man) (files orpie.1 orpie-curses-keys.1 orpierc.5)) orpie-release-1.6.0/doc/manual.html000066400000000000000000003704721334120314700171750ustar00rootroot00000000000000 Orpie v1.6 User Manual

Orpie v1.6 User Manual

Paul J. Pelzl

August 27, 2018

“Because the equals key is for the weak.”

Contents

1  Introduction

Orpie is a console-based RPN (reverse polish notation) desktop calculator. The interface is similar to that of modern Hewlett-PackardTM calculators, but has been optimized for efficiency on a PC keyboard. The design is also influenced to some degree by the Mutt email client and the Vim editor.

Orpie does not have graphing capability, nor does it offer much in the way of a programming interface; other applications such as GNU Octave. are already very effective for such tasks. Orpie focuses specifically on helping you to crunch numbers quickly.

Orpie is written in OCaml).

2  Installation

The recommended method to install Orpie is through the OPAM package manager. “opam install orpie” should get the job done.

If you want to install without using OPAM, you will need the following OCaml packages installed:

  • OCaml 4.03+
  • dune
  • camlp5
  • ocamlfind
  • curses (registered with ocamlfind)
  • gsl (registered with ocamlfind)

If you have satisfied all the dependencies, then use the Makefile to build:

# optionally set an installation prefix (default is /usr/local)
$ export PREFIX=/usr

# optionally set a staging directory (useful if you're creating a package)
$ export DESTDIR=/tmp/orpie

# build
$ make

# install build products (use 'sudo' if installing to a root-owned location)
$ make install

3  Quick Start

This section describes how to use Orpie in its default configuration. After familiarizing yourself with the basic operations as outlined in this section, you may wish to consult Section 4 to see how Orpie can be configured to better fit your needs.

3.1  Overview

You can start the calculator by executing orpie. The interface has two panels. The left panel combines status information with context-sensitive help; the right panel represents the calculator’s stack. (Note that the left panel will be hidden if Orpie is run in a terminal with less than 80 columns.)

In general, you perform calculations by first entering data on to the stack, then executing functions that operate on the stack data. As an example, you can hit 1<enter>2<enter>+ in order to add 1 and 2.

3.2  Entering Data

3.2.1  Entering Real Numbers

To enter a real number, just type the desired digits and hit enter. The space bar will begin entry of a scientific notation exponent. The ’n’ key is used for negation. Here are some examples:

KeypressesResulting Entry
1.23<enter>1.23
1.23<space>23n<enter>1.23e-23
1.23n<space>23<enter>-1.23e23

3.2.2  Entering Complex Numbers

Orpie can represent complex numbers using either cartesian (rectangular) or polar coordinates. See Section 3.5 to see how to change the complex number display mode.

A complex number is entered by first pressing ’(’, then entering the real part, then pressing ’,’ followed by the imaginary part. Alternatively, you can press ’(’ followed by the magnitude, then ’<’ followed by the phase angle. The angle will be interpreted in degrees or radians, depending on the current setting of the angle mode (see Section 3.5). Examples:

KeypressesResulting Entry
(1.23, 4.56<enter>(1.23, 4.56)
(0.7072<45<enter>(0.500065915655126, 0.50006591...
(1.23n,4.56<space>10<enter>(-1.23, 45600000000)

3.2.3  Entering Matrices

You can enter matrices by pressing ’[’. The elements of the matrix may then be entered as described in the previous sections, and should be separated using ’,’. To start a new row of the matrix, press ’[’ again. On the stack, each row of the matrix is enclosed in a set of brackets; for example, the matrix

12
34

would appear on the stack as [[1, 2][3, 4]].

Examples of matrix entry:

KeypressesResulting Entry
[1,2[3,4<enter>[[1, 2][3, 4]]
[1.2<space>10,0[3n,5n<enter>[[ 12000000000, 0 ][ -3, -5 ]]
[(1,2,3,4[5,6,7,8<enter>[[ (1, 2), (3, 4) ][ (5, 6), (...

3.2.4  Entering Data With Units

Real and complex scalars and matrices can optionally be labeled with units. After typing in the numeric portion of the data, press ’_’ followed by a units string. The format of units strings is described in Section 3.8.

Examples of entering dimensioned data:

KeypressesResulting Entry
1.234_N*mm^2/s<enter>1.234_N*mm^2*s^-1
(2.3,5_s^-4<enter>(2.3, 5)_s^-4
[1,2[3,4_lbf*in<enter>[[ 1, 2 ][ 3, 4 ]]_lbf*in
_nm<enter>1_nm

3.2.5  Entering Exact Integers

An exact integer may be entered by pressing ’#’ followed by the desired digits. The base of the integer will be assumed to be the same as the current calculator base mode (see Section 3.5 to see how to set this mode). Alternatively, the desired base may be specified by pressing space and appending one of {b, o, d, h}, to represent binary, octal, decimal, or hexadecimal, respectively. On the stack, the representation of the integer will be changed to match the current base mode. Examples:

KeypressesResulting Entry
#123456<enter># 123456`d
#ffff<space>h<enter># 65535`d
#10101n<space>b<enter># -21`d

Note that exact integers may have unlimited length, and the basic arithmetic operations (addition, subtraction, multiplication, division) will be performed using exact arithmetic when both arguments are integers.

3.2.6  Entering Variable Names

A variable name may be entered by pressing ’@’ followed by the desired variable name string. The string may contain alphanumeric characters, dashes, and underscores. Example:

KeypressesResulting Entry
@myvar@ myvar

Orpie also supports autocompletion of variable names. The help panel displays a list of pre-existing variables that partially match the name currently being entered. You can press ’<tab>’ to iterate through the list of matching variables.

As a shortcut, keys <f1>-<f4> will enter the variables (“registers”) @ r01 through @ r04.

3.2.7  Entering Physical Constants

Orpie includes definitions for a number of fundamental physical constants. To enter a constant, press ’C’, followed by the first few letters/digits of the constant’s symbol, then hit enter. Orpie offers an autocompletion feature for physical constants, so you only need to type enough of the constant to identify it uniquely. A list of matching constants will appear in the left panel of the display, to assist you in finding the desired choice.

The following is a list of Orpie’s physical constant symbols:

SymbolPhysical Constant
NAAvagadro’s number
kBoltzmann constant
Vmmolar volume
Runiversal gas constant
stdTstandard temperature
stdPstandard pressure
sigmaStefan-Boltzmann constant
cspeed of light
eps0permittivity of free space
u0permeability of free space
gacceleration of gravity
GNewtonian gravitational constant
hPlanck’s constant
hbarDirac’s constant
eelectron charge
meelectron mass
mpproton mass
alphafine structure constant
phimagnetic flux quantum
FFaraday’s constant
Rinf“infinity” Rydberg constant
a0Bohr radius
uBBohr magneton
uNnuclear magneton
lam0wavelength of a 1eV photon
f0frequency of a 1eV photon
lamcCompton wavelength
c3Wien’s constant

All physical constants are defined in the Orpie run-configuration file; consult Section 4 if you wish to define your own constants or change the existing definitions.

3.2.8  Entering Data With an External Editor

Orpie can also parse input entered via an external editor. You may find this to be a convenient method for entering large matrices. Pressing ’E’ will launch the external editor, and the various data types may be entered as illustrated by the examples below:

Data TypeSample Input String
exact integer#12345678`d, where the trailing letter is one of the base characters {b, o, d, h}
real number-123.45e67
complex number(1e10, 2) or (1 <90)
real matrix[[1, 2][3.1, 4.5e10]]
complex matrix[[(1, 0), 5][1e10, (2 <90)]]
variable@myvar

Real and complex numbers and matrices may have units appended; just add a units string such as “_N*m/s” immediately following the numeric portion of the expression.

Notice that the complex matrix input parser is quite flexible; real and complex matrix elements may be mixed, and cartesian and polar complex formats may be mixed as well.

Multiple stack entries may be specified in the same file, if they are separated by whitespace. For example, entering (1, 2) 1.5 into the editor will cause the complex value (1, 2) to be placed on the stack, followed by the real value 1.5.

The input parser will discard whitespace where possible, so feel free to add any form of whitespace between matrix rows, matrix elements, real and complex components, etc.

3.3  Executing Basic Function Operations

Once some data has been entered on the stack, you can apply operations to that data. For example, ’+’ will add the last two elements on the stack. By default, the following keys have been bound to such operations:

KeysOperations
+add last two stack elements
-subtract element 1 from element 2
*multiply last two stack elements
/divide element 2 by element 1
^raise element 2 to the power of element 1
nnegate last element
iinvert last element
ssquare root function
aabsolute value function
eexponential function
lnatural logarithm function
ccomplex conjugate function
!factorial function
%element 2 mod element 1
Sstore element 2 in (variable) element 1
;evaluate variable to obtain contents

As a shortcut, function operators will automatically enter any data that you were in the process of entering. So instead of the sequence 2<enter>2<enter>+, you could type simply 2<enter>2+ and the second number would be entered before the addition operation is applied.

As an additional shortcut, any variable names used as function arguments will be evaluated before application of the function. In other words, it is not necessary to evaluate variables before performing arithmetic operations on them.

3.4  Executing Function Abbreviations

One could bind nearly all calculator operations to specific keypresses, but this would rapidly get confusing since the PC keyboard is not labeled as nicely as a calculator keyboard is. For this reason, Orpie includes an abbreviation syntax.

To activate an abbreviation, press ’’ (quote key), followed by the first few letters/digits of the abbreviation, then hit enter. Orpie offers an autocompletion feature for abbreviations, so you only need to type enough of the operation to identify it uniquely. The matching abbreviations will appear in the left panel of the display, to assist you in finding the appropriate operation.

To avoid interface conflicts, abbreviations may be entered only when the entry buffer (the bottom line of the screen) is empty.

The following functions are available as abbreviations:

AbbreviationsFunctions
invinverse function
powraise element 2 to the power of element 1
sqsquare last element
sqrtsquare root function
absabsolute value function
expexponential function
lnnatural logarithm function
10^base 10 exponential function
log10base 10 logarithm function
conjcomplex conjugate function
sinsine function
coscosine function
tantangent function
sinhhyperbolic sine function
coshhyperbolic cosine function
tanhhyperbolic tangent function
asinarcsine function
acosarccosine function
atanarctangent function
asinhinverse hyperbolic sine function
acoshinverse hyperbolic cosine function
atanhinverse hyperbolic tangent function
rereal part of complex number
imimaginary part of complex number
gammaEuler gamma function
lngammanatural log of Euler gamma function
erferror function
erfccomplementary error function
factfactorial function
gcdgreatest common divisor function
lcmleast common multiple function
binombinomial coefficient function
permpermutation function
transmatrix transpose
tracetrace of a matrix
solvelinsolve a linear system of the form Ax = b
modelement 2 mod element 1
floorfloor function
ceilceiling function
tointconvert a real number to an integer type
torealconvert an integer type to a real number
addadd last two elements
subsubtract element 1 from element 2
multmultiply last two elements
divdivide element 2 by element 1
negnegate last element
storestore element 2 in (variable) element 1
evalevaluate variable to obtain contents
purgedelete a variable
totalsum the columns of a real matrix
meancompute the sample means of the columns of a real matrix
sumsqsum the squares of the columns of a real matrix
varcompute the unbiased sample variances of the columns of a real matrix
varbiascompute the biased (population) sample variances of the columns of a real matrix
stdevcompute the unbiased sample standard deviations of the columns of a real matrix
stdevbiascompute the biased (pop.) sample standard deviations of the columns of a matrix
minfind the minima of the columns of a real matrix
maxfind the maxima of the columns of a real matrix
utpncompute the upper tail probability of a normal distribution
uconvertconvert element 2 to an equivalent expression with units matching element 1
ustandconvert to equivalent expression using SI standard base units
uvaluedrop the units of the last element

Entering abbreviations can become tedious when performing repetitive calculations. To save some keystrokes, Orpie will automatically bind recently-used operations with no prexisting binding to keys <f5>-<f12>. The current autobindings can be viewed by pressing ’h’ to cycle between the various pages of the help panel.

3.5  Executing Basic Command Operations

In addition to the function operations listed in Section 3.3, a number of basic calculator commands have been bound to single keypresses:

KeysOperations
\drop last element
|clear all stack elements
<pagedown>swap last two elements
<enter>duplicate last element (when entry buffer is empty)
uundo last operation
rtoggle angle mode between degrees and radians
ptoggle complex display mode between rectangular and polar
bcycle base display mode between binary, octal, decimal, hex
hcycle through multiple help windows
vview last stack element in a fullscreen editor
Ecreate a new stack element using an external editor
Penter 3.1415…on the stack
C-Lrefresh the display
<up>begin stack browsing mode
Qquit Orpie

3.6  Executing Command Abbreviations

In addition to the function operations listed in Section 3.4, there are a large number of calculator commands that have been implemented using the abbreviation syntax:

AbbreviationsCalculator Operation
dropdrop last element
clearclear all stack elements
swapswap last two elements
dupduplicate last element
undoundo last operation
radset angle mode to radians
degset angle mode to degrees
rectset complex display mode to rectangular
polarset complex display mode to polar
binset base display mode to binary
octset base display mode to octal
decset base display mode to decimal
hexset base display mode to hexidecimal
viewview last stack element in a fullscreen editor
editcreate a new stack element using an external editor
pienter 3.1415…on the stack
randgenerate a random number between 0 and 1 (uniformly distributed)
refreshrefresh the display
aboutdisplay a nifty “About Orpie” screen
quitquit Orpie

3.7  Browsing the Stack

Orpie offers a stack browsing mode to assist in viewing and manipulating stack data. Press <up> to enter stack browsing mode; this should highlight the last stack element. You can use the up and down arrow keys to select different stack elements. The following keys are useful in stack browsing mode:

KeysOperations
qquit stack browsing mode
<left>scroll selected entry to the left
<right>scroll selected entry to the right
rcyclically “roll” stack elements downward, below the selected element (inclusive)
Rcyclically “roll” stack elements upward, below the selected element (inclusive)
vview the currently selected element in a fullscreen editor
Eedit the currently selected element with an external editor
<enter>duplicate the currently selected element

The left and right scrolling option may prove useful for viewing very lengthy stack entries, such as large matrices. The edit option provides a convenient way to correct data after it has been entered on the stack.

3.8  Units Formatting

A units string is a list of units separated by ’*’ to indicate multiplication and ’/’ to indicate division. Units may be raised to real-valued powers using the ’^’ character. A contrived example of a valid unit string would be "N*nm^2*kg/s/in^-3*GHz^2.34".

Orpie supports the standard SI prefix set, {y, z, a, f, p, n, u, m, c, d, da, h, k, M, G, T, P, E, Z, Y} (note the use of ’u’ for micro-). These prefixes may be applied to any of the following exhaustive sets of units:

StringLength Unit
mmeter
ftfoot
ininch
ydyard
mimile
pcparsec
AUastronomical unit
Angangstrom
furlongfurlong
ptPostScript point
picaPostScript pica
nminautical mile
lyrlightyear
StringMass Unit
ggram
lbpound mass
ozounce
slugslug
lbtTroy pound
ton(USA) short ton
tonl(UK) long ton
tonmmetric ton
ctcarat
grgrain
StringTime Unit
ssecond
minminute
hrhour
dayday
yryear
HzHertz
StringTemperature Unit
KKelvin
RRankine

Note: No, Celsius and Fahrenheit will not be supported. Because these temperature units do not share a common zero point, their behavior is ill-defined under many operations.

String“Amount of Substance” Unit
molMole
StringForce Unit
NNewton
lbfpound force
dyndyne
kipkip
StringEnergy Unit
JJoule
ergerg
calcalorie
BTUbritish thermal unit
eVelectron volt
StringElectrical Unit
AAmpere
CCoulomb
Vvolt
OhmOhm
FFarad
HHenry
TTesla
GGauss
WbWeber
MxMaxwell
StringPower Unit
WWatt
hphorsepower
StringPressure Unit
PaPascal
atmatmosphere
barbar
OhmOhm
mmHgmillimeters of mercury
inHginches of mercury
StringLuminance Unit
cdcandela
lmlumen
lxlux

Note: Although the lumen is defined by 1_lm = 1_cd * sr, Orpie drops the steridian because it is a dimensionless unit and therefore is of questionable use to a calculator.

StringVolume Unit
ozflfluid ounce (US)
cupcup (US)
ptpint (US)
qtquart (US)
galgallon (US)
Lliter

All units are defined in the Orpie run-configuration file; consult Section 4 if you wish to define your own units or change the existing definitions.

4  Advanced Configuration

Orpie reads a run-configuration textfile (generally /etc/orpierc or /usr/local/etc/orpierc) to determine key and command bindings. You can create a personalized configuration file in $HOME/.orpierc, and select bindings that match your usage patterns. The recommended procedure is to “include” the orpierc file provided with Orpie (see Section 4.1.1), and add or remove settings as desired.

4.1  orpierc Syntax

You may notice that the orpierc syntax is similar to the syntax used in the configuration file for the Mutt email client (muttrc).

Within the orpierc file, strings should be enclosed in double quotes ("). A double quote character inside a string may be represented by \" . The backslash character must be represented by doubling it (\\).

4.1.1  Including Other Rcfiles

Syntax: include filename_string

This syntax can be used to include one run-configuration file within another. This command could be used to load the default orpierc file (probably found in /etc/orpierc) within your personalized rcfile, ~/.orpierc. The filename string should be enclosed in quotes.

4.1.2  Setting Configuration Variables

Syntax: set variable=value_string

Several configuration variables can be set using this syntax; check Section 4.2 to see a list. The variables are unquoted, but the values should be quoted strings.

4.1.3  Creating Key Bindings

Syntax: bind key_identifier operation

This command will bind a keypress to execute a calculator operation. The various operations, which should not be enclosed in quotes, may be found in Section 4.3. Key identifiers may be specified by strings that represent a single keypress, for example "m" (quotes included). The key may be prefixed with "\\C" or "\\M" to represent Control or Meta (Alt) modifiers, respectively; note that the backslash must be doubled. A number of special keys lack single-character representations, so the following strings may be used to represent them:

  • "<esc>"
  • "<tab>"
  • "<enter>"
  • "<return>"
  • "<insert>"
  • "<home>"
  • "<end>"
  • "<pageup>"
  • "<pagedown>"
  • "<space>"
  • "<left>"
  • "<right>"
  • "<up>"
  • "<down>"
  • "<f1>" to "<f12>"

Due to differences between various terminal emulators, this key identifier syntax may not be adequate to describe every keypress. As a workaround, Orpie will also accept key identifiers in octal notation. As an example, you could use \024 (do not enclose it in quotes) to represent Ctrl-T.

Orpie includes a secondary executable, orpie-curses-keys, that prints out the key identifiers associated with keypresses. You may find it useful when customizing orpierc.

Multiple keys may be bound to the same operation, if desired.

4.1.4  Removing Key Bindings

Syntax:
unbind_function key_identifier
unbind_command key_identifier
unbind_edit key_identifier
unbind_browse key_identifier
unbind_abbrev key_identifier
unbind_variable key_identifier
unbind_integer key_identifier

These commands will remove key bindings associated with the various entry modes (functions, commands, editing operations, etc.). The key identifiers should be defined using the syntax described in the previous section.

4.1.5  Creating Key Auto-Bindings

Syntax: autobind key_identifier

In order to make repetitive calculations more pleasant, Orpie offers an automatic key binding feature. When a function or command is executed using its abbreviation, one of the keys selected by the autobind syntax will be automatically bound to that operation (unless the operation has already been bound to a key). The current set of autobindings can be viewed in the help panel by executing command_cycle_help (bound to ’h’ by default).

The syntax for the key identifiers is provided in the previous section.

4.1.6  Creating Operation Abbreviations

Syntax: abbrev operation_abbreviation operation

You can use this syntax to set the abbreviations used within Orpie to represent the various functions and commands. A list of available operations may be found in Section 4.3. The operation abbreviations should be quoted strings, for example "sin" or "log".

Orpie performs autocompletion on these abbreviations, allowing you to type usually just a few letters in order to select the desired command. The order of the autocompletion matches will be the same as the order in which the abbreviations are registered by the rcfile–so you may wish to place the more commonly used operation abbreviations earlier in the list.

Multiple abbreviations may be bound to the same operation, if desired.

4.1.7  Removing Operation Abbreviations

Syntax: unabbrev operation_abbreviation

This syntax can be used to remove an operation abbreviation. The operation abbreviations should be quoted strings, as described in the previous section.

4.1.8  Creating Macros

Syntax: macro key_identifier macro_string

You can use this syntax to cause a single keypress (the key_identifier) to be interpreted as the series of keypresses listed in macro_string. The syntax for defining a keypress is the same as that defined in Section 4.1.3. The macro string should be a list of whitespace-separated keypresses, e.g. "2 <return> 2 +" (including quotes).

This macro syntax provides a way to create small programs; by way of example, the default orpierc file includes macros for the base 2 logarithm and the binary entropy function (bound to L and H, respectively), as well as “register” variable shortcuts (<f1> to <f12>).

Macros may call other macros recursively. However, take care that a macro does not call itself recursively; Orpie will not trap the infinite loop.

Note that operation abbreviations may be accessed within macros. For example, macro "A" "’ a b o u t <return>" would bind A to display the “about Orpie” screen.

4.1.9  Creating Units

Syntax:
base_unit unit_symbol preferred_prefix
unit unit_symbol unit_definition

Units are defined in a two-step process:

  1. Define a set of orthogonal “base units.” All other units must be expressible in terms of these base units. The base units can be given a preferred SI prefix, which will be used whenever the units are standardized (e.g. via ustand). The unit symbols and preferred prefixes should all be quoted strings; to prefer no prefix, use the empty string ("").

    It is expected that most users will use the fundamental SI units for base units.

  2. Define all other units in terms of either base units or previously-defined units. Again, the unit symbol and unit definition should be quoted strings. The definition should take the form of a numeric value followed by a units string, e.g. "2.5_kN*m/s". See Section 3.8 for more details on the unit string format.

4.1.10  Creating Constants

Syntax: constant constant_symbol constant_definition

This syntax can be used to define a physical constant. Both the constant symbol and definition must be quoted strings. The constant definition should be a numeric constant followed by a units string e.g. "1.60217733e-19_C". All units used in the constant definition must already have been defined.

4.2  Configuration Variables

The following configuration variables may be set as described in Section 4.1.2:

  • datadir
    This variable should be set to the full path of the Orpie data directory, which will contain the calculator state save file, temporary buffers, etc. The default directory is "~/.orpie/".
  • editor
    This variable may be set to the fullscreen editor of your choice. The default value is "vi". It is recommended that you choose an editor that offers horizontal scrolling in place of word wrapping, so that the columns of large matrices can be properly aligned. (The Vim editor could be used in this fashion by setting editor to "vim -c ’set nowrap’".)
  • hide_help
    Set this variable to "true" to hide the left help/status panel, or leave it on the default of "false" to display the help panel.
  • conserve_memory
    Set this variable to "true" to minimize memory usage, or leave it on the default of "false" to improve rendering performance. (By default, Orpie caches multiple string representations of all stack elements. Very large integers in particular require significant computation for string representation, so caching these strings can make display updates much faster.)

4.3  Calculator Operations

Every calculator operation can be made available to the interface using the syntax described in Sections 4.1.3 and 4.1.6. The following is a list of every available operation.

4.3.1  Functions

The following operations are functions–that is, they will consume at least one argument from the stack. Orpie will generally abort the computation and provide an informative error message if a function cannot be successfully applied (for example, if you try to compute the transpose of something that is not a matrix).

For the exact integer data type, basic arithmetic operations will yield an exact integer result. Division of two exact integers will yield the quotient of the division. The more complicated functions will generally promote the integer to a real number, and as such the arithmetic will no longer be exact.

  • function_10_x
    Raise 10 to the power of the last stack element (inverse of function_log10).
  • function_abs
    Compute the absolute value of the last stack element.
  • function_acos
    Compute the inverse cosine of the last stack element. For real numbers, The result will be provided either in degrees or radians, depending on the angle mode of the calculator.
  • function_acosh
    Compute the inverse hyperbolic cosine of the last stack element.
  • function_add
    Add last two stack elements.
  • function_arg
    Compute the argument (phase angle of complex number) of the last stack element. The value will be provided in either degrees or radians, depending on the current angle mode of the calculator.
  • function_asin
    Compute the inverse sine of the last stack element. For real numbers, The result will be provided either in degrees or radians, depending on the angle mode of the calculator.
  • function_asinh
    Compute the inverse hyperbolic sine of the last stack element.
  • function_atan
    Compute the inverse tangent of the last stack element. For real numbers, The result will be provided either in degrees or radians, depending on the angle mode of the calculator.
  • function_atanh
    Compute the inverse hyperbolic tangent of the last stack element.
  • function_binomial_coeff
    Compute the binomial coefficient (“n choose k”) formed by the last two stack elements. If these arguments are real, the coefficient is computed using a fast approximation to the log of the gamma function, and therefore the result is subject to rounding errors. For exact integer arguments, the coefficient is computed using exact arithmetic; this has the potential to be a slow operation.
  • function_ceiling
    Compute the ceiling of the last stack element.
  • function_convert_units
    Convert stack element 2 to an equivalent expression in the units of element 1. Element 1 should be real-valued, and its magnitude will be ignored when computing the conversion.
  • function_cos
    Compute the cosine of the last stack element. If the argument is real, it will be assumed to be either degrees or radians, depending on the angle mode of the calculator.
  • function_cosh
    Compute the hyperbolic cosine of the last stack element.
  • function_conj
    Compute the complex conjugate of the last stack element.
  • function_div
    Divide element 2 by element 1.
  • function_erf
    Compute the error function of the last stack element.
  • function_erfc
    Compute the complementary error function of the last stack element.
  • function_eval
    Obtain the contents of the variable in the last stack position.
  • function_exp
    Evaluate the exponential function of the last stack element.
  • function_factorial
    Compute the factorial of the last stack element. For a real argument, this is computed using a fast approximation to the gamma function, and therefore the result may be subject to rounding errors (or overflow). For an exact integer argument, the factorial is computed using exact arithmetic; this has the potential to be a slow operation.
  • function_floor
    Compute the floor of the last stack element.
  • function_gamma
    Compute the Euler gamma function of the last stack element.
  • function_gcd
    Compute the greatest common divisor of the last two stack elements. This operation may be applied only to integer type data.
  • function_im
    Compute the imaginary part of the last stack element.
  • function_inv
    Compute the multiplicative inverse of the last stack element.
  • function_lcm
    Compute the least common multiple of the last two stack elements. This operation may be applied only to integer type data.
  • function_ln
    Compute the natural logarithm of the last stack element.
  • function_lngamma
    Compute the natural logarithm of the Euler gamma function of the last stack element.
  • function_log10
    Compute the base-10 logarithm of the last stack element.
  • function_maximum
    Find the maximum values of each of the columns of a real NxM matrix, returning a 1xM matrix as a result.
  • function_minimum
    Find the minimum values of each of the columns of a real NxM matrix, returning a 1xM matrix as a result.
  • function_mean
    Compute the sample means of each of the columns of a real NxM matrix, returning a 1xM matrix as a result.
  • function_mod
    Compute element 2 mod element 1. This operation can be applied only to integer type data.
  • function_mult
    Multiply last two stack elements.
  • function_neg
    Negate last stack element.
  • function_permutation
    Compute the permutation coefficient determined by the last two stack elements ’n’ and ’k’: the number of ways of obtaining an ordered subset of k elements from a set of n elements. If these arguments are real, the coefficient is computed using a fast approximation to the log of the gamma function, and therefore the result is subject to rounding errors. For exact integer arguments, the coefficient is computed using exact arithmetic; this has the potential to be a slow operation.
  • function_pow
    Raise element 2 to the power of element 1.
  • function_purge
    Delete the variable in the last stack position.
  • function_re
    Compute the real part of the last stack element.
  • function_sin
    Compute the sine of the last stack element. If the argument is real, it will be assumed to be either degrees or radians, depending on the angle mode of the calculator.
  • function_sinh
    Compute the hyperbolic sine of the last stack element.
  • function_solve_linear
    Solve a linear system of the form Ax = b, where A and b are the last two elements on the stack. A must be a square matrix and b must be a matrix with one column. This function does not compute inv(A), but obtains the solution by a more efficient LU decomposition method. This function is recommended over explicitly computing the inverse, especially when solving linear systems with relatively large dimension or with poorly conditioned matrices.
  • function_sq
    Square the last stack element.
  • function_sqrt
    Compute the square root of the last stack element.
  • function_standardize_units
    Convert the last stack element to an equivalent expression using the SI standard base units (kg, m, s, etc.).
  • function_stdev_unbiased
    Compute the unbiased sample standard deviation of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. (Compare to HP48’s sdev function.)
  • function_stdev_biased
    Compute the biased (population) sample standard deviation of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. (Compare to HP48’s psdev function.)
  • function_store
    Store element 2 in (variable) element 1.
  • function_sub
    Subtract element 1 from element 2.
  • function_sumsq
    Sum the squares of each of the columns of a real NxM matrix, returning a 1xM matrix as a result.
  • function_tan
    Compute the tangent of the last stack element. If the argument is real, it will be assumed to be either degrees or radians, depending on the angle mode of the calculator.
  • function_tanh
    Compute the hyperbolic tangent of the last stack element.
  • function_to_int
    Convert a real number to an integer type.
  • function_to_real
    Convert an integer type to a real number.
  • function_total
    Sum each of the columns of a real NxM matrix, returning a 1xM matrix as a result.
  • function_trace
    Compute the trace of a square matrix.
  • function_transpose
    Compute the matrix transpose of the last stack element.
  • function_unit_value
    Drop the units of the last stack element.
  • function_utpn
    Compute the upper tail probability of a normal distribution.
    UTPN(m, v, x) = Integrate[ 1/Sqrt[2 Pi v] Exp[-(m-y)2/(2 v)], {y, x, Infinity}]
  • function_var_unbiased
    Compute the unbiased sample variance of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. (Compare to HP48’s var function.)
  • function_var_biased
    Compute the biased (population) sample variance of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. (Compare to HP48’s pvar function.)

4.3.2  Commands

The following operations are referred to as commands; they differ from functions because they do not take an argument. Many calculator interface settings are implemented as commands.

  • command_about
    Display a nifty “about Orpie” credits screen.
  • command_begin_abbrev
    Begin entry of an operation abbreviation.
  • command_begin_browsing
    Enter stack browsing mode.
  • command_begin_constant
    Begin entry of a physical constant.
  • command_begin_variable
    Begin entry of a variable name.
  • command_bin
    Set the base of exact integer representation to 2 (binary).
  • command_clear
    Clear all elements from the stack.
  • command_cycle_base
    Cycle the base of exact integer representation between 2, 8, 10, and 16 (bin, oct, dec, and hex).
  • command_cycle_help
    Cycle through multiple help pages. The first page displays commonly used bindings, and the second page displays the current autobindings.
  • command_dec
    Set the base of exact integer representation to 10 (decimal).
  • command_deg
    Set the angle mode to degrees.
  • command_drop
    Drop the last element off the stack.
  • command_dup
    Duplicate the last stack element.
  • command_enter_pi
    Enter 3.1415…on the stack.
  • command_hex
    Set the base of exact integer representation to 16 (hexadecimal).
  • command_oct
    Set the base of exact integer representation to 8 (octal).
  • command_polar
    Set the complex display mode to polar.
  • command_rad
    Set the angle mode to radians.
  • command_rand
    Generate a random real-valued number between 0 (inclusive) and 1 (exclusive). The deviates are uniformly distributed.
  • command_rect
    Set the complex display mode to rectangular (cartesian).
  • command_refresh
    Refresh the display.
  • command_swap
    Swap stack elements 1 and 2.
  • command_quit
    Quit Orpie.
  • command_toggle_angle_mode
    Toggle the angle mode between degrees and radians.
  • command_toggle_complex_mode
    Toggle the complex display mode between rectangular and polar.
  • command_undo
    Undo the last calculator operation.
  • command_view
    View the last stack element in an external fullscreen editor.
  • command_edit_input
    Create a new stack element using an external editor.

4.3.3  Edit Operations

The following operations are related to editing during data entry. These commands cannot be made available as operation abbreviations, since abbreviations are not accessible while entering data. These operations should be made available as single keypresses using the bind keyword.

  • edit_angle
    Begin entering the phase angle of a complex number. (Orpie will assume the angle is in either degrees or radians, depending on the current angle mode.)
  • edit_backspace
    Delete the last character entered.
  • edit_begin_integer
    Begin entering an exact integer.
  • edit_begin_units
    Begin appending units to a numeric expression.
  • edit_complex
    Begin entering a complex number.
  • edit_enter
    Enter the data that is currently being edited.
  • edit_matrix
    Begin entering a matrix, or begin entering the next row of a matrix.
  • edit_minus
    Enter a minus sign in input.
  • edit_scientific_notation_base
    Begin entering the scientific notation exponent of a real number, or the base of an exact integer.
  • edit_separator
    Begin editing the next element of a complex number or matrix. (This will insert a comma between elements.)

4.3.4  Browsing Operations

The following list of operations is available only in stack browsing mode. As abbreviations are unavailable while browsing the stack, these operations should be bound to single keypresses using the bind keyword.

  • browse_echo
    Echo the currently selected element to stack level 1.
  • browse_end
    Exit stack browsing mode.
  • browse_drop
    Drop the currently selected stack element.
  • browse_dropn
    Drop all stack elements below the current selection (inclusive).
  • browse_keep
    Drop all stack elements except the current selection. (This is complementary to browse_drop.
  • browse_keepn
    Drop all stack elements above the current selection (non-inclusive). (This is complementary to browse_dropn.
  • browse_next_line
    Move the selection cursor down one line.
  • browse_prev_line
    Move the selection cursor up one line.
  • browse_rolldown
    Cyclically “roll” stack elements downward, below the selected element (inclusive).
  • browse_rollup
    Cyclically “roll” stack elements upward, below the selected element (inclusive) .
  • browse_scroll_left
    Scroll the selected element to the left (for viewing very large entries such as matrices).
  • browse_scroll_right
    Scroll the selected element to the right.
  • browse_view
    View the currently selected stack element in a fullscreen editor.
  • browse_edit
    Edit the currently selected stack element using an external editor.

4.3.5  Abbreviation Entry Operations

The following list of operations is available only while entering a function or command abbreviation, or while entering a physical constant. These operations must be bound to single keypresses using the bind keyword.

  • abbrev_backspace
    Delete a character from the abbreviation string.
  • abbrev_enter
    Execute the operation associated with the selected abbreviation.
  • abbrev_exit
    Cancel abbreviation entry.

4.3.6  Variable Entry Operations

The following list of operations is available only while entering a variable name. As abbreviations are unavailable while entering variables, these operations should be bound to single keypresses using the bind keyword.

  • variable_backspace
    Delete a character from the variable name.
  • variable_cancel
    Cancel entry of the variable name.
  • variable_complete
    Autocomplete the variable name.
  • variable_enter
    Enter the variable name on the stack.

4.3.7  Integer Entry Operations

The following operation is available only while entering an integer; it can be made accessible by binding it to a single keypress using the bind keyword.

  • integer_cancel
    Cancel entry of an integer.

5  Licensing

Orpie is Free Software; you can redistribute it and/or modify it under the terms of the GNU General Public License (GPL), Version 3, as published by the Free Software Foundation. You should have received a copy of the GPL along with this program, in the file “LICENSE.md”.

6  Contact info

Orpie author: Paul Pelzl <pelzlpj@gmail.com>
Orpie website: https://github.com/pelzlpj/orpie


This document was translated from LATEX by HEVEA.
orpie-release-1.6.0/doc/manual.pdf000066400000000000000000004562641334120314700170060ustar00rootroot00000000000000%PDF-1.3 %Çì¢ 5 0 obj <> stream xœíœKsÛ6Çgrô§à‘êT(Þc’¦3Iß­Ó2=¸‰ëhb;‰#»I¿Fû» ‚e¹–]ù1>˜¢p üðÇb±öû†3Ñpüé~¿<Úùâ'×|ØáÍÁÎû¿lº_/šG»PÀ7Â1)ŒivÿØIE#¥dJÙÆZÃwÍîÑ΋öû g\I+œnOÞÁ)lp¶M¦ðH©” íþd*u`N†ö,—ù’Á¥àA×Z(k8”mŸ“–?àµöR8h Ê‚mORY-Uû-6f•s¦ÝËŽó#Nó%)p˜¬ÁŠßvŸ¥,#ðÅfÊrÙLg J5»¯à˜ÍÚºk[¡àŰ}¡8<¯+Lû KH®¥Ðð’ñ¶·ÐÜUÒ;¯±oКŸ "ÚÈ•nÿZº+•—hâT@Uçh‹iè›dÓCì#¸ºÄ’ƒ|™îJ§tf6jÞÙªD+sa—/?O}lýžçK‘/ýâ2õ¥@¬X€qÅΜB+FèM- ¼N–ÿÆp¤2¢}”:Æ:-Ó8ë´j_æË½I.}J®ã;iÎM0=n^uþš”éï‹¢ñ÷#-Ò'~H½ `8ÞLö5G%´f¤h?å'Î>¤kxÕö·¤“¾ 3jÕŸòÕW~o;Ç à$4 £¶í?¤ »ž— 0ª´•±ç…V XŽ N³Ôñc-‡€‹ö-΋Hàœàš‹Áº6‘ˆJ*"Φ45­ !?;:P ½â›Ýovv?{´LCÀ9 ڧ݈qè¨ãh‹:ð!6®8×g¸ðLz«[Ú•¯(3§]]e´èHñ^êÔŒ ÁÄ›ÑÚǃî‚^bF[¸Ø/rR|;í¿N®¹Æ‹P&©5Y‡ôÓ96§—¼°Í½H¨lóE¯ÍH·½I˜¢ýLË,½@wÿ¤ÚãÕd©/ÔF› dT(¨eTQÉ`< pð¸W!µ„Yx†rŸ¿ŸM9!Öžkg0g±4ŒÈŸ“© –ù(Ì0ÓXPnã—†ÃRý}…¸7'^Fâ|ò¡'®, jÉA­‡NûJ <*àÉ Þ“´B >Ç1¹#ðÍh¡ƒ$õAÊöË ð™AàQÀte΄íëá{sÖãÏ_Œ?@‚Mü–Ôù#P%G7#¾ý n+ð5`5'5÷² r˜Áw\NZá-ý^¸4‹š'i1ÿxp¤xÒW*¹w޼!„\[GEpTùº’Uå«'7OÞãLuwŽÞQiWøÀMýØm•½$‡N9ì†DöÎaV—5dj¡aÉÝgImž¥oc;<ï@µk^Ô%[Éý¼—ŠÊd™ênâv™UL ƽÙ‚NEÐ+ÊS¤ OOÉ:vœ‚~Ú)|>®‘Öуõ1)Ùµqv‹‘Cz”·Ì WwûχoÚ×_׳›çñ—‰‚io¨– ™ÔÅ:ÜïôNê’‘ÒGBOk/yS†÷¦™SY:‡´KçûM·yÚ~ Å×tî½öí§‘xþlÿ'›RTEkã6¢+\FMë Î÷²¶ï;é˜ÔæŽýÿfNÔÀÅ8'ôaª¡¿¶ç·È‹# ww¡Hg–6\ëú|d%&K8arA¶,|Ux‹ƒ³%åð¤ÅßI‡8±KZè™p¢®…)ö«ê±ßÑßâ Z8¢S4&1Oðuz×âG9úAàH_w²(Û¯¨jž’)ÚÄ„y:AJtw‘뻳“¸LÓ0Ë¡{K ÖÅœS–Ž%·Žk0'i¡aZhá¹—c¬ö­®8§ÐWÈêèK:<¿Œ ÁÓfü¨#ù{)g áNv>£ò·.–z S5¶ƒŽ[ «[k]XÍÖëXú¨Xã‹ôƒ¨¥à%^–*ÐÞu:+«º•#‡»«é Ãe¿kj…”Ú+¤ó¼]¯xz#˜çÛÁ–19ÄS`_gà˜ôâcÞ\ßÕ|Èg×VRO]Ë…q™O"vtìßâØ;Ÿ²Êº-N¡€ÕÌ7’œ¦Ù@{UüßL¦V¯ÏÌmã`ËÌÉXž¿¼×°U,Wz >cùÎm%•¶èCéyÌ)-E2'/JòV±‚¬õ öà0×s(óXÓy,ò‡nt‘ÕkðÉ‹G£•R*SòñRc„ì]¾;Ëw÷«e_.îVìÕóHEÿ´b!ú4²¥ ÔG`ÏaÎùN…{sV^Ö–¥¥ˆ‡ì5I©ÎK tOÉ\‰¢ÒXGñŸHjòÀ•ZÄw»?AÅÔ»Î!#í$Fîy*˜U}q’oÄho9õøñZV³éÆ1$Ùt…¢‘ØÁ²ÃÓÑF¶´ôºÖ”‹PÝÕ"õàÏçÄÅ / q¬8'¦{˜»Ô1ð=ޛő$ä¸Ê` 5¸‹óVuPµ_ÇØƒuHtŸ¿ù©??Sq×ZÛ‰F‰MÊ7h¹M뙿E½½æ¤]¤aVé5\Íi_´Œ]ŒsG2óêièGoSÌMÚ2ˆ5Ä.æ»UØq.‹E·T\×èß9Öˆ²åL–Eðb˜>н®Ä™k–8¬-âÂtm’µs‰ÍÄã §³`S{à ¿Ïi¿ê•v CºÒ®±ÿ°›Æì8.w•DôºOâ¿0+2Wî–ç&TÕ°\ΠÉUç«£ÛðB¼A*#çöº3¸c:㎳øŸ$rÈ¢¥m¦Ê3ÿPcèEb¡'»;?ÂÏ¿ÁÔàendstream endobj 6 0 obj 2247 endobj 19 0 obj <> stream xœí™ßÓ8Ç%óWä19)ÆöŒ=â¤;¨ðrº(¥ªDw(wâ¿gœ4ëqêí±ˆ,=õ!ŽcO¦ñ'ßñL>ÔR¨ZÆßþ¸ÜV÷_¸zý©’õºúP©þb½?,·õà ðÔ!‚ ª^¼«†‰ªV ¬ B‚¯Ûê¯[)$ F5BñßvJK)@7¨B@g›m'…Á{Û¬¨[¡÷›×©¹Û\03ë¶Ó4OãTêöãÉ$4˾éœ4™ñKfäSÛyi"6¢í 9嬊M² ¸ŸÚ<;wþ^üY¡2ÂW‹·DǦvÙC;ÂÏ †aÆõÜ…Ûæîe£QK×ð›]ÄÊ{3}Zg·¤ss'È(_~ÂUÄ)Bd€ zR-~;†?‘mç"Qî–ˆbV¸L]ä’%Ji‚iv¯“Äñ1@«”ppFK;wz“ ¤ó£€]Ü›j™öB¹²”¹€¶šÔKZîp %÷"% °ÔþÌÚT7w›Üj§M|Z¾yÕ*‚ ½á,s#Öÿ†Ùx¿Jœš¨µpámüAîD  ÚÕ8Fá÷ªä”Øñ=²¡€,‘eŒ¼_¦v¦‘`xá4^² #ŒJÈàšg)øþÃÜ\¥™‡xïÅ—[œHküWÆ…_‹ùº3ʦÕßìI”:/ Øc{DȃûÕñ÷^B÷˜|¾™åWåMoIž9Ëå½Ëî$ÆzH5i·#¤þ ˆ—O…MNÑvË”#õ6ÇËzHbçðÔf¶ˆ3sgdJ³5Ô9Jä'WÜ<îKÚXJ8›ÝT(ß)ËŠ†h„´SE;¿Õœ—;… Ç §Šus %#yË ïe«)ÒyÝü—¨ã)k)óUO…¤VW¹t1[É! ^8yš.ÞÝEŸƒ;)õ …Ô—3h¦ îg‰ƒ&1ø AÃÓÍ7yÆÀŠ1Ħš¹TQ¼¡00ê,‚)«´pû_FœÇìÄ€¶·‰3x/”7spgÈ€ãZûrBQârZ^ œª6qùí…’1ñ] Õ‹> í?9/=íiïÊÂÏ×Óõñà3G¦p#‡.qø#%ChÕ*ÁSì^_‹Jä ©÷8‡ ö‘ÃêqhœÁ)ÌÜæàÎmhãýJóÁ‚`tM3 »©Ùõäù“´[Ü,Ó’³Ï#¼b½ß1*§ îY!fÔ²¥Œ2èÒ^÷à=Úí}´Ê 3lòñQÚM\²2fvûÝ$S*Õ ×d1wý]Ûß5Oñoønúà³ðÖFxEÚ€H¦áÀ@œôxQ=§ßW9d{endstream endobj 20 0 obj 1018 endobj 24 0 obj <> stream xœÕ[Ys·®rÞö%a«’*Í&Ú!îÃ)?$Žâ(;²Ì¤ReçaÅ%©±f¹¹´Ìüä§qÌ Á,©#WéAXlèóCwcùfIZº$î_üÿl·8y®——7 ²¼\¼YPÿå2þw¶[þæ([RÑr¡ØòôbVÒ¥fKMlK¸Yžîß6tµ¶Ö¶ÖêæéŠ´D(®¹l®`L55¦9¬Ö0¤–2Ù\¯¨i cºÙo=Óœ6·ix扥µJ ­VMç¶6ÒŠf5RÿíôÀ¦¾[K,u\2Ñnµ?.Nñmó'GË#ކÝ8!B¨æµgÔj ãî|µf g¶énÂØÚl†i LÁ.§»•’p)¨va\ÒæÆí.‘Vš8ߟ§µkG{£šhŸM"ÁÛ •[Ç‹iÝìs˜åÖ ÐÉ3G¥M»Z iZetsêõÈ” TÙŽ¥›KyðòHFÁë°¹.V¶•†ÏoÁYŽû±ä¸Z•@ p çìº~“íîüȹ=È•cÌ%"?x‘׃ÌkÊ[)„ ¢ïQ ]€óC)n›žŸÍûò*,eŠ7¿OÁê=ÕR½M“8–Mϼ;SŽC YúU)Þ”mŠÇ€*L-uk5¡V(cnÕ’·VZ­ƒÀÎ0×jΚ/ÝV>QV% pI´Y®³åâŠI,D f€£Ç Wîx²€5À`§ ^~ÚLÌ?Æ“[Pý‹zô¡¡·£l)±MzèDðÆ¿—‹½ ÛèÚyÒˆAû$þõà`Ÿyá¼D()šO<Æs;Z8äΜÃX+ïâÑ2ç{pK#ÝEà†Y#âr ÿyÔ`ï+TøŸ8ݸ}owh3¬½}©ëiÜã¨X¯l™¬0bàX#%Y}ØÂE M[ÉcØnQxFUr“ð" é.sE0âq@ÆÆ(Œúüî ä´À¸Ÿà»ê]~(¦s~2Þƒ’´zÀ/Ï›½|wçô›#8*ka9ó{rt a‘[ñ..rìÏ‹ˆs+ »C\ª°'ïPØu}¤0ÙuÕwY´á@ `ÐæxE½ˆB©* ¥[FŒ-AjS?s4„œUÀ_VÊ)€ƒ@»Ad†|43I7‡iÑ …ÛȲ±£²JªD&„Öé÷p¹I¸AÇËMò°Ýñ¼1°ìd…Ś廯;÷M 15®Q‰±´@dê< öÀY[<›Í:1²fïœEèe<@¸~3½NpŒfqTt}w¸[)Ù2&¹»˜b \5›º|¼„B²?Qa5×sÔª»¨+¢LFn©Dv‘ä9®[ǹnv8,Ññ2*‚ § q<ñs§¨XÞ®ÀÑ1ÙÕyñÙx&YÆ ’kå oËÌNØ”ûÌLGÌk·«–”üþiç8üULà©UÅMƒÁAyåµãÌÁ)ØiÖ_ñ¸ï&ž’ÁîHYÆ ’à2ø’˜ZÞoù2P[aŠšÌïaùE‚…¯ÒðÏÀ€/Ü.©BûBsˆýZáUÁ8ªÊaœ"T…ò{ð®4+=Æ¥5H«×YD–y¦÷þº4ˆ‡é]T—¬‹•i)ãõn&\±;G¬\œ9¶„Ë…0ú<Àµ*y&<–T¯r÷ç[W€óV+#/À5¹=çQôJ¤£»9…âè¥>£­»OPN‹æ7>´™6îñÁú¥µ!É”ÊÝ­GjÃ!Í ô™^®P$_Æ,CÉ,ÆÁ~›²@œ¢Ž ¶N<§Ù¦'7)òQ¼r7S ýŸ*”¼)·DEwΫä°}¸A9dù­·U ’¶…Šš¡øä•g¹ûÛbiTNw8dÉxÐ1ጊEÇ~¾;Q-w}-¥®RbÌçl®ö"ˆûr6K¡¸Y£5!g+;“ éRm …”š1#<Þ—¼ ³ÚÚ¬Ó¸éWC«16¸‰ùÐ^¤k@Æ¢M*Æ ÿ‡[S"3_ú²šfWVLÑ+gd®´Ú'Mxe–Íì³m½ %–cR(“gHq#¯ïãÛc\ $²C:pî鯯Å\„ §D/–ù$asb­0gÏVT“¿N“_FV¤È¢iS5Í´µä5q™ÄN¶±È›6#ÐôeiÄ!=óé$$FÈÆC(äU_ë|Qäiƒ<f”¼Èþ1V@%™E¦ŒZœÖ2Ї1(!˜*ÎBù§ˆ«­8{•hoÒì! 7‰ O³ý8RO5•Ô•Ô“1¨µÜ‹Ã8|†]ž{W´ÃA£®ïEý§» üL½¶Øïž»œPîº J 6&VŽN¼îŒš)ªyó=ŽÃUÅg ЫÉIÞ=ÃŲ¦²ÕZËSGáéÕ.àX i|`„Âò¡»ú>h M¦Ø¡Øèþê‚¢âs1¶\Ô4“ŠgżÃGìåÐòÁбËó8u9&Ê™ hy›6éᬭY¬ì´nïí=ͤÂ}¿÷@TÈìøŠè°‰nzŸPâ2³ÒXY¥+(9aÏ!rPf9Ì£Ðwá†W%2u@Ûô}ÖWN >y Ë|Í•»Ì!»b ø¹ÃOÓM]ƒL7Ô#(<$%ŠŠâó$ŽZ‚>p4þeÙÿ*xð<ûå«ñXƒÐø$ÉóV·ï…sžÅ_öš‹«‚i—|ìË;p¦rœk‰%Ô½^扄"<<Þei€ŸÝ¥aŸ†‰¶K³Wi¸‡mKÓ£éÀƒ{q°³nQMANý±‡õ<¶ýÕÿ¨¹ŒS´’÷sA½&ó¼ï?n.0ƒr‘çxp<¥ñõXyKûÜä}xÒ*•?Be½Éø8Ïß)§!P‹V’W¡Î$„3UߤïS{¹—Á!~|Â’>žÍYÑŽC¾2—| òð‡=§¡Ë¿¼*Î->0¢uÈyÐ^J:éÌ›x××2†< Ç ?0úY¬m”ÅEª4¥»û(A±sU>Í¢âçÎÆZ®™Îía`GàÍÀÒº‹€MCÎܤÙC"ä`%Z†j¼=>ͳn¢sÆÙëR h3?üÑí@[F…ÃÇ.Žl¸¼­Šqˆ; L³7ƒ*Us’–…ʪ‰q’z¬Ÿ‚Bƒoöì’+ \‡n¸6]ëó‚íçÉÏ“ßýXz#2ˆwÄóà5Šœqòy"}’f—fŸ¦Ù¿¦ÙÏJUøY¤ø› ¡× øgþ/"+†“:Ú¹ð^tYuez—ƒW£{‡ÐušEî}VF^nhÙ(šÍâfb’òŒ‹*m?òÛaÒ1VîÒ,2Åmš}TÊæ]ý<÷Ù A-XÂo¸um9ÄU½É”½¸584P» qðªJpYºH=\#[¡7%øÇ×ߦÉ'‰ô›4{šfíÓ4û<Í~–fOðaãì.Í"?©ù¢E.“·ÝŠ€‘´q›†È®(ˆ·ÙF#2AÊÞ¥6iˆì›‚}D#Ë*h„z‡Wi8A‚£¸]‘EIQŒx‚9I£Ýö‘ë*ömKì-Ë*taßD€àôA16sá-j¡Ž AÓ£êuù¨9J½Æ¸LpŸáçã³eŸ‚}_ªÒ×lËòß}Äïߦ hçØn‘1‘§5lÚ”,x‚IúUž6…©ÀmÞB·È‰´Sèõøn±™{rºøz! ™_¾]å ÍÈ’Z—XîLB€ûÅ7³É`–²µZƒèŒh•—BVÅþšÊÎ4·Cñi\ߘû•¼†ûICÁJAQ/à "Ms8¼N>=9qÖæPNÛð3æq¨$8oZ˜#ÀlêuL,·Ü°ðçœ eßíÓ×+ªÝ3ŒñwQ˜8F²®–5X‡¹G¢ð4ú0!‰&ü^!HLuA É,Ø3Š=©ˆŽ¿ÓÏQ‘Fij¹J»ºxà‚mÑÿ—L‡¬ræ‡Jß÷¤™Ãêðã Æ£\þk,ù>ÌÊô»˜•j6¼§D³Ö¥Ú$Ë`íz)ãXHlRùÑLŠ=p”Îó:q¶I‚béflu–t1³°?nå9µàfƒûI=WË5ø²ÈÑQOÜcä׋{dkÍendstream endobj 25 0 obj 3368 endobj 33 0 obj <> stream xœÅŽ9ŸÎáÙ.ÆÓ8M¥›ß…Õ”ÓöžÄ‚ë‡X8ƒýÞᛊI#¦Ã]\œÙéðPÏ (5ÄøÍÞÍšIn¦›2H¡:žÃ ”­éZžÿ ALܓ蚛1·Ê󼿖NÏZ¹éw…oÈ2g‚Óû1¦ ¨ ¡gåPü_Ý‚¼û-g€Ñép<ŒYTÍè?—i Ì}£Ï§7kzƒ” eÂòÇ7aIløTð;œßFÚ3P«·-ý»ˆÞuÂYIéš?µ‰TVVZsuÒ †VðႲ’qŠÊMó åOjÐqKç›fz¢ÅMÏÇŠä†Ûð2L¼¬ã³5yiî­Ê2òº0} þ×°Œa`kø§a' fMN'"HŸ#dZV ¥À¼ ´‘B‹ày–ŽÑU:U…e ýÕûXmô‹µ“Œ €=¡àÔë•ɸ™3•”é. XŒÌ®Ö¨µcQŽÞQ¾ÝEqÔРD_ÀNO&úAôú DÔ‡mÊz5 Ö+Êà¦õŠÐׂɥó²ŽôÏ 8½»Ä?×z…Ž”;^CpŽË:ù‹SÀü2FM½òø]Éãí@ æ}pÎ:°Ù)¼³©â"ygë†gÆu´²Êº‰í¯– /r[HðÁÁ8  ¼¤znò?ç•0È[.,¯æ~ ŽnQB®… ðÈt‚ë¿ö  ±Á#á*ê(¾ä%2@O LEŠóÍ~ÕôÃôE¸Ö*û}¬®‚²Ï‘l_).5¦5G½'“•}Ž.ž¡EƒøcÓÏøäWX·rK¼pnÜYú!2^Rú2+gÍMÒvŽs` RAÕ‚ÇÑCy¼Ë+|ì¿´i]ïàÕ ¬ŸT‹œÖˆ žª¿qü9 ÀÀFYî*z¤õÀôÑmÈäÌÞ@Áš®Qù¢¬ØÊtUÒ´f˜¦#5¥_ !³Ik#ù:Âf:’eÞ¬ ¢îaIör–(A ÄÎRÉ;Q Wà +_Bòf—3ð=² Ch¼q¦ÑÞG¢¢HëE€$Ëš»ÛF1òøi#„„U!ÿIÁ K1w«p¨O•šÕF˜š— ÜÏtøº ìú(cäµç:H#4 X×AJ1uÁ^4 ”Í¿é F^£[ÁìÐ P…8PFçŸÃ|Éø³%:B_²ÚˆÌx7`Y+DÛ”;géðapˆè!Z¶4Y€EàeÖÆ#î oo oö«½«œ RŠyª©â¢3Y…lôYõÝ€V€ùa"kÿ¾EµÈj[ó–»±¿§DêœiÊ|¢ËD4$)óó,[¤Ô9аòù"àEö_4¬Ò°ã±D.uä#­jÛ~ ÁÈ-QC²ÓC\‘C¾û&:îó²c?`8>+QëqŽiÌŽò3’",‚>àqN•å¢n?Q·Ïîü8øqʪ¦Â²9ÈÁ‹_EN–LXòè ì=R=8·ç‚×ù‡½—29xúƲÿ+‘ „B À­ › ½™R’}ëAvÃãÐ7l¹¸Jh[îÄÅŸåIpE>ÃêÛ\P·‹a²R£3²m0ç±€d¸ªPɪ‚Ò£äCs9϶ˆá·y¾]—«óM˜²tùRØç9ðì«{ ò¯rµP“PÌ?þL:³`G²MÏÐ-M“ÁIŒƒa±&î¬2û6Žòd+aùز3>“57 ¸¦Òòi;®|& ¹%ÞNv|KÈ£Rö%³'zöÃÐq -³§@D%~J†QnHO~žC$Ë«wÞI$S¬xkÍ£d•Û¬îq"VžЯ“S²[e›dÙ»”Ðkw‚C4žê¼_Ë’à7ðF9KbûœývŸ³¤’y‰ç}“0ù¹ïËèß—Gþ¿®ý›ü¸’©1æãò„uŠ#;ÚÕšƒ¤§ú„æ¤BRéBD‡Ï±®¥å6·‘]Ry"•°k­˜q-`1n!Qñe ÝÙ ¨ÁKQ㛇«9‹eC$šÒìy]Ú(Ϥ¢Aß¼èÀÓ?ym¸oÊödé2Z0)¼Iµ˜d"fNÿ`¡¯c!=üfѪ“FHH…i­–õ¾èÄá¡®èG:O,šL^¼!þ9…=Àîß—Ééö'¿=dþ¾PF~¨ìÛZ®°"¨¡mdܬaótÎQ¬Ý3ý$ÈÁl´ö‘V•ÀÁ*}MhïIp£tiZÊ> Fï÷¤o, í×0ÇLÿ3jãF@œ©Q«‚‡œñ®[0Rì9}¯Ã Q»@ˆGW9”úQh8á$H‹FÉÃmä‹0­a ãöyì”̂i@ß‚ÊÉ6+G,5žƒDçu FÔ¬9# ¦_‚ÕõÜ›Z€`†r¬Šñ}"„huj9ð%ðÍ4\ö¶5§Šs(w>j Ç+Sü&ÂbDf ‘hÆJb—Ãh£CŽ–À7D+‘>uB@KéU:KÚO9Vò³rV§îžv¢FAÖH`æXƒ¤f¥œeCZŸø~U»Z] ˜½ž>l¹L 4á.‡_%¿§ pÂЋ-Œ û¹x7šŸŽ[䆩W÷B‹F&—k]C1/~hÏ+EÌI™pU¿ÀŽ93ýKïA¢‘ ²Ñµµª™ç\ÒµÿÎëõ7¯®¾½’0w÷éjÙýó–cvRZh¹;]IŽEW‘GŽWßÅYl'}«ÏÀ¬<"­˜ ó³F‡0êh*žÁà°:c¸¤­Ñá Æ¿û®™ö "ëÆþå´Ô4^Ô“DÈEÌ‚ ˜.ÅÏ÷[ü0fØ„§igvéYöoˆ¥ß0Žù‡Âyd®qϪŒõt±Í'WÓÐN02Y©¤\DUòo„^£êó€À~ýb!ÊxTbI"#œ0³¶Tˆâ{— ‘p³´— ÑC+O~î‡2º%O-³·äé‹ Q!k#U— QEÕëDGà º{©§øUG2üwt§#YþçäÚM_)¨ÖQÜB$ûc¶ée•L†“ü¡lIk©ïé|Iof£ÎÀC˜ ™U h&3˜Òl±õU1ˆ+¹—µ®O4J<Æ@ÒZOdDÁоT±Öô÷Â2 sà å@º÷qת så‰0ƒ¯ïruÓjp<ó<ˆ kL1±ëqèÉß ¬JÁTÞ3ÜM9T @ÓAcÞGÉöÃ>ãÖTMã(åÚc½t]@ã¦ÔèØÆs*²}›26"H% 9«"˜ž-¤‹áT!9fGíÀ,s0c»Yâ§ÇºB `ÝήLšJ•c]U‘º“²H.GçCoØT‡öˆj½%ªEš!½P¤Âeì£è¾›ð­Þ&å«“Xª¶±:pÚ†ºCÝã;tU°¾‹™kn:Q õÃy£Kü»X¤î‹K¸//®†z.ž‹5É%—{£:,dT¥„…‰®{ë+púùÝ[© ç¿$™ïòbXRiJXa×Õ.ž‘á.™'0‰4{²†•òõj´èÎ?V–׳î7©}ÉÙöÊ„T_†ÖKôƒCr•[IÅxÑ5H«¾sdú&{SJŸ7™¸Q‡„ÜYHQÚä/«Cß”?¹~âxŒ–Q*ZÇ&è à-¶ä­»ö¬5½{€ÇU¹¯fuç’ü8ø•ɘ֦">*·Ó*¸_g|"³b0¸3%䬬¤Ýèc'qþ€x€OiQà+R÷y¯AExhïÃf¾¸Í[1nGº 쾉WHž2˜0{©yÀ]”`£Jô–de+@<1²lO;7Ⓦ€¯î]PÙ¡œ;œ©Ãô§=£$°¥ù –§ÔTaƒ7Ó§dç/§å…j: f£½ äÈ:õq”Tx•…U)ÑøÐ4½ÄÆÞpá]ä´rÅ‚&„r Èr`Á­ÎÍ‹XÙT„q¹´ý¹0]òç6†Rž›^*¯–pa·²½£—[4O.]§B¸²³ªÏ34Z©Ñå»*Bl³Ÿ|‚-Ÿ D7©¬¿†9ˆ ßŔĴăh`˜žÜ‡m˜2Ý!&¤_ mÊŽŽ|Q FJH<…=ê‚Æ€nƒ»{ç¾KÜTºkÉFúo^ä‹Ôªé “~O´ñ˜ó¶°7¹ ÀVBÞJ,;•²ºîl èSë0Ÿu!­RòÚˆŠü)q†Á÷ñ!¦²Usg³ßWÛÎñ‘†Ÿ3z6f&LB’º;]< ì㤬ºá$ËôŽKœÍ`VÑþíM>°aÙS|ƒÜüÿmòq«Å¬)†yäR !ªžñTU…!¶ùzE¡úó¢6Ÿ¿ó¢6Ÿg/D2ÑT·ŒËô*Mï2ÛI ‹æF]U!k«Ý)‡úB_ÕôàŽ¿ Ö†R?Ù`?JQw—.3`VŒ?yïoä0åoä Ø¸ºâtn5é‹^Çp#)ÙžúG¡Â ÙŸâïª^ ”Ècé„Öz@N®Ü—ñû™hb¥¯2ð´4åK/ꂆ v­ŸîûáqtQ›éç÷ý4’š”û=6œ ›¤ƒ•I\\ ½fOf„3胛®ÿè £¯T—K‹¢¥«œUë~ýµ·1îPõ½­(Z§³ ï¥ ~|itbcìæÓé üZ@º«¯†W˜ˆá:>ôvåzÇøSƒÓçŒê0•ίãǘ3T]ІŔÖbgºùoâŽÊnÝDÁÍõè¦O]ßõ½nf«òqªöŠ/2ÛõŸ`Š—Fž¥¹Aw„¢ÊsÍðS? i×ø™Ÿ%Êó_ï¯ÓlCgã¡MÆÌîÚ‚aƒEÓàk‡'™¤¤×Qý™¤$éCZy¢Ü b+k@§9~Ø¿“‰ûü*Cek Ëví®±ÜŸÇÂÉÍ>Eùöê/7ª™endstream endobj 34 0 obj 4858 endobj 42 0 obj <> stream xœÍ\K·rÜa ƒ{ Ý|“a$AŒ $°³I‰Òj%M<³v´ã‡þA~vªH6Y$»gg{-'ðÁ­Új6YõU±äü{32¾ñ¿ôÿ›ãÕÇ_ØÍëû«qóúêßW<üq“þwsÜüú˜=ß\¿ºŠ/ò;z6J·¹>^ýcø~ Lãh‡¯·ðå­Wfø6> ©ùpx¹Ý å™çjxt®œjøYô(µâñ9±ß–Èí½’Ã[CL5}'0ßE²5r8½© Ÿt\÷ÛÝÈÔ8j¯‡ùþMyü*MPV$/~yý{W•<„cRH®_‚þï aF">JëÍÀ õÙvgF蓃(Ô/ /A6¯Lz9¨Ù×¾ÌÔ8ÉJgž3#œ“Øöú_W;1JPßì¸dZ)çÿ.V m„~ Š$ò8Rn‹nî£øt­šWIc^ G2Êé-~Ixïœö?L’7TïwäCÿ;ò‡_lqÁŸ]_}~¥€¸ù üÛ+¯ã£Ø+9³vs¼ÒÜjæ}¦®þ¹”Þí“À” VjT`z”U©˜€#™‰ÓøÃV(挰8®`‚Ç%$QØÓeFQX;ê ½ô¹?Åi(Ý0%Ë3aí•`N®^à[„¦“ÚÆ iÁJäÊœöw䟯“íê |Æ*1PŽ^÷ñy™Q·D™@–™!¡½`¢‚D¢TPtF!¡ýˆ†~NbgH"ãNƒ¨8 ‰Æ‘Üeç”çÌøšÔkìG¦Ý!XˆjûùçPÀË[lª˜¥vŽ7Ph¦êY̪œÌ`[jxt?•QdÉWf²JòÂkf0ôé%ßZERB ·Þ98Ýýþ¹å c̸zàPÕùãóF†K Ÿ³C@´å c°ÂÁZêüF“UQÙNRE^ °bÎÑà%QÎ*L,dpÚCðÏ““Ûï3Aÿ¡¶;Vï <ÿ,„z^Y *v$>ïâÖ¿-ñé>ò7QŸ~ƒ,B HŸÓ  ø¼$fßrÇ ÊöÞr¼‰Ã0ÿ†ñ4Ä‘CõÅÈ/GÁç’º*Øøh7×¼ºþhŠnÃ’S¸-]•”¶;),ÓRS*`_Fåhº²¤$¾#Í)Mpr°ø¢åUØOF¡ó¨å,œû8=ÍãhvUæ—r´tĸ;IlS¯³|sôÓÒ• r—†6¾J ézOû:—/Ïtq‡wi$¯†Û*ÇG²t¾@ÍR†!ÏeÕßÌìOo&™«*m¡óÚŸ¨6lŠŠ3ÁÕð«2Ð+¢ÓmÈRÁI0Øÿ¦bÁ[üEÛá´”ÆTIK˃ÍÄùê`Lø®‡ÖV<‚4`·§¦ 9’­ÊÂo2'»›f*Z¦ Á7Ä1Pµ%¬Œ è­BwS Qv… [tmÍÆ¢ÍBáDžÁý Hú´x ‹nSäP­€Ï;À凅;»eî '„0_cn¨58\î…f<9s|o  B£Ïýg¶.l…*§ÃáëP?€¸ˆâ´Æ1JL@~CQÿ.® ãy’)ç!ü#¾*–U©3÷ò 6 8àQŠá:¦ñJj˜S,O¶‘Ktƒà-ä@ýM刢”ŒèKKA¬Ê)]$.[Õ$n‡}ˆW¸\(xÜ46&G%ö[Š-t¢Ð…N°.04G`1Q*X8ˆý…p)Ì V¤Ä6H‰8•qLR#iWLJÚÜWjÉrÉöè,Bư#ÅíR¦¬˜§Çz…ªçù§êÛÛ+á-ø( Ð![ >꣜èQžŠå˜YÒcXã •$œ—ÇûÂÛVçóð6Q Ÿl$«›XÍJuƒw„œÎÏ©;L£+¶%;ÕÏS: ]Ô-=ðÚ'¨F`Üš9u·ª\©î°Æøñÿ´+Ÿ`4·ÜÏ~§…^Årßb)|‡ÔËxo:Ù]d’užÍÞ8ɼ¡Žß[å,D2#X¨à”t8­Y*2 q]·Zä?ž ÞàjËXš€¹O”5%LöÛ)ß—YîæüžÚ!“fÝTv¼ÊhÝÈ,ÁœnÚê1›µÚÜã¨ê¤RU(¥eÜEAhŠê#@l1«¨3Ö1³±d[Ȳ­ì¥1­`OÔ8Òk+¬†r¸ûÄMü1Mº@í:O-¯ÊÔ¤ñ RÓè C2!H72e…ÉŒ¥òj‡‚’e>¯*`ƒlð2º@'ò´Ç¾Ý ‚½2+½µÃf7Âæ…Øê”~¦‘ó¬PÅÔQä}mÆ@ Ba®%3ÓFQb„ŒA€(H :ŠÕ6ƒ0‹ÅR_Uæy!s{dâvëNÆ•Ú:€ÊÞÌ[jïøžæ ††ñÁ‚Á1‰— ¡éŸ+LIrˆûñÌÐÚDQ*ÄçeW6–ýXGíC—¦ZÓ\”ògœ Si€¬‚8èýœ4›@­GG@…—$€œY)IGE3«DYÕÁr–’µFôûlhjÓ3)ºŸ¦ÂÊð;’¥_2•PòJ,Uíe®°Ð/áfê—Äxhè`•ç Ïå3SF _#=SzÍ‹º4w*Z¥«ËEø¦4²:mö. ¨E×:2fü—–¢Ru²é"àØJ·ÕW$Â=¥ÆÒ+â*–¬ã\æ%ŠÑÁ[ùTâÏ+ßÒ(orÚ‰÷Ã$AÏ—ª´¤ÃDyWž´Ðõ,P ¦/¬öuûý‚§oŽu½nO’Ú6©D‹mHk%Ì5©ýÌÍL×ý¤€¬¾æ¸aS•¾-x‰º…ek×£35){”w]›Pï…Ì‹ig›zï÷Å÷‡Cê“ÈŒÒ`·‘,íÒ‘×ê¹ï"E~¬•zÉ Hïôu}VcjÓ‡ºnÒ}­eZ^ίzê.î#ÕK·0  ¾‘ΜÚñ˜î ÚOqm–›…çá¦<K¬„VŠ›FNÐqÞcª‡û´ð G:ÊË9ÙZLg›ÜÔšò2õ*ƒ ëh ¡w É=nrwIÍNM›cléIýž@A7f’¿ÂÅø™ê0ù@pÒå%cr'°ÂްǶfZHÝç8Òcë nO‘ä'6¼ W÷Nö»ÃRë¢ojÇn+µ&ýnmH ëðnk4¸n‹™äNŽàEìL»ÔØbÕ½'Žâ¥¾”øM[µé°áè-³Öõ¦oJ. Òö'a@oÿ³»{jZ3ø'»~'®AësÇÅ!¹°‹S¯"˜`(B0+Äd(·q²Æò3‡C¢ì-ÝXè7:QE–zöÌ]‚ÅÍu¹ég¨m»mÑMæã/DÝ“m¸Dùª¼7Žh¦47)B<Ëç ¿¦Dš ì”°¦ÙÝ^&ø›¦™õ›L›3ÖyÝè§y¼ngíÎLH<Ë}ÌÉzpu—ZXzçÜŸñ;\Ô;žw&¿‰~ƒÃ eÞT¹4m ‡l7fânÉ›7÷ÇîUøRí¹! €®Úm…‡w‰M¶ñ½Ãì×›ý}Á žõ=Q*Ñ3cϾõÌñƒÅ33ôÌx"X.áð]Tžça? Ž’Q'%u ˆ¢Ñ׈ ÛðWdÆÓZçBŸ4bºÞ©Ðc ‡| b:(¤5 ”3žv`É”±ÍÁ¢i)9.M·&¸zˆÏ$!†³‹}±Éëy·/á7ªÔó ±¿ö°= ReË9wÐR,Šú ®¬ýˆñ+NÅ\º±‡ÛsÑf~‘SŒ¹{ý,˜t{Æqv+¦)œ…Üt#¹u#¬}p´Ï”Pü LÀ5jÙ –' wŸofÌŸ£€÷$dS!Üü}ŽBboe´d…™²v…Cê[¯ðì9Š.ý{èWÂãŽQíRMf Yç 0ÀT–œ@‘(ƒ‡‰§2L”cfM«VÄ`îá˜;8»(³RX¡*j«Ó¹p®†xÏõ*I ¹ªãNX)¢(xZ+ òܶ¢È=ùK/çDA.HÞr˜â¼º6pQÖT°SÖ”A`­eÚLï­i®ˆíÑEÀV.L‚Ò1!íµ…Çî¢V ~SŸ—Çî¢VÓ2Hqïû»0’a”…Ùk Œ<„­,3ŠL r7£µ-Bý ð¾Ì(ê¡“×Ñ‚©‚ŽQ"¶Ý*0™5rŽ2X¦0"A¡Óœ¡×¸ˆ)uÔö(ñ ÈÍ?¨Ö•›/~*¿”E\Al•ˆ¥Å“ÙºqF9""ZÁ?;yÊšLµAÓˆ}ø²AM”GïÖÎѹB¦8/¥R€K+W§pÈZ€A5¹-:E–à¦Pf!d»éßš«V?Ø“ÉUEË|À;°¼Kƒs7Ô“Æ[ À*Û¡Ç€×}öÇý©©áèÍñä¥Cß•ÜÂý¼Ûq®zƒƒËþ"FTÄE÷þ&-Ëù.¥Ÿ.àTÃÓK§ø¶5å¥K6 —>ž§‹)‚—kËr&ý™TiΣ“ì E©åA³ŒÅâoPîÚtÒbKÛ·é$½ÒA:åøÔtÚˆȃš¨·:쾟¿ˆ5³ÔôFØ×‰Å0Úï(õ¢3ëX• ‘VÕ=&L{9ˆUuY/R!Í[ª‚PÐ-]J©njÔ¦†sÑõ¡ë¾¾sÓ׉›ëK]ÑÔèÆ%Hÿ([™Ts?ª 7'âzÓÊb*{ÄO!KÑèûsg•Ê/Žf™ÄÛ-·áÖõ1K÷±*Ïzg R©Ç£ÙeT&¸Óõ-ÄÊèâ]öXÄnZ6*Ó£˜¹PRŠ0ïóTÄß¶á&•­BП‹{A?z¸£àµ9²ƒÒqŽa^z4³M¼OpÁ“!Çw¡l0ò¾P›<ÓžlÌ/¶ÍL=žk±K7"±1 £0^Ú0Óa|ÓŒÝa‡0+¾ü;vÀATßÙdñ#·^NöáÊi›«ú¿ÌñÜE‡ð#Þ¨'vˆ"1½H¢ú~¨A>½_­-Clº6Eœa˜î!ÙÓlÕŠËs¡CjAîÜùŠ0üÔ_{ÌøéäÇ›¢Mo¨ -±þ©è²´—ÒÛO?*¦«ã ð ªÅß–cåèÈoRÝUØóµèÎ4» 2Ö™ R:GŸõ,ÉÅ©¥ûm”»Û{–i@/Ï…Êá²Õò}Í—Åò.XYrœœ¿ýËR7¼ërå:\ Á+{¶'‡±XÃ&ukøÛˆ¿y•ÑÌbRZƒ§Ì2%œVE.¹á?.ÜüIe$s«®ÿqØñaÛ†4úÿà—Ç<¤Õòaíòð¨¶h–÷ä’u}õïñ5ë¨XO–™)dxc_T€H ,v; ˜ØÊ_d÷=~E„Œ#ʬlôüò»Bý®<’‚Q¹Ó©¸L8ë|å„%Þ°GáÕÎgìÉ-¾weîç&\1'Ýäýå)gmqgâO­dm%Ê ìJ&¬ÜL5)=¥Ï¯þ ÅFQþendstream endobj 43 0 obj 4610 endobj 47 0 obj <> stream xœ½\ÙŽ·œ·ùŠ~swà.w2 ŒÈŽc ¼qäÑŒ¦ãnI¶Z–•߈?8—KYK÷ÔÌ~pÅåòž»“¬WmÃV­ÿ/ýÿêpñá×fõüõE»z~ñã ÿ¸Jÿ»:¬>¾¤–×:¶º¼¹ˆÙÊð•i]Ó »º<\ükýņ&å’·fýÓfÛ6¢m¥ÔëWÔÜJgýÞ]o¶Ìq?Õú©ï.­år½íû˶UN­_ú.¢±R¬±ùMœ† Å┪J²núÐþ~¸sÖêõñu\V ‹Ë¾iŽ/á«Ü§<àºûëÐÉ9¢ó¸Ãn/Ò¸*FßÄf¡×?o¸llËX"&Ì‘XƉƒë´Sì¯ã†)Z%·C÷…ìk6[îXÃ9[_†•¸T­\ßÂ,6¬jíçÛ¿J;×aõÓ8ÍÖÏ Ç Œ¼eÒP÷§›_þíBp×HnHÖ.Ÿ‘x½ƒ‘žÌÐê‘%ܬ÷qBá¬Pf}ŒÿÐê ^MK [qfÙz1‘Bsjf²ñ=Ö¿ ¶Ð­¸Ãý?Oì¤àqr\síà};¬ä¹³íسe¢QRºÈ¥ã-¨ CÜàÆ4¬ãtÙCXY*̵sÿ. Vë´AMn»¢§ “)ßÞj1#°± Ar•›ßÔè$Õ %i“6Dé9öäŠR{@ k Bw)± ö8B;Í(פk‚tÄzýÏ #i…+„ðMælù)n'v8!¥±3jÑk?P5Nºõû™P//~Í$ZqÑZ‚…uÚõ'šsÝ Â5üÆEñH­ßçŸõâÄ…wœÌ“´Ý¼ï{‚l#ˆdb{FïŽ Ù¸/P„c ÝxQ»`CÉUÄ4ÿîdSŽ‹&¡40~YAf®²ݲQÿdÛ££þ)‘ôï€:à¢Z¤O:BŒüBЬ¡™ˆíßOø¢Òè{J™nUŠ'Ù[{¡Q´}£¼5õÔxʼnoè(“20êUì¡8~çdB­Øø²–uæ”ÜQe×»5ÇdÖª†4¼Ù §7á§td¡Xný(·n³ô†…VY‹·ÿ9"ÞšQÁ;Þ&*Á ›ý>!騙vÄ3´u(–6”‡»Ä h"¸výÝÆþ¯š²·+«d¡B…§>Âì8ûüšgÿn3oˆ˜²n{7ÿçÍV9ç•&ï‘jÃOË €Ç¬Iˆj{ësO»Ñÿ5 ºÑZÙº¥mj;A¶h ûvyxšûˆëC î:vo'K"ç&Þ~M=(ÁE ÝHæã×4„EPµ–²).;U»—ÕI[Öi÷‘ªÝ æq…»PÃwi²‡»nNË 1Ú5›”0:F(jEŽ6ÕZ ĈO/ˆ˜º8u/±¦Ìé_B„z“òK'Æj©h—Å<±é¨I< Vø)•6èo‹¤†Nø¥KBÃVÉ’ú„%Ì'ØdZÕ Oi¨•w/o‡fàR;¦H9³Þ¯eÅGe©¨ât'Ñ”Gµ‰­Ê‚ÙVaHæFíî]]ZßdÖ"›S¸äz*¬¯xä_†©Ð0ÌÜM„¢Ïâ$­³èíõ«Ž.è“4Iɘ;+ÿvY¤¾Yß:꥘t^9è”…Œ0§¥X~וn”úÜ~@1´±Aöø BIÒ¶YŽÑ™¶P|ÀÜ*IìC…’iïš-ˆqÄÞŸ^^|uA6W­Þ^´«Ï.(¼ô§›+2B²1lu¸­aM«û–ýÅ7©—Y©Xѧ^}‹d°¹ÐëN«œÚ¬q+%Xc¬ˆ§«ß`ec¸Åˆd±Ã¼)ÂÑV4v-‹iTšh³%_"·_à5$˜ËÅÛ$Ÿö1ÓÙ‚’O„zAÉø9°¢oVôÐûÃ^@ŸZ è™0. çF4lž­U2ÜñÕóS2šÓ¶'¾~¾é‹ÓO6˜%—èf22Þ'ɘG©x²1’¢Ê5F ÓÏ=Þ–i‡hÉÑú šÉä„'ÄŠ/)Da¹„ºç €ß󥇬m)G ý0#ahn%M)Nz¸÷Cá¸~iDòÄÒó¸áÊ+Ñþøß¬-‡  ~þÁŒö nÿbiÿÒj_pG€â°å:&]Û!#—¾Í:v˜Ö1 ¢o1T‡ òÔÇã ¥"åc(WÅP\…Êé€×y÷ ^âP0{÷º‡2Hoomɯ§•ÖFžŸX{žÅ¸ôdªæmš‘­¿žCÞ=¤!Çþ(æ#~K{[¨ªP<6¡Q‹2ã ¬ãz iÖF„6[-³–z%5eóBEþ¾Îz žåÖËì3q•4,Ǿ¤í4g'N#ÿ.Ç  +…‡a¦:8#ÅL|P\íbÜ3¨F½ÀXjÏbÄØ3ú>S*к3!þrâL‚~‚¶yˆ Òá»_À½²8:nž{ö'öôp o« %öãÄ2%æ £PW Jv… ¼Ë?¯]døùtìL'‚Îy°‘Ì"':e²¶V13U½ÙØ'⣨ۧâ#ñ˜–½gc!&¢Òzrq (iÜ=œ6÷‡ ,rûjè¼výr qéAilªÖôñY._êÎC‘Ìîw=FÙßo®¤`ët-²ß‹ÔÎè¶ž\ypUîUÖIÐÔvŸL"v‚¸y|¶ LФvÇcŒ±(>ú¯ã»8±ú|¬¯Ÿb™vÜ’Æú›¥ TOJ hÜU––9kܳ­QFËÂ\\"òiÜòÔDÑ·ŽçµÀx452*Ôc\PqÆãõyÌÙwûoÇÜ”~ž‹w¨?Ü™ƒµpËÊ­¥q;+hJѽ£x>­Ç°6¢~bíyŒqéq–]m*+Ó !À·~a¼‹èÔr53oεŽâDº³gUobU–¦áXºëÆÝLmi‚T4ûl̼vïr0qi_5L…ÿt­¤5xc5=yÜÇÚ‘IÙ]Á™‘ƒ~Y0q<0Šžåµ˜B¡kГ‡aâ>¥@¡…)I¹‘‰~i’KÏ‹®\è÷|öÿͱLk­Ã:í#BÕq¢ÀN”EA!üû, ¦aËc.!Y£¬‘JïêW?Í@™i+Á]%’ö—¬È»‰ ˜ðtéÊýVàõ{¯à,Àã²%ÿàùaâ>UFÁýmÔ:`b“—F´N,=Nµòˆ—<ŽS‡!`p‡¯G†%‹p>ÃËk7sN¿ï)QÖkÉf`y¡w€–Ɇ„(•¹³]Ï´™Œ ¶å !ƒÒ|ØD!hC¿³„;Mñ-–ñÂ8±¨Œ×ÁÐúwi^M£T .'¨˜Gˆ˜*¯!·#¬gŒ/b}ÞN†( lÜ IöXß[^`ãÖ¿ßÐ9ÐN¼ßgÇò*·‚ç™)°\‹Á)È|ã…òöSºÂKbùlÙLÝ;]Ùcž5fŽÕQ௥Í!þ~œXTKã*œ>ù3g§Xûé€ÿnéLb‚¢y¤‘ Ô¥ç5Ë»;¡p1ø* ÀïPHÜ_rnprþr]!#‡{n¸Š²$G$\ˆb·<Àà¾ðêRÙë¯3xåµ+—ãU.í)Û2à›HÃ'<ðZÎØŒGTž95 |þśźZ'îÝs©«x} §/rëÍ ¾™8Düqóø"mð<³¸8i‘‹jÈú×®:fÂ.GnÆ¡PL^ªñá¥$ï^þ‰ÇLÎ3K 9eÖÀ¹ã¤(~œ[¦Ô1*â‹£¸æ™j+q‚Œyü‘ЉC©ê=k|A|–âïÞŒ#4„¡ßYŒ«”“)/­Cw˜kŒƒ¢÷Ç30d2*`–ÀTÜ †;:Ñ™Ç5%"ý&kŒ Dþ¦°–~˜[VµJ€´ºaÎ ù|ž Dèóx S÷Á*í‡Å{{+ ¦nën®¬X1kUc±b•†-÷iá]˜J¥‡}7Ÿ©ÞÌ)!ÐV¢¹»‚´·êÙú'-±d,„¿k…–wãÁÜæ›‰§ pgž;¸oS¥ª+îù7ÔÈÏ„ÌÇJ4 A0D¦ÄêWæ–U¿¢¾2C‰qdöMø¨¶('ˆ˜‡i8yŠ7•LHÀUüjH^Ó áâà§ÎÚØ‡…¿gj!®,²1í%‹lݸ{X­(rWgZ‚™û@\%)Ëåi›¸×_Ü «ú8ùàV¤ÃwcσØFiüg¸F?ÎݧHǧnÝ„}L)r&!Ëeîκ¿›üPÄÈçè¼—£dyµ•ÒßÑMÏjõH@õÕÅÿ‹’ûendstream endobj 48 0 obj 4246 endobj 52 0 obj <> stream xœÕ\ÍŽ$7rì[?„Q†a(KV§’ÿ¤×CX­±k{×»jÀ0$z¦{fJ[Õ-ÍÔ¬4oà»ýÀþG0“YÝU3C‡É¦H_D0"¬Ÿ6ÓÈ6“ÿ/ýûòpõåŸÌæõ»«ióúê§+þç&ýóò°ùê:0-£›ÛܼºŠ#ÙÆð™Ü8 »¹9\};ˆí4NÒ'õ0rü‡Ý^;çF+ôð5´ ç¤ÑÃÃöú¨I(Ɇ£ÿƒ;g­î¡“Ör9¼ ŸÐ_ »Ø_:.^o¯¹šF#‡_û.\òÉ ·ÛÚOx ¥Ãn™¹nØáoât“Ðd’‡ÔÌDX¸œ&åÔðK‡L\·1“‚u35NÈ¢ñÔû´&ËñÔw¸{\¡˜8loxôcL/S<ïo~ðXŒ÷ŸfsóoW7Ÿ;ü¡2çmœl’€ÈÝýöZh6òI/+ço+çb1ÙÔ:ìßù Óº}øËå©Ë~ÓoŸðÀû8PXé®Cñ4ï·DNÂZ`J$&xäñ¾¡šˆšïÒŽþ‚Fîn3'ÜÂö¹c0“£sœQh)¦}>,³b·¬8§kÂÀ?¢¿ÞnµÖ ãöZK1Jøü¯-›¼F¹á‘p*rg8ÜVYüÿ ^Ü]Bϸáø¯ãÝÖK˜vÔ\‚ÒßÜž3ØÚ/Pï$GÌzîÅOg¢H)Î,#+}Ø 7jh€§ÅûÚ¯n×iÏ¢ †êA6ðH6é¥HÆüž®ó¦®™•.îíRŠÁ5·bäZcÌVÅ. A”ê$½ÞjY9*k‡=R%°f„¾¯)7=E L> ®G27ÒW¤ X¿@d$c0 þÛÛ·3»Vއâo¢›a|œœ>« m“ä¨ÐfäÒdÉñ†Žs= ¹`¸ô¨$w¥ïg‘U ¬ÒÏÁˆ)ÁaÇû}ä‹q"³0,üývY´_Ö.oóáÌ B’9­>šz‡RÆ®©7h‚ɾ€‘øÚ_¢§aj½° ê땉ƒ2M,­(ªj.ê÷»8· ¿k7–4ë6Ñ?~èØj¤‰yF“8¶~H(»_,Ž ÚÉŒ‡8*§”I9Ÿªybb µÁ€À'Ø»xº %«†Eô‰ÆaMD—æ÷7õ!9)Z̃ŽÈ!ü?ÖƒO55,Þš9“ >a`Xþ1ô¯o®þx%aÖÍÏà÷ýË•±näŒm4Ì62½9\)!&@nÙ_}{)¹Ñ~íÚA¯Ò"¬§*ôêùvÉ…d€/,¦æñäG6]fwrâü‘w³5~“fb˜Ì[Ŧ업£S¡å—–³—oàYþ7ØÌ΀ÌÞß}„Oƒºý9)ì™T=¿Îì7ø@?bg§±×å»p#Œ¹QZ7Š`€‡µbÁH-D0ðˆÁàRæÎN ²ŸÜh“8{RWÐqxÌî5eÅ1Íðëe­AÚ>²A$L'7؉­ìàþ]8 …qz`áS:¦^[Em•µUÕO]ŽØÁÔO[?ÿ¦ö½[;Ž l,ÆþhR|”`Ì‘5y³ÂÂâu'9WÈ— lð[wûR‡× Pæˆ<IÔúH\×h•müÎù8u_Å‘Üõ¬6'máØÎ ejo‰]à7Ë;@÷˽É"§Ñ ñ‰²{K,A¹‰ŽåÎx“_$î7ÜDÍwËÍoZŠt}ÂŽÌT1[µTEùí*Ê—-ŽrF‚åÀ6(;Ã)Ø‚Ùè‰@2jèÜon£eØcaÃAìa~b§8 o{êÆZktíÁ;»~†íkëÌ +s_?gÉ÷¡[·×âMÐ…ÓäÜ0ÌÈçƒ .¨÷f4>¶Ž,@Á–òöäÎ'È/Ù$‰‹tÞ$þÄ&×&»ünXDú~±uª­ Õ^`8X4^1ýnK@mƒ:=e±(MD90ÛÓix™Ö(²íµrÎG‹Ã?ÕVW:ÍV—Ï8*r™ÏDŸ‹€™IDϧ‘Á3|]ï8Ä)ä椟@þÐ\œÊ»@œð¾­Œÿ¶Ê ›É ³Dn¾o‡…VQ?GŒø\“áA]Wl«ŸH ¿oW³,&…à1QÚ¢°˜øaÐùù–É ÚÒÓh­»Ô0)3e;DŠNlç„Ðà - MÇ2ÌÍ‚jW©2àø©S"ÆV$¤5a^>N‚ Äϳ@s[> ó‰&æù’‚†&?îœX‹û $":!T¨I$ÍN-Ÿ,/í/Љ‡œó¹X•5A»$|"‹þçŠÀ¡ŠÏ‡Úú—Úz[[ß®`QVLСÑ1tš¼Z£ãÜòl,àÄSüp“ŒëŸ¶åº,ñÕß”4Ç ES mÎ:¥pxüˆ}”+âGJ}‰{U aþC"ƒ³âh‰øÞ‚&4 l²`04 N¯<ÂŒ®Òˆ‹U”\’K‡°<Éú9äÝ1ïÁ`.c¶ÍR K@,vÁÍ¿2Â+#~è¦#ãR”!wTwËˆßÆû¡GÃuö¥NoS“¸²97Ú›¿&ÉL¿qaº‰™3Íÿ)­5Ié„(Ö+ìÁx›‚L––盘a”\QEû=±ù.âšÁ!êãçÀ¸Ïû.‡jU¾ Ÿ–{\q‘A$5·%Dÿ_„k©`œn©¤ˆ}v‡² ûx™‹dçÅ÷%mâWB{ûœ0ùÊ%ä`g€§Dw/£Ýµ hvJÃh¢F`a8îÐUøCÍ =3…³œLü±C%™ºWd½4Ó”éщٕÆï+ŸŒGz³w͵¿š{+ìÙù.vdb!Ë–†žaüÃå\±É¹“Ö —÷ 6sV©W«Òì´Jwz È<æøàÛj5üÔJ]1j‰ÎØá¯q¾<îÕ:K`÷¢®ç½i6 tÐöŠ šC9mtåTöœSüɾ½¯'bLʾ¦^ûœ•ÍÆ#„ˆ{ôïÎI¦ü]ªíüþö6©öòŸ˜-aùhnn×°žpî:Ó;sêШ5oÁ÷à¼S®BŒEçn{GpI6„­‘ ÅFjn¡²ÛK¬•ëà)[ˆÛ^õJj3ÑËwq¨jôõCšQ¶•ñ* !Ò8%qÊ06Ó+ƒ.ð)³_³uÿÖ®ÀH9r0ì»öjŒÁ©ß˜€#Âáþgß™û$ò¬(W ï…–Ix:–i%H3ZÞò,®Ê‘eu¿—‚],»‰øQG:áˆM»˜ŸW¾<Bß*æuT¤¾/¯T>‚&í^ÅþQ¶¤‚9¬0ÀuMs¦)ºVznßóMsÏ1óT[| Vs•žQù9%M×ÅÒ‘²%|Y‚ýÒÙªb䢢£¯Æ€½A¬ø› #“­Sâ—¤„Î΄ó›Éde /ÿã‹m´—:ä£?ï™k—¾­3,„¼Í¯.f^|^Eâ‹€ï¶5ÿò&cW/$—rÂÎËV=£ÈÖ|m™ä£àÅ“ÑΙX\&¨‘ìmUM˜ÇŸ^mùŸ‡Á±l3Ñ÷ÕØ&ÙæVðHÄ’Í=¬¼®gÛú°ˆÒÚ\K´¥ï·K–e K9Í6‹¹òyV5Xæ0e*¿3æJlRÐa³ó$09?©cw‰ MµÝC‚¹9ŒÌæ4>]¦ógâVD5SÜ_E7S¯è¯DDóº3Ú²Ÿxèöb¤g¥ýSqÏÇñT¯uÏÐX¢Â¨›®®³£±¼ñðn+!4ç"›äÐä`aŸ^æ›xõì‚bœ…ò“pÍH<m€ zÄ€àiV\ËtÞ¥YT¯"b·à|[‚ B{ÙŸ\…[Ï®¶ÊWæv#énôЏñ„/ÆcÔïyL»—–¸ª÷xX˜3š¸s§õ>žï WÂZjz\ˆûº7y¯ˆÀ=‚ÅB­XZ²ì¦@ðŠ©»mjÁ³x²]l>ŠûÍ•ðʤsõ‚èUIg& Á¼â”Z¿JL&MnDK8A‰È`HóÓ&GëÂâvèù< ´ xb0ž,éJ0N"Ê3âµ' ì—!=}Ç4L;"ª1òŠouØè”Š®Ã¯Aüí*Ì0Lÿ¤•UÀCŽìäÝ<3|ýKírïé3p#&ï+Õï÷ÑÆ;ëãNl€µfìü'ã"Խԯ󩧇¯nëäasNr&ÑÈ—éÁ ÈóoB+­¡$ñäqYÊqpóâR×OøHNÚ™çáá§žÄdü{•0P‘yÇ f‚•¦¾­“<…J™/mRr¶Ta×}©ó€œž Ü¿Q5ê‚Ã’Š[éÃæUÜIfnS¡çÕh5·|ö¾£gT ³–ж¢³üIrÂ$Ís–2÷BÁ»Ó,ØRŠàá“ú0£wÿiépó†&®Åt"„¼*EÌLï6(dÞÓ|>aÔºÙå –óGd¼íñòWÞQÌSÆ)a˜–e×êýq0,$à„vƒaïg]GdÀ’|v¢4hòYI•7økžë£Ø´š¨0^¶HéÕ«ñiW®Ù©QßeW7T¼ÞøO{‡J¸•?µa¹XïRåÓd~8†ïµÃ1»Îõ‡"%–Ž+ÔÏU/“ ¯õ¾Ú–ån©{Éùj Þ,“œÜŸâf,‰_Û½²éäDK˜&)ª ÅÃ!aèÂ&ü˜›”H}ÊÕqz‘°œ Ï®,Ĭڕ\KqÃhÔØ{˜³ä©€ˆ³¸öÔ»×À½n…:õÈÉ"¬Y¡²ôÆ„9ÿ2BoÀ&Iè»9\Iæ@;eiñ×·¡cÁ´¥¿ä--ÂWß°3jZœõm÷F+•‡ükmyú,ÓeÜ_v(´´Üpîʸà¾âœ®ìUl?â¹0ÛLd¾E»)-h;4¦ÁÐR mâ´ƒÆ4Õ:k–oæÁO ñxR ‰7Ѿ“Ò¼ŒA!]Q:IyLø”ÝçäÒgÁ¾ûú÷lß5ÃöýIf7©7É«?7TšC_x†„¡0-CÈ­/»àÔ‰/çƒÊq­ù¦•rù$áUL1]Ìwl(±>vT ]é”ÇEîÙØÔ‘lù²ÝȵOÝ5¿€&¯Í3ɨœG²RX_$Ã(åÿßa&+H†1•¥êôÏû¢QH7²r¾høg„ZGÂêìfN¼çádÿ?*}å\ 6VsbhÒãtNUœþ²m!]±>IyZL˜dy½`ølä§˜Ý:ÉÎÑ4ð켯Ê+îËW—Ž%.ŒDØN\¥UãD”ØÓ)±‚SD›Èðÿî#]HcèOP^GîØî½!Hn»¹È’.zâó4EŒ³h:~ö,»©oJo;mç åªä`ˆ,éÆFˆaÉIã.á_O§ú¡/9…t#JçKNKÕBÄ_ïy½UMBzÍzÒ÷câ ŸƒRöÜÂD@ñœœ%^]Š/ŽÕéLÞõA)¤1J'(¯ƒBÇŸ‰‘‚·?3Š2ã›b¬Îý°)['h¥½$&lQ›4îl|‚Ÿ‹ìÒv±)¤°Îdž®ŒíU_ö뇲~»œÓy\°ºZGjÌð%ºé½àxŽeaU‹.Æ’9_«€¡ôÃô933”>O!´«ç2”…2†öáU$gtJØW¡×½È&5½˧ԱäÂÃO…dáVÓœ³“Ÿ!™†]¤…­s™¨¢ d¡L‘=HJ÷TMûã2«Qy;Êšq•ì>¹Ó§ÓÂÊœM‚6Üÿ{ hêsž&ð yò ÷}ì e æ Âëà!º_“:bõé”O–néó§v½õÒ0iÕ³Ã;â®ê4Ÿ áÂB‚yâaT =*œRÌã.€X™ÑpUŸÓv0.¤ÐÏǸ%ü¼òQ to äT@ÞZú*ðˆî+>À…-ä`é qz1ŒÓ—¤™”#èSäóßö.¤1â'(¯Œ û‹.e\øY¢ÈÉYµQVEtÁœõÙ}Âc²° §i²q­üë»Ã ¹ p>¨L9·¿ïãTH7À&üQ;úó’©ýù©»§¥v*GZ d~› 'ñÂ8}I1!'Ë?oÖ…¬Æž ¼&L¹^ÑÞ}¬ññÖó£@¼{¨¹ï¤ŒÉ×5¶{¶v¼ê=òµ|!}Höȳ„æ YŠ #Mó0û¨pþ »@f&1ªìÛüª+3…r#Cç‹ ¦{ïArlzJh²xP.{"ÌåU+NbÁ%·Nø‡Ïr…ÔO8ø=A5Ç3Á(Œo%ßC(¾YuÈeiy6>páFø8И¡ýãÕÿr§Zendstream endobj 53 0 obj 4859 endobj 57 0 obj <> stream xœ½ZKÜÆœÛþй™k¨~?‚À@Ø€ƒIœ¹Ù9¬vv%&œYi5«xÿ}ªº›dÉ­-ÙÐA­V±ºº_=zÞmD+7ÿ”¿oŽW/¿÷›×ï¯ÄæõÕ»+™þsSþº9nþ¼‚mQnöwWùC¹ ¡ÚDZ:löÇ«š?m³2Jøæýv§£o•’Íu^ZpW´Fmóˆ…‰J[ÙÜãÚ mlF TÍù9šn ýùÅvg´Fš;¤W1†à͉ðÌlBP¦9wô°S–Í —e(ß¾%ëÛI„"š‚;­Æí3ý”Ò JСù_RÕÊ5]ß—S¤lØ)Ë#=«#*!û}ÿ„LCk¤§"Ÿ(S~!K¹œ¶Ò€©…nžŠˆ24‚JRì ÿjÎoæ4VÉ'.&Ù¹Ëú±P Ýnÿ½ÿë•vÎG‡ÛÀ¹ˆoËÑÁ4Ý`B.D¡°Ò2cR&TŠ›IæÛi™Ü”ª­§¶¤ïC™wÚ‡Öš¸ÙI Ã"Éþ<ƒŒnßQ¢×d݂ۛnõ/¤Üø~»SѶ¬Å¾¥ñVKAp=-ø©U.î…ÝR­¸­¤g¼ ëw̶«atš+>og ï…EM¾ü^<Î´Ê Î €V)'´iþ˜–€E.q(»§iyž–·íôûõ´üµø~5.óÝ8¦J ~5::`›†ÿ•Ñ\ šd=¢ÀûŠêûÃD~¦,ß®Xܺ}w¤D=ùzÕH"´"Hn¥¤–¬Í ¼¢Ê:MËó*Áôûõ´Kø~5¬(Ö…Öùq0Q<ð;íŒU­²9‚­.\¤É Í“:UX‚MR³¢yç°nº¦+™}TumvîÏÌì«,üº÷Y}ÚJÿ¶rRßMàB§ì¦M+œÊ¾ À¾ÿ$é±~š%^UAE•ðŸ@ø)ï ­9°ONpQOùºÏ<±ÀúœE` È«5ž¼k>l•iƒ¬â FêÈ>u½F8Ëœ™‰Ý‰B©¨ËÜ6º;*Ú±¢©ðl`Þžƒl¾–ð’#,½+Y1ò;ƒOÃË*ê±½ÃÕ"’¿)¨— P/X˜ŠÐtˆ=ÇØö—`„(Uœ›#chÿ8×o)úH5jL."SÜU*²bl£jF›jzN=Øvó¢Ì˜V{W Ý謂õBz4JBz4 ¤#å¢:‚m- ³Àþ'¶%¬æC\pÐI}GxŽ’q|›‰Cc'Ilí2‚¬2…  /Ê™À¤;—µ1 Uf1ÇäšÈOë®V‚ìxº®ÀBˆ§[¨|¤=tX–ž\|¡Ø€­†H)´À­¦>å2Ñ[%…A_ô‰aÞ…â"­e.Ô¨wäËšá³Î#+Óv”¾cîv$'—>5ý#Ç`VOm4Ô.gfa€äBff›2³WÍZò6k!’BðØnKM«6P‘GkS© ‰a³ÿÛÕþ÷?4é!¡Ój‘³Á†Ø˜íržÚ|óÓDr‹ÂK(ûEÿžÖéR*Ì@®¬SЀu¸”Ðb*w¤x=ÔA®ù6)?˜à9JNZg~?I˜ÔÉR™ùJX…bl—W‰1œgr.ŠRcÅ9;„¨ ,•pEÈ×Ón–À*ç—$‚9!E¸Fɵ> S€tƒ%þ>•E'âF.¶NËg·F@îgl7¯Ô¡•ÀëV°‹N43Œ.Ð@©RBõ¦NÊ£;†ÏXíò˜‚{hÀ««(¼ˆ +G‘r§û",RItSÌšùïÃR&˜ÆR0B¦«u©¼møh–à•Ëc^C§Š¥OFpÓ:3 8+ZËmí¬7éF•øJB]ºº¿B¥šéhžœˆÕ€k‹Œ†Ž±j¦¬Õ%¬é¯ÌŸ ']ñö˜¶Á G–Õw š¹¬Rò>Tÿ C­¿ä}˜ú1yEj'zñJ\2ge³ñ)—¥Kax.Ô€¡}%·?ëgý‚¶ØæÍû…t$;±[Ô?™ÝÓ «[çqW íeA›b£'!Hz’‹P ŒÏS;ÓXbM_h´á³ÚÇhlKM‡cðÛ”aIJúɲ­#Èá@d¢ž¾ì„5º„v­ýæ4“ÆÒ”5 ×âÁ1Ì GVbæ®3Ž›Ó-b¬ŒªJy"hRÜ Šã•Õ5‰ÁWK“¥ý‡-ÞQz7š×å|ž½¢»¦8XË +yÚb•`Õ`Jjë§…¾KŒoü‰Ðdߨ`Î~ë!ƒ`"‚œé ÔiÈ“x8wX¾{³ìeKûu›9 ×óÉŠp$Ÿ¬P´yŽ“¸^ê8 Aü@=…YͶiláÆ¿$÷á-´ŽÍ—SŽ_ÔZp4í}¹½0ŸvàW*Ú¤eÜ ©$ÞM@uƒšAs—·ä¾ƒJàÌÅD±àYŸ +åOI““Hnµ§bc³ì哨*4¹páØðÀ7½¯ö.²[Á*3tC/X²÷¹Ò°QœzÉ&x´èΣÑí|F‘Ìäk÷båÇ F¬;]&ßMÍd PétwaBØtk…f™±Lëdg%Ôg¥M; ±Î†‘Ý þÀi İØ"\CXã‹W°Gú‰K@Ÿu§gªì“©sÎ îɬCŒ²)hAV‘WŸ„+­ ¬¹]ÑøÐ çÃbM`j¬Çª;Èôµ˜|ÈŠ?2_tµQ”!j>”ê@µÁ›µËÅ®e  Sý,ÿg¯ÑúâuÚAý€!v¥C\¼*F>ßOV¿ð0Çç(E9Ò¬¾øÀLÃ쑬)€¼ÁfÈA®#NoÛ`ã²Zr¥ã²Ó,‘Æ_ªx³³žûÒQøšŸºK\ÇÀ» yPe u½§réò|Y³œ¾ÑüŒ§j°¬ÃGQpDœÃßz«¡ðH# c…iVu£‡ßlä9?©Àf͇m(¯Ÿ+‹Rã=œ­™ÿB%EÅJɯ¼ŸÖfïóŠ{ÌjÉ— 5v§ÁÝVº@4¼T,;ÒTz.Ÿz>ï¬<ë&}Ñ­ösçÏåºÏ{ù=ÐäFû_†ÁÄé³+)¨0_”Y$þºâ~xBU4@jnVqà§Ò¬€W Ålj{wšÖ_Ð>›&ÉÃ%÷LãSµòâ! ”díiÍ=ÖóD¤£A4¨^ÿ*®Üh@é‘~ÕÆËÖg( ÜRó Ó÷…¨ƒà$oU»C¦Å_X1Mž+ðxõ° FØJ s›ù íèаöjô;jáŽ>4ÍaÐqj¨y~cЛX|p ~* sjñ ÛÁªOü¡ò¶öbO4ÚS,¬¼§ë.Þ²´åGA#.å"ÆAêŠÎÔúåp™†ïƒ?Ѥ4«1—%9‚¬?…+æ¶l@Í5ñœ^omæB3BŽH/[!mÓw§%#¡ÜeH5 5þ‚ÓÜ,lš~\@LP) °oL¿Žñe0Ì­v”üH«•sÁeg>)ç[¬ý×Ä~ë²òf7¶¦zȲ©ËXÃWãç{9^Âbu:êÞ0<[yéìj£Ïžñ`Êt¶ù‘eĶ?$c*i°3ÞìtÄ1F¶e˜>AšoöWÿ„?ÿh¤endstream endobj 58 0 obj 2829 endobj 62 0 obj <> stream xœÝœËn$·†,û)zÙZ˜æý²tÛ@vNfd!É’¦®ÑŒ§ÇoÇÎa]ÈsÈj–Ô¥Ã0º§†¬þy¾Ÿ·S¬ù²çLìyüoü¼ïvßþÝ퟾î4ãfÿûŽïÜ §SjïxL‹}·ÓÊ{ælºrÚýc,å÷6Í‚‚RÓ(Å™v})¾Ú}Ù‰þ7÷ãÇ}·ÿëø]XàAì?<î=b/\`‚¸m`ÆÂ_u»¾»ÁRKîwð•ë •ÃwÕÑâðëÍ7œÉ¼·‡‡©™NþJoã]tZÎÇgô7ŸÐ÷¯ñ>šsÌ¿>üm÷ý‡ÝO;©­eR¡¦NWpS_ÕLi$³\Ñfþ{òÛ÷U+ŒÐÐÔ¥F jÄt7bb¥ßÿ‘P÷Â0©Chf;B7‘{ìcq„æH©\°}ˤÔA˜žçpµ†“!\‹ŠÚpˆ O7*0«•ü奧%½—àS(òÔžøúˆŠü6On ë7—=Z#MMGSÓ q#‹õŒ\ƒÌIh…"ô9#{ÎÈ~o KŠÄEmdXF‚:ÿ±Â Ã<¡ïúŽQ¡&DÙ yÃMÜäü<~WPâ#*>þP,þ]~îÇ*чIH£¤-LCz}Àfx/‡ÑF /*ÑQÑrT"C<6’Iþ1Z3­±£Æz+e<3B¿fG}IŽªm”d”ƺÞFTE¶Ë—›ùúv~ @î:¡"øŽã_oÀš[ EI’pÓ<H˜[¬7ñWrƒ@Ü 0éê¹ÿ ‘Ö5Í$Žð]צIµ½M|MÂÏ3]ÍÚíþ+b€r®–N1m0î±Þ Üp#-ÔÒÛ ö.ãÎ&¨'E%õë—‚F~x½ˆÑãŸ0ûó4P+}œ¹ Ž ¥Sáí§X•Ô c¡ÈÌ+½°pÌ›0Äó!þOü¹xCx/ÈiÓ¥j`ù‚½˜)†tRÿCŽÿ§\âŒ7§i~”ÛbCøŽ±Iø8‡}–Å@c¥4¹f Šôñ;Í-­k„Iz=B,oËp×Â;º9BúpÂ!º‰¦àÖ\½d9ÖåÛl:E Ç ·æ :V2fk»9gǾ"2kž{î/÷ܬˆaAQ“<t7O~fIî…ÃkTÐ?Óÿï@1qÊâdø »ò®2Ë k8Ǥ0Õ0€¶kOùªH‹´Ñ;³ëµ¬³´Ðõ†Á2ßÂ0ï| È!,-B a‚e\bCÄOc×Â*fÇÝòý¬>å«¿4L´[,hk› 6‚Â1í>g§qPð…´¿Pû‘ì­á½¦“Òð۶vÃ!!ÅxbŒ_â­-x¯ó¦z+-ÂVû¶ãœjîIQé„ë¹SA¹—éH>fÕ¶[xç¶•”% ã8Ú×3k¡FY¸huËÆæ*+"”µ™”‚fúTŒÎr»}pn2ghÔ˜!Ý'Ö«ºT0NÃÔ9£º}Y÷IмEmTDÆpŸª5ÑlvsëuPj=á(ŠƆ[`jc½Ôx€ù¶N2Îz‡y/‰+‘^kûxc\Xã^xöp— ‘Çñ>¯’û*”ÙŒz YéLxfpв¯'Ö¤(u0°(ð/V[Ô³8âƒqMêDÛFÔßáÀ#Iì!hS;¯âãé®²Ç 3xÉ”S/¸›fHâJ{\o¬m#3¼ï©!µt ñ…ÕŽá,Y_M¬I{jë!<¡ÊkͶ¨žLÏÛbÒF|² ­íŠRÚ yÔÅ—Î~Ãn=5žð4É©Œ|CµôŒaÒéŠÞ}¦÷\2§—´<¯§WJkÓ»v¬a;¨)&fU9Ë,NhöÕÄš„¦Ö‚M "õœ¡Þ¾¬K&mó‚¶6ÕBZꌹn»ýn!.hZRKh2ÇiÉ©Þ ÀÒ3áÌ«Ýæ´œt–ø¯‡e¾Áq ?áâ>G½tñà†YœÉìë‰UC?0°R¼zèoz(é$®ZÐÙö–ù~=ô· 1› ó nsJÆÿ Àÿ˜Îš?“=RÄKÃ{ôÏ/qÚº¯'Ö¤­U| ¥ÆízPrV]iš~"ºmLDI'qԂζ°ÌïûHJm¸&‘?§œ#aí Uä±|î¾nw|>‡€ 4#­úå?éﱞ[ÕßµeÞùƱ¬s´+¿jÌI=¿ ¾‹ßä@×p]ÊKgßßÂ[qU¢ÅvÞJq'nsÅ`!Ä'¸§z+¼¥ƒÿ«œ¿fo=¦«µ_’¢ÒA×û¥43`‹zÙx«"k+Ür½7¨´ø×уS8Ò¯|Öî6'¡¶;i—ÃPÅ%ŒIœÃ59ì~pñÕÊ e§~¾<gAç‚ &½RÏÜNËÒãp­÷˜„¹¼Lï(¦aunlßô´­ˆ¯\8¥}w·zuc0ÄÔõ·[æØ9š—.®QqV|ª·Â2ñSÕ‡u‘gº†g’¢ÒE×{†šOÌ:B¾ÚYÎx÷xJïÜl7Näè•> Ô­0L=ÖskòØÒIE>ûÜ<šô<·%¨½tw,èl{¡”IÓu°›éãNÿ‚3»㈠Շ¿à÷ã×ã…sýÓ xú$E–8ÇѤ´Ô×â¤ôTo…OW¶zó:§ q±eŽ$®´Ëõæ(µ-¤IoÇëÃÄx®{»u^FIÿãPÊÅ ÿûÓ•W‡LqÅ$짇ÈaFÜO»ÿ/Kó endstream endobj 63 0 obj 2385 endobj 67 0 obj <> stream xœÝ\[o·Ú·ýû–Ý¢šò~)ÚM‘) iô–ôÁ–d{Û]K¶×—üûÞÉ™YíNÀ†ah4"9‡ü¾s%gÞ¬É@×Äý‹?o«?ü[¯_¾[‰ÈõÇY»ÒŒ¬5±lt}XI!É U¾³_ýàZ Dèµ²R –C«tZ‘~ºVdýrõfEýóÖñÇÍaýõ5<ÓÀÁK××/VAºÖz°Z1ÖRÁŸ«7Ý‚°L0¢7Ï·î!V[¡Ò5ã’nÞn¯ÈÀ pÿnËÄ`¬›¨Åî™EÃÄæ¸»Gy®ßm¯L–½ùiãÆdÖ£67¥³ï* —¢îúÕ–êÁ(©6ÇŸ¶¥ë®ÿ±úæzõýŠj˜±hÅÒ¼bg­5|P†Ö«õw÷ì$àû aÃ|¬<-†¤Bóv-@,B¤•yž Í!ÞÀSpä1ÇÿZÑÆP9Lhœ_*f9cä h\…#L„1Á¥näËgåòu¸´TÂDÝ%×v¬,'‚卵ó`a1˜•oávŸ€‰ÂÂ"Ëú¡ƒÖ$L!↰Ú< ïÇñIøwÐæ¬ó„3XJƒhÃçºI¶>Í`6ú‘ðÝøîfàËrV€žs¾JÌ%2wÁ¢(n*0^xÐÏ_n<ºÇhÓ˜>G‡Ü·C,OCç˜ñ‘B B`Äb·ˆI3HÊÂR¼+ØÜ—Ë}¹ü0†XjàîîÊÝ¢œ=¤y"-È—CZÏ£h lÿaKÁæ®=¤0„Ý~‡gQ0¬±o˜­5öç -=¢QáITÚŽGqÄã«êùA0Ê\“Ì#dê1½âàV±èl%gj“Lå›?ÇÇT^·#b†¦afEDAÌ`-&¢ëýÃ…D„¬4ѤŽñ6_öŒÊU;!Ñ<£°@wÅìÑõ‹™s Kíc€ý6Ýdž< Ørzp:d^Œlë™æƒÈØmœB€Éú½5#÷Ý¥[†·3> ËÙ~9¼XÌßâx ‡±¤øFVêø~<òºA^`*Tí0ËÓl@¬0£¼rÚ®Ks1¯´a%n 4wår×Á8jÖƒdh'$›‡¨,»ã¬»Úb¿ iaž ¢0G Xœb„Ȱ†ÈõY€‡&mPu?ŠЉ3 •…¬À»*,ãd¢Åí $£ï†[(±/;&_VEV¸E盃ùŒý «€~®8嬼£ÝïSÜNñà¯ËÝŠtàá]Tñ#ðovl ˜à8á¡ðò.s±c\† S3NRÌ8×Gʤ“Ö%}vŽt(¨¿+¤{6vü+"WŒf‰Z†^ÎG,ÐSXVÜ"ÔwývË®Ïè]Ø‹±O‡4ˆ9×TŸÅMœêlTÆ›õÉhÝBøkê •‘\Ó9iºé&ÓÎ6©FÖq¡UÏÔkT¨ÒJä ðÞ…ïFE%TÀôq“ö1šp(ž9žZ¤¯ôí„ôóÚ…… íÂiLj5ÇS³1UMÐÞ'&Û”d¸7}ü;QÌÙñÕ(­ósRžççÂpASêlëå–é¬ã.j°œß®o]³Ê›™µ™Ö1·.på9u[ ¼Ä 5WÎDèð‡i5-r¶Š{¹šb1¿D'X•h'TJ&´.•¤MYÅik(™Œ›£ùwD|*¾Õ¬Üi«ˆ…»fVŠh8x ¼è»Ñ%{€ÜèArºDùÖ2§JOÌiVi«)}Þ¾up®’;(M¿,ÕŽ¯=6T~zÕ¾(ø-ìÅzLë[î^#x·&u[ ¶š»2@W’뙎¥‹E¢V /W:,Ðoðaí×H홛̆թ½Ý!AD{²üz¥”X]rÎï¢RD©![Å-b o5ûntÉV3wÇQ8븅«O3ÜÊUl;!Ñ<·°@‹¸…pø4β3£Ï¾àUà´£õ~3÷Å Ê‚¹nz‘ƒ,Å踧‹j±(ð@‰3› E¸Š‚'„›'–í©Ãþ‡öº=nŽßgÃŽ|—°‡Öx©S‰8l™Ø ÜíwÇt؉MÖ~A1±§NxFè*M}¾« V*î>ßRx”$º^ä>¸ð.¿£w&æ»nŒ(³lPxGAݹá >þAÜ.Ë«ïTÌ+vêÐ)Vb V4]Ÿq‚A&ƒ+ôZ WD Œ‹N¯N!ŒmŸ×ëU–¸Ñ´Ëõ |;ÁA¬d¥”snE·Xâ³Ï}<â%…Å›ËâÖJŸšáÄÙ üµ™tçìÕ7Ì X3f¨;VÿÏf&Ë’úÏÑ»üUU¦ñS‚ÈÁ¨”ùæøE¡î+FÙÀøÃ¤Ðñ¢¯ G ˰M®£/–›‰ú&’G€Í-¥­ÎV!3îð¾Kûãî(Zx5.Àë²rã-°{1‘YT¹E¢‰\vâ±ÇÝ18é±)ͧÃÅ@dF÷7qÌŒãi¬ß£ŒQиÙÛ+­Í  ß\o5xYßpqÛg.]±®\;*4nÚ ž„râ_¹’ uEÝÛë’‡iüoë‚1Jhv£*}ÕÄ+ËXå?Ö……ɬÅ=f…PÀyó/ÏÿI0¬4²LÎPW`Æ?–æ»ý>Ü×µNâ/e«tºR9¤8¼ponQKÙÔà‘K¿-("ÜøNpe÷?—¯Ú¯ŒÄu±ß/GSÛ~„r=¶tî¦G…èRôØ:% €©C,¯#œ: %‰¯ØÈ¦4†{luÛ?ÓŠ4FB£ÑÎ&¼ÌPU=Ãa5ºïö” §‹Hˆ€èÁ¦)ý©Äá3'†i¶‘åî_ÊåUi0Þ–»¬Á5òTO¹o|Ä3rýßÕ•ÕR2j¶ä6HzÜß+¤Ñ ]¼ ¦>&ô~ÛØÞ¨˜}èìõ© Å9w™ÙãÔò`Kïwáa‚Úiéh%éo+Ó¸åà­ 5˜üµòùáŒh_ßó·ítì1‰F7Ë’Õ¶<n€*©Äïþ‰áÂC äà1ÀºDÇ+¢z> •ŒôåFT*ŽÀ‡m$[S°±RHÝ ½¾þçêúw?n¸kÏ©2n%c(ÁâÎn¾ùTšÜ¹e¥ÀH½oÐõû`7¬!±fʰ H÷ߘ(-^¦œBm¾~V÷,gDÂ"àž7!3!0Ñ¿y¥Ç.BÙ2v<ø±•”‚W×al¦}Ø?Þ8ôI0ÄQBûÈb4¢­Ë©r‹8Mè÷>¨´Õ0 QVÇY‚¯ÐXÖûÒ?1ÎL0:ªcòŠ—"û¥©¬¶y·ú]믃ê¥åÌí]ð’ÂVu—9F+ŠLgúŠh*ùD9@›,Œ5œôD˜Í¾.з&VÊú”,Èñ_UNAƵÿ"j³hÀ*ŽnøL¹„¸ž8B„ÇÆ¢Ü˜‘™ùˆŠ|DwæÏßæbj£GB¡ˆ=‘ˆ(=FO&"M:å^+5uâá€K¹Sn9ãWWxC†"„n …8ûB×1 þ*ð]1‚µÓÅD[5žrµà±¦¯QM’=Ê—C/ÌŒ¤HµŸÄÊ1.Mw âí?zœ!¡`ÉÜw¶”K†ŠßR{_|ù~õûUÙ endstream endobj 68 0 obj 3946 endobj 72 0 obj <> stream xœÝ\_·×7蛥"Þð?—E -‚¢m€6‰ß’>œïÎg¡'Ÿc˱ ä[´¸Ã]’3äîRåsà–xär8¿g†ÃYý¼f_3ÿ/ü½_}ù½]ß½Y©Žéõ»[ÿeÅ…³kËœè_ïWªðɦ–ûÕ¡—Y§Uç$ôŠ-ЋùÞ¾[ß­~^ñaÎuøïz¿þÓS˜·‡†Î1Ç×OŸ¯Fyøš[× ÖÃc]§ üi¿úqó÷­P]o,ßÜn9Ìæß|زŽ)'¤æ›7Û'0#cÚé=ýÛê›§«ïV‚YÞYMä‹-T¾³d\vf²ýäàBKa6¯ˆH·¾Y9§äæµ—N2Á…Ú\aóa÷@ú¿<²œI–[èr"tÐK܆¯h=×~ìžÂ ®+EÌ#ætg¦×áA+d9SDÒ䣣“סsßUºÎõ½ÙPu¿Ú>ÊuP»'˜Ío¡‹î)˜÷äóž|¦ø¶SØÒš iÍ ìw=…ÅÓ¢ª®æaQñóX[ÛŽªù¤BZg¦¨¤¹3œŽÌ]G…N}=¯ÃQùÖ2í± a²mrpè]éCžýïÐ[ê³Q«n»¤ Ñ „–VªSŠâÆ5à'a[I0„ºï4£ÿ˜ðŒ€¥r=h+´Þaë-¶Þ`ë¶¾ÃÖ—ØúUjR$-¯$M;Eèê(¼ï¶0ŠI§)⮕ü”]{Ÿ! ‡U­\~\V$…”<ÉX¡X>‹²Â &ùLVhÝI럩ü'¬¸Åô0ËŠ×ØúUÅ.$é3‘¾:žZë·™…†ÏºN;²Á©½-†ÑG­8—ê|+><¼çbóÓ†¸ðwèð_ Œ·hÇ^F²e&({4uKÐb=Ûr×õ°¡šxÁ,>'b䆂Òj³{3~ÖÑþiÁ{¾ÙÓÐä@C§Ÿ¶(Ê”ß ûŒñe0!¬„É)¿Ã¸ ¼–ähÊ‘"o+ìLs—|mgg9õ\ˆvƒÈGÛ˜œ`”JŠ_/NaKJ)Ì`ã2‹4ü ­.ÁŒ[OÞQq¯+˜g™¸÷@•t‡(Ý‘æûÛžg ~YŽ¤Ý½™Qz³(Ið)ìâYXÄpæÌD óX#‹GC¤ÁX.µ ÄöÜfôO“fi7‘¸b‰rTŠÝš“ÉaEº^j;%âˆtÆË€t¢cð7C‰è]ÀB¦: ]6¼ª°0Mœñ²…tÞsYHü¹§N0}û~ó>ðDš!jrèH‚ht6Ü}2*¿žwZdɇ6!5Ä÷„ŠñÐ`ùf „æÔà>,?pÙ&Îd §|Ö½±ã”Ï~ÖPZ;¯É`àž-SçÎH~dî*«³©¯g“/ùÁ.d*£Î‚[ˆæý’|4‡èð"ûyòõì^¢Tù–DuFw‚+¾ù"l—QnBÿ‘}÷_`¨pS =v¹Æ»}9Ô¬¬ÓÜ}H vc9f@îddÜIì¶>8²„ÝqÜì¶¥Gн¨°;Í]ò½Ýtê³Ø} ^J2Q›EmÙ‹p&apøÈìÚaG-ÎWŠ"½Jü$T¦ô¤$zð ë=é\;ߢÊKšd¤€ ³))ü8m.!…‘ ™£_*œHSg,92uÅÌiÏßzí9ƽöF©NJ>nÒ “c»h«¤7¸Á…»ìÌø–Ó{*‘eÁ]ÏØÄü|›Yð,þ!Oœ2,˜q.˜ø¤ ˆJO+qÜ ƒÅ¹þ¦B±4wIºvŠÑ©«¾Ìx„Øì*X#2F 4í=ò„¦ŸŠƒÔšÑ9©EºC×}5%•¤w<ïé,å 9\* MG¡õGHºö!JÚf$•ü,½SÆéúµÆ’J÷ÿ¬4ÍÑöÈÜu’Ò©—ŸÏZxñ¾üÞf+>m¤`†§7ð¼ßùÎÌæ˜{g²¸N*Á]ìç=¢Ã;ä>1œ¥Äi\¯BI•ü:¿©Ñ¼wÎ,’Ç›,Rž¯…×\ÏŸØ›'Û”§ý¶B$QFŠ#Õ)@Z0NÏÉV\èB‰¡à´„ßùG ˜I)¼¼Ø¹L+x3ŒkÓ{àOš9ˆœ¦×6oLLNÔ.]P¸év\©lùÑh&Ó”â qâÞ"éì¥Ù»(®ôBÜEŠœ1Z^>êÅù§Hú+IA)ªï;Mïh†q¼éŽ&oå4x¹pÍñÝòÎŹ3ÌÌ]E8›úç…#É.:nÇiÝÅdËí*:ÆdZçôž@ø^Þ¥º„ØRÕ±Xs´Î ìUÆt"²Xzá$·w^f83iˆGÌö g –c7ß¼Ç.·~‰Ü0 Gåkòùí°táz¦Ç;3mœ–vþ#²÷‘ö¸‹6Ë Öši€O7¬ažýÐÛ( [‡~¾Â.ô7c>Ä*»ùzT<Ÿ ÛÈoW®ü§¡so!ªy½åÖ_hš1^ê{'†ó —Ì8Ke¾ÂÖqQZ˜¬ÃvxIfv¤`à‰ÕŒðÑǰ߮žþþÇÍ_GÒ0¥Ìà¢!x°r)•>[w嵌µÃõÕá!|V3¾Úa0u>“›ͤVñß÷Àæ0UøCSÈé¥Lèr‹c[Šx@¹?/ÞgnìšF`‰»( øa2Û6$˜®—'U¥å°bs§È—!³ä·Æ$tš«£–óûƒÜÅØ\W¡³Î<ó@ÑaÞÍÀ§r}Qüöφ ©E'{Ã@¢ˆ×ã3ü}>UÃó±yØÕ)æ§)2Ò¼”P.Oq24KÑïÓ΋w$Oâžp¢Ü¸¾ˆÊ°/(ÇâQ†oòÜÇÁÄ/£„sÿBÊ“P:åè|û"ñ”:-úHŸ˜açæÜàR HVO×1 (}j†[•gl*û4Ä(ypúa;¿•$ÇK¤À:/8΃Ç_ÎÀù»á†Àê1lÔð_•¯©t ¾žƒ…(F:P Aú׃Wàˆ¦z 6-ßã>s€ÉŒÞ¨É<±H‘;ðµ°²ÄÐÒ¾JІ£d¶Lïn¡AÙlŸÜó8ÝÝåîîŽä#TqbD ¾RuDê@.—ub R§÷>¿Ô¿ËÆâÀÀ^ƒ}¶Xˆ3­ "õbXq6C •­x)Û±lû+<'qMŠÈPDØ„5 3ß·­l-`&ygbõV†òÙ3‚äCeë©(š-em »R°¹(¬©ÑÃ>jâ€Ú¨4~ ¨q8‰*Ñ÷mG÷•Wè屮©ì:…Ù•&¯v)Otpj|ŒúÃCd‚äKN ûbér¢S]À `“ÂgÖÊŸÎmµO[¦:ëÈ>$uîwlQŠvKAT‚— ô›ÃûqÊDœgRÂË<¶¢7.ÔQ䛡ŽÃx=ÙÆjøâ+ Ô‰`šQÏd;Žú§.çd SÊà–,ÃÇ,Ëœ”Âå¬Bp2’!«¬T¡˜(‘¬­’(°ÊZ?]Q Lüx8Bqn""RƵ%~)ÿ?èU/µ=F’¤aäŒ&^Fhׇz Ê™v’9^fä¶ãIù6Õ”(MÁv>PŽâÔZ[Ÿ\µ]3NYpvÑìB0‰*Îø„P °±ö&ñ£­ð&¿8‹_H ì q¨¥¡i©ÆI„ }Æ„¨–@ÇÊ­û¤M¤B¬ÆÀ†4”â$*´Õáì¥ñZ©…œ×ìQʆ–2œ„=è3Æ~¾`ƒÒ Ô³/ð iiÁ© àN„*J‹v‰õ/fyð¾Â”¦`F;¨@Ÿ1f^NÈK=w$«1û]'JR}Æ$ Ó&Ôú$Þ´ú¢0çoíËbu=Ü<¦t§œAÁ(‹Z gJÙ’Ž[ÊÙ}âèÑK‰Ã;ןºœxA9û@´Ò“l3|çþZoŸÓ°™wcn%VèNíawÒ™V0±™w™lä"hš8VâpÿíKÜ/Ϩp`®x÷öã—¸‡÷½–‰»xÆBÐ3ò"[­ÿMHÖ¡7¿ ÍÎ{Ñî&ñ]…–IÊÓ#BÔYI…È*a* |¼šv)z¾þ¤5íÐQ;'©xn´/ K@½Ûq†£ºWu%O~˜ä¦‚>Ê•ó¡}*Z^‘6»éO¸ É~H Ùª…ÞKÕah8ö ÆÔjã–DïÒK¿Îzk›áuìñŸàØ9û©â éÈ­Û\x g³¿O!æ\t:VÓ忤² ¿ŸÂl¦±Ã @ó?}2l’Ä=ºƒpËÀ’Àޤ-ã{ó .9üúhQK|?ÇVR›C|þly…6í œî²#²×7U&{éÜëÚòŠHßùÂë‰9ô‰Æ¦<æ’mL AÜ9¹ãà FXfB:toGZñÎÙp@*:ž!|$ÛDÊ*Ù&"cs;¨TÌ ’Æ ¯(fûÿð_ ú϶‡Ç°¦_£©¢Ölòvõ8}Ï,Pæ¬6ÿ F»xÿäÔóCqgZp+á’Q ¹%”ô¯Eì Õø%žWôßù˜²èÄã F)vD¶:¡JÙæ<ÛGxePtaàÉz¥–Ð…w&¦’|[Î^¡ ieÖ1/ÃÉ‚øŒÙùnõ?Gã½endstream endobj 73 0 obj 3695 endobj 79 0 obj <> stream xœÝ\Ks·®Ê‘¿bošu‰ãÁHR©²«lj“øA;…¤¨-í’4µ’ÍüßãœÆc€n`f–\º’²KÍB Ñýõ=ún5ôl5ø?éïóÝÉû_˜ÕÕ›“auuòÝ ÿ¸JïVžÁÆWŒõN)¶:{yßd+ÃWfpý ìêlwòM'Ö°ª`†Ù®‡ÇA+«¬ëÌú” ÃÐ Ó}x·f¦wƒSÝÍÚõŽ9íºïý\Å#U÷f}:ô~ Õm`œ3.,ï®ý°àΪ»ZŸr »Â콟¡4·¶{…f\ kºîK4Š&¿„r#\wîg0 ¿\÷:ζÆÊžýŽmžÚpjîÍêìÓ“³÷¾éþî—à¨îîÂkà ¥înýY¤3ž7žÇ€§ ãp,Ö½\sÙKe<ùÓ:g­¢aAi-—qÁ4 á8eU¤:̈Ա“§%0ØÌÙÈ"0RƒVï_ ÝÏ× –SÒúãò@DÿBîÖRöÎ í…$œ“F“¥7×hÎU\Áyw=JpMV¹@ÏñÀ°¤˜`¶S½´,Ÿf“–¶,±Á¿G(!T‘ÓûW=ñÂS›—y‡HIHÜgþ¾Ϧ˜4"ÍH¼ÿ59ZšÁ»]œb  OÙÜ¢oÑó­¹oYÊ[i2Åý!ÎËãëtLÍ —Ñdüb¿>•BD÷™_{PÂ0FwYf‡í…³Büœ`(‰–HÕ‹"¸ß{¥ãÂ8̹åÀ Û2ú‡ü8¥r®‡mì u˜_{4Žã׆Ë^q·:e¢WJ8ª(ƒYåp^=k¥íÁ0TŠR˜uãd™Ò @Á0,*Š4Þt»F=P"¶¿‹ÁèþÆÓ›¸ŽuŽì…çÜ %#ä´åî¶éPZù›«z<¾û‡}\G0G‰»ŒãL+ïZ3ëøž‚E–£Û¢çz&˜° æ}‰ê¾^3ðX ?æûÛ$Av$Z} ë-֋˵™øëÁeÄNrDEnÓÁ@Í‘‚Ö¦ÅÏqAD0‡MÖõ(T×Â@MÎJz;Íó  °Ãkï—%<½ Œ¢çÄýô™ƒV'Z%¥Cª sH¸^ÃfD‡¸¾O/:jÇ6Ñk û ¯9‡„Q‘ÔâÂ*çåñõx‰·Ù¢çYÄáÕ3úLw ©Ù5ÈXÂ2/£| î‘IwƒÆm·IÚ@ú‚‹ò‡k%ÈY-A m°/»Cô0ÙÂ= “L¼Z"H«èx+8,êzÐ%µ4£.0½”1X3[Ó D bÂôâÈ$ªÎo%|~"Ñ«ï!LþøÄqNh¥……¿õjw¢˜ãÍæ‘íÉ—q’0+í÷Ófå@qÏE˜5q?8ÜŒsð`–[ÄSÔý—p~mÄ=ò;Ÿ…Aˆ¶pœÀ~«Ñqò:Î(5oé4‘Z!RcpFF¤æC³Ì eÖÈy4ìÃT?˜ÄšïHàC¥’÷Fr:¸÷²Tê­IšØ¼É¶wRKìããus6,zˆnæ¬J?³ á!³m£rFöÀ‘]#ê#;xpÁ ë•‘»(ä݆GéX´—iôeÝ×°mðPH®r<0ÅX H¾T&ÛѰ+=á'‰}ûe ©.¢ò9øœÅܧ=±…Uɉ˻-N‚ ìŸöë6…»5l fì9BMxÍÈ#Pã,Ø(É`I0 Ö6¨¹+øØ”Ñ«2úªŒîË(ͤ(€Fâ ¢¿ˆBû¯?8Š™OšCSŸL#§ º§3tÌ ¢ªÐu¼kQÆÇ2Êãn yë Ç#ï|> n7 (øÛnï“P¤îþ3•b—°ÝþTbæ‡fQüÍÇpYÙÃÙ®—ÏfØ\Œ×zž<©¦•8LiÙ.P£õTº±;¦7ùVª ¡h÷"ÑÁ Ž·èy7Ww‰dùí¿íÚÿŽvÝΥћ”F _—CÿwV2hûo×´¾Cõ0ã¼ÒL¢‡JC®Ì°ú׌zŠ"jàPÔá‹EÌ{Õ<°÷²&â­åšH (µÚqý`µóuüAjΨŸ¬vÑ}Õ®ìÿ®v¾¤Kt-ª¢¿‘£*.k]5ÑÃê¬dRpØ «ï)™•òÅ+¡"ôß-h]Þšèá«fçúNaÐ;ÍÉ»” :E#ÂSÎ!‹zú½Pà†™»íº‹3$Ìx|›F:n•3¶áÄ}™»ÁöUº‡r½– Ýéf2FÃè…ÞŒ 6’ÀÎÖª63­>_2ß—§QÌZ´ç«ñx _¿‰£Ê¥Kùñ‚˜™>ôja*âE¯Ð=$ÑUûÄ·®àN(l‘žÕ7£ƒëdkÚZߌ> :it"Íiu"ä·öø+ø] !Óù&Â.:=Ý[˜€©Óø$ÝBË—ØÎº”í´)jLß}uÝÂÏôoÓ^΃}o›ŒÒŒ£ã#URƒ' 5Ìú 4)X–hÕiRÁØWÀB7™{œAô,2D‹Gv á飃ª«±ä‡ç*ɳåÅ~šªHÃåibÐ)_ëldÓL·¥-Ý–_¼ÆÆJÜA‰+QSdâ)ã®û£·³~4œ5mr;%1ØnV£ÈSÓJœW3 ÔxáÙ>Îñ½™fK¦ÇfË‹d(a0fÄ22¸órDfÕjwœñV¤¼wÓ 3ô{z,DïUú«Q5zU5| ƒ¨¾vÔì¹®:FÞB©^r=×õ±™¹¼ðoŠÞ¦üUSÌ>ESÞÅv7…;©iE”*•ßRZ¢™·>3á囸hÉl¬\E®q?Þx?®¬<¤Åœ)ßd6‚îÇCúKê%3и¥ñ½í!›“XÞ­•ï&ÜcM{·°û ìùlœ4!zŽMP)µ/þP$Õ‰AÉ蔵u¬Ñ!º T.BÉŒ¬·Ý\ŒØ­+Èú”æè*6 ‡¤n*?ýãœù: ÃS$hFÚƒÂû™3|Y…L)dí~ÊJ1….i|D™ûºÿFÌ_€“²ÑƒXŽ™ˆÃ3Ph·ÎŸË(Ï£~ ®}w½xì.¯Ëz¨‡¢ØoÃÔ(j½¸Îª„´ª;-‚(‚xcŠÎQ§èü¸¬÷§²÷¿'÷æå±/sE•KÚ¯8øÉFYþTÔ'†Ÿ§âG'd2ñJÄyËŸ¼Œ9Ë©€ó NïÚ뺉RZ“•ŒÕ˜7qMoXj˨j¾Y­<â¶h ½ ”ã‡C㫟øq°%5Ï%ŒÈâý¿úCZRÌÞŸÓÚ–ÿ–CðQF/©¨µvS%z_€ð¬®Ï4}19úrrô–Œ*ç<Œ±z ¹o'FmÒó€Ö<[|ÚÑ‹C£‘ôðE šðм‰ÄFày~+bñÞpYÙÓ¿F›¦ŸéãÉѳ‰ y÷Ùõ]îàÑLŽ~]…˜@À E~U„L¶6OŽwÅ õ(Á*¢MÃ{6LÜZÂø`—Š€ùÕ:S‚a¥f$F†<7‘[ ô°XÛïQÝ5VÅÅ@D›™«FL5!ãíwšäƒgc jb ýyù†?¬çæƒâ£&lµ¶?’ôïP@â3@#zÿÝEíò½tpì‚É yVUñëAÐòX„‚~µÂ·È{nû†Q¥ÖÈa¯rŒ—ÈëД– ‡?¿šê-†3—u¯¦ýÃÄ÷)U¬ªû»h¦&¬}¼«…ªU 5„€æ#G¼&ú0 á?2] 98ˆ©W»alùé¯SÂP14W”<"ÙÅã/]|& ftå×U㥠q›{¬QK!m<DB½%$Ž#G“èto•¦$~ŠTuúJ ×ÀóÕïR†\DŒ·' Cätˆ,0nüBXúIÆèµÀãÚuޏ¡åÆá¿«-²cGl+•DÙ;ËæðÞË’ [#öÓ”±í0ÊlÌ4VšFÁËÆ3x×ðñ®1Èô@:¾‘ɘH: J Ã@DFÅÒãH©˜tpÄ‹·Ø,TÕL%,´šõ[Šð[VÃeúï“U‘øMaáõ ˜©ÈXd!¥¢i”kj-3M„¥ÌRŸ¯bþ¥×žÀ?£{=~ăbü‹þe**†Ͼšˆ%¢‹%NŽÔÕ¬%ŒTnˆßgPÖóÁNæ¤=?)&%ý-' „·G|„“YI¨Ø [è–SywÊ;GY%•Š¥fÞX壕±C %€ç ¬*dTÌ;žU5P7“lŸ/p8]óœp84hÌaÿÞ1Ÿnd !zú€âƒÂà¯\¨À,?â+ŽÌ_LÄÜçúM†ý 6¡Ø÷ìfîhrŸ›[ª’¶ʇ&"ãT>ÌJÒ°|ÒkGH+ÿ%ýŠsÞKÛÈU/¯æƒ(DP%¼ãe…虹ÖÂ!l+BÜg4Êj‚ß™òZ„ჴà0Ãý{ü˜B.ü±û ¾¡K5¡ÖÛÂñ»2º-7–N¡ËëõËÒ¡ÄO¶;cEÚÎhÒTŽ”‘É&Ââ4w`²gNtÁ¿wT£òh¬˜S½ð¾•7Xˆ‚˜ëÇ4-<&T|†sΛ9à“ÌÙ%ríBúèR«‹¨/‘•šéƒ…RNNg¨1ˆç{m‘˜Æ÷Ž$åþŠ„Y×ØÇŠÄ„.5ÎËè‹yAÄUâ<^x˜¶ÿ­ðÚ–ƒÉP º–$‘›–Üÿ×;*IsTjž\ óßÏØ”£º:*›o„U(Ââ;@Ѳ°j‚&â.ìÄ÷ >?uÖ/ºå D†&øL :þGµ4žÀ{++RjœÊý”×iy_(ª¤q<ï AóÍÅ8 »œøä—™½™ðZ¸T:ìKUãÏGŸM C¯¤^Õ8†hçïó“ÿàvÜ`endstream endobj 80 0 obj 4311 endobj 84 0 obj <> stream xœ­[ÛŽ·ò8_1=@–áý’·(pØHÛë¼ÄyV+ià™]YÛ~ÃùàT±y)öu¶WÐÃŒjÙÍÃsŠÅ*’óóž3±çø/}ÞwúÎíß~ÜiÆÍþ·ßÿ}'µ1ÌØ½ãA2-öç²V3­‹å´û¾oeÅÞ£YPÐ*[ gÚÅV|ÿv÷óNÄ>÷éãî¼q ýz0°ÀƒØß¾ÙõxÄ^ÁŒÆ×À:ïþÓ}¸áŒë •ÝåþOq)¤îއú—·õûo¿Þ}u»ûv'½Ì 3Y(̧A„ç„ -ÄFPF(º—€Cè´ê>¢Ysn‚ÁïR{æ„è~ÀÒ(i;:€ãåP€GΞ-xÖ Z)"Tüo£’†iª4¡-_èÉñ;Á¬5ðN|Q?r/¥åJ©¯}W1Vû^¦~Øuf0ú† Á{›dpŽ›î<ÁkAE˜.¨ KÖ<å Ÿ3r oÖ€èro„Éæzð§Â[÷*~U.Ø1…%uÆ2…Å{Bá#ùþ ~7@¢jüô5z2À,ayàìùû˜ú2šFŒ4šB´ÑÑ—Ï#1žA½ñÌÙú±Rÿyú c Ævê‡(ºÓ÷‡Jý]¥ø¾„–1«èç†UÍ= ²ŠÏ¥pñDV ¸tðNˆPÆ÷ãùXY=VãÈ’u)RTp”ýpË\·Øªƒž~®‘ì1­SCô0¾J§˜6”ÖôÜZÁ=€U%`­U a¢»,PY ÈÝN%t{P@†T¶£Ëñãdô3”ŽyúWÎS‰J<,(‘ÐPaVÐ,Ë@ÑüØöIVÑdL©ö}èÖÊ7³áÝŒFt=½ô‚`Ýåq2RõêÇÞÈ—Æ^ÔárJKõ‡ž8ôM®R‹L¬±p•r»pØœpßįÎXÝhuš&WY‰¯Ö¤ «Q‰jb¼uŒ ª >dÌ3d1À‚³WN"HØfe!à¨P+à•i°ïërÛÖw=Ùdº–ì ¶¡?-|; ­ŽðŸÛžê瘦Ó]eö2Ÿê®·3;D‘ê"’5ŽSöØb¢ªø†ô6dš`Áã(™øIÏ Ó*fݰÒèó\V”ÝË\@¬Ô<´4SYà5ä&x¹ª„Všñ@ Ël‰­ŒW g¶¶“whe”…éQ,µ•ÚCtS¸Çp®Ëë–„s|¥eÊ©çï •f7¥žN0¥ahÞú²4ÕÐs\Î=±a~Î2Ñ šfbÑrêR—Ð1“SCm•yÓAH @ç!µ[ˆ„tÙkx¥Ù—²¯wË1¼g‹€hèÜÌÞÂJUD‚ØˆÆ n@lC£ƒÁ êñ1»)K©½ÀÞâ^W_Vü4ë¡xÎ2¥4™E’H|Z˜ÖPÃkBTH³°‚GiLÏ=Ã-Ç2%FŸ®rÇŠ¢es;yC ™=6 ‘‹îXÀ ©mˆ4ÜöKSC­Ý”še"†¬(mÿcyK/ñVQfW@,9ÄôûIòºËçùðXÁ5Ô&ty‡Vš OÖøléÓ7Xï-«ÇOL߂ǚ/[j+¿‡…S1Ño]%‹ǰ[3Œ@KB2yþ‡7ܱ`(ÄlÙ Ñ©E7oàžûF±óûé1“n_èHG™r=/®Ëõ¢R‚ ¹Xȋ’vÑ(œ,Â<(ŒiDa)›r=ŒسPL«”r}3U­'¡jßEÌõ¾—¥k»–R/©D§_)»ÓuP"µÀ«ÄWx™. Í–”?|ÌmÊë*€K¸”I}7Ï_횺Òõ"}£žS®L"=uΟf¨œ>Šè9, šn|ÙÃ’ïfË“‡æä^9Ãxò‰Åi¦%w9³rAÛœ•Æþùpƒ«Ÿñf¢uÞ7 ÂY°¶û+˜5ír|ðæõ‰Ç>mãÊhˆ'—¿¬íH‹×} !}÷·ƒÐ}ì!mß‘¶4ÞÜ^—°¾›ŽTWún¬ê~«¡çx:e»h^C‡Ÿu!8Ô·G3‡*–ŸÆ¾÷3ß'ÛFšÁ±ÃÑ–tÝ‹ÃJAG\—"i+Á"Ew™¡«mŽN É:ãà|ûw-W#…ŸØŒéÍãÕ¡(Ú:€Þ.Ñ¡·$RáÉ$¼Ò2í¿@jj¬#…˜-›!Z¨è㨠ÄßwA0rŠw^¼4Pç>z€ íºö&*évøô5¯f„¼ÌädòŸù_À¾&ñE?P”Ðb!„ÿ‘R2%©ÿ$Kã?Ükü™UXŒ­IþõSe•œŠ½_`µ"¢ð‚²¦¹d‘Ë%}Õ %ðd0ÙBG“¥ƒV<ÝðOÒeK#7 Ö™ˆÙn™FàG°ü …O@_OL˜¤Ií:‹´Þó¢&ƒŽk…1WØL毙å¯ò^ñ¶È$åŸÓ›ÎžŒ¾æ¯Iç>÷5 ÕC¼ÉÛÎ‰Ä ˆÐ¼‚g™Õ!œÆ£õô¼+œ8 ¯ OáÌ@Îßßgðú ¡QÜŽnª‘#婊*‘Vµ´ngqg|eí4ÊÙs”[rÓ‚tHyC°ÆMOE ÆçV~§´B0þê(/N/*Á·•àæ ®€ã›~7• ¦p^ ™LÕÏñrlï'÷á7Œg¶oéÓg²QqJZ»°Ã:»Ë¸ó﮲8 j,¨\é¹m·Íµ†?) ™»QTùw‘k¤QEÑŠ¶]£!†LæôüåÃ0S';¾)™ÿ7S½ mÅ{𢾠g(F³l*(Sû{ÌyÙL– çøœxÐ%§ šˆ™ßîþ:š¦žendstream endobj 85 0 obj 2924 endobj 89 0 obj <> stream xœµ[Érd9€]~E.Óš@4Mt05mšÍÂeW¹2"Ó®Á¦ ¾ž+= ÷ê i¿4Q‹Ì’õ¤£s¤;éåÇ-gbËã¿üysÜüâon{÷y£7Û7|ûõFj[ǃdZle•dÆ×–Ãæ»ÜËnm0š½J ôâL»Ô‹oï67"͹Í7Çío¯`^ ,ð ¶Wï6±•0𓆠ÌXøÓqóÏÝw—œq¤2b÷ø)þOq)¤Þíï/Ú_îÚ÷]}³ùêjóíFz§˜3fnÁ0_1(¦Œ¡¿J ¤6\ïoˆÐ!hµ»i_)ðáÎq³»n}—R&ŒÞý=¶J£¤ÝáEî/êâ]4Z\iÁ‹+zB/…ÄLÿ%Jza˜ÆJBXþ2EBOrä4óFÀ˜†Y‘9ú ,CJË•ËÓæn‚œ{Yž~êLæñbómS!©#CðÞc®+RÄ~EZ™³.°à1—ñ9#ÏáÒI8¤nXÐï¸lscvO̽Ìe?µæÜ³{@T>¡ïü‡ã›‰sYÉ«` Ál%ÏhÍ´ÆtæçΠÓxf„ÖôýmîŽàõtâ©ÿ}! –°y˜:æIÏáJsÏBÀ\Åçòa!WVÀ‚ã˜`_Àf$À©\íÞ§¯Êøãƒ æô¢e{@ùp¿Çûn‚ÁŠ„pÚB (µÁ æçÎØmJ€çTàß/pÕæîØ[ÏZ 6yì˜PóíÒ¹­8{n “B‚®gÐ(\<+ÃZþ°@cž³zbâeûyó~Cž;iLèb»'3áK”„ÓŒ²RƹŒŸGÊé rÍtI+¹Z`°MŒ9]Ï ×†a"?GÒ²c9\/xàŠŒðˆY3Þ:Æf->0Ög ¢r6o¯ç‰Csc*O̽ÈÝhê¼ûÐYÅÞ‰¾ˆlH µiåÒEûá—å¹5þÄñð0¦cR˜aAÿhþäMõ'cZŒŽèõ´RælI¹àft¼Ç¤Vœ=Í„Tð÷ŒKLj|\í¤ZÅlŽÑþÔ8ý²ÀiCY>b™S"Òd„2oÕ/ˆÔ'Méá0vÝ +a:c-9ô‚L6 4§´¤^ò$ÃhËcˆ ½Œ…Q]mi½ MI³âckÑ1p[ãŠ!ŽRÀ( Ÿêü¬6hè¬1ÄÒ²¢â0 ·â_1ć ¹9äG3ªÁ $dBÒ?/wMÌK´„Ú‚–PSRÀˆX±ÜB^Eó…S\óU›‡Íg–àl¤êMÕXŠ6wçôÜËRЩ‹}BGéqŠÐ ¤QÜ€Tb¸öL`¦âcvml@/ñŸ/^ê}3@& P¡¨¢À,ž@±ÌY¢íÞ.<Ë©>v–¨ð¡Fmþ÷s¶jÒâËòˆ6yyuks64ÚÚ¹%õR<™/„d6ócX|i©½@Àn˜He”Ò€,»¦Œí@€â9pò|“ecEJ`ˆ¥e5D‰KI!“…ÎDšøûS¿G꣗2*iÕ³,[È[´ÒÚ‚VZ…uày6·a­2Ìaû·ª6§ àë5„%Úêy½ž8¯Eª£ÉyƲxÅDvŠš«šŽÉ¯H‘i%ÑD³0­ù¹5´ªd­u²§9ºn´>.×J Ÿ QÇùz†{@Åy±MÄ,ã )¶¡Ófo²ZZH¯Ëé…!2(«£YH¬`|Ì­:ƒ‚*æ]Ž)÷M«û—*Ø aIO@[Bà ºxœ1៽”³#A]·èÿEÆÊÑÕiA'm[ ‹JËîÁ ÚWR$7T°¬g®––Ú )¯Åp{”[¼„@l1ózàÝÅâÂ+DÅ^9³1ÄÒ²¢‘‚ðèv¹G9¢™ôvØ2VËç]OGM ‡)8æ·Áè_Í/AÁô%ΘÀŠ©§”Ë!Ž›øœ\Ëc¡³¸iDàdi7ÓÕ``JOÀX$€À~™ºáÊ U„EÙ} ÆÊã“?4¼ªËÌ-X–|Ïôçd¤´ä®¸8´%Í//.µïoèü’Hê}xLNÓpX̾æp‡ú¼¿¸T.˜˜éQ‡ íñÄ4ub×y/c ‘ú8îwûÏÃw`bâ'Ä?Áîæ~ÚÜæ¥!©Fç–Gû.Va?óíÕ-°'¦"é⎖=)”±ʄڲf—[E…#VâÒBž ùuùê§°)È])6•ÞW;ŒÀd‡mÖCˆ‚±Kã8/FBÀÍ@"]>·?‘ûmÇ[è¬ÏÏAdã˜ä§A8þ@UÌ}وʓ…ã:¼Ïq  ìIfööP=PÁž|ºŒžŽ¾ÇÓî¯[§´ý5óaâÂmèqÓ¾¢çFwž@]©áÈp¬AGK¢Q"Í—`#§¨\,ƒ±LüÞ"“±Ÿðé¸n²@ŸÌ:ì]ñqȤ,YÌ=&® UìB вÉh]ns){yj÷é—Ûb¬?)þ¨ø Y†‰Á„W™¹Ìï㌩œ©¾>îúuƒÑ7”ÉŽ³wÞ¥K4Å™GÌÍa&¾:Lç5\3£,Å×.ƒ;Ö‚^©5ì!0jÎÃNK>ÍÙxUZǧÓ4š37D[¹%öVkœ˜ÊI Ÿ>®á2EÇKËjˆœ»Ñâ÷B‚q޾þ†ø?VÉŸùRläs[[ð"“Qñ¾Ë”[ˆLšC LdŠ×¤~UP :ÁY‚15“™„‡æþÛŠ ïZkó‡c¸*ÙipËal?Å{¹}µ;z6BB) :\%4z÷ÃmJ¤.Ù¹?\4;5Ö¹®¿)ßÖ_“Ö0¯±„ñ¹u¯RÓUhRq ã¼ñ©IøaA·†+¹ê«¢[hâå ÍY¼÷u Dú6VfZ.…©Ï­‰U œ`HqT®7’Ñ%˘`=Ý=ˆz2жGîûø.kèÀt МyƒùN­ªa¾y Œ†¥~|ßQ`M¹ºÞ£˜293ïµükØœ¶6ªL B@¬Î”@B”çÖÛáã^ÊßVjÆ×Z ¾!êTZ­IˆÞË¿Â}ó¯ @]H/ ÀI5xw"ɺ—\x|ÝYÀdÚèRU'ºÍŒ©?ÃØ™ûÑÝÌèuÄLKB¨Ã#öåçCCTZ^Œ6‚)°0ƱøÇ7C]%þÀ)¾¸%¦â„’ñÄ4#ˆÅ)ÍaR f¹J2‘‹¡>·èMvýÞç¸ÃLò™Ivê­éò„AûuAîz.%"…Ÿ»ó‡YB¹MŸh%˜æ%ƒâ_®ý ú( D¨ç`›{æð˜§»KŽ7*VáßÈaŠø>×NÄa·—‚}iƒòµX…J[]4šuÕø®=®¼Ïc 3䋹ûhc U‡¼±”ý¾ÊÒ/@!2Åy´0ùý2.Èf]!ú2–¯¤ ©B¡u®P<¡B͇Êåž%…ÏOínï¦,\êìK •Î½ÃØ£“’Ò´xO$bf,yÙwO3|æïŠs/®zîçkx¾!E—°iŒ¶ÛËXsL?Á*…ÎòŒžÈ…¿Ýü&ÞYendstream endobj 90 0 obj 3031 endobj 94 0 obj <> stream xœÕ\IoÇ|ä%aäÐhZ]{U¶ad5 [D‚ÀÎA)j¢R"‡²”¿‘òsó^-]¯ºª[CZø šbÕ«í-ß[Ú¯WCÏVþÿ}¶?yü­Y]Þž «Ë“×'Ìÿqÿy¶_}~ _1Ù ©ùêôùI˜ÉV†¯ÌàúAØÕéþä»N®7ιÞ9Ó}¶Þ ý`3ZwçkXÍpøÕ½Y3èçNvO¯rï3Í”sÚt¤}¾Þp¿¸ë¾ ¯ÉÜOróòη™µÝ ¡òôàðÁrÝmaÈ`µqƒKd`üßOÿ§´pìþÀð\öV88ÙŸONù]÷5å’&Ðà ¥î^!9錃ö¶Îë­c¸sÖj8L•Ör8´o:'…¿˜É…bÝ-Nä½Ä‘„(HÜ‘Á¸éA BIÖmÂVØ0(¸Ä‘öõÌðOü%Æ—dÐtCh’ý¶%U¿OØ\w¸X3Õ¬{Kþ~KÅ_»t3Fwßw>X†=(&ˆ·d ¬zE]äÐ yšGïvïòøðL/É ¬§€eOÏKÃhÎõ ¤'ι0Nw‡Üû,7ÉØë<ö&÷¾ÊÍm“îM‹nƒÝìÐ[¥\Ú$½krîÖñà^å Xãxwy·¹÷fzÁÐ] PoøšÛp Ûnèsnî#“ U¢áÊû ×siç0Åx`…€›ä¥˜W·d\¨Êb2½’ §è×y¼B°Yg%ƒ VÈW£›r½>F4XÏIžLömÆa [Ú]„©|ý×3ǹÎSi8ä6Ì4¢„IÍ€g=×£Wüó¥·Õ QÜÊ!ü¼o¢¬7ãê̱å#ÇöƒÎœdmo¨†«lZèž Z’‰‡‹æh¯Y„„ãéip*1©·ÎԵݖa+Ð3¼eTÆ–k1~Œ`M&€{¢2ù@§ÁǨІAÏ…½_…23¤fëZ–‘™´Cº•*Ì˸ –=ÁU€ÕŽ•l:ƒ @éÁÉ48S’´ä÷úGÑň¡ àåŒüS Pj¥ñ¡Š8Bs*E—÷—=ñú‘´T¾ŸC ‡ø8`Q—ø„:ý+¯ot¤)þA=é®ù$áû>/èÞꢖ8“ :¢ÍnW ò”¹a=ˆCƒk¦,9›ÆÎ¨Ã쿘pµ×2ø/`ÂDv`’¦H.eúá³xÜÈ£^Q³JÓõrÏé¨m1ç2;·_ȶ_ÐAe87DL÷ízôÞ ¶™¦˜Z¢ÐJìE‡'äõžÐÕˆÙßÉÈÅ[Òÿ«uKo`ô¹1•Cb W-@Ó)¾—dlÎ[(z•ÅyïAFF5õ¼‰ªHzç"÷^åÞ§¹w?›tÞ—§'ßœ€AR«N†ÕïN8“§ÄJ œ÷Ê­ö˜jØJ=»“'³9åò)§ì“uÆ XH‡Ä2É^¦ðpš{»ÊÍË%`±aRù•6ÜôZ}Q]ƒ+=*ÌÜEwÍè2š\±Lò} –ñS•.£Ù FºÒ—0M*u¬o†‹ ¼NmÓ+Žb;Vô{á N[Ú˜,9Á”tô† ¹e!ÊZ£ªÚ'Ä[Ý_g£ýþmŒ úøß¶ðo¶i¶tsÑ´¾È‡/¬eKanŸSp`xooFnÓ”b±úÈm|΋ŽÀÚü%FvnêÔ9ñ¼gjcí™OÔÌW›_Â@·Æ—¸ù&r¶£Oã…«F ¡ò7ÃT¦çâûÏ×®×  )цݡ©®­irÏU59X±%PSg&±h*môAgd[ÞêÌŠ ¡dõªÈ$3”¿zûþd#<oÔ"ü•ZÛ+V=ø õ¢6œ+–spY?ápÎW•¬®ýB*Tsj‘{ÌV7RRñlWEƒÂˆIhlìÔŒàh´¦RüŸõ˜zœ›}nV)$’öRe…}o‚ÅÞVDü2pZ©6e¼×Zµl7uµgƒCÔvý±OáLiŽyØÊ( ‰)Örœ¬è>X¬(„nUƽÀ‡”ˆ4]ôTǺ<»Of¼ê9Ê7hùÝëÑu˜`ƒ)äh–Åm¨LÐÄgó&!i@ÂÝ_Öàð`y1ó&ŸjKûÏþ?üšÛ¦g|ÞòU¸”í›L‚øDcKêÏÎroÓ¯isÀK’rÔ¿]Üæ$èXm“¸m¤ÐŽØ£Âc∯ôJ80<°ð’Ä ì=ð’0‚H¡×©üö§ñ’ŒáJ¥—4‘N.q‚.2¿Óø’/—ü†¥t; ‘©ž A"š>l䉈ˆÆö²WÒŠS±Äu±Hø nÜ¬Ðø…b'ýgINâÞðœ¶ŒŠ”Åo~Agªè?vOìD©êü 0çÿѲ¾Ë°Zæ0õ%ŽVúéÁÜÿëu€õÚÍ«šOãe<Æ §Þ7±µ63•áEYÁÓkêà]àBÌ̹)ãcH[˜:Øméèà iÅ9ï,’Ÿ²Pí¶¤’uhÅ4ƒ÷]uÄÖKÔ˜¥”‰I¼§ºl×c¸¢€yÌ£˜}ÄÍ3XF¹²¬,¦ì™ªÝµ6m)+Â¥4ù¢Í~ôtO€3ÖÂØ£¡_[JÛèhÄDÁ`Ï£"‘QA37kf{nuÒµ˜¤'­Ñ¨¾t£û¾ÜR"ï$ì>_#_:i¦üÍK˜,ê#"–³&° AÐsm¡P~†çJ“—Müó®°¦Þþ31Í•4–@ƒýgÚçÇÇžûÛ €y¤Ï¨‚ýß¶Bd~>4¯¢ŠO jYž¾ žù$`LÇÓ&2!Kb™´àˆ=²S„#Ž´•¼w³_Ç,ÕÑãLøUÔî|õ†ôf–jƒã\R¯l’šù Ïš+y$áDªTâјˆ…A¸)É í·-0Fì2 ×.â6yø è 7‡gvm4I?²yÑkc‹›?¢ˆáë#ÖÞ^‚œ/§ZñHª2FqÍ¥«Ëض™³3‰§&AçY¨^jGX}E§èÄihªwE‚ߘLiþHäHÛYÄõ6é2J B5qýFhìãÆFª@ÀngL•Ò/òˆ,.ü˜ó¸Ž²!"çÛª¶ë‘ú¤j£Yk€EAJ ±£EZ,¡²dŠÙaM¦äCb¨¸ÛTQåw.T!6æJ,J‰PæKxûnµå…Î Ëœ˜+œ.Üí'3›œ„0‘¨'gOtEZK‹É=zÓ ¢Z5Êug„‰0û–Æ2'AP¿¢&üÓF ´tí T]ºå9T¦#•µu%^o¹Qí0R·R‰÷a5ÿ5YôK>bãX>á0`];Î8_Á3•00F/”`âF Ó…ýšÜt¬¥$ùâ‡7³–õlÃ8¹€‡¦¸Ëz8b‡?i™×m¡oèœ2L ’’múfEñ¿w[QW‹%3èŸÛÌ=wˆØŸŠ°÷¼ëèE`EÜRç<,q[&R‹k;9›ƒih]æ ùRPo±æfšÕ*Šn¾˜2~ÉÖªÕѽÌy¦{|Ñ»‘ÊÂM¾Ì kaZåøÓMcGœî«q™´b; Ã&TÒ,A5ØQmçôÃ,ÌêŽk0€ÌúwˆÞé˜*ô;´ d,¯¿sÑÈÖ2}+&”+‚rOí&iÚÏ2ßîþ‹cOÈâ—1m±nÖÅYjã…næ+:z`Ð"¸ÿš·#·ôÃz ÿa[Šò†§•±þn´j}ã‘‚šáýÕö8— ?ù‹˜ZªºÈhÖwåC ûEŒ–âÅ "#;YE‘pÖ>¤ÀxÔ¦õ÷F?ÁÈX7ÚŸ…¬4FÊ©,Ó5ögÕ §6NXÉòÏcž›»ãÙÄÓ£QpŒÚ+ü"Ú¾2܆õKïA¿³õÍ—a„Ôö=€¥²ösEEªÄψU*3bvOW}Y/T.µ]!„R?"+Ÿð:Þc%R£ ‰Ñ Íø-;p¿ÐU`Rx’ÙöË(÷@ — ¶²£…Kï"MS;ZÈýºQúÖª> L)ëÿ»C:ýÇä3!ñ\U•ÓòôPɹá ?{da³?Ëâ„ùΨûÍh¾ið6÷’ÔOsï2ŠÚ¤õ6€gîÃm’„I6øìGoR p“ï §¶+Zz¿\|¸­ß47I¶~×KŽùéùfløÑ¸¤]NÝ.F §8uŠI~‚á7T¾zÅZÅ‘~~­Jmã£ýßœüŒMõ3endstream endobj 95 0 obj 4346 endobj 103 0 obj <> stream xœå[Ýo·úx/ýyÚ+|›å7Yœ´(ú… Ž€¢Hú ëË[ßIе²¢þõ!¹Ëá.)Ÿe©qQä!ç1—Îüæƒ3ãŸÖ]ËÖþÿ²_}ùʬ/nVÝúbõÓŠù¿\Çÿì×_ÁnҺαõÑù*|ÉÖÖ¶Bs¶6k;a×GûÕͯ7]ÛIgœÔÿ<ú3|Ëdö-þ4¾?:…Õ_ÀjÎ…qºùÿ)SÍ›D½JÔ}¢ž%êW‰úÅD 'ç\oÇ£·œ·ªs§`÷,Q/õ4Q¿JԴÇydöéx¼NÔãD½(Šô6Q¯?YпÔ%N‹º+ªê3¸ÚMQæÇ‰zRÄÛ#‘õ”èßÅž¨Ãg%èw‰Ú1ô&Q‡ÏÊ„ŸÔ,Ÿÿc¦F Éb,ãKò¶ë®G\(V䈵œIöŽøAèË…f»VZÃr¡ý>a\òÎx EùµnW­‘o¿eë¬ðÚŒjúó P¥°ÈãvÎÚ`çLZË%˜Óœìœ^ãã&'i5YqNÔÌ6¯Éb²b¸K|29ü÷Э¶yŒÚŽ1ï3ãRÏœè8lÒô^QŠI#F)ø ÃYùÓ}O¯Cöß vÂQ÷tû]XnL§2¬dÜàoÙuÊ©æì)eÛqÀªŠ›p˜T¦y»q­“¬Cq1К㬹béOË¥·ú_á¡ù*Ïå»°‘€s(_÷Ä›¬enDs¶¡¿¨â̲æçM¦ÙòÈ" ˜~I¥2D‰*7CE€*šì·¸f ÿDÈ·sÅ…%ø!nnÁÛxÇ-­’2úÉÓÌp¼H…Mšë„Š8§èé$¬îçél+"EÄÜïQ‰Ä>†u¾R ²êÓ×åÀŽdÛô;S­Òßíf+­n–ÍËdy7álp8?ñÞwð:]gsíÑß.LfD״䪢( ŽÓdµ/ª8çÍ·ž?ÆŸÙÑuZÜŸÌÚæ.­îwh³ð^:3d*‡«¨#gé’“t¢hb]×3ã!–ƒ¼Ýt¬•Zކ“›-®dæ£ÌV Ã2ÐG1Hx%•5ýeDËuBL˜Å$]7PjEýUEkJ¦U ü—Iø_ŠÚ"ù.˜_•·?%E.1 Î%Yâq²Ä=•ÿŽÀ`ÃU×ÚÌL®ˆ™Ü V%8 Uù ²õiZ>‹#‚’òJ™ ëT«Lɉ–ôøÂM D—’ž’ùPR©€ÐrJŠ~lˆÅóZ&2]Ƶi9>ö ®?wà˜n­Ö ü¤ Á¯ª‚õºÔ¬ez`åH6JV?‰þCž…àæÜ‚6Ü„¥Ò>! ZÎ#uÅÞ—žÖ;Š2&rƒoášo6Óân¸ÛŽ6F`Ô°`cÞ\\ìl®¾MœÆ"Ê3zHn!»Ó®é+ùÚŽŠð”XDNpfÑ]ÆKW÷‹‰’k¾ƒs\‡xŽÒ‚xÒŸgQtîµo ˆ_§ÑyG¾|±)¦óNã“ÏÝ Iߕ׉ڧŸgéç6­=IÔÛùfþ') œÍ©d3ÿómqí}é³’•â[%½lÐo:Óv™²D”ÈnzÐRV­!CÖ0aÅUƒÿ¸'dvìˆVÖ9°â!!À°B]šƒã(·˜Þg¹£G¶€—dDÛtÓ»DÛ9c—ˆÆH“ÄE’ìI$H~9ˆ{ä5ÔƒdÉçÌ+KÚ)…ú¤®µðþÊÕ˜“1ûöw?Q³—Gnúq ŸÜ»[>×J.õœ¸È,ZG÷ªÍßÌ2úåFd–âÕNÏ^sûþßD™ôN“Û)ùîðÉÆ>ÑŸµÁŸXnx©v¡ð‰ÌÃi Æ£­ÂãתYüø›—§bâÒ-Ðnègy–—/\áƒ8¼ KáŒX ³w’ô*'×’²ˆ˜Ê¢øÂ™ ÛŒg*%®m?.2Ãöu:4G ²š!¿ˆœ›éÏÃe¹•Ë'm–¦x&ûJ²A?mÇàÅó’šQ@æ Ž“<¦Ör“ê–Ñ?ÈÍÖ9”n^%ïC¼ÂÞ³"XH mÀÒÞÓ,ã2øÙ ³‹ÑÉæ/øü5‚[ºßÑ|½A'ë¤ÑãI½i¿ü¯.Ê–ZJ‰;ć^ýuuô›šïéž÷KyiФ¾A~[NÆÇK8!Äv)”‹¡øóuúÙÏxk•Õ±ù‡£Õw+ Œ®ïVÝú°1s\ Å!ò‰õ(ŠÁÍ&Ênõ}µ§7ã7öô`‡–ã÷-3¡¡GŠ“·;”IÒ™ sR\¿JÔËìB_¾r™Z<ÁWŽÞp‘ÎøÝî'ê$:¡×A8£[m„+67aØ@g¢èçš™§Z—éç0wÚ~íùᮼ„ª-0ÏUî­Ÿ]¤„µ-7R‘òhH Ûj®‚ Oåï>QKè!ò/‰‡ÎôGÉÛÂS™\ßaV,º~ H =g.€’ÊäçD*šËYªú Hâ ŒÜ9EÊ£‘„b ϊ‚$r2ÖæJÀ öã¢-¸z9ƒ¯O}Ðå*81¬…Hš_ï@œ”ï“àâ¿tr^œ0¬09Š“Hy4NÀr×A¯é.QOîz0Ÿfî1n„Ùew¶~¯ƒî\DÂéÖŸƒ›áž­%ž >ÜYÖZIà3R ,~ÀA$½N‚,ƒŠˆ÷ý³Â‡Ü9Âç;? ŸìÖŸ|ž1JÁÛK·4HE£Áƒ¥*ÓÆ÷‰2¶TD)­8cót’CL ÃÃùs&‰ÿŸš85÷G‘òhH™ËÛ A–3¢îò|Ü\NO›@“ëÇúë?œ@×ðßrM¶œ?sÓvÎÆQ›#_âJs1VéÈÀ¼–ã‹LºUBÖZ{Z+»$AË=¾´l•U‰w»°½ÖÕÊïþÊwM\>„_), ÖMü¦³ÁPô ý•Ó}Q M<Z0«U¢—es_óêétMV ‹<ê¬ùÊæ@ç¥z Æ‰©O$Â=¸¨B ó²‚:¦·; ó ¸ÑB‡|*™'!gQó!´%eœ@õç³|Ö)ŒÔ+ptç³y²™"7 øš1åo/syfS4W!∱œB Çú“ÀŸ´…’8QãaWÊó YÞ,Ѳ忑rr»T ªd¹9ðeEów4§ŽIV˜ã¨L”F)ä5HVÃp sbm…±oV' TFK@e0Ôèbɶ3xÚyÎ8¤ˆ8õÛ8š#Î'Ýæ“u²œ:rªMºürtlCäúÌh(0^/µ‘íçÜ̳—ƒ§$8ChVÇÏ¢õÌÈ¢ôíò¡t ú!ó*^ò¶6tž$ä™ 6œzHt¶:ϽRg‰ô}ÒÉP†s) ã”ªÔ¢ÐqþgÐIUÑ/X‘y¡ñß.!ÙpCutRiÈL-{ùS!ôÝuá<Éõ£†ŽðS#-²,œ›ÄÆ=álQØ’É|À›VÊ/ ‚³š n#uÁnà~Jˆçà¸"¦›p(À2K*óØd Kñ-ˆ‰šÛ­¼Ö²Z€Báɵ€Þ+!‡Ñh¬¨Û´A¨(ĉÿ’B`mÇŠ‚ưWw[Æ1O¶ë­„%:擌¬×¹ú|û»Õ.v|Åendstream endobj 104 0 obj 3291 endobj 108 0 obj <> stream xœí\Io$·rÔ¯høâîdºRÜÉ80àYØ1l+‡ÀÎA-n»5’¥Ûú6ò{óÉ*>n¥žÅãK0‡©¦Xä#ù–ï-¬ïVãÀV#þ‹ÿ?¿9ùýçfuýp2®®O¾;aþ«øßó›ÕŸN¡ƒ…†ÁŽ­N¯N‹leøÊŒn…]Þœ|¹~¾A¥sR¬_Âã(Š­ï7Ûqã(¥Îž/S÷¤ûa³åF Jóõö–ã¨œŠ½­årî!ùú–¼xš­rë³ÔÙS¢F¡$ }K~œ“Ww”€‹Þ®ÉÛ8™ŒqqÙ~²3º$ßA—Ítš9_O§¹Üp ûËìú‚+Á³ ºk“Î Iad úºú0¹Î›çQöwQë;Òã¬}ôÅøžÎ·íqîrÀ~$ýˆO~3Èûa‹ŒU<ø!Ö‡bwÿsú`5&)¯Á*5à·Ó‹ÈbœëQHôœ ãôú¦ùxæ-7Ü/+¶^Ì­8Ý_NO>;‘@Ðêàÿ¿HáÜ ò5‡\ÝœH)üÓÔ²?ù¢+%9铘ÀWµ¨D‚™¤uŸ/·×Ì»szµn­ùœ·iNÊîÓ"¤nX¾Rv·å‘ÆçaR¤œò-r‘ž^mA*Ay&’„qFèÍÖ*´À öIòÑÀÚ˜¸Õ’žÜe'²xÜ8ªÞýi½þôηÇ7Èêî‹1 Hcæ‹öCŽÜ®?JVóøÏjÃðiKÅ“.)[f‡rF S@)VVXPx&'ÔÒðÈôêôã“Óß~¹þ‚.¸’ĸå¹$¦ö?´í"`æÜ¤†Î’F>ŠÌ1}ë}j½L­ßÏ­a—-B˜bnV–ÄàÞ5G#sz©u—Zo“!1?Îö tô¼·Ò‚¹Á(°oœÐ~nnY²o®‰VaÐGØ&‚};Kdœ—[è[ïÓãezü>õÝ¥V2Ø!=ö½Õ#ޥɘÒÉîf(IènkÁŸ[‡‹»̨µáˆÿ½a# ½]; :j+ˆ®âçX×wЪÔgD‰ù.Úš\‰¢æÆ?îz{%@™9­Ñî¬Zð°‡mQªqO™VtIÔ®Óç{¢hgFÖŠ$ŽÒWÒqÙ’™…-£ð߯¼ âìòœNÁ˜õ§©%ø.©²ÝeØ<+ Ùh<ì8Sbå'6hŒŸÁwþ¢=Ÿ÷i„¹ N&2HŽ‹« ·K5–nzÜT¶¼©“xS„–SÐad¿G2çÞEGlGÅe3)aXã¸[,S tÍ=ÞWš»@çÄFM`\¬¿Ë,W5YÀZÄ.\D`Z îCä[?Ã@óÏ;ïèõÝ'˜‹[“ELš¡˜zôûDpÓ­0––#¼—PÀC&$l@ú6`‚†˜B`ƒK:޶mPD‚­\K0›Üo«ÓoË8‹–¤Ý–u¤»ºó\ƒgoµW<É…SÛo¼ÇÁŽ B’ØafîHŸ=!§–k˜Këüí¹¹B:ó84ó›0‡%ÞÊ%‡ƒ Ë,Ä~‹ã {IQÏ‹¤Ú¯ãJ­Íc®WüêБEœã»ƒª>ÐþùÙû>ªÂ²ÑH°¹³×E;•¸ß?ÆI•]SÁ»¨¡þ'üjÓ¢¥—ƒ3¼ˆ¾aVi%5=Ðe [àñ °ò53ƒT@n!´0ö(tY_Ú!›‘B?‹#SfÿoÇÝF”\´(þpuåŸ)¯Ó±ã9:¸ÜÓ ™<î}åÈ‚ºž!ؼäÞVQ=žžúÀŸ7÷C;`~?¯NÖñŠÙéŸÇ¬á°ˆ¾ÍYv=r[€1»`Þµ `úÁš2Þ€Žûé3%Q€Ç“ 0ººtëCxŽæg˜÷¿]`‰Àêåy¥]¡õƒ·ÄÙ»Þ1ÌoÑxGJ›•Æ7@ÉåTá™ÏýzÏ›<‘»¡›Kݯ٣”¿H”ãY¤ Œüîjr »™Ã'OÌYQØrDÈ=•2) ôy2c„“o<%Ò®ƒMÂ}ÆpÆ/hîã:.^ý¨d‹7Ï¡@D×:xøe.æí冔…GõvrCG'1BnˆpáÜJ*pû‡oN82ЦsËkä†0\¬ŽÇü깡ÅÌ l=Òê37ÖÙårŸ¬o o]€ÆÅBŒ­ÀÍ­p<@HY…å;¹ ? ¸OÁ•ô<݈AãÐ4~ª4 ªŠ8˾Eý֫廌”xTÃYÛeTZxÃ¥zÀ ”塞Í+¯ùíÄŽ‘i|Øq8wÁšL >ßwWé‡%4w( Y>*]Š[wT¿_“ç*æ¦5EdsY‹8!ÍòdçÕ«sàÛcK`h#äàa¨yªÆÅvÿרq™PM°·}\c¹â–OðU\o3ô’ L#ÐÁ¼Í÷å 1šw$ª*Jo¸Iy"1çÉô·p‚€#1zv_¿MC\¦!³!¼•gÚrðOVB»qÀf&ÿ¦†W·ñ0Z*ÀVFO ðE·UÿxUZóÜO•Sq*Á~„ç´o%äc´DÚnq\ýú[ Ü@¶ä¡D*äÀËe’<ÒuÁ9há£õ3[nb,è¾Pnâ;¸Årc3òF¹‰q>$ø:å&0¦Ûë+&„.4"¾iœ‡[XÐexOùÎá±pîêL£dW¤á,Ú–m»¿T¬§|„€ŒºtrGQ}Œ™~RXwÎ’”3¿…è+b‰Š¦£´ H°õöº¦ÏÏꪚ>ìâOsOî9ÔÊÓRJDlyoK¤±ã¼u®•õBE‰+nŠãoGC(XÄ©Imuh.ÜpÎ+áè¦4‡¦%Û±¨wòÈ™o¶Ê¡Ç»þc §ýe*âl¼ý2u¸/½ßúat+„H3ÈõïRß÷æ¾-@.´ÇL1Œ ÿÕq Âû—¤½¾,Y„úA7Tñ–Ø¥Ž·Ì–{>Æ}¨vêk1 °Rf?N`ïP:÷Êþ¾j}ê’i\Aj†váÁ:),…ùFh=ÚL¼§™y¡â¤«–X—uç²æ0ùÊi‘Œ/ÜPÔ[XaSô4À ê]pwUòWgÙ“«/«h`4x^ªµŒæõ.æÚABìú¤¥VÏ&Úñ1<Ó3¯áΟäY½JfqS9ëV£E‹Mxþ‡Aà(2“NKi NU@òW–õ‚Oôhð(˃F´\¾p]„m &h7… =Û4ÕºïÍ¿£²Ä-¼¸Ï™YdÌoP®Ío[Ü„aFÛ»=}ç‘­[ÚHx^ÆN«‰!¦;÷8˜Y¯ E÷6ú-ªÆ»èq¼xAÁu'•¾‹ ´€UËbçAްNÏÆð¸ë½.™Õ°uB•ܤšä7 q' V7 ä®nT¥ÌÌÑÞÄAþ¾4¡Æ¯¸ùÊ˳¸MV÷|wêw×ùšÆ&<í+bñ ¿ÏïÙÇÊSj§2Óù,\%–£Àëí¹é̅ƵÀ$…kôKûh×]™§ˆ¯ý”Zó%35Œ ¸þ©ò°êÕŸ7¾®KËšU²E§œ%U¼Èe¼ÞP¾‡Îw-s.#‚&¡šKW¡ôqÓ«„ØXjýp~lyãè~¥Òʧ¦¸óþäÜüH2²Rü/#»E=o®sÞ©ÒEy$­R"¼×I_sý–¹²‚‹tYï[È´¾ŠWå7–zÕñ÷a4Ÿ7•a ‰‡0V¶‡¸–¿‘R¹VAÞå¨Ky {ÜàòÞ#F+l˜×PѼ&û®ˆOÌŽ¦=èý“6CoˆYoŸ)Ð¥þ™_ïèêH}£Ê3s`Å\îK9F<`?2œÀä˱tÅ…(ÅʇóJªÛòœSKgæ ãÄ8dƬÖÓ ;4#ÉÝ¡_Ý_mZ®ŽéíŽé¨>ªâñÞ=Þê“—ØÁX)a'¿iF~4m¸M©cìiöƒ˜°V¦ºiÓ­óg)ˆaÙ%sÂê3HiWß½:>QwS aÿ³Yô¦$RN¶ä’þ3-´¸|LÅÍ›’0_V†.F7nf%s•º\¨Ä C ±îÉתù<âž3`4üE±´'¦ÎàÆ°Ú,,„¥–sÓç<ütL=q;Ɍң”þNC-¬±Äš¯ÿêEh¬ýÜÑÌßLJùù®WŠWõUÛ+m|tÿm?™_~kˆ˜n1ªäñ a}”^{;Œø ÚáýÔšÛÁÝ ŸªnnÆþg Á+ŸøZ©µŠš•)zò‰—;äëO¦Ö÷2BËOœá¦"¨w0µ+3áKRtÜsëžp3|<ΧöÑôû¬ãÎeÎY@ŽŠ‰ôî‹è‰'¼¾‰?UHÿÔ´íçi¼žƒwˆe1 G}‘`žæç ­çUBO´ˆÌ“ïUC…µÀÇVC»_®jè_i¼¬^zG_þ•K„f3ÄRbü^ÖYz|H—óãœó„e °\a8 Äsžp‚øõµ©åÕ?[# ,Œ€ur!çÙ.„&U;Ig4‹–ÐLòÙ³yÅÑbý~µËÐ¥j¼ó!ŽZj§‚IK„8ùRÉïÇDÑù¤æ›Tiï“¢¿›5 µ¤‚‰”8UŸ¥)‹ƒH‡‹LƒÅ &´}šì 7Ba×ëï Xt¾'¯²"RÍôcar†Ûrدҕ}™øýEzÜ¥ÇC&-FãâÍM`]0w ´82Þ¶ânny F«²`vy[ŒväòrŽÁ©µš,3Lò7XŒ0?YÞEÉà Õ}/ÊÖ‚úÅË ½9&¬§*/‘Ë,Éj¦›¶½XØlÁ»·Š~C­DçÓŸó—ðxV%|6ÝŽØÐ/nÓnÛ¦+ óÍ~ÛQù$â ÿƒØŠ(ÒÛxkq«qWÕä°ÌsÝg'ÿ䀱endstream endobj 109 0 obj 4881 endobj 113 0 obj <> stream xœ½<Ër$¹q>òâ_èØ‹»Û5x?ä“%+R8ìЊ±Éî3ÓÞnrwس»ÔoØ®3T!ñ*69#ŦˆF%‰|g¢~ܰ‰oþKÿ¿=]½ùÆnÞ?^±Íû«¯xøq“þ{{Úüæ&8˜<ó|sýî*¾È7NOFj±±ÌOLºÍõéêÏ[¾cSÞze¶Ón¯¼Ÿ¼ÑÛ…a.”`v{‡Ê9¡¶ÿ°Û‡ÙBj¾½ßåç»Ý^J5Y§¶7ø('ïÌö§+Æ´×Ê9ÎÞoŒw†’åðGs< ?„IšI­øö}=ÞÁñ&``-ÓÛc\Šs³ýŸ0ê½’ÛïšÉa8íB8)â>asÖo? –9œé¶§EBµýßÝ^>§·ÿˆ«¥0Û㌊(ðŽÕ€®L” xœIÄ×—ÇIP?ÑY­ù\¹ëe{xê\MÞ ¾ý…Løap0w»ÿºþÕv2V³]ßk…¥wRÛâù@—<¦5¹6ÛÃ}DY·=‡mkÁ]¹ÒiÞžÐ-÷ÈÉp[r Cq Ù{oö<ŒÛÜK ОK ³òq»Í‰$Þ¬dÌLƸíuXAh#äöCu \dñ K&Á‚ÙÀW^¬c€ á—íÛ,ˆâ}œ`˜oQ¾±ÃN€°KÔŸM°»¼u±³\­ðÎÂßïúÃtvx–LðrÉÛ¸Žcnû'ª¤~Qáê¢õ ýFùþk#Ü$ÌÏYxé¡Þf0Òþ¥£³AÖIcúD6p‚QŽÄd…ìÉaçÛ™Êz„™\*k8=ÏEsz‹´’i_KRDH¼®|£²Ô'w”¥´× KZz²åYWÝdŽ¥‹Ý'SèZ··¿æ9%ã®'»ý˶A3ÌžÞ“•@~…w ¿¨G g p[o¾áŠš`¯'ÇŸOé'„´mäòx†É(™éñ>?Þ.sã…•—6aݼÄ_vDPÒ~R`ž“ÒQš©Zé ¸ë¨‘åtãY* '2åDùšªçã#¾ Ìæ]©vèy 31÷·Wˆ‰Rî*"¹.Í Âó¬´r«’ž)u3÷&₊"ZØZQà0øM?>D©+Â:¦@ñL÷| gA•ýcÔI šŠìµýçÝõ_í•´“e¦²ƒçXL þÒ:Ãq´Y‹q(Vq»wv¨ûšQZ;b¨'8½x‚‘’øWF!ìÓ”"Sgœâ@x“.5Þ[¾ŒsAW:QlÎO <×¥:\9´ðzµQ•EÞïé#0 ¾Ê:æ«5ÄcNûŽ úœ{4À\$Xâ÷Ô‡OÛ1?<Æg-Íš¿™HO˜ƒø0g2Œ\®Ùdµ,-±Ëê «‡6Ï1+ǺœU2óvÀ–Ôv?ôüŒ’E>1Ê;²Ò§4&±uO…oFXŒÌ™qÔ­…4晈'ž£-°|ÈX~L`\Çßîú´qתZ4zGà}+=3ar¯“JK\(%F"~³è°$.»/ê‚õ[S¾Šg¬'Æe'£þØÂ6B™‰³îµ#LÅ}„B ™9¾á¾Ócœb@þ«È¡H^hµÃ8Ä ˆ©‹Ï ŒºÕÏ1GÜE×Ïh•°@÷ÔU®a«‘8©S²x„­Žoªr`¯_&']‚ãÊdÏÕ,¹—Ìp8ø¿ßijò Eþu<ô1[e1køÌ‡¢0õ—9iIx¸Ÿ˜Iá­V2n°ö—"YœJtR #ã –Œ?Cÿ8¿ÜÏæyî#$À¢8îQZ¨ñµðÝ6úÂa ̱êUEæ8W¹Ø>÷™u‘`uõªwŽH¹NÁ.GÊŒØey«‰‚Ôt|¿Eª8Ëhê& “Ð7…»÷IWÁ–À¥HöÌŒã<'8zÍËŠ÷´OƒLDìñ]‹0jÆ‹¨õø©&5I%¤K×·Ü¡Üñ!,Àåœ?¨Í ’ÖzÐÝ¥Þ}JÃ!YµÏX ÁOJÆáÏ1O¢Úq&¼Lk¢S¢ym¼Ié ¶ìy“bñ&áÕeT/íw×W¼“®7?_±Í¿] ú  Æp»›ŒXПn9^ýi˜ù®0K©o€€l‹€WbæûûŒÐNoðr…³Å Îñ¸«_HLÏy»)¦œ2Ä7yãµs=èœkV×òŹF­ÀðDé[}—XŽ~4åm>ÈVÎ2[IØÍŒè×\9˜PKº´Ü3Þ±¸Q¸ÎÔøg¾UrÛE²ã#øŽ(P;ˆ¯Zùò7H8ÆôðR•˜Ix¡µˆn°ÐeáG7„at=;xñÒª Þx‰ûĬ c¡»V%F,}!‰e°üâ3GˆTÁç™ÉXóª 3¼ ’UIã¡Ü$"sùLÈצÈ0ä Pì—”™h¥¿…†aH7 ͪ¢d[ÕtqóŽ×a`öyóÏÔ`-6wˆ‹]ß:Š,Ç"?pžžÙxrÉ-0Š)8oø$r& Hc÷2 ¬§¢K’fßGãäÝ\)OÏïó”OqØao` 4µ A8EXé£÷ ιèýr!¨– ŒÌÜöۇ݀t%á÷—IV@ 3¾#XÃŒè­ã{ÚÃxäÁ„%φ¦#Ïz>ð¹¬-x$¥{…\ðä/1íEîœ:T ¨î§åЮ,V ÑŽ)%ãU?ß"5K=TÏ9‘0ü´pz‡GÕ¢ÕHAÍÒ«EUÂ÷dOpÚŠéÒ*Ìa#Ÿ•Nt…N|U¶-lYÌIž(£<•’ä$U*%m'ñkì®xó(ƒ<É Ö­Ëšæ?fîëffYÈOÎÆ$7Ñ$F?µAæòèÇÝJºdʆRõ6ÃRšhø¬iá"U‘]scß(QúsT b±E¶fAiÄ  z&´¹ ^•¡@,2(ÙëÍd¨‡Ðh”æ ?à=ɶ– °™15±¿Çmÿ3;–T^ÈËîâò¦ÊÎÒåoÀº‰nÐ#CÄêܤEO; ؇£Ýz7e4jRÕßA¬³prÓ_'‘Ôe|m䔉çûD康|9CAAÒÚ.X‹Ìi)‘û™öó~J×yÔ÷xž-‹(#Ëèuåz^‡/MѪKë’/â×¢‰JÏÀ|Ðá¥] e °¶ šß¡¨+LoP±¨#µ6Q‘u“”…j>eœä`öW+¨É4ÌÄ»]¨¿…‰ g»4‰/(ÉÔÇ´\™/u¹° ܆%0 U¶ÿ˪úM%7’HÌ <¨Q"¨a’м«… «ma–c 2›¯¾ñÒ î¢éâN½Æv¼n³=jr¨a”ä’?.£ƒdïyƒ%uðî?Ã`! ðDæ<Åalލó0Œ±ÃØáŒÐ§ý€H×öl#”ªšWØ¢Ö=jœ=²jÕæ¯ì$lFÒ­â{ ¼.lcÓGUUyšÑR} ³ËÄë(³D³·´¦qÍjÇE¤\G_Dõ^é‹ÙD‡Îƒå^zƒŽóQî»¤Ó ¼N*!æoz"‚0™® ïíêÊöª·õç@#/ëöÍÀi^4ØíuUUL[åHZ8›ôÁéDæ”}F#ûy/[>¥ÁÆ7üµ8‡q ÛL5꽋P„ôõÍšÛ\.%0ß2ßÛÑ"ùaG¶¤—X¼zÁî³ÐjíŸ×J;iDÑw¯F¿”r“› VC ·… ïùt;¸©÷E´»È$̪Fñhì^üy‚šyL€Û´¡âPãÅõ(Jôy¥ó&u­Æ}BŒ9¬ ] + ÎÖ…|aÛ2á&ËG)…¨ƒÒ§*‡¯*)›X†1—s,’ÜN!}ô¾^{pl‘ 8WÙùž²^–/rŽÈ}"J{™ß3~|mñapPƒòÁñ)¯Dyýð>Û§¶{”$à tV½šÀ#*ÚôW=W$pï·;ÃðEˆáN霥%kµ à3EÛ:áR+#ãøÆ?‰ªÛÜr÷®Z ÄÊ!æJϵ—U˜“ÙºÇOÚ9-)×7€¹#®n ÓôØÅtÔè¾òù@-QÕ(TÛdïa þenëØMïXjþ¶Ï·Éè9wéÐkÁ;ÃO ÒvpŠý¸|ŸGßîöÚûp+õŸò(¹¾v—GÏÂ}½½0÷çz§õ­·ò\²Újÿ—//þK¼B¯U1‰p|²ZÍQÉk‚’5ëÔY.IåŠö x¿Ô½°Xq^b[â<òŠŠ“‡â*ðø&/T¬8}衳ß4ÄF{ ŒŒl­‘NmàEWV•ƒU{ÛJ5þ®åg&ûLìV|ŽÛ,Ñu‰I|H‚àO™»îjF‰ŸyòTürVŠ2j$:ÿ.Aõòv8Žç±¾—ÌÂ9Aw»½ÉþPÓõfTt;Wj: 8÷ƒäàÄ“1VOºr>F€°j±‘˜Ÿ’`¤ÊüT …Š]š¸9+*¯22—>¹: «AY•Ä ôäµ7S*‹*ù‚ ĪÃU±Ë+Z¤+Ùcž@lÁWËhWô<^3å•å£+ CO¼u‡—4CLÏuüÂpÕªî.¡ì•ÊB/`–x)j®AVŠwV¢ŸQÉxI%!{ÎQÛ×SÒï¥óÞ;†JZk=)»Œ¼BI`i„€×ž¢’>u•ô©»=¢¶žVU·6ÀÊ?”ü³¢º%v£ºñÆš /L{AD€âþ¬ê6ÀÛ^ÿu·-<§€œãt*¢³Ã‰F¼w‘xWíDœú¶ý=guÃ|ï‡DzÊ8iþ:~èÄ7æˆ1èÔKm ¼õAÑÆ3n:»Ãþ;iCÄþsô,‚6âEjVKp€üßIÏb22+õš;÷J ¼‘P…ã‡Ó8š Ÿu ÞU£<ØzŽ«³¦Oþ\^Ó›«7æ+¤N*eU231kË@–t¯>͊Ϩº„úX pž.,nŠëAº`á«2€µr¢l%:ž…y—âe½jý†>¼À½ãqáðâ‰;)¤´SÐÏ‚N„Ú£Zç|‘²ézŒHÕ|P ‰€9òúÓx¢åÇ.ÎýÄÛ÷•D¥í–K©Ã@k‘£KëÍ×”0ý»ývÇ!ÎUN$ç)}‰ÈûÂ/Šé*S'«ÂÔº[÷œ>RàÜHgW_À¢¤aéÃVáóDÂ?}óL3amuyyAbUtPª]g=”ó–ÎTmïtƒ÷D¼Šn<¢VÚc±}Q þDv)’gš%lš‹‘–sM«ºL ê],d&’zA',Pƒ×êë¿»`ÎYá´¥õ¬pðííàR-¼Z7õ>žAÄ©ó‰ô_Õê>÷cœÒEd¤*ÒÅñ¦±÷¹ÓNúgB¡Tå$ÄX"¡Ò9£K"wʆUkva>›B)¿6÷‹Åœ¥UÃ)”ßtÐèõ9ÛKaUÝœôásÝœ²ßÍIš$cc$—¡¶š;*i'™B^Œ œýEÒµ™¾ùÁ\´Û+¼?&Ñ;²íàl¯w†.¸=ßÁ‰Š¥þß‘RÿOÑoÓm­,õo>ŵգ¯ç×õuÌ"×^¼F±nKí×n.íÝDˆÚŠŽa¨Â6ïá§Á¨¤Ýv¦X]„Ñ•³æ+äfDÕµÙ8Ç4f·k&£TA Ûªºùzt€·òÙ1mïçSbU~P#˜›@:ÛûUñ¿¤Ã) ™øOÔÀ´-ªùÊ¢mDt3eS°Œ ™M€×ü³LOžF”6YfÆ—•åÜ4+G^Áü¡&P?U;,ùÃÐ?’]Çyå>v‰áw¥^ø†^ï5"˜•|¼t4Û›cadÈ•‚Ú{ÆïyL„Ï-2ÛÿXf§ÌÉ}½¤ÍßNzay“&‹[ÞXNyB`÷¢2›=æStdzx.?/)µ?^ý?äïÚ"endstream endobj 114 0 obj 5187 endobj 118 0 obj <> stream xœí\K#·rÔ%AG)ð´ù~ §¼$0Ø™›ÃÎseK3ëY­wý;ä÷¦Šdw›dFÚqÆÆÂ‡ÕÐìb±X¯Š–¬ãK†ÿ¥/w‹Ï¿¶ËÛ· ¶¼]ü°àá.Ó?—»åÏ¡ÐÒyæùòüf¿äK+––ùŽI·<ß-¾Y©5ë˜òÖ+³ê$ýƒ¯Ï¼÷“fõÅúŒuRh#äêþfšI­øêŽþq _så½’«=¶ ï3«ÍýºñÁ[üC1¦½þ×ùßeG9øÓ.Ï¿\œÿî›Õ9aá5aóz}&µë¬”«›Ð…1Í÷±‹š¯¶ÛûµPãR­Þ#B fW›;ÒçÉØÎ;Ÿ}ú†°~¦çœP«2½Wcó~C¿¥äß"yß #hJæ:1Àm?|‚´‘®¬ÌèEQ[Ëôü ½xWÿéì_“>¯²5CI:ÇWúégÐî4ðîóO¯×\À$_ý”¾”>ÉWKë¾ÝމnÂî;Ò¾–ØÔØ5°À×c3éA ½Q5PÙ¤â4hçW`wÙÄâxÌGjZp˜ÁÚÛÎ)T•±ë„Ï¡3%7È5ihZð‡¸ÊJ‚ND ‘–•Ø›5•ö „0/ ’×vµ'^Ž?¿'_va¾gý„ϸ촖>Îû£=<óyC,lC ‰óÌ|py…äã2“ YŒ»¢¹a>¡÷vûS‰«ÌL.ˆRå¡döéK' ±a;Ȫ¥‚»7£iÐåÜS]K>,Ùa éMÆ<êUbFôr,ؽGÛq^»Õ¤Çæjez>HZËÜ{Ýd¤ëCî²ù C´†áøÈ ŠGࣸ¢Kø@œÑCs˜`Q  B¸Þ¢¨9P¥¿‰òÞN 8áN¯67qÊÊ¡iǵÔ>›0]´ÂGFâS—ÂéCx¹kP¤D’Ây—ô“jy .É$RÉèzÇ*qO4”ü$‹A?¤Þ­%ŽhQ ½·Ô¢hX£*ºÝÁ®â§^ŠÕ·+2ÀŒ²aëCƒ|õ¡k¨ÑQï ±FhXbÆãÊ#=ƒ¦¾ïbw«ÁYRåL³æ Tý¾ŸF35=Àu$o”- t`àûÒ—…¨|7’¤ õ¦1Ú'Pw ¾œË›ÈŒð9ÅÍ„ë4@»Ìö> œO1OŒL 'uqàOí³6Ël½J"’69žDŠkC•ãÛ5ù?}Èâ¼3RMBÖÁcëº'A èJ&;6Å7×±]iù¸R^$?Ónâh÷‰ˆs­—ñàE¯8ëUâ üÏžj6QC²~¿¸ &qQ'HÕasÙw´ õâ›L@;Âû~øZ•(¸:Õh,(¨sgKœˆÒþ©<¨3Ú^¥>FNbh•ª_^h,–·€GûD;ßM±ÐºG[Á6=ŒçùÇW£^æ•÷ÝúLYÒ?A L¤@É”¢ ¨1Z‹Ñ0²Äj$èB ï¤Pû÷kHµ˜+ ­ò©&>“Ç˜È ®·‘%ÈùJl í ÈšJP%¼‚ù ]€Åü‹‰×JéЦX÷‰é ? Ô«AºKë2Ë*H¹¨½Í¶ìÔD*Ý<)ýUauTˆ©òK¶™äÁaEˆ€Û I.©ë¾ŽF£58xÛͰüTL–¹‰ ¼ta,|Ã&‹|c0ÕZŒFt4ô&”&Ž’qŽ4Jrå°r™iíèûë1úÕÃ16¢¿Uш1ÌúÒ E+¡ùUÌóžå°–퇗¦­#BC"óÙO‚y H¯š‰>QÎ×= ÓD^ÇE·~Y ÅÂÑüîÇfp5¥Û ¶R.VP¬a±ô$7ˆd!ê …‡'…­ˆ‹>ÿZd…2p™dþÚEÊ¿52~ÅUV åµ!]»ÞBHë£ñ ˜906µ^Ž­û±u3¶Þ­wC+Žü—óÅW 2[¾_°å_0ÑY¾ÔÞ[0³åZÀð”Z¶‹¶ËšªVÖ € 4R@¨k›|䈕qã!|;ʆy VhT96*UÎ3+Á{*> Û_¯ÑV¼²Yq3Í0;Ñ&L´W6h-u-7”'$¡JJq^´ JíD+-iYê¶]“ YšÌ]¾Àáç÷ýd4uL4yÜÍrL*yŸÈö®â½r›UAªn¥’ ÁZ·$ñ‘*¸ƒªJÏ±Ž°Ôã8U? I†ZæTÕÕ4tÒy$07md~îvdœ³j¦D-.[‰QqTÚ„<ç%:¤Á ¤qÔ ¥–¼Q‡ƒP_l\ŒÌ½u Ú€/ÈŸF§PAo±œS-`hÝt:k^b ¹KA?Ôƒó`¤•S—_ìLL]>`ÆÓ˜nlf Ý]9:›Lã&2+M%EÌšY½‡.‚¬÷0ƒ ûéz^¾S`2}Ñ­,ù'z,ëÛqÛĈ6G¤“eIå¬1ÈÇÙX%& r¿ÛÇ”éÍîÉ›$qYæ 8#XËr =ãGíÿ”ù+i¶òp6y]”¨ñf¢Rˆó•t@‡ù¸¡ð•ïÈ”É%Ùš”ûü®T4^cÏüŠØÐfšQftá€Ó”~ò°?QÕ•F±2ÏP‰Ì³t.Ö¯•[åˆl@Ôç¢yèа™ óJÍU…¤|2Æ&O+eº¥eÊFDrðÍÑXâWAx2%žÞËxÊ1PC‚_ßr|ôSĮԓ£_pM¾Á°ŸöÃæN%/×°Kp1ê`*‡Ê ·­ål|ÅOE_ ¸Rª—m\J™N{j\©åãRªSÚÆu52wu„ýa=”5¨Ï½JNßô~qˆge¡Bû&Ò åA.kjˆ¬8¨Iff[ò»©&”úa*ó¢5†ùð/јÔr‚Æà‰Fã y™»ýÙ¯‡¹T êr:?þN”A°/Ž”%â;¢EE ¦¬ó‚ÙÅ)ÏøPÔ)ûüžŸWü¬ë´ðm.Ò ‹9*HsÆrT`@Vê ‘¯ÚR€æÏúè"àÓÒ°+Gí"A¯(dß’ß»>Íðªvâ3-DTÖÎ%2qœi¥%7­´„3„•|%¨œ=³”øÌ³ñG2ûH"S)¢éÇ3—Ó²•l7ª‘­4Ï6d™ÃìqŸœ­;}7•Zå¤ßuuš=HjnNœG°&:#ôpJi. ø•? Ò5ŽómIs–Ö·Ó NŠóf}MCÊ_BåO&Ad$Üö-LJ[ N¥h$ÏÙ´˜›¯ýiy|ä¤ÜW¦1ª#Öˆr ï­I¼Q7ÈŒb5æ¨mbÐ;œ¡#uœwÅ>1ޤås!É4³<·ú„$?!Éÿ’|áA0Ø(E’}Ë AP˜Î™I>?!ÉG‘äþczä@0ûØ[ÆÏ”ù—¿ÿÇ=7"‰™õ-Ç›PBé¼íE•92‘û±uWíKLr~&Õ0ÁœF¯0NDpî:%N˜PèœæùD.‡±©h¯ÇÖ›ÚÏš¿à¯%Lß~8ªÈ •s^4ÃuFŸá›,¿£g%w›IÝA¹4­ Db 7¸÷¥´Q«ß ™õ=HøÚ4·+E5¹ðojf¡‡Ì.ɽžÎ ò{Z+FIøìŽÿŒq»{Xɳ[äB"u„ô"÷Ubpr«è§Ô\Þ”Šk2_dF™ðñ:¨ ·ž"=ØC#ñN^‡ ºµÊj7õötH%ŒÅ#¥~õ·üÑà p®ª°|º¥™øËódz—šÁü™49A „CÉkûjÅ%=,zKVªÌR%0©\s£ýcÙ :3/áˆâ˜‡è–o ÈUÖ¥¬ ÐÇÔ(Ì(5†³ñྫྷjÞzßÇ;òÖg›"Á*íŽÞ¿Ûì:.^ |Å Vq›Awœ Ûšåªá,üôrMšgà±ÙËÊ3‰Ìí:»–»#Ï+¤íƒÈM~‡uörnö~}Éjx´küà3<® ÊeWG(\q+^ Ê†u­¥™o+«êâoÅ<`T©¦wŸ« SlÿIá' “R±Ù·WO<ö¨Bë$‹`æ—PŸá\@¿4qL-'$Žà~ÂýÔ1ß"IÖt§o[m%U›'2}ÖKÄSÛl‹çŸB³¬‚à³[†•óÓ~Æ*ˆ`6ZÔ]˜µC!_¥Kÿ}G?þÔé«Åÿòó*‹endstream endobj 119 0 obj 3748 endobj 123 0 obj <> stream xœíËr·ñÛ^ò {Vy'Ïò-‰“J.)'¼Å9Ð\“yV¤Ä•%ý}43ËÕ®i‘²K¡š˜F£Ñïî}³f-_3ÿ/ý½[ýéßf}û°bëÛÕ›\§ÿ®wë?_âaÒ:æøúòf¿äkk[Ђ¯ s-»¾Ü­þÛüñ‚µL:ã¤þßå?ñ[.‹oýÒXüþr‹»op·`œnÞ…¥t\5¯3ô:C÷Úeè]†¾¡þäo.Wß®dËÔú=Þíï+®„hM V´’¯wQª•f„ô«ÿ,r`r‹ÄÄÐ*­J\gŠîfïôs†þ¡oçn:^D00 Ô ¦DëÜÔ#†˜-©Ï HJr—¡û¼|—ñ© )Ùp¦Ú ‡VIéâ‹ÿ¿礉ÜAQ 8ž®ÕRIä—­µBæp–o/6x”sÖj$`Œ·<€`ɘrÈ´«¼ý:/ò»ñ[ _OÖ»¸Fš \y i8G@#â’Y„Þ ›+ú]Ü ªô Áöެ» ![£™ÅËâÊ2Î)²þ‚€ã!È$ço> ÷dM9ñÃTÁ1ºîÊÇØà—(T¢éÈÍö?’=?D68[^ÒÛíâ&0ÅÁ(8”[ñÚJpD¹#ë’^Òt+•Œ'Ú‹Ô¢eÌ5ßøëÐFŠ#qGörƒÇ÷LgA™†Þÿnámûm’Í÷¿†Cý 6Âr(HÊQ?ZP*²%HqJ5DöûwÓSã—[þ XºÖiM¿ ÌP ”ä¸;î">£PÈcšÙKö—ïNIç*?ÿØ©EÍ}ïÑ Ú–¾O‡9>aY8V£Qèn§21ÒWÌ&¸å½#r|VrèóLz¯}¡ô”Ý}>^sß²n#åFÛJiø"úhQN¦v0Ê+*™õ J"Ñ- ÇJ‚zqùªräPÐðõ]£bIŸ—ܶVN£6·=@NwÛˆ¡•pÈk?žxêá”Æ·@ÇV*ñ‚‡K‚Õ¥/–£)Õµ´XoË ‡vGМªoBÉœ^Ö¢6æa»qMOŒMá^ãjÎë†íD¤Š»ÑøÖ>®½®·à˜0Ô¤3ô•°À$ fòí7­EFP»A™~ÀéÇkBÓ=ĵÁ+/Øj˜½Mµ®UÖÀ„lm视.@Âuý!ª±—Q®u£J_ß'~¸¬’Z…h%°ekæD®µ.ݾ2‹•Ù‰Þ+C;‘¶mñ1Ç7dåÔî-°ŽxºÁÓè©e÷PáD‚¸¢-‘ýîjþi©Ø&È14ØN¯¿¼¯ÀÙGRÏ6ø¼ë/m3ª¼o™Ñ½ä§,ì§(U³»[ fô³¢ÒþÀƪ½ÒUûë¼~—ïIMž|/å£Sîñh7Å45ˆ>$ønŸµßáÌqMýN‚œáw˜çö‘Ž'„*™¸ƒ>è±,ë° ±–Z @:Tó‘ü‰âZHըɡ~«ï®1æK‘PíÛJ’ûdé>äÛ¼f*{¬oó»AàÛ^º:(ƒñ©¢aØ9]CkÔ‘Å“WÇkÃyÙ#Ú°‘í¨ßèSšn-¦éÑ'€®¾œÉœ_+{{(¬e¨¹M¼û"•àyç" Œm­£J g(˜V[µ`;KÜÏ#ôxÑÿk?¥`&UT*@ŽË\àPÔÞŽ¬•y¹³\úZYÌ·ËúÇÇ1ú9µ˜z^¼)eܶH"‘¢9CŠfw<ÙÒùâòÍñR4FöOa@Bº¦†uÈ4x#7n©ji;1²ûª¶™ ×ÔpJ_ XŽÎ_„á|Þ")©AãDD~€œ.òˆˆãDž\äóÓG†[Ǣ葋S¼1$øõŽJÑH²í5Aò¨hi;‰Hèù$3§ö‰‹ïYTe‰tí' ÏŸM4Éããº*úã…5 ÇóC*1‘*àw;}J³Q!‡3ÍßBw›Õ±§Ç-µçP¤[Ûcšoñò¬ì¡œÞü ñ»1a¦o úƒÀɸF ÑGöÄVÙDXB O›–‰A['B3ºmâ«Å–¢Ë_v…Ò~“y¥“ˆ™¡Z8t3Ž8A³I4•‘J*æKcEôªÈþnG=Oe¿ }·Ðš ë™, áLûÚg1Ú²CÄñoJéFò:ôm=©\ê¿m¡PZzõ´ÕÍrŽE$:^©ÆžØ°8ÙB•µAò>ÍèÆq@ÌÇtj0)ž#åbs™nEÚÂDì÷‹ÝñacãÏ ŠÕ£MK³S‡›«YîÃ5†Ñ‚z0&±KÂÄDN†g<‹ŒãÍwÍ¡W*ºÞñF¥£¸çt\üƒ_çP{ˆßâˆ@È{¾» Ûý$fmìsÉE`E1Æ ZØ…˜®²ë±L–>3®ä÷>71n…ßéŒü嬯°-@ÍùsçS3G/Õ}IÛ:ÚÖöøxp1Á˜#n°Ô\ü*g͹<Ö†MAÛmäè ¿]õhsj7<ÙP„ é˜È¾#ˆ¾öPÝŒÇóŒ‘ÅÈܼs‰Z’nSº–Ê´ùú¨*ùL>dlRácw›¼IÈ DeÝbÞs•;fÔpöA-3‰©ûQjY= Råöüݧv,äsΚ ––oàôøß*iiªø¿Ÿ%´„ߎÐÏ[È6fÈ3Ùòм° i«ø½éð„B+%:"C¥6AÎ[)ÑÿÈ(¶·™ ’¶î2t—¡WNj퓎/ÐJR;–¡KO3‡Ë˜U~åœßæƒ`ÖøŸ½H3ô»Á+½ $çÙ†'Öƒ!©¨«!ÎÐ1˜JDžv ¦ò†…âUßa rW%À€©·+3œ Æ&Ål=é.ë–WLã“ë 8–3îÄ3ƒÙƒjƒŸJÿéû |&´wa‘XæŽU&ÄV~²2Qì˜õ@ȇ¡¹$¿à™¥þFàp¤V‡ÃW)7Œ:ÆÍÔ(0«².¥ÏËÇôÒ¼0ƒ¤³Û÷ Tõ]1§=þ!å—‘ªrö¢î»¤Êa¢“ý†v¯:!y í^¿¹šîœÄëZBK£^²~ùWŽ·ŒF ätËȕſ¥ˆ”©wŸ/Žíª_vÍ_¦½Í¤Uƒ(‚.äf¡:øh_¤×æ²-€ù=ÙN3dQc(XÉv&ã¤Ùŧó]Y(í(®¾#.¼Héãïu¡¨ðÕ„.ývDU¬DŸJg@FÁ½ÔàW0ŽÉú+0¾ïÄã« ²‘•.â·«ÿ9ȵÇendstream endobj 124 0 obj 2735 endobj 128 0 obj <> stream xœíËn$·ðq.ù…¹¹°¾É-‰$€ØÑ-Èa¬×¶=-É»£¬õ÷©"ÙÝÅf÷h4Ò&+ÃØƒzKd±X¬w•~^s&ÖÿåŸÝê÷ß»õ͇_߬~^‰øËuþqÑ­ÿt ¤ <ˆõùõ*íkR¬Œ+¿>ïVÿj~·áŒëà‚¶ÿ>ÿ;ìºØ‹ŸÎÃþóKX} «¥T.Øæ!~ê Ls;B/Fè~„¶#ôn„ÞP<ùëóÕw+͸Y„»ýu%Œ”ÌEj%ÓbÝĦÝÙ­þ¹ÈÉ-23Ö”ØÍRß ÐÄ–‚£gÆ2tXŸ ÅŒ†È?Âv6^Ø*•Mwÿ1\-"Ûú_ì¯6gJh&œköïÈ/"\1¥L³»¸ÐÞKÝlÇÏ›3Î4ç& £aµdÖéx¼âŽÚu”žÛ„]¸%%g·oïé{*Œ+®sÀŸ¸"Z2#`k¢X¡jJcÂ6û`(—VòDܶXL\ŒŸ?åÛ*Ý\ÐùîÈ÷-e7ÅÎ6g°‚Á£Ÿ#TI`Ÿlè}Ú‰.X^Üÿ¾¸3œd¤ð¢yhdÞÛôP ¼oçX¯BÓ‘EleóCeOsϬTl»_ j׺.Ç_ ¶³]]£²è–DŽ’³Ò@!XºÙÃCIÙÁi Æ^m@ŠñêÍÍ”9éÞ',?.ò2b·VSš"T"Ø&éeiêÎz{uÆÃð|¹ÏËÆõ†Íš`Aîˆaë!§6ÀÂUÙµ’ŠƒÆlˆSŒY|?SÛ|{/Jm¤ïú@pVz”ÌÙ.Ë¡‡OJÏÍ$Ï{] Œ8—6¶1݈fbÓvÔíòM‹?ÒzÅÕ`ëžk½Ž‘xÐëÏYâá"`{¨ÄgÈ $^¢‡ª]ùHF´Iºsðù3B·ÏpûFý¦)sšòur¨Úp]Ü{7qˆÆ!]†RÔcL¨t^LŠƒn,Hí(¯>$ªT¨Òï|Ò¶G^¨wuƒ¤Pi­€\ö•2ÞÄáŒ÷Û|œ!ùÔÂtÐ _áÇ”(#âÓöônt ½f!7™LÅŽäãƒ^RûWÑ®bé¦Õ{z&Оpƒé"k.ShL”óÑeVTÝ´.ÇPAÝýŒž‰Â‡gÈ Œö›ÊHµ³yU#ÕG퇔“LÌY)€Þ¦+6´E­KË$D‰Ï6N°ÏÖå6׎3Nx4K(æ.Ľ>Í:!µÖ/µÍìÏ~œ}ŠLÓ‡ì(Ü3ì“sðŒêuíSd°ê¯'@D2UØJ=¨çíÜ>aŸ"n7µO‘Ϻ²Où¼ÿú¼ i‰£ê!§(ÀÀ8w•º‰Û.÷ ëkÖƒ9 31†‹>“¾2±8Î]^lZe¬¢ÇŸ‰B3Þ2y03ªFá!Ý• ÷Q'ÆL<êLt–¨ÝæÛulÔ„!Ž:59ÚY¥Q±'F íœ|M£”"%Ï@/Ç6A¾§85¹hZžˆš á¶vb•úòGŽ•Òáe°í+ú3—}¸IÖv‰KîÚ¥ y]â’¡™˜%Rû¸|F,ôUòš´W¹˜µ§^èFö­%0DôØËþü’@_Ì…Ð3l¯ÁHÏImrÚìí`ž7ûêz -ÏVHl—iA 1}…âòí—j,…)z¢×—^ö º²˜CY46ôLjèÅ⋨züŸRuCoèv¦ú­™6ôR($·¢W¸áB®îìe›C¾4_y>ëMdLÊZèÞgÀéj@jëp„\ƒTB÷'„#ßF‰6B™0±ÄåPÀã(2»åy„T&C—_a–0hûLY0"!Ó ¹,(ijM@Ùäë±­R\ƒ¥²”!/&ÇY„‰LbÛ±€~¼¥Icþ’ç$mîÄg‡•O‰˜Y”0Œƒï‰Þ¾tÀ+h ‹HG9]:’tÜÏJÇûJZ§sˆfI j9Ê\YýÎü(}ÊÒßbÜ/0aOD¿/ä)÷„í%}ÉX\ñÇ>xÊVúz钜̊ѣ%‚óÇŒÇN||¬ªÂ=üâÝ<5}Ý’¥A3ʰ»šaU:ÔN:8Xn涨v.•[óW(8 ŽÛø<ÛÇ~4Õ®?ëOBh¡úÍ7=$×¢ië!/(I€IW«J¤ëñq€ßõø~3”ˆø·eDû…c:¶ÏÂh›cÁ™ž'žú^J{¼x»Ü|œù£[O{äÑÍÚ'±7#'í“·>Â$¬çøsÊrºP >ÍúJ(ÉEHìf„^ ªÙÚêTûÏPY]<žG*Ì=Ó¼~×#Aý¸= ßêoe–\@ágìjò¿VÈrT€3ä¬CžÁ#¢zUñÿ“?"ãî×2F˜Î½gƒE®‡߯Ÿ~IÈy¤³kˆý!Ëç2±^’…Äj"ôÝê¿›;"endstream endobj 129 0 obj 2845 endobj 133 0 obj <> stream xœí\Ks·®Ê‘—ü…½e¶Jœ ÞÀ!'•JœŠSqÌœâø¥-ï’¢8¤$ÿú4^ƒ0.wååRù °4ýøºøn5ôd5Øÿ¿—»“ßÿK­ÞÜŸ «7'w'Äý¸ ÿ\îV<ƒT¥7ƒ!«³ëÿ%YiÝ3IÉJ ¦˜^íNþÓýv=ô7Êpùß³¿Á·„gßÚ¦ÒðýÙô¾†Þ”2ed÷àšÜÑÝ$êe¢Ž‰ºIÔÛD½™¨væ?Ÿ|ÂûA¬>ÀÚþrB¥½rÜÒž“Õ(Bô\M”íÉM «€z!E.ûY>Óš¼X2‰ž ÙSÃÍê”°^ph8éü ¾aÆp%Ý2A¬” ÒíÞÙ?ÄÀ'Nlñ‡ñõú”Q ,Én|‹~ptÑ+*€»Ó¡çà Œè67s}L6Ùµ’HÝ’wÛs Œ²løÑwÑÒädÔý25 ½‰{¢nQ{‡Ú˜ó±_Ÿ ¦zCX÷­ˆ ”_‘uEš¬#VÞ¯‰ê5Hô ꊥۜ>.Su›{?%U0\b}ˆ&ܾ‚Μûã šul*`œn³Ý&^Ì,?¢ D/ef4*k?¬­Î1©z)L4=¿ÿáU˜…±n¼ mN[3¢7…`§Üò©1Z;}?“;,(øUö1ý0äòGÃ`)ú¦Ö [X'°ÒÜDÞ{*54â<}w…lhs>¿³Xka³À¡-¸ž¾|W‘ÝDx@ü¥7>A¸b°dÇ®â:[ÅŸÓðJ-YÚ5Ç"ܾöƒK.ºÝmƒ™8$£¥ñ{1²&È‚=3J ¢Û^¦öCZ'öc¾_‚÷Æ€í÷kïósãÆ)8q1õyY±&PsÙ3ŠL¤``„ž³ýâK÷6ãm1ÔL–xH¨qZ%DC5ð!ºOè'<2l ØãÜ¢îÛÍeÔb²Â(× Gì¹Z¼œ ½gs½™8 „=­öD¿hµ‡…ôãªH9Bí)뙨qÕmbn›¨‰ú:C[žO­MÏæÓç„Ã'GJiÎç¶"’œ̹æyj¾_ˆ0‘4àCÁüÖþ`U2šHf.k­8‡q(³Áï”NåVÝCð©lÈ÷þK©sô† :3 4ÊÎÏjPÙ!õ :µtöPÓmŠLÅÁaEß$¤ôÑsËH÷‡8KŽ˜lü¶oÈV…^"Äà-HPÃ|¾™ã¸<À{ƒ@.&ŽÎÛ»e×ÈÚÍ2˜E·deÈqü°†Î•V⌈mQC¾ 9ø%£¯ïýÀFŠ ˆØ‘™jìËðæˆ0éejþ„¾ÈN!(XÌ7Q/M·{(µ+ޏö(2°›§IL¥5‰$Nߥ´âÀ¢»+Ñ}0‚\óè„AÀ›As‡ºŒ¸Ï棟‰Oz‘€_ý*ô Æé…kάm¯íÛ[­Mº]HkÛ!Ô5bõJ\Ñ,¥€ Ðbk)Š ßáÞÛ,3ºÉw–ZQÝ™c†r1ðÌÎl’dxnï'âñ’7¥R†õa({».LzÊܤ L7ÞcH“t¯&ô·F3Yâ8ÉŽ[p @@Õ¨ËRäÃ~\#ú« ñ›­]¬ x8B:JDOˆd!S(˜åXj4úLzŽ|¾_“SÙíòðÓJDQQb"çïÌÿ­E:Îß>N­<¹`‚†¶T‹‰ %æ“›eL_nÆí'û1ƒŸM‡~À@«í6y>îLRU¢€;B _‘/¤BXµP[ORq`“w®Kn“fopŸ@öM7~1€aÛ®â1ÙǬ€Àœ·EJ7W^ˆ»*L†*lÑÆ@¶D'H‰”aÑY⮾¬ ¡à[pÆõDJŠjãBßOA.„gßùz£xý¦\¯ëàu>°ãÝ?guÙl³+kMÉ£'ÿƒu6V•(i•̼\8T€ ÎòxW{:”zÌTììÒ}B¬ZÞ³Y+ýϤ7Ho¯RÙ¬ ƒÐÄ «øòsoÁC‰·ûóð¬>¬û.c`¿#Œ,E½C:ƒåövEÉ™ú§-Vmѧ3uÂÄ\ÝÆuG}ªÛd¡c‹3Ú_oÝ¢¶.’Ô'R×  $_Ð TîÜ?O¯~‰zå‘i¢SU‘1kØaƲ¢”U„ÛBoKáåS…JÂø×BåS / éö—‘r„ÂKø Æ.>©v(õ̹˜¨ç³Æá;¸ á&5NR5qZ)¥šôF •RÈ­ýù+… 4ùJG7åŽ8ꘚ÷Ÿ¥•fÒžV ²hcþÉ­](]fÌè °òR Q6îBlg6A– Ê~{¸ß½ºA¨Ìë‹~ ™­Vþ)â&F ¢–HVÜ.qÁä©¥4©y(ë`–R >Q"é–H»Võ«<µÝIkûT@þ­§3i·Òáy"­’MY-à-è™aô´Ò«À çY1£qüd¼™vo\µ:q­òš.”âäÀÖ°h>¬nçÚ"ŽÌæe£°ì"µÿ.\=^>Ÿrp?»oN†'jÜ[ÃJ—´]AÚž†¾µ¶+€>žgFµp.j™tˆÈqNº¿¦ý'ù¨QûwîJ‘ʰÅ\¢LC@›B‚©µCwÔÇ%?HuñŽ9ÞBEÿ¡íH³áÙb÷p?ŠI9®Fá9RÏ0ø/ùYÃ3â3„ç#ø á9ãó¢"J‹”©Äû\9êvヿ`Pú~Ƚ”|NÀu~å¿Ô“ŸΩª)Ï$™ÃYˆ„©míÅ.€¾|· [¸èu@|göDUßa!RÖײ™õºt¿øî†à­!ª=¶{Ÿ_Þ(â»ÇäD§ ÅE gÖVjHÁw0yÎM+ÀñÝ*YŠï y0iNoù…©¥™§Y6èè¹T×»Õã­büǛȊêû¡ˆ—-U8ê ‹êîþ¦ùÿDu m:aÅw¥S-kkU€oøWf1uÒùýŽ×ÑYá>ݘ¯"<£ ~_BΈ½‰o@GÊž@¦f)Â#æÞ'êë¥íÜçÔª¸¨^ýóìc#o šðŽF¸ïNă]Ðp\Þ¢òœU0wºÀŸ_ô1÷X_~¥ˆ*I{‰¡h¤®¨0B[U)*ZÈÅþ*9A¸L%ñ‰Þï,¦Íª§½”G(-IJ‹=`ëèk§ô!šçæ¤`(ÏÓ¼—Q'öm"Ò¼@9BóZ CýJ-d—¨÷‰z÷9õ1l¶QíSû£Z³ú…E:RÏ¢fG1Õ9¨£¾ HÄ1Ïžö»;ÿN Íô4ZÃç8ÑŠé vóApZ‚ê%¢9ÿˆJ¸Ë“¥, zUz]"aß_˜éêá„~ùb­.G¿é–¯C¿þj©Õ7ÙÊ/ò’9øG`\ö˜Øø‹ö/'aÿ(Gø—Ù½ôþ1w>·_ø*‡dîžcežÒŸâSÚêadýì¥ 9’÷©Ý Îg®púIÅâÝ @Œ«fm¡¸›BŸ,ÝÕ{ÿF®xmçw©WƒõÏôÚ1ïC[¨ò*÷¬3{nòkáB»Q4¯B;úÌ“{(k=L<#—ª¹ÉÆ[ö^<„v³€WqYªmº5åA¡ƒ¹ |}ýõ!tدç<„~ÙÙ\ôcÜH9<Á=£l¿ô« Ý*OW7Êrúg+úg}+}X˜ûÒ³?á 2!lr„eØ79²2ŒÛ.¤ø¥1Êý?ŽáB¹G6ÕëçBns6i/oNŠ‹KŸç ÷[ËD«Î,ç-¬ƒY›£<½3%‰Ï±‹—Ã!ýäyÞ4†;xZ·,ù}Hr!qkÇR¾“ðCÒSûx–‚izÑâ² jOÊòýÉÿ÷¯¶¿endstream endobj 134 0 obj 3418 endobj 138 0 obj <> stream xœÕ[Ks#·Ι—$ÇÜx ™ Çx?Ê•‹WR©¼lë–ä ]I»ŒII^s½ñïH~p>°­,—XÀëB}› ÊjQ¡à&Û¢«ÃÛ‘ýCã“;²ø®’„Ø÷‰§4“•ÀQI­8·ø8+þK  -…Yý›pþsæìôjŸ&ZËp”÷–NøuaÙXÿ6N²B·Y¡þÍ,F91–8h*:&qf™½ŽÜ%å‚ %5VÎêº\Ÿ“ÚÂn‚ÁI Ãr2Apµ;të‹MÁ-ê`­Ÿ¸¥åPw¦œni:ÏÅÜïfCÙÈõéàÖòxtÚ߯G˜/á%ºÖMĨ=ˆE<ŒV-w@Ì8Ú‘bL{½ú®IbkLe.öDlžo˜Ñ&ɰ°Ô$3å “d é–?Ñ$‹p8ºz?»‘4À0©²ù†Ï‡ÍWÉç3ß=1ÙÊ”‹K4M¢Ániru×epÈÝä<(dCd±Úó§ûÌQ»š<‰ÑñóÛâñI,Ú‘ï=ù®’ÎO6Ú]z›žòxØ€CgI°y? m¡f„sBv^Qá€p–lNwÆÖ¢ýð@®˜e?¥Îtja~³&%˜­òjá÷½á©&()ˆo ¸·‡vº÷@b¹#°=4(|мìôG ‡­ šL94µš€¦À#‡’áN+hž#h¼¯Ì¹ð©‹jZÞæIÌpÛWVT3[À¡å31OJÞòWáZ…d[ÔDï2ìg $À劮[a§Ê·^­9–RNÕZÞÖí‘iNfk»Ñø”øüÓââWÁR°‚ÖLKŒÕ4Ãb÷QjÍLä†xo­L ¥¥ aÒB2Þcè«8ÜjæÜú £=3ýð}Ј÷ܺÕçë 7^–ó¢šYã³§úP‹³…TÞÍ "Q@xUYCÃÖvÖèÕoÂLTþ6‹$u¥ ÇÁ›„?¨»Òô¯ƒ&=ŽÇóÞs°¥Æã$Û Ë]n$ê¶4…'E)aòa茀C—(ã6¨zõ6ÍŠ»B ŸÕ/$è4èWaÇŽ ㇥p<ÑÌ“$öþaø©µ2|ŒàÚub w1ÕãUç#O ™çèwŸlÓH1ˆ ; N†Ñàe¨‰p\1'xv <$Á>’¢RÂXÝÁÐ|û/`R0}î£uÁè³ÂÏÙGü°¿ʸl ×aбÚŒg€ªqbR8R¿/Î4OL~V\Á\ƒÁsü.ô“‘鎄³Ž6«;i%ÓƒpáÌØ9ÕÈðÿ+ »e¨TDÃÔ•³Èš‘H;¸o=ÇÛ £Œm0çœìµ=Úì6JN`[ËÎ29Œ‡ö{á¥ÕKbòñˆ8Ǭ<cç‚âXï˜=óæh$œ¾†9"ø2õsœÚ {i¿ÎX‘æü|l?vœÌ Á™ÏUñÆù(Rt˜M¡Óp)¤õü%¦Pùš¥Ï¤v‘I&ÕSŸI)«J—x>Ç/7%·ƒyÀT"ë9‚Ä9Y$…‘‘h¾ z5Öe¬:.Ë'i)”Z$¦í²Ë;mŸ" ”ø÷LȨZUJ•5Ê’ÂÖ…ððu5O’®=Íw×i”}LY°±BM]|–¶ñ@Žâ©y4Eûñ–ޝ½AÚ7-pÝL|G´ʤ1ó*o.Ä£»üßÐT(·<4[®y¿o„”«z›ž?,}lD¢@x"è€Õ“RBÓ¼jÞß„ÓRåÍü~Þ¥= Ó©F´.*öyË&è0††ÐZCsíÌåƒåί&Ï S¤x= <éóýXkù©Òuâ§Œ™ÕâTCký¹*Þ¼D9ä™á>‹ˆòí®SqF¥N!,«©.Ï*3è_VNš[Tu‚÷O@#L«ÀN4º“ÜH83•ŠšKí냘r«þZ©~¤GkibÈ3ÙYìèŽ)ÞH=yÔ:n 7턟yu†<ÿi$œ‡ÃÈÕv®z0rd{ãDq|ýå™ÙS¤_åבÒ=B¬ªÕÇ{jÐó÷Ìܤ§ègT|¯KÜ%/O÷…º_Ï,¤ÛWW)©¬RY¦[JÎû7Ê©Ê# +œ"pè¬Ë…‘èU‘ˆÔ ¤Š=TrŽ«)p·±aÝx÷°m½¢@.imë6vKÃÏp©‹Ìà?³è£Î í¹²áá_KÚFmü¾xæ-±êÿ–ÜØf3ø‘M†t¶~Ù÷0›1’TÏ÷ôèYÍ[XíC³ ˜wOy¼yƒCHŸ&}òc†7…Jº ·3ræ§GDÎôôè 1ÓÓ£JÊyÙKO»?¬á‘¼Bê{_¬‡Þâ¸î“²òöÛÛ U•…´à«jµ~G^°.}”~ºÌ6Ç^Û‚<ý®…Ôó’G½™&Ï÷béy±ºTZS¬eÊX ?ßÑâi±VäÌX{¼˜k )ÉÃ]ð>€‘¦âv¬îH}3|GàÐwù’T3yPõ,Êν¼¿!ÿ¶ pbëC ‹NmhÑIYÏýˆ‚3µÚSÐðEÂѧ &-ðOÁ)g€…ßuø§C‘3ƒáñbf0P)çuLn+ås¢îÑ€Áð GCRISз밺‹¯*æ»ÛFq×ÿ*Ñ´ª“Ö[øC£hyƱϊ(n,þÒJ¥§<Qá© æIEäLˆ:CÌ„¨Jʇ.Îï9Èævs¹ÞKNðDoƒ&‰½ûzE–Ü Mô ô ê#cÎ ,v¸ä¦3†"$SÎ@ˆä2òØ%i…“ªöêÇ®´OTfO^®„ i[±'¿ð £é³˜øÛT™›Œ^Ô¯6æS›‹wÇÿpݪµ[¯ÏI Þè¨U’ÐßÜÍÕ/œùêîH”–buI• “º ˜ÐÐPFod2~ ÷ôf¹Qá ¹ïŸ^–9²|¶üÕâL´ãyendstream endobj 139 0 obj 3267 endobj 149 0 obj <> stream xœí[Mo#7ö¨ËžsÓ±Œ~“}Ín`/‹™ø–ä0cyl!’í±å™¿Å&»ùØêÖHê,`mÜ(“Å*òÕc±Hšs&æ<ü¤ß×ÛÙ÷ïÜüöyÆç·³O3Ñüqž~]oç?\QéIÂj^‹ùÕÇYì)æÞ3e¥˜;^3®üüj;û¥úû‚3®kWkûÛÕ¿¨¯ÐEßðé<õ¿ZQëkj-¥rµ­šO] Sm³t›¥ï³ô>KW4 ÷ãÕìíL3næŸÉ¡ŸfBÇÈz2Q2-æ[’ÔÁÀN²™ý<êvÏôä6i`\ÊÒmpd“»ÉÒ÷YúT˜üý»br—¤Ý9_ ÅŒÖuœ§PU×Ú‘ú ”l¨µj” í½Ô¤t)`¼Ö(ÝlHL뺺)|oAÝ}\;©Œ¨vÏA!jêêãbI×µ÷6ŒÓ}?@ûmH(Wíî@~å^Èê9tÕœ›ÚT»÷yØëüù;ôd‹8=%ø–-‚–R0oyš W»kÖ) Xk%çc40«äÖ¾f‹¾…Àl§—ŠÕíôÆ27ÅLo˜u¥•±ÿœ¥Ñ4Ë• o™‚¯€€Ïæ&‚Öj9‚BÕØÕŠ„ήµ+ þ1*—µ¤BSPIQ}Vw#l—BÐWë"À’ï«ÛÂÒNÇSÏ™uŠKAú Å#tÄÀŒMœã¦ç×p¨¿ÏÍwkôø>™®Êi=»Ïá[%-Š‹‘/œU•o’jã+?, æM¼Æ G‡V©…”…ÛŠ"”¢D¿V0oèd±XoZvó"šU6RxšÄ7ÑÏšU®bhÝúP'ÒJ Ã8§Aàýº'FxS_já^1o*¯ÉäÍ$™À›ÄhÄ1,of;ožofâM´ò®Ï=›,}<–7:‰7­g®îíÞc»ýKø6\- öº zh5)|·/ ßìÖÐ%Žç(†ëº³II•2ÍAÈÐ&2[,)E¥8sÕUC¡R®«þÖL±²ú[hR¸ž(·È\B{šd{œYQ9Å9’ÂƉ!þ%§A .ÕsTV­H¿¶%oÃÔkˉ€_J^‡$µjWгá*)¥›VµÔP4¸'О“´¦õ!Úk†ØÀ›UÒc†»ŽM,Ap`™DB> ]XtèTØAõ熯&0)Këí‚(àV¹‰Y0‰µR'gÁ!=âêbÓ`Áë))%½5ŠuJ(“‘«K` pøz Óµ‰y\ê{©ác4eûXji%ÂÇ(ÊYëCás{|øh59|€ÐïGR¡M?”Á›ój£Æ6J²dïÑdü¥ò”fA—‰$Aɬµˆ¤$™€$á)uS{HzÊ=déc'=š“ÿ–@jÉÝhòüØ.´Ý÷1cè'ŸÍñøôz^{<Õ=6—šÑ|›Ž×Cü¹ xšÂ‘€]œ:R@züê^²=GóWW¨½ð‚ƒÖ¶`X±]—ÂNÂà šæ0èïìxºƒïQÄ^,~´#’)”$¤]¨jFÝ Z´ÜŸìL… °32&˜ …•ÑÕú ”,~Œ‡uceÀt@¯Q«+L“–)›ë|ß5›½#„ˆ¡«™RÒ‹¶õPt RR6pTä Áµ¦­ ûŠqMdCoÄu’LÀµ倃¥¯/§3ã_£Ws0²Ã£;<5 ®1c“µWNÇ9l¬:E®†¿úMD‘cÄ€l­äü`SÎÒQ#Æd¹×C{È_±v±æÓ'%lE€=äƒ@Aõ§W^eœhaiY0N’dBœhÁ´Ô{i X÷1ç½ì0Ó‹ìH/¶Eé9#ûKFÊUëÔ×Ô2zG‡¢–! •ûê(>- é¨çÅÂ[Jd°êÕJ&À[ÆE:5> nu<àþ¾þW½5G¶ÿ5€æ>3\‚°ôŒ é¸'×A­ä|‘f…<„ û,]uÒ£ÁôÓ¢+Aôïž „õl¥P‹PÖ‡—C$ŽŸb {º½·h*\9v;†éâšü'”Ã\~Z[`uåÙëÊoG^Í<ÅžÒèIïjšÑ‰Îyú¤¨ÈŠçzÄ!¤ÿuã¾åž&‚¶fª`È0„‰ÕE~Õ»¾MS"ú6u‹÷£æYJæÃK·¸ûV¦Kßh¥òºoeì×Kÿ—Ö*šÝ>°N÷ŸÔ$ÌéÞv&ƒn+h”>¥'?0èsT¢ ^l§ñê9¹]ÞH Çô5¾ÀĽ>墂–¯ÚnÚ]ZØáwØeýa!ˆ‡u*EUY·/ôAÒb0[0k’L`VòÕZ±Ç¬ƒºéXuÒ=Âÿ}^8rŒšÚ æ/û†¥Œ2ž×l]R"¨íhËnäÀ¶—zœðöîÕ'Â[Í´‡i%ç‡H¸«“Ú ‘Y pPÞ~ÎÒ»Âú£²“w‹îá0ÚØkvh‚ë~×’ª½)9#l¬!gD}¹€14m[˜$™˜PšI§nx'þyÐÌã¯f‡ÚŸ"˜-ŠSÍcz«lÔ W[¢HrOýß ‘†Tö@NÕ¾ÆÔ‹a,SX°i%# A/AçS¶îW×çìÆ“¼] ^µ®»—uºúwn‚¬‚°lÄå“\0éÂ1‰„Ñp™uþî¦òí쿃YGâendstream endobj 150 0 obj 2131 endobj 154 0 obj <> stream xœÕ[Ks#·®Ê‘—üÉTãý¸:ÙJU*U.ÛJ.qZJ«2¹’%*Úý÷i›ßsŽùà…ÂX’Ìc¿±èVOG4B6ÕÜ ¦4ЧŒ®’Wý3 JrjF/ûÃ]”RSOòxØ¢4ŽÝaŸ0 v6˜-‚cjáõ=†Ì€;U é~]u˜F^Ýwx,¶ûìq”o{T‚¡zQ0yTãJTò¿YŸ.냒ÍóéÈë¹ä¿Ö4T"èÍšKb30OŒÎfÔ 1zAð×ø6ëÑ„[ô¼+‚jÖd´Œ¯ºOiIÁÊ8HSî?ã)ÆM4/ƒ]$Ígåê‚óSæ€Û-¶xS‡;_„¼TP×"½ÜÏGi A§Ei/|cž¢408­‘«ô’ó}f€”›ÈòŠë¬Q—¥û ­4ÊÂvaå ¤næ(t‰Å²r]u»î³ô)“´ýA’³[eGɧÿC@ÈèJ„bŒë=Óĉû¸Â b`ðÒçô‚4/po'ä ÿÆù¯Ù¡òë6.D‡€1嫇€3½”ñâãqã™›/ÝiJg2;銈ò çü×Ñ«wù _{Å{’Ð¥ãF®~¸ÇÿR˜Zàíù°£Ì|Qýà®ÌàÈ7é;R‰“&þ,»ÝUp ÊcÕ'¨ SF_Žùê‹Ç ÜØdd|üÔøäxÍ}Ú* 2e˜¶2!šq\@Xx 1þ”êæáLY¡G·EàUbˆ ro÷EoxIÝ®ŠýÚøTßí?î aJG9~ç=ò®÷ È…ta@Â:êœ÷º œöô·ø%NÃ0bn¢ÂÆEVr¢ð@Ê‚Â3+ )U†xÕrö¨—s‡ÀöZYB,nkÏå@ivLeðØnŠíП®"ZÇC#®\Mݦoè†ËÍh̺Gð-œJ‚-If8•DÕ¸¶6>ºEœ§zÓ.][;óœíW‹é$ϸGæuzV¡ú&Ƶ“P9 €õ’ó3®ùätý(ix8J=x‡N+ܳçµñ¨³IŸqÅFk1Pô™^Å =C!ö„ öÂ:p‹®!6V-[kqìn–úo.dNÿç d I˜+®Û‹@„̇ª²Ÿ e_³[øÕâ>$(Îd;œVûŽŠåõÖA°rÉ~¸aôõ…5a½ýRizå£['I—âÊFoN±L¼,àÌTô¤³™Ìî`ðš}¡öç·ïCÌópL"zÉùž3ø†éÄ“ªÜH¯ò#’"íï²4O–ñ¡˜ñÇ`Ù®§’Ï0Äÿz EiÈû±ÊA¹Ç,=ò“˜ÓطЫiQºéÛxÜ=^¶wØîøòZ_„²W1pN_ˆ‰Úµ®N~_ù^™ÓZ'~û *9ž´-×Ú=E~Fe#Àû©=o`£S?ÄZ9OŽÅÓÈBA/ “mËÑÁÚøJ7(7éÚÅwÑůC]» ˜‹¶#];!4‘J;Àƒ2Çý$™÷ý½k'qExtQq쇪´þ£¿Ô„/£Û‚B µ8 G“›”á}7Á² ¢™9‰ú)Œ°Øð9Ï+ÎaääÞKKÞçƒÃOä^pÌ2g¸Éé¤Xè•:•2_HË•·âü¾u3ÔרÞV©íäÐÞË­u1Ë+`Õû]û8Ô1™wÒ猅ŒwÖ»àø®i°VY5­ÎSDÿpr–ŽtgéiÁ2±¾ôR> stream xœí[É’#·½óâ_à±è0!ìËQ²ŽpØáÅðEòaÜ+=Õ‹º9Óšï?؉­¨…MV÷XlcSL‚‰D"ßCfú§%%lIý¿ôÿÙÍâ«¿™åÕã‚.¯?-Xør™þ;»Y~³Œƒ„8êØrs¹ˆ¿dK׆:B…]nn?4rE •Î8©"ð¹Z;爺ùäÂ9itó°b–pgTs·rÄIFYó_3.95ÍãjM‰¤T9Õloýª¨P’5W«5W”Íš¿Þã/.ü¯%h Ü?ZËeó.šÂ…bÍÎçÎY«›íú"N> ©ÿ±ù8Àâõsÿh–›?/6¿ý¡Ùø±‚+ÍEs}FjJ”tÍeB©1ž³mïV\Ë„Ä ‡Å–1a­à:X]»Å.Ù¥/Œ­t^ÆiYðj߯ú^ ®y@îxWÄ»žk*ÏpÍc²Ù>Æ©¸1ð[;(„j>†QÆ’¾°Û}ø'ö@ö’2ÍÔ”í§´Rð’?^‹:>vHÿYy|ŸFK^ÍŠWv@ª´JpݽÞnXâ¬jn°½çè!Y­…äD@ ~]i~S&úHqfèCM@\;ç¶9HMY}á­vÌHðw±EêÒ*aØÈú]Ó׺»-ù0±%q»À¹·ÝQ[Úî¼®j»³Ü/vW»fæ..Aj‚Eú@`Ïm§Ò{˜¤ÞNPèÀ»ëj ãDLŒEY€ÈYy|°õ»¤Ñöv£±ÂnArÖa4=Dá,qZï+¨WÒV³bÃðOñn·çi!¬öugq%hQLQÙrž8[¸fw—t ¶ok:5yß@Á×ù¤¸X1€€ã¬ù4ẇˆ(®‰.ˆŠþ4†ªjîð,œõ%H;Š xôh@±A@ḇUâE¢ Óôœ ã æÃ£å†g&éy÷8v:Y:Ù¬là0ˆì°§ü–2So"†æ6²Š3òjʵ¤HÀ.לEŒS°;¶èðXìD‹~Ò±x„%éS‘>éE'õs|»Y|·7Ë'Ègþ¸`Â1bÕR+0̰åÍ‚IàÍ:I»ø~:ë‘cYh Òx„ê”ù\ƒÎŠ™×EzW™ÙÛ±µ„÷8ð[”U ª©¡›P$'É ýò")¼»5ówÃqPï x’õè«ߢgtïÜ:FQ˜ÔZô|ƒž+"wXÆ'¹cOF+?‘?·ph‚©Ø4Àžø½!â™ýu#¾ s©™³8Ì“d~˜3œ¡a~[lÛËF(¶¥‰íŸqú²Ë[0šÜ Ý.þ_5`pÌ–ܽÈP¢AŠ"#KfG†2!ã‘qþœÅ÷ T"úàeÃêÅgý›%Àã¨ëXº|S‘)¥Ï qd&ÉüÈ„p²æèÀDÖ‡ôq1ŠX¬Í‡;’è&n2ãÉ)”ëeñ% GÂÎEÔ@¥5@“ò·ÆÑ‰³a\— ÑBÿ¶dó‰ä™Ð#PÄøYÑѯs¢S¨m~l£#ƒÛAþž¿~£éDZðã )?X¿vúÛ‹Z0Àa`%É|`Qi~BÖû1+pŠp(å¿-8Õ}AFë=UD UJÍÜûy4 Ñ,¸lÝ­Fü%(”ˆ"k?îø«Ô½Ÿ8‡^ ꈯ–+¬¦Ö©T´®l¶]Ù­âs 0‹—‡§º¹/Æœe}ÙÁãS9qwHÿzÁ|U7‰ÞP±ÐË€–R¼ZÀKé¤hz¥£>ï$Ç£4 ° „«©Œ­oòý¾Ók Kë‡ýÝük¤)À-‘Ìœ+J°šHœgÉlVÄHy4+~®|C M„}‚”®ù ™•Ì`HÿS+E3Ñò¿ m(êDuÞ†_q;h›F¹f¯Fm^Ÿ¤î¥¹Gð¬¡â34Ó®_žª(-JUÖV[bxÎ’c6±)*|& 3ÎúäëWG™IÏ@?6»¢Còö÷ç°oQîçØwͯKÆÏÀR‡á†J–Ìg `q-Õ+W'Â73¥ÝËÇ`ɹ9­òO‚«¿$˜ï_îO攣ގÙ²±$ÝX¦çmg™á P÷Ë 8ÜÊÚ´¶±ÛäžÓú§d ‚Éþ›³¿PÌ×{w‘"´«y6¾½q½ï: ½1U“ÐxÁ3E-ÚùîëCÔ"eÝy+uj÷"5ÏǦù6W¯R5í¶þâ¨FÞ©ä/ÂjN4Î_²d6b@¡:å/÷££üåã4bm 2/°-a¦2îÿ˜938yÈå²üìø8•óD(ʈSI2¡³Àc>+Ê;=˜m‘žŽE¥àíÞpiŠÿ~Õ]žÂïpqî¸EQ„2ð6Þ¢ñUÕüR† êˆtmûïÜ<ðÄž¦Êþn5*ðŸåû±÷Öh•¸Àºò­ ý*mÐ#K‡W{:Ö1ûÖùºBÌÿ¥"ºƒa¯šˆb5‘¦™ ¡4°ô©Qp<S@’̧@¡ãîx ø0–ÊøšôìõAÕG¶¯ù¿LdOvžEöØ‹ŒˆvîÛrÙ![Гׄ·‰Ó67–zÖ*ÀÎ’ÙÀæÆ«ÓõdºÅsÜ‹ÅJÅÚb Tù2‹-PvÏâvÌoÍe‘ÆúVS!ÇHrkÙ#ïñujD5SýÆîÕ‹9ì åÁoÌÃs‡*=õÃ#¯ M½8Á@½D ìÀ)kp^N¸#çáÊÖsã}]Ê,Ng¶¨_ØÝD•ã½=ÌŸÊض¢H¡¡€G®F)š)Oá­Ý¢A™ªu½ƒ˜~-]çË3¾ñ›ï]Bù—é ks¼¹Á×”«Ù‘¾úfGre%Âsb}.ßµ÷÷w%É|ÆRñÙ¸«Xœ¹k¾Å‰»°ÅÈ6TÌ_éuÇ]ÓXWÔ¡4†Yb‹Y?èzЩô»8¸›HÜ Î’ù¸¡Š˜ÔQú8„c?óŽN¦ýï+íÓ[žIßú»àO)Åœ®/ó¢ZI}··%ºß%oi^Åè ÚÛÕÇw&­æKàª"—Û]}¢+8¹-˜ø–Ú`L;[_xÏ’Ù°°%à ïçŶm?ñ<¢ÚÅ—‚ëÝxž™O<5ÙÿÀ“ކÉ÷ÅÓ:Ëj„ºò÷4øNö®Ÿƒ&Ö V /ùò,tqÊ7z¹¶²äD©ý@—ç.è¿[üúUtýendstream endobj 160 0 obj 2438 endobj 164 0 obj <> stream xœå[Ko¹rœKþ»—ïrÚ$Ž‘ÀØG¬,ìæ K²<ðŒä•Æöêw$?8ÅG“Åf÷x4’ŒE>˜¢ØõbU±ê#õ󒶤þ_úÿl»øêfyy» ËËÅÏ ~¹Lÿm—:ŒÃ qÔ±åÉ›Eü’- _êvy²]üØÉž*qRwDàT¿rÎ+t÷5Ì3.95Ý릴aF+X­¨P’u7=³„[Å»‹žIâgÝÇH źõ)úaç¿äÎY«»õ5úÅU¿â ~ä¶{ÓÂ9i´Ÿ-Œð·7^,i-—Ý]üÒÓ}û>®gÒÆ/:"­ÃFA9èדÉÜú±¤T9õï“¿ƒq-¶-÷C³Ö¾‡9ÍÐy"è%»Žcgy½5u¤†PšH8ÙR¨l²eX÷Î-'eÔïf¬UÇF‚À€P¦×Kœ®F«®1UZÝ$˜ &˜°Ùé@27LVg9UDX6¨éM˹0Ü? -7ræk¦‰Ðœ-WüžÙÈø÷…Ü´îþ¬Í[|Z´ŒfŽ)l‘›2{Qf?æYÏãùÉâû…a—Ÿ ¢z±`ÂIÔR kÓËí‚IÈšå™ÍâÕ|Ý%§ê. @¤ñ@Uk/$æió¬Ì¾+³·eö}™EêŸM):¹ß‚ðÑfÿ¥$b”o7(k@¾ –ÖÜçRà…¡ÃÉï-ÚnT9Ìgiœœ<neû SõôQx¿ ¢î×TJkz¢ fŽ* µm‚ê—"ÛºÌî*ÙöDÒP²ÿ¹Ïm9òR\t£#Ÿ%1`,ôuè»×è»Ï4'ZLùž8ó½&׃Á9*QjØdÅ áÚAuÄø V˜MtM~è}9O «à„BDL$«@ŽM>mõ#!8!$$÷4Œ„@ytb òÁHÑLN !Æ«eƒ„Àzgå“!!ƪ¬x¨–…=¡Õ~(Ä›@òýXˆ·vÈã±G†ò.íß´J‰½ÚùZXU¢ Ìa‹úªœÂ(bœòñ×EãÁÞ’AìcØpã0Ó¦y ‡aDA’üuñýEFm ï8uû§$RdZîS7WKqаFÊÙ,y ÂâÉa!› Úã ¹dÀQüÌÓ–„S;ÑO¢ A#öT ˜¶ÃUL±\ ª1èwžŒÄù‰[p 8 ꎄ±ô•Z®|A2Û|D€š}ApÄ3„=‰jº{#U0î1Á‘qôű‘ãêÕEI/ÜŒ 2Pm»)³¥ý*U¬Ù [J%`CB«”òEè0sD ”V@C2±O†“h*dÒɧ¶^ipséF­äÿ1Tò Ã<áã,/ß"ÉŸöøbÏ4f{|šy€ÇCÄ¡£ÇŸM*r5öâq«¹©DÞëйüx„Žî°>+µeMM¼ØM,Á-Ûó>…ÅoË-dLbrËaæx· DѸåun;™r'En½ƒ²ovVô¡ÂéZ8,li•qŸ7™¯¡þx¨Îã¿%”Ò&°¦™ø  ØØ#ãĵ‡© ¨x¢G6>$­ÇôŒü‘{ü%-áÝð%g j={"æúòDŒ ¥é<$f $ö7$À,˜… }‰,3²0€; ´<›y´ttì wBÜ?©FÇ<&ô£^QIG¸üX™‡!\AÓQ×;F¸"Ïý¯}íð>ƒpVZ`ñ¥õȾ»ÔûY[µÕ3ŒþT D!³Ýz—vÆjyèæf`)M5u‡ßǶ_ÂÊ+¡°²fMMÇøËD¿Ž9*@`ƒ¹)«ºa§êÙu \ÌüâyÖ®`YR¨n½#ÞáÝ•æÊ渿þâáó†háY‹xä·“É gÓÛHY Ÿ=ÏURšÄ¶<ùÉ#ºjÍu m„N]é\•Ùó}·U}ð9ƒ$L£}·–Mƒ7w®Ž‘àö†?Ôh4Ýsþ_PÄâE‚³¬\JC¬ ÷qÎK•g޹s„rî)eåöuÀȨ'¸¨{œ¶®âžß?“*éßñª&‘DHÍëšD(’K’ô€Ù9Ó½ô´³ÀÂ;µ*¼Ü[ùè|j¿@c/x©H{ä,êÐwè÷—ýTdÁ: çü·å€¾Açù{tä¯C‘fÃUB8Ya¬ü_ë'Öã4l“>uŒw¯ðrüD»ÉÂð¹Ú÷Ç–æD]%LûCâï*  bꮾãÏ+ªc¡}F3œÕ¯}a…™x£áÃWÇ“dáù–¹)0«¯¢¡p»‰B aºíõÌúõ›‘ã+jc¶X²†/~’ŽÛ«šz^òó¨r‰}£Åó{àÛ¨¿qmú[¿> stream xœSMÓ0½çWøè Åñ÷T!„ćr[qè–nÛUKÙÝ®ö×3NÂŽU”CžŸß|ùÙw *(ßô_›Å—¤¶ ¨ms×à°©¦ßú¨Þô,@«Ðç£UýM3F¢JV% .«þØ\éØvDdˆ’~Ûv` 9L1êÓ÷–Ë%ËK}.ˆ¬‰zµ(¦²a=™ AïY9×UàMk½¡˜ƒ>µ_û÷ÜTæ. aé‰7³#näCÓ¿¸ÒK õ’¾/E€÷Qÿ(™=%b¼ßL½Ó«"÷9[¯‹$€ ž›ÝU‹Ók]À1§%Ê9êeÛ9nsÒŸZ>¥ Lþ%áa,IѲ¶~3ȉ¸•ÃS…§IÑ×£"dã";ÐãCÉrk]¢q:Ëù1 'ö0ÀÎë'‡Ë°½øZàV´GaW÷³y°k'GѾzfg¼í¦‰;t&xOãàÿhñO‘oÄœëÊœ‡’Åâ+x®DËYb2Ñýöa'ÜŇó¬;Â.…]»¶:üýlÞè£Àëÿóa!lu-6³þþq›.ÃngóV…ï/ê1/ Ï\ Œhø5ª.P61¸Ñ[=Ó,¸Ä¿ë›ÏüýH ¦endstream endobj 170 0 obj 468 endobj 4 0 obj <> /Contents 5 0 R >> endobj 18 0 obj <> /Contents 19 0 R >> endobj 23 0 obj <> /Contents 24 0 R >> endobj 32 0 obj <> /Contents 33 0 R >> endobj 41 0 obj <> /Contents 42 0 R >> endobj 46 0 obj <> /Contents 47 0 R >> endobj 51 0 obj <> /Contents 52 0 R >> endobj 56 0 obj <> /Contents 57 0 R >> endobj 61 0 obj <> /Contents 62 0 R >> endobj 66 0 obj <> /Contents 67 0 R >> endobj 71 0 obj <> /Contents 72 0 R >> endobj 78 0 obj <> /Contents 79 0 R >> endobj 83 0 obj <> /Contents 84 0 R >> endobj 88 0 obj <> /Contents 89 0 R >> endobj 93 0 obj <> /Contents 94 0 R >> endobj 102 0 obj <> /Contents 103 0 R >> endobj 107 0 obj <> /Contents 108 0 R >> endobj 112 0 obj <> /Contents 113 0 R >> endobj 117 0 obj <> /Contents 118 0 R >> endobj 122 0 obj <> /Contents 123 0 R >> endobj 127 0 obj <> /Contents 128 0 R >> endobj 132 0 obj <> /Contents 133 0 R >> endobj 137 0 obj <> /Contents 138 0 R >> endobj 148 0 obj <> /Contents 149 0 R >> endobj 153 0 obj <> /Contents 154 0 R >> endobj 158 0 obj <> /Contents 159 0 R >> endobj 163 0 obj <> /Contents 164 0 R >> endobj 168 0 obj <> /Contents 169 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 18 0 R 23 0 R 32 0 R 41 0 R 46 0 R 51 0 R 56 0 R 61 0 R 66 0 R 71 0 R 78 0 R 83 0 R 88 0 R 93 0 R 102 0 R 107 0 R 112 0 R 117 0 R 122 0 R 127 0 R 132 0 R 137 0 R 148 0 R 153 0 R 158 0 R 163 0 R 168 0 R ] /Count 28 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 16 0 obj <> endobj 17 0 obj <> endobj 21 0 obj <> endobj 22 0 obj <> endobj 30 0 obj <> endobj 31 0 obj <> endobj 39 0 obj <> endobj 40 0 obj <> endobj 44 0 obj <> endobj 45 0 obj <> endobj 49 0 obj <> endobj 50 0 obj <> endobj 54 0 obj <> endobj 55 0 obj <> endobj 59 0 obj <> endobj 60 0 obj <> endobj 64 0 obj <> endobj 65 0 obj <> endobj 69 0 obj <> endobj 70 0 obj <> endobj 76 0 obj <> endobj 77 0 obj <> endobj 81 0 obj <> endobj 82 0 obj <> endobj 86 0 obj <> endobj 87 0 obj <> endobj 91 0 obj <> endobj 92 0 obj <> endobj 100 0 obj <> endobj 101 0 obj <> endobj 105 0 obj <> endobj 106 0 obj <> endobj 110 0 obj <> endobj 111 0 obj <> endobj 115 0 obj <> endobj 116 0 obj <> endobj 120 0 obj <> endobj 121 0 obj <> endobj 125 0 obj <> endobj 126 0 obj <> endobj 130 0 obj <> endobj 131 0 obj <> endobj 135 0 obj <> endobj 136 0 obj <> endobj 146 0 obj <> endobj 147 0 obj <> endobj 151 0 obj <> endobj 152 0 obj <> endobj 156 0 obj <> endobj 157 0 obj <> endobj 161 0 obj <> endobj 162 0 obj <> endobj 166 0 obj <> endobj 167 0 obj <> endobj 171 0 obj <> endobj 172 0 obj <> endobj 142 0 obj <> endobj 140 0 obj <> endobj 187 0 obj <> endobj 12 0 obj <> endobj 188 0 obj <> endobj 98 0 obj <> endobj 10 0 obj <> endobj 189 0 obj <> endobj 96 0 obj <> endobj 74 0 obj <> endobj 190 0 obj <> endobj 8 0 obj <> endobj 191 0 obj <> endobj 37 0 obj <> endobj 35 0 obj <> endobj 192 0 obj <> endobj 28 0 obj <> endobj 193 0 obj <> endobj 26 0 obj <> endobj 194 0 obj <> endobj 14 0 obj <> endobj 195 0 obj <> endobj 144 0 obj <> endobj 143 0 obj <> endobj 173 0 obj <>stream xœE]HSqÆÿÇ}·£¥±À¯í")9½1C’˜óB!TZóä&Î3'—ίtúŽ%™MÝ…»•\чQQÚáU`DvчAï±mWñÀÃûÜüxÞ‡#Ú ÂqœÎéj<’>JÔN-ÌP‹4À¢j÷Þ° Ú›êç\tç`ó>lÜO4çïtJ>Ù×îUh©§ŒVTWWÙh¥ÃQMûEÙçqwQ—[ñŠ~·’ ´IòøD¥–Öx%p´¼< ÚÝþ»$·+³Ñ OñÒF±G”{Å6Z'u)´Áíiºš=mNɸ ˆ2uIm¢ÜEÑV––Ur€d#É%ºÔ/DKzÉ®„ ¨álu >á¯ÝŸIîÙ.*;¼…gÍñùûÉøÜÆË×/à+¦CïYj•­wGç7æï[­`ÑÄŸ¦î÷[ n³¨y:±Ë“‘Y++B³ËWÑ„ëæf#˜â§Æ¯ õ·4XX>£«&S~°z,<Ìÿgb8ÝÀ¨vúQïBcë7ÇÇ>à‡Â£cã0 Y‘²¼v ˜Ø=fb…Íg&'aŒ‹ŽÎÄ×Ö¯N[0éÖ |z ¤º’|b‡>hps/Ǽ8 XÎyN÷Ø€ï0,ÂsxµÈÿ1œ‡lo‡~5º Sú«á5¡8é5ØÂ±ÛÖìÞ„êœGif:¡gîk†¤qËd1j«„Ì¥)AØŠ Y„üº7 endstream endobj 141 0 obj <> endobj 174 0 obj <>stream xœcd`ab`dddsö Ž´±TH3þaú!ËÜýÏÿWý_Önæn–…?¦ }/üžÍÿ=M€•‘1¯¸©k¢s~AeQfzF‰‚F²¦‚¡¥¥¹Ž‚‘¥‚cnjQfrbž‚obIFjnb “£œŸœ™ZR© a“QRR`¥¯_^^®—˜[¬—_”n§©£PžY’¡”ZœZT–š¢à–ŸW¢à—˜›ªv˜tÎÏ-(-I-RðÍOI-ÊËÌKËÌË,©,JLÙÀÀÀÀÌ`XÀÀ°Œ±‹±ì9M†ÍŒ?:ø~žû^¾“ñÉOiæŸþßËEg/ì^´¨´»ZþÏE¶êÒî’’…ݳåù~íjÜøƒ{#ã±å¿x–3ÿØð}™hÀ¥æÉKc×ù-óìæøÍg®ð›û7ç½ß ß¹ï¾Þö]àžü1—ÉÍë wfœ(8ÛÍñïþ·ïÜß9-¾3üæ¶ÔNø-`!¿2Mtã©»Ÿ¾sus|;®e¨åè`lpå;¶¼m‚hª¥Êo Œ‚ÿ›§o®^{üø˜Óož×@—pþó_ð+d>ã՟טü}(ú;ð‡DÛ´ö ÍÝ’íÝ]-ª.µMÝmÝ­š§ô÷vO.7còüYs¦Oûã,1¥²¿{j÷œî¾ “gÿ(ÿe+1yfwow?ÇôÆiuuíÍu-r|Å‹Ú/`û-7}'×Nîsyx€˜—Ñ™ßì endstream endobj 13 0 obj <> endobj 175 0 obj <>stream xœXiXSW·>É9*¥JŒ‡$µX­CµUÛ:ԡΈ#Ì ’’0›y&A$€2 ‚€¨Õ:}NŸSÕÚ¹ZkmíôÕÞ®C7÷>w¨mïðÜ÷!ggï½Öû®w½ëð¨‘#('ð”GÅhw¨¢õ.µ€ZO-¤6P¯Q©EÔ&j1µ…zƒÚJ-¡<©¥”€O © ÔDÊQ“¨(Ê•z‘G¹QK [©‘”‚zÀ“ó:FLÑìäîTäôÃHï‘:OpÖ;wñ_ä—Ò³é>f7Ó1jĨÂQ_ŒÞ2ú˜icRÇ|ì²Ü¥óÕ ¿¹†»þöâÆŽõ{}oÜ>·Ñn¥nàäx÷ñ¹ã W {'ì˜1!c£‰s ßu 9`ÕQÐÚ`§7°Î!Ì䇠ÄÚ}0kÙzwkûdóL†)" ø;h¯]9j@•%ŽÚ÷-GÝ!†ß¶¼" Å Xcrxv2æ$e3¸OfYø-dþŒ3]Ù3Èκ]³»Ý ˆA2QÐál ïÔò{ÓJ“Q ƒûi˜šâ—Âh!ÝN‡š,¨ƒ ŠËÊJÁýú:{EýäöjõN ΠQ¨1QžI›ìtH~r:Æ@ý`ÏÙeÛ|U^¡b×ùÈ¡ia?¬q¸=ƒÅ°–a1¬š(xÅ:°T(°hè›™5‰h/3¨§Ñ^ƒiu*#ˆÐÀ=–ÃÈ{àܧïo–„–[ÖY˜¶žî'ý2ï<~Kì‰ó¿œÚG¼´ïMÿ„ê^ ›;òˆ¥ò¨X ¸Õðê´~1KâÊf%5³Ë›y˜ a–°QB,ð˜†§à—¿›AøôI‹tÎ3,”ä*„jvá¥x^œ÷v¯¸÷`&¬=xú²„`¥?10ª•wÖ9 Ì€¹B¯­‘Úýz¥)"]…6¢-íÊÓÊ3I Ÿ¾ #À¥¿'^Ö#î ©h_É8À¨æ'…›Íz”‚ 1e^öåƒàÙxÀA0 Ï„àÛ«;ÎJZêêl}§‚¬µk!Ðx–}žàw\ô ð˜(Xÿ7üÊ8ü³i”šr€dñe-4Ðk-홟`C×x‰>áÓ`ìE Œüäx€dÕwØÉ+L·]!LÎó¢âRkE¥­®¾¢1§íÛðDk#@~ON/ƒÕ¹ß?"MÜöl‚nÒ €é„ZÁ„¥0'Jð¾`І¾š^c@¾€oÔ ëmG?x/âÝ6q㞪Õh š÷z¤ŸÜ÷€nbüŒ•}’aÚ4³Ð6D›7& <د _ïò•ÿ…2ÞIæwH°áö3º BœU|ņ8U˜Ò7tWâJÄlÖWöH ƒê©À Ó£ùÌ ÃT.º³²ô4,9)Ç"‡ØÁwe½H‘,´§ÚÜ>ç ä&(¹o×ò{ÒË(x¨@‚’Íþ©„ó5vÚ³ÀT‰.3°>ÝÔÓZRš–X.®Q[RÓÑÐtT"XÐ'oõÙ'So𻲀ì¿ÿ’û ÒU¾ c($òþ[HtÐó â+ÐY’èew„V‘û;ెùáá³w}WGeÙ{ß\Lo×iÊÕU[;‰B”ÔÆ.må5þâ§Nð>ÂŒ:ÝžaBé(9/±0¢\Wƒ˜·.ÙØíûs€äFT“E2!QÑ~ë|ûÆŠUx™ƒ_^[T\Š QIvYf£éŒ¹ 1¯\þúvHÇ1ÉúC*jcZ› æØ:qm\y¼ÅŸq…¦Ûð{/ï8Á’ÁWa¹†ïùz°ß¦]Œú4¾Ooê ¾y½ëðgÝbMyœ,1)‰Tæê. ¼ÿ-MŸtMQÏŠínמ+•ž…={ÁµâËÅîõ´I|ƒØWKŸÊ¬0¡¡}±fÕä ƒí= |ƒ½´twjI §l…ô¢SÞŸŸn·žº@ !ÇßÁgxà œŸ Ѿø¤Ui$ë¿8è•…ú*tša §k­wóþECQk ¤ã”ÄÆ®·ñØxŽs3²+Ò™3Óâ×àMîoƒZMv*!KY±#/•ä–ä2vœ¯¥ë³Š²ŠRòrûgYƒÀŸtoàÃbVZÓ[RÒœ/"E6¼÷€7·÷d-¿#³<éɪxÛ’k’'wgUÄÞ»™Ö„J‘µ¬°3ŸìlÔÒG3ssj6éÝ×ã•Ú«)µi5U"Õ-s ª$ËJš È2–nÈ(Ho܃£Ýí|Wþ…¯8×óa>;ÃÚQRÒP(ú#À7Q=Wã©0•uU˜œœ•••“DIi¥V TÑ_¬<ƒ]±óʽ«‚ìšî®&{[Uz¥É*N)Ë"ñ3µMÖv±àÉ…µ§d5Ç„í ÕDÅÊ Xžý~WÏ·:+.ß[§?‰zP³µ³›üŒ½Ï ÕŠ8“1Q±]Žû¤–BÍ€"€ ”9„š\s¡Ñ‚/°®îà{Ž8¾/!8ÓÐÕ9U9…9G²+SQJÌHU(pVºCn[¬w ɪÇ\½|^o¤ë¬áêík½¦4±d`/Ýè€M8®F[˜‚Ì"dLʈàú*JÊ! †f!»Œ@o&¦¤•cлÿ n5õ¶òæB‘¿­¥KsJròQ}NI:2 Cr‚*2=Ý 7ĦgeD‹î!àTTµþÁŽ†Ì¢ [ŒÇûÜ•¡êˆ”¤ïÄ%„f©Yn§Ãò’Š‘YŽuÏË/.).­QVkŠRê#ÊôÈ„LIé neúPù´°3Þ‡õv®a ×îˆÔà=Zþñ”Ò"cƒÏ*èÏ:’x ‰@ôäÌñЧØé@hBH¨dÇÈ꛵1—›76 ŽÚ´9PÝ|â¹.P·_9 S¸AœPÉX§ Ý¿1žœæ² Ý«ª|X@Òý/ }#óàŠ-(D{æÕ)ŒÀKÃÞã2‰ϳl<+Œqbuÿ­¦²SR4ëñ.÷5°5î=„P‰UU”:ò¬¹î«§|Ô7»:ÔÓ ÷ø= û¢#uH”’ZRš‹òró%¹…¹…¨€9¤µÉCÕÑ2ß®ˆóm‡*jkŵõ¶®öoa6û’{õ±²²æ|RÜ^I×Yg^rbO„˜Å»”_Z®œ¶ZRÍE „s÷Ôu æXƒýèå 'á)³^ÂÓ±øk{ÿ\óÝ ×ß׳ö×clÌwx“ý7á`v&™†ÄæK½¼,¹ÝfXO}`±ÜáèYHcŸAöaθHÿ“n5rTUÑh›Ñ´œ£ªÑA/+Ò× » ACáàcçF>û¹˜zvô9<)^<N|c¯t^.¾\B„Ó7ßXI\ûŽÔÒ×3JÓH[Æ›é€ãQV_âDF,“âA+žò¡Ç¯gZ­§úÅYÿ¢ôä“1Nk&-cæ aãÅ ­ÍZYg âmÙ\œnÏzÙ5½œX'pt Óðm¨Úpb3¨¤ÑCA]qzEi| óL¢×ìÎÁnç&ö¯¬¼VFÂÔÐwÓjbÑZ_ 9D>€m°¸™Ç΄qÂàýŠø‚”cÛã[R[3o2PÈOÿÂÐÙqÔ×¶‹1n‰ž‰g~6Æ~z²åÉ5 ¶ÀJa>¸ì:‰šPMb‰¢PŸ§ÊÛËh¢,úÖ÷V^;¬ƒßˆ­c§¬bqd8G6‹øò—d4ðl˜ƒ}%9Bq¿@–nÅ#0é­Äïùâ 00JBZ 1S{á@3ï6é‚·Ù×…øÌ&ß R¦Ívz]ž©]càúÃÞÅàJŸµÔµ,O3ef¥¥'‹S ™f”Ì(´mGÇE¯î%¼µþjqLxZzÙáÏöƒíÄOüD Ù›œ«Þ 8'„ɹmù½?mî3ÀÌý'qËø_Ч¡o¥W%¢uÌ áÛVcœOzJÎ6sÓÂCývYJ)ºÃö²I#ë«+Z?oõÇÔ“,Z'ÖÆE¤­E\÷ÃA-¤-¸3 u™«š˜ƒ{„‚;æ÷"|wE{,ò­ï­-.oª—æPØalÞ<}˜Ïø#O¾­éèçº^Û?ÐôØJÈï ¼M_“l‰ïø´¡ÝÖsüÒµÖè{Bˆ¶ÏÆAÄŸ¿‚çâÍK.lwû¶Ä}¨ªõ¯Ð×ëÎíü"ò+t õ”Ükdð]By»n sƒñ„@èUÒ¾ø’ç“'Ð7‡ Ž„p³‹èâTÄ ÿŸƒX]=™Âó¿aaùF 7†åa{Bã¶{‹‡Üû°õ>áÊfX9QpÀß!ŒGÆ¢äJì îk•MŽCÖ.ÔÇíqo~Nš»U´fˆ4«““—seüøO邽4ã:C _°wp3û½Xò‹ŠÊ¬…Ä|Ó0m³!$2V O[™CìaóɾóÝþóÉÄCtê „pÚO2Ð˵¨ a§–êk&a¥ÚéðBSê$þ`û›0 <<Я5¢··­µ·GÞÀ¥”Tñ¤f½Í­—¤:ÆL,eõ¬JQQtZtjj¹Ìù1%d„µúDÉ&ã)KW`·E§·À˜’ë‘_Ú£‘·èÝý~+¶ïsœ”‰ÕI­ ·%F‡øEŽ’R+*AåYui§:Òzˆ÷öøéKp»ç{os‹ϺófWŒ]>Öy£ï˜>ôˆ¸-ܪ´mb†°`ì¼[d¶Œ 1BÇ/Ï.ËÉϱg•§O‘’o6ãñx¼;»oÓÒmYU H6Ä€C|DÖpü>ÅE¨Ÿ>]oÿOªÖ›Pªˆkûª²Âh§Í(#?¥ïZ÷Ÿ~m8x¥„áÆÕ“<ïX÷zœØ+Bt±Êz•ÓYµ†þ(݇Vs:‹=v¾‹ÇÌð»SÄðF]Eu–ªŠ2kÁ‰¼aÃt+­ÂH(€É³Ùµû/ùŸ¿ÝE·êz:ú:>DO†qåb½Î:ˆ#ô-¿=ËB‡Â Mˆ“ÿNDnb êb†zÇÀ4âx·sËåþí KŠD‰Y¦„,ÀŸ¹cr}_f²ˆP½¥¼3¯UæZrÉ­ièSi¥©Ž]P:øÔ½‘zö ûåªÊÓÅÄIß}þ²ãöp… '!øùÀœ^nzÎ2óð<—ƱÌ̱Œ«³"kMe#øÔZ[]\=ù¨E·m¨Ò‚ÌÆÐ¬á„‡¢^\È-®«&+ÓiÑz - 4›dÜÒ;’g®àÆ•áw#;dºÍÞâ¨+áö-h òU˶1®¿ 6v£ öØx¿Ë¹\lÒðßϲ$#2f2’ðHüÔSð(¹%Ó‚JEÈn)íÍÏ#£Y—‹kº;Û–R®mŸãŽGãKØ…¥2¬Ù%ÜÊC¥§9Ö¯¡¥—¥Tï‡ðÁÝ›øàÉFÃÊÁhbÀ‡ý±²«¾¶5ŸóÿDìVàóà Lqb5&Ôò•kLé3HoÃéǼïµ_GŠ~[ñ!¦öˆ ‡„ÄÉU«˜&~ó厦÷Q/jÕ×…UÄ–¨Èøâ¯ß¹ïòð@Ò•0:v1gû’N±7>uƒ#0sð' ¾¼ÀÚ„—ZëO#æÊÑà-ÃÃçFIèÏ•­ GäŒàf³ò ßIkd¾Û´úò–qÈÑ(kbdj­|÷¥°oà-Ò„ø0ó¸¡GÙ.‰µ'Td_^=Zsè⤞ïÍðXµoú.ñs‡±àÄ.´¹ fà—á剂Ÿ‰Ù+²ëÞ¢½VR|kãümsEØrñtšHÓ ˜~œø‚'8h–ñA§œÉN©—Ø4ðØQ9y\Íü8Ñ|ÿð]Zwzo’è‘êÌ´‘Ùéï¿S§®8,h‰ªŒFLX´&|Ï…ðG° Fý/=Ûü æ5Kd劂e…\›56Âô²ñš`aï\'6b`­‹ô[cVzã‘+¼ðH„E ~çrM)Œ}ü#LV„ù‘1™Æ(qÌúüS˜y˜ÁÉø¨ðzý.¼6Eí×Å—Õ$KZ“ï 'èrÑùâÂ’¢ª‚ª¡ÖNf+Lå±ÑÜ{‰Å|¬qvðk®ÚkϽ æWzÄù;>w¹–ñ‡Üšà<„$ qŠûÞ…ûÂz1Z£TdÈŸ™ñåzb%O>!Ž!à²ô†Xpçi[W÷ÕIVžÃcðä×ÖM[ÕµëG/1¸ü \Òã{cúé?×â·Ü'UáOæÇÙ˜oµ=½ÃlˆÚívæÑÙò残šF >øF^Ì%yZàÔl‚ÔvÃñÑÅÝÿ7Þ™ÎXŠÇ-ôÀSñè;o¯³¡°ªQ€ç¾ù6ž®d nsŒ9YÙiúLb¶ê.ÂB`*ŸtT›u勪lZ‹ÔÙ‘Y~âΙ;ŸÃÏåO—‰B‰Ï{Æo(I–à³4 6Þ÷å<Ò—»v ½ Å/+1/ñѯí–ÆÊºúr2j ~=l7rr²r²$æl321‘ 1ŽöóëÛñˆÕ;ãÃBÅÁÁŸì׈ý;‰eH6û¤“í3Éö…æJt”{õ+áõB¦tA—gb2ù®o´ñÈÀ¿@è+ ñ;p$´ÿDgÇñ~ÙQî娱i@ÔijÃk„D¯9A>¡Ú&´Ñ?hM䮘WÑR}Kl°„OÉ è >¯,ß "¯Ôå)j³21I‡˜}ú‹à!xæÔ\8qҶϪ8;ÛÐðˆï Np‹ãq!æ9GÓ¥ù%ùùyeŽâJÄ8Êc|$ƒB-‹Kð =c§ä%Xˆ…e¯Ó®q66ÐÞ6~ëhŽimuqi½Ë õŸX¨Îó endstream endobj 99 0 obj <> endobj 176 0 obj <>stream xœViTY®ª(•Ön2rpR¥¶=à6££Ý.m+‚ -²)îÎ1@uILÂ*‹„°È [Dˆ»+3ŠM;î¨ãÒ¸uÛ.Gm¦>}ÔÑQû•óžKÍŒITóÎõ1bDî%þ^Zˆ¢_}íMŠJ_øJ¯wËp߃~¯G þw˜Øû~Ÿ£*ÉÉ Nˆ_2vì¸0­.Wϧ¦Ù‰Ó¦McsÙþ6œ3ð©ö#ü‘Å©µº NcœÀ.â8֘Ʊ)¼šcÃbb—ÏŽdƒ#£ØHNÃéUj66g–ÄFñIœÆÀ…°)Z=«îûa“´šdÞÈk5† l¨U±—ÄãC\N§snŒcuœ>ƒ7ð7ËØT½Jcä’Y£–å5IêÌdgx¼ž¢ÕY^‹÷3ðv«5 Iz^gdqÄØðˆ¾i*£3®ÇÛ¬6[&k“2hÞîU¼ÆÀ¹£3N"Ç&óZ•‹ãbW:=ïJ!ÓÀkRÝÑDZz.U¥OVs—_'+n|¬j•N§ÎuÕº¬ÞÆçN2¡¯²,.­–bÅU«ô¬³Àk3¹w6ÝÕúmõ#B®Ñêôcf–*'17)™KIåÓÕCÄqÄ"b1‘@,!–sˆåD±‚'æD$1øœˆ"Ñ„?–áM؈{’•’‹^MR…´\úwº÷RMÞ£8ê¦ÏŸ¯èÙtå ïA¡ƒžŠ‡Š{à'Võ@»8E>›Ê.2­[o-kföSᨗ\=°°™”£¡~SCiMóê$¬#eí·o·m¾ÔZJÙ›òíÙ ƒŠ æ¥òn_PBµ6öåñQ«Åf©õН—ù4:)gA˜§³N%‚J;D¿©$†Éчh8òG̨oC¡Ž†Ã¡2¯fÝ@^Œ)QþèBìdD!éH4dRÔ¹‡„ÒWp0ƒ=¡hþQ€ß¹ÀuuÈ~„Ü8wB')äõ¦…LðH1£³7ãs™1Ô X³wþ±Œ[@Y(ƒïA# |6÷2"f-ÒEÌSÊ–Á¸Sò‡çÏ1*QÇœ¾óòÅH2´ª?Ù øŽŠh*·°¸ È䦋ӱTŽsÁV¶‰=NbšJjóI} ×’2ö 8´­3h+Ø\Üj¨3WUÖº©±±ÁfjÈob²7çYׂ¥`‰yLãxyz‡èsˆwøÁøNXq4@½vÉ++*Í`]b3Ûšlö–eõFR¶/eWî¥ 8ìùµûÛ vg73k·¤mŒ±~f ÛžeÏs€fúÈÕC·ö·›ŠZ•õf‡”W¨kKjËë@#¨¯¯iiê ´ÔZjA5-n3[M…Åë³Ê•²}{4ªMqAH2964³ÑÐRÈlÏÛe¾^rªt‡y[AKQSÈ£ãf®;#nÏájKŪ´TjPE;˜L¹å}*€/èçÀiR¸¸©² ¨V`ÃâB—¡ !g»¥y“ŠG[7W–€bEüqîö‰››¶t)¿hÜh t½ÙZRVQYV¬\cœ•·ÐqÜÎC5«ÅÊœêÜÚtÅ×;ëçGã¦Ð‰ ü¿q«ž§îˆ¾$ò¡²Ü£&ÜÑó5‘Ak2Ú:l ¦ªŽ¹O‘ïÈ ›U¬¯\Ö+b:¸ËW®ì:uY9  ü§?¬Äžw(äqº‡BZ,›êSØK^°ÉeÐX·@õ¾ñ%ò` à÷c|ÅŸ/~âÿ“Ûs…Q»úÏ®è^$LçÆçä§*‹ÊJ1y´Éj¶UW[,µÊ[Û~@_êÐ%–Ì“¾¨9£v5P %:†‘YK¾wõÒÎ37píë;åÓ¢â s¶³³çûãЧ³³Ôtàm?HÄ!P+‡+àD²Î¥—A…¦!ÿП<êa uX)è&"=Š M}«p—/’‚±0C!ä‹@·E15@ÑsEâQLÐ2¸C|†§CN‘)¿°®|³s:Hß´öM‡|WãþDµö’²Îˆ½ÇÓ®á‘pÄó°k†F¥‡ÎÀø òûçc¦"D°Ègzôù»Ðç$ Õ?Ža´è‹GÂlxÀ_¶e4M"? 2ÌeP¢Tl!]l4×®ë#©€àÌÖ¡ûãá}ò¸‡»šóQ*¤áyðF×#v‰¾ò´öe­ Æ @€B‚¿™ûò뎃ÝÌ>Çn›Én.3—›K”±«æ¬ èÉ ÏÜírœÜ×Átœ¼´ç*8ŽmÉk0íÈe©µ¦Ú +v<Zâv®q„?õ÷rN…ò]¢ŸãL:_vñœíæy9ZÏ_[¾o6Vƒ'} |3ôÉ·Wö_û#ËÉ™Dbâû´°ŽYL¡Ñð¹üAwìTFö¢G°ˆþ$¶ûôùù¤úgÇŽ$/Q~H*vÕòÚÕÛAýtæU$AÁûP¤DÒ;³ }ñFKÛIe[s}=Æo/µšÊÊ7”™”+Ó?ËŒô˜È ?Ô›¥ž± òö=8ûÐ.ÆOßÊp3 È~„»÷æÆyˆ6Žj<_ÿ÷û/Å6 Tl«8Z @â ›¬eu¦*&½n¾5L³–äÏ)Œü³[i½®+"Ľ¯ö‰Ãìô<—úÅõöózK´Áïåê+«ÚÂp…Ðè}äÒñøFwÛ•sŒÉ "ç/ŽÄÓšó¡¾Bóá¿¶ïmÝchYðC1˜<éF1Ž’-Ca:ùÝÓ 3Œ¼f,>uç忟þüý^HÄñÎ+Î#8ØMq¶S¨ÿ¹`­b“)8T¼#¥2G‘ÝÔ;<à<ù©¿Á‘¸Áµð¯ÃÏ4® aù¥ëâltÍA¹Ûz+…Ö ?܇8oÙ_ëgWd±¦,J4ÉÅL‘Óê3ÿž§³Îƒ«1 ¡¿|<½}mx¹ú•wN`|iœ=Ö¾ý@ÐîmkÓ±¤*K™y¨‡¼éyÛœ†Žcgn?m[õé}c8]ÇF¯©nx Êf±«âXÚÁª”¬å+”E]ºmk€ ð9kVÑC³â¾RJ|xˆàëKÿåáß endstream endobj 11 0 obj <> endobj 177 0 obj <>stream xœWgxSg–¾Â±|C(kK!ÑU &¤œL `j(Æ`0`â*ËÂ’,«XÕêý“dI–›,[r¯ƒ1ئb)Z †Ì $$$›ÉnÊ&û]róìî'ØLfŸý3Ï>ús¥óÝsÎwÞsÞóІ=0 £Ñh)›¢…,³L´©,mÃs™<¾b<_7-$çÐÈǦ‘'Pó(ÞÝmwßL$9300#Ìx òXòŠä»ëÿpwölòû‡ï;óÒh'§aó³2w<»pᢕeµTÀ/‘s—¤¥¥q ÔÜÿ±pÓy2_Ì‹*xÂ2‰ˆ'–§r·òx\y [,ò¸+7gì\·i wþšMYÜ5<1Oš/äf( „‚BîA!O,ã=Ë-.“r…÷¿p ËÄE¹ L,Kå.—qó¹2 ¯P€^â© y’¸aW“Š2zæ d\¾4_,çqåe\¸P¨(ЇG¿—‰å\‰´ ÙEÈ‚\e”Éä²B©@"碈é«ïç(/É—ÇãÊÈÌ-+F'‹Ê ñÛüÝ&ψe\9O%Ç)àq‹2‰0_â"W©à^ ™@Ìÿ=ú"®”ÇÏ— y²{~ãUùý~ܸu¾D"Tß{·ìÞ©¿ÇÈe…ñ½å8@‡K ö8}Ð0 h!”­Êº-—#Ýu}]8ãë®¶ð¾¡G¿I§˜ªVKÿÀ¶€2 sH­2jö¯›Xê-VÛVθÝðóvD-‡4í¹ §°åë¨YùŠpg]ÝÑÖO9õ#Öú8ãý«0 ´ÔüÓAÁòLYnžŒ(w[—Í#Sc´Î›°ãf¼NndR ¨ùÔbjÉËYwÎÔsú qþÜÐ-pÿë–ÓK9† æá®âÌ{çå®'Š·ó¤yÏîw²ûö±ËÄÁ‰ážÃ÷Ý6“K¢ªhò[0z¿LaLMó™­t¼Àã©t¨€˜ùÔ“%K2O¶È*6îÑòܸ懓r4¡XCäzt’h×79ZçÂTø'øâõܱôõ»K_ÝBtç àö¸½l—'‘±Ìã€Ã˜r¹©TE"lDYºšËÁ°NürÞÆüŒÝÂ/S†[öí‡)Ý“Dç…3ÇŽ;ã‰R¼&¸ö$9³…ÖþÙAhK çP[™z1¨¨ñ4Ÿ§ÊS—‘“¬t]ŽvTÙXØ‹z™Ú@-J?øÎ‡—G!ã âjT_¨µ¬·³µÐ–´ê.Îìï–­¡ž\“I”+$ÀŠž±Éq˜pî<ÑÛÛßÔ¯DÉg¢48ó‘qøFäðèµþ‰®Þ¿ÃoªÖTÛ£ïŽ4õöªber‰NRNäfh +ù¬“Päô®Ð›]+ lMR›ß‚o©7©+Ю!^¥F‘}T#µ"$ í“Ñk) Ï~8—©¤^Ll¤W7ü5 49¢vœ!´¶Dœsnž=uí@Å‘œ3oLÛ :ñþXl`¼§øõN#ñX‰•tK¥ÅQ ¬ÀäUùÑ›ÕRW;gÅî¼µ¥±¼á,â`v¤ˆñwD¥¹Û„‡¾ÑrfÁ^ã1øK #ßb¾FïúÑãw;èôÙmN·ÅÆ)ÊÞ¸³Ô,µXmÀÜÀåqâ“ô•T—Ëìv:Íl³ÈR,¸"¦ííïúúCŽ>¸kƒV«l³­ºÎƒšÊG܆Ÿ%ޤǛïdöɈ&OÁ7®§0²É|D;Öë*µ·:µš¯7àxârz&µØªÝ³gŒ,Ú¼+»tŽÁ\®^äîü/§7SÏ·fZÙ•I€×²\~3º·Å.5 Š©'Yêt«¨q~“jàòÉS0 &qÄ\íjP3²EMêž±Öè·d¨£ä+QùЉ„9Yë©Fu?j4 ‹kV×Q Á¼Ìk ¶ý3|òÎÀñŽs`ï+ï)tú mÀФåÔü`{ÖQ éË…¥,+|Â6®ŸTþ¼f`V·Å©¶o3ñlø¬»‹Q¸¥ññ#¿=šÂlD³¥{€ø=]ÞSW—Y÷„ïOU/²¼E^KåißÑ_è9ù|žÕM]ñJ¼Î `WƒššÎ~§Ê_å‡ÁW®ÁdðÞ£hÉPfz¿¥QÇiÔLÀòYT õJ)ü‰ÅȨuùlv§ËbçPÛ¨ÓÎKÆŸŒ—ØŒç?0ºª sÄN½Ãè9öÚÀä2¸îQƱÝQrq49rf’Â!‹à4¦Ng³[-"¡Þd¸ÅF‡á›cð-ÂWãñzCVk“Õé4î¶ Šcy÷F3•z›zaÕ‚.î‡ß&”¯;õ@†ð׺†Ú›ïLrõÔkð sŸ«EJÀ+;©é«—Ë$à%òîÑS~<ü1tf¬o(NÆ3ðΜ ˜³Ýf6ÈE“1N`õÖ.Wµ;àöI¿7Ž‚ð‘©ýGšÐðV;kìuÖ «ెp,¦i”åòøÜÕ„ñÝÊ Æ¨°@`Þ¤4:XUº¡7Øí `nØÜMÍf¥Q*EJµ°mÀä3ôA£Ïp•N§,j{zëBï':ûbÃgz¨¬úìzm8:ÚÎ|y¯Œ­äÃÇh$ëVœv7…ùž+à `vl —ÕeuÛ]Võد®DG’|J4ÂWáJ¸äÒ®ãkWí( ¦¥sÆaf°s¾i÷³všù¶µQ1æáνÛ×es36ż¼ ÄûB]mëÙžo‡'‰#S#ŽÆù,ƒLC]þÈPB˜|šYï zjÁi–ŽRoEÖ·Q ÐÚ´:µ°@Yn4T( =°«ÏX­«3úWh4 i‹ªû#˜À]#.ÀÙ]WÀ1¼GÚ.’ku ]µ1ªç4ƒfP ‹__J=¡„8Kw{+LÓÜAw»ÀÌn¾m»ÉîÖ;+]÷0ÜO1ZÏgÐt%þø3s[F‘bÀ3‡/ßøbâ=¢»g¨ ­ÈÖ°Å`wÙfªwY—…+Û:cÑŽÑw6£ÕÈ¥æRÏ/:µíëÉw{¯E“íD {”üc49ü9܈øUE¢>ÉÚh4kn²„júà¬~8èü*öé ˜ƒŠºÝÈ·½mÃ5IﺂNPôn[ev.µŒeV7œz„ZJeP Ò‡òÎ]8Ò÷ݘíÒ9Ψ—DÔíÃã‡>ç0²©eð³ÇV>N-zŠzk±@Öyð¯Ç!~þ§«{°¹ëþÍGÈ?¶Òz?‡ªó ðHc®Ìjz™ÅûO…½áê>¢« .ˆý| |Æu줞¦–PiÔ‚WOm½~yêÐÇïq(1\ÌÜ÷ÅäÔ‰x?kåB_("ŠKxê—+pDíTaŒ¼ÑU-4jÞÇäš‹ $—ù%ýÅ_—)¸{·å¶Á ù<>ŸŸ¨ xª@©#Zµ\0 9òÅ¥ƒq®ÀO!èK¤0z7\2xyàõ!»Õév9í„ÃìB‡Ðж´67µ wíÚU¤ÈásDEÊ=¢…øâ8ïS-ðv n?ØÐ’\ýnÿu¸ðìÞ)Œÿ$…ä³ÌMt'°{-IÓÞ*rÂ’7žÛÑ":]Ndª•@ôÕ†æÊùëÑó|õùw¾‡É7ßûüÃÝGµsrkËA·vÔ{½ÀíãT»Ü!€3~¸ó/¾ÞèŒÚ¢Äq:ãjŠºÃô MáuûÜ5ÎFûÏ{Ë»¬-…o¼–sࣶ@¤±™SîGÔÀøÎ'ç0›e9%†\•€@ÿ‚€ó]c_þôñ›¤¥ÁC7|×HÚbYk F´ÅÌÖP¨¦ñÃÈ8ÑýMó$hÇ$EëÓ×QϤrà r›Ý¿E_dÝleë:’ÐrDë±!ЦAS‰6—ØívVÚÔœ_CHØÁÔú}Cï߸ðÁ¶õ½‘³–ØFêwY…ÀÀ.h;O t~~öÞ#™Q˜¥»š¥Î”•Z-&£F£»ÇЖê`/l&€ÏôVãÞ$÷6¼êôÎU)EN$­O¢Â imqjmÊÝÔ*–v‹M”xq£²gt°çæiNy”)QÊE¥ÍÝ­­]Ýò oäqùê_]¹Zh0ê»TôýÍ^¯Û¯9çÁ”Þè˜o¤ÞÖ6&µ7XÊ»RÓ‰J£Á¤1 šìaÙýzž5Ê’8êi¶zT.U¢ßã¨t]—™E1~U&^¥…k‡ûs;65ñÙ}šTÝ`t´ÜŒ·º1JšJ€*8›i6¢¡4_¥S£A·kƒ¾:”ðø<~.$¯ý¯xQ«Öé¶9Íõø¯í(ž ØÙüˆ²o| ûoç8³î#¤ÿ}œÿc .½‘pw‚Úd°˜œŽ2~…ñšÅ¨ñW5Ú!"IêEk†½_Ú)P)­:gWiÁîÕÔ´ökáÓ · 6­B½°˜sòœÞ­† Ç2[ÛžÔQoÕåÏó)!OsšôÙxüº/†÷OÜ:;±M\Ь5—R37Pz"ÞÏGŸ¸|ìÂñ#H®Ö†LF»Ë`W[(¹Ø¡&¶¨AÛ1v¤ò$ÇDzó$ùR#Aû¹–ystô4˜À‡¤½{Å ¥D06k8 •3’1årÉŽŠ5ÿÄ©p{ìn*ó•œ=[ÁV¼°]ØßÝíŒØjUŽ®Áޤ èlíÀaNäŸ8'ŒÂ6¸rR}†(”µ$SO|L&^=t)…ñd!³és‹¨í"êYC®F¨.U둌×Ù«~#³ "3ÿod¦à÷—|yñ_áÆáûd¶˜^ëwZ­2È£žS½Ž\ܬìÚß55Áцì6»Íåd—í-¯0Üf÷ûƒýðáSD°ÃðV±ª=A‘b÷¡¥æ6¹ÙŒÅ‰Ä%™C- ÷üŠö¶è‰B¼\o3!¦t8›Õ…t.hÚÚ#‘öCyÝ›·n“•pòs”Âgð—èÖÒÄYЇZù/ß1EÒr¡¸]ÚÕ×ÑÖÓ[Þ!"f)£dzî‰Ò[§_¨54c†ý7'•¹§ endstream endobj 97 0 obj <> endobj 178 0 obj <>stream xœ’]LEÇwùXNDZÁ«&àÝ´U>©–ôøÒR„`€­Å´¤ÇÝ[Ýåv¹‡ÅÛãøè„¶rw”ê ±D¨Ò´Ñ4Úª1FÒàG“ú"±©æ’E4™mÖgïʫٗÙùÏüÿÿ™ßDRA’äΦ¥¡M¨æØªK9‡]Ÿ{AÍ"ÕìõÙD¯fU¿˜˜¬šÒ˜–Ó’¦²“›3ÔߟTïíPWvÆMI™œ#£yǾ‘_PPx˜ã;œLS³^.)) `Ce´À4± \´ƒã[hV,ÇhˆÍ4hd48|Är¢²¦äUÔ4K;­`ikp06PÅØhV óA#çŽø°q¬ŽŠÀ!XÀÓ6o¢Ûm4¯ …€§-Œ à1`Ðä´²"m"Öæh³ëñx¾‘cEÀ;9¬·`[Y8AlN†N´”•Ç;ŠÍVQÏ,®¯´s¶6ý4›šheXˆt»¨ç4ÐÀμÃÚs±ïdbÚ†mÚJ/NºÉê´;h!æ«ßÊÖùÀ#§¶ò¼£#¶—‹­ÚÌgDv4Å©Œ•U@»mf ÑÿƒFÅñNÍ„…¨%ŽeD9ñ‘‚Ÿ‘DTÉpB^‚”HªÑt5êVЄB^Sв’ˆ¾PëŒZž–œ£=£íBÔsHÿ¨u´ 5ê/m·ù\®ñ×eË¡ƒåµû÷—ßúñ§å/ï›±‰fUPŠ‚zò-a£Òۮ»oC2¯ßG;qoTÕV·õ¤ù†fH 4 õvIR'ì„nWÈì D aìŸ7…Õs’ïU z‡ÞÅf¥Ô ú/†ƒ18ÇzÂgƒž!Ï`+4ÔR(_­7.Â9)rfÖ5ÑO^­>q ¸féΕ‰…ÙISDž \ƒÜ*È£+?£S¹ÊÓ™Ëè}µÒ¨eQ°½ß§×rǃG¤@<8óë4O-Õ {¶ŠuùÝ!)Ð3Ú?‹-Q“0|~èB8׋ycżƒ4hf”Ke¾t÷«—¯dÂ`oø,{G¡á½éé™K=!׸Ùy3Ô OBºó•#†M*7•DuA}Êøq—|Úõ¶äò™ìr}ð(¶,Ø“«í­™¬ûÈfþ´þW®ÀoC‹òâätF 3â4ß-õy%S««·¯Ý'xzmx×c–Å_®ŽÎG"æ™Ù…ÑÏàu8/EÜ#Ò;Ò…Ž‹mƒ>¿Ïï ûFà ûƒ#CÁ 6‹µY]ýM!ÕOÐAã‡)Ȥ%¬iùZŽ–ô<~'Ùˆ,@ûîüyI¾aº<<< eÃŒkŠ—ºú»Mûgß)X ëfį çWá›·çWpÙ[Íc¯o‚¸÷wbQêLâZ×I”ã›ön{:‰Nâ®æÑÒ·ƒˆÉþ QJŽƒØx!ÿàñùө̵·TÙ¨¥îA©TúY­”Q†,ËÔZêÚãüiikiOÄ¿4Ama endstream endobj 75 0 obj <> endobj 179 0 obj <>stream xœ”mL[×ǯcp-!Í4kEëîõš-M%’&­´(kµ¤eYB#B $ ØØ&Æ/ü‚1~·ñcƒßÁÄæ%¼’2B a-!K“}è6©“JÖˆj6¤ê8½š6é´jÙ—}º÷~¸çùŸÿHÙF0ŒíÙ¹¹9l¼îIü‘xe[âGÌjšûôñÓ÷S! é)¯l_ÿÖïÂÂøâKD ƒ!©3[³%Rµ\X%PpöV¼Î9xøð¡,Λæ¼+æÉ…ÜN.W!à‰¹ŠäÇeÎYI…§Psö¾#P(¤?ã ¥R¹Ÿ+®Ý/‘Wýâõ,ŽR¨pòyµX% ª4¨„y`C?¸üNgÌCFFoŠt¢z»^wŽÊH]8ˆ³6ƒ'~öˆ9Mg°MÂfs) &=¨Ln›—êƒþ–8\ƒaû̦ïv‡ 0@c´ù‚SøP[/ª‰ ~#¼ ïÁ¯­å¯)ºPU)¢ óe]üÍ4FµZ¨; öÿN ›x »Þ¦¡–õì¶¾¾xgûÈ`[Ðd /ODoWó(á± "> Ýç¸Z‚TþG²îÌAœe,ÂÄ剟°×XQÿf65õk¿êŽ‚Ešü»Ä!0’•ô[©ûXJ¨š¼¥VYÙô˜MjT0ΛÿûâFÓ&°v½•,Û]X+To]ëqµµF’M=x†—X,1ñú;P>"»#ÐÝÝ1 ý\h•ÃÖh'ϪŽs·ˆozG¨8&ZÚýã­í™înÿx2Xg£Ok•9„&Rõ^)ÝTè°U¼ý s†é±…ø¯> ‚î0\AÓÕcEY|ú§Æ­ê\n²÷‹ñ±q@¾Gu­¾Æ¢¥ø{ës¡e=ϯŒNÇ"dFBõ,/cÙ¿„°ÆLˆ=/ëPËkeu:·% '»”!T!î)ZM§WŒÌ-ôý~ðs²-òï¹ûxß™Ûÿ·ëÝý€¼¨–¤f5UJ#] ”!šXm˜Áï,â?Ï%GÿN =ÆÜÇ¡£ÿ ¾ÿ„‰¿ÀwØž«ÝO>7ô@—NbUÊ¡5T}±¡Îɹ¼°ðŒ PÑDÊW ü5€vçÓlùÆ\o‹Ë碆¿^éteÄxæ²æŸæP ¹n)X@õ¡^w¬ ⨯ΫËeÕ%³Ê+£ŸzZÈ ´r Ðú"f÷n 16ÛÌÍ”ˆs¼¶PeÃÐpòn$Ùêhnljuê?'4‰]ìÖ0¸ m“”¡Á¢5’ÍVÓe›Ú,3K-ÒKô‰—mÚ²c9€ÎëoŒÍD>^¥:~Ý€èóõ­M [̬¢*~¬Î‡‹èÍOÄw'†#ñ8©;×T\Q(òjJ .µh&: á ßç%q J[ QCqÑ þÇëë_âÔx²éÈ&dÌŸbL>d&BO°à×i³ŒÍÒä°XA‡š|Ð~Ëe6våiÚ «é»™týý½gßœ?Dg[.Q^îˆèÖÿ:Íš÷Õy値ö«Ñ­ëp2̶ª¡Y«)ý ¼A¨¨ñÎÄìÞÙ=C Þ›t/|Øbi¶8¬TÆ7Û¶ŽâÓÿ¯åf‰ILIh¤Q)RÊ%Ò¨ìZ0Ôêó“-.§Ó Èét4¾+8-Qz=4'—5»mîÀŸ>ÃiIÄ÷õ]øŸ1ð㙸3‘ÆÆ©,½×X'@ R¯Ð_°´'¥ÞÕõ€Ë¦jÁ=ˆÃí¶ùÀÂ,Vh†{êAEÑ«¬:àAéEA¥¬$Ù¿ŒA¯«Õë!?üì#ˆZdUÓ; èÜ&m2‡©:L¡o|,H†gÀšEu±Dv Ââ‹æ¶¾ðèEò…”CÑô´^Oz:Aü _ûÛw endstream endobj 9 0 obj <> endobj 180 0 obj <>stream xœxiXS×ÚöŽìÝÖ¡%¦±IjµÚ:k­¨µN8UT&Ed3I BÈ Ób S0È<Ïâ<áÐjµÎÚö¨çtnO[Ûž®ÍYœïûÖ†öô\ïéŸ÷».~„½ö^ëîç¹ïg±‡i‹ÅâîIK–{IöK×»/óG%3ߤ]Xô¼iô+ìHòO×q7GZ0ÓÙ`ºCݼùœÆ?xi|õ,ú³§ö)`µ°FYã‹zù½±dÉÒíRYZRLT´B´jýúõ¢°4Ño+"7±<&J"zÿHÇKe b‰b¹È[,)¢Å¢È˜x±hû€=ûw‰ïÚP´K,'…Æ‹<’ÃâcÂEî1áb‰\ü†(Rš$ŠŸúG.•DÄ(b¤ùrÑV¹(T$—‰ÃcðGbe¸XÆ,,ÉÄI 1r9þ-Š‘‹¢’B% q„H!ÅHÂã“#˜ãñóH©D!’%Iñz^Á[yHå yxRŒL!Â'z¸íœ²Qª`ΕÇàe‘4¿! Of¼ù÷š"4F")ÄJsN˜X#—ҦásñV²¤˜I’å1’¨?N_*JG…&EÄ‹å“û2QùÃ?Ñx*“ŧM~+|ëßçÇ(äâøÈåS‰á̆JD8¹"w“ÝøÐ¤ÿ^ù#_ÿ$Â}«d›4`»Ì-qGÒNù.Åîä=)牢îU†¹§…ïSEìˆôˆòŒöŠñŽõ‰;ï›à·zÍ[kËÞ^çº~ÃÆM‹J¿±dÚRö²Šå+V®"ˆùÄâ5ƒØ@, <‰…„ñ:áM,"|ˆÅÄAâ —x“ð#–þÄ6b)@l'–„±œ"v+ˆÄJb±ŠØM¬&ökˆ÷ˆ·ˆ½ÄZÂx›ØG¬#ö®—˜Mðˆ—‰TbáLð‰¹„ 1CΪԃ­Ô„ ¶jµ®97V ÿJ>·êz»z:ìÇÀ 0’6ßšÐ,«|¯úŒ¥®¼±ŽâÆ­·öÎ…«ÇÐVÁzôï;0šÕ£ÆŸ_”7‡»ÏS"Âqia™{¨)ëÂAle¹Ì*ápã!y%hõ&ßeBC ½º…Õú9¬úç>ÂC/¯\‚Èå«×¡túöPç¬øÍj¥¼Ç—– Wãá][""R„Ò3é[Ô7ǯÞ0)´³í,xã{Ü;‰¤JΙ¼šL †,]†½†RœÑb˜¤¯6Õ€ >¨­*ë((å "Ÿ²¡N%y"·JózÎA>Îh Þ2Vfãl^²‘« 4•à>}I¸¦\ù[Sýé"&Æšú§N2{œ }y0Å1…cJI×¥‚  .T—V”ëkü>ŸûÄËš–·­=íuù½/Åßè¾½ò •V»ÕmO‚+à»oû‘>Ÿ“ñŸÈ¦àž¯àspáÅ3)ႎ8«Ôö…£†jTµi0ÁÐAϼìtï1L~:‡›UF¿Â+Ë7ƒ2@u™ ‡„Ñ$ðÓë=L÷uÌ"¹‡kÉ-ùÚ ð>W‘ݱ†!@Áç¿„p!|qÇgoz‡$ > MS¢µ>*>|s ¯íBÏèÝ“›…Ø[¶òêi›ŠõïØúùͬ9ÜüŸ!‹¶ë´›³0J”°—„ø¥óê…k—z?ßÏúœßu±zÐ+€B î<%ùÈX–AÈÖÉk¯(|z-=æ„àáÁÁµ=¶nÃ!‰§î-@…ë0”&}¯ÒÔ@¯+´S‹SãS˜þxW 'èM¼à愲¼óKKZ€œ®¯þzì|Ó‰!Â÷NS»a»Nª ¾Šô©Ì,CýžÜ†þÂòl­!3Y«‹ñ@VžjkM¹®ãØœ½ûäj{ìnÁL2ÃáAXÖ‡þþÙ®‚þnâI9ˆ8"G¯JÆ ¡ÅFsVan¨¡ €ËÉOUkq¥ ZÁ1†e«õ­Jšy›¹&qZJJšÐ…ƒQP¯®YEåäÀ×'Z8Ü|%y×dÑ€“ºC£Þ†­wSÒ½äLø·’MvÖýÇl3=—WUX–_ ¨£eé!BTH‚£q¯·£•ÜU¨²‚‹L&;†O5—”e*Õš “PÖš–Î{ ft·@á—dVor”ÖCÉŸIŸ6xå?üŒ¦å0‡ uÓÔ±é Ó‹Óçý‰éþ0} \õ ”°ÓF¾[¤©·)( ÉO@ºrÆ2ßP¯³ÓËíÚz§ÓWáúæp—ÂðC^6œç¨çäæfóò²ß úbŠ[Sšœ\”èòŽ—ÿvïŽð'A±ØîÔ9ˆç‡ÄIýcâ-õJAÊÑŒíE*­´pÌåE8:\÷2P•Û€+¡ÆÔÒœ]ëòàƒ±NÉ;ôñ´Z}4óÚ[F/¶Å¬n`® `u@-úŽ=å¡Ò4ÎpNeNe¦5£DŽPèm2n¿l;Z·ràþ¯[¾9)9yµù¸N*û£Ú¤Õ²£û©N&ŒrL昔iš`é…iæÃÖàÒÃ8’‡pSZ„Ž¼Ñ¼å‚‡ðÃߊá4ðÀ—ºº)B‚K•–±bj&l7œ¡§w8Y`Ú‡ÿ–âfýù:¤8åy…&1Ôa4ˆ}Ž¥gSÜ'z£É˜5ääçæRÜ›gÑ9κ¶#—Nö5žéh+R’T†dÀT7¿/„ý$÷ɜɒ†'G`¯õë3xáŠ!âá~nWT<0ãÙ®$+ô`ûd^×t›˜¼¶ÛÈ-%Z nÃ0 ^@~¶B{üÙ.È—d`ÌËNo¨ÑT9Ýz wbßæÑòS¼,N°¾bT¿E«In§Š¼šSiÒ‘@š¥ÑèeÉñº@EÄu ¹5ðz]CžÈ©Rƒ` “ïœð¿ÓÛ_ÝÜ,vt%¹îE¹§ì=§«øLñØÆl,HbI¦cmÑêÖå`sOÙÈõ…éUàΤ¹wª,‹°¹§”ä“)¢Ä¶jìô;vˆc„jNGŽÕ”ÀkÈ6,F•Î a™©*§ ”aj©0wRVä¯&ûr͆Ñp:p.0 3KÒJ2K@ 0[¬½p6¼ëÜz£ÄÜVÈÔ‚¦þ¿Ø67l[;}ŠîTT|R‚­P’ŸšpжQáäa]J¢ÆTzÔ(0t§W'⾓¬…&ŒAâÊmø’7°¡j²ËXn* 6Éõ²U(ÉyT¨F>¨¯07c3CÔdov¥Þ~Ь4+KÒV¢ çŰ*»<·”óA]eiW~k·šlÈ-5ÔûCôseB©¦ƒRKef£óH_Yd*|3(.«ÀÌý½síPa±Sía¸è5F;7:]ø \üÉî1zÿužJ£ËRJa¬B ž3Y­êÆäž#6@-Ûè®°§64ÖÙë‹òŠóÌÂܲ¼R`¦š;êN6K ö“hù>Uæqrª&ÄR\÷‘+#ƒug®¸¿âÚ”A—.lw^B+p«ÏÊHÑÉ5ÒŒ@EK»†…E¸È{àŒjJA0Øxñ›^5>‡W™WŠ@[ne&HÉŠ´­u†¡ä¿3Ó«$š*µÀí÷f÷ýn¹‰‘gw(x€lªûÇüû[ι9h4Fi^УÇl¤_¾¦|åwH ¡ô¸q­ºôF1߆˜€å;½ÆÊ‚í/Ã`+'¤VÇ ÙÐßù/pº­¹­º#ŸoE«Õd[n¹ h€N—œŸeÒ¥iS5vgéPZ°€*‹¹-ç.\]F¶jmû¡¯ÑgÄAëÂ ÆØ¾.´’R]±¥ÄlµØ…à¬/¨ÈT`&>H3fÇæ2ÈÇBã…÷Y7ÃÔ¿±á²ñM¼¬‚Pµ,ó–/ÉtL'Kò‹€Pe¡Â‰j2þIìÇÐùœ_ƒ³6½h¿gT`ªÀסÿxÏ™‡§6¡™ˆ´w½ßᆣ¿k?ýã×Èèà6žJ¬Q¨UÑ>i®€ÚÍÙ¦-éÒ $¸UUõ¨½û?É‘[ü;=z(énòÜÆ¨m$yL_Á% ejÕ×juFzvž)Ç(|µ"–˜ª²-“"Ðj.À ÓMÙ5úRä <çÂLkBK@uPóZá®JÐ$&+5À2 ÅJ³ªH…1›š*íHúðĸàœ.§—Z:*Ê ù ÖoÓŽ­¬£Øô|¸’´#J{?†äOço}ÒcÑG– K’K¥µiu€ßTok¸°ãøº@ÿÔ¨0a@ˆtx‡B/?ZÙǪÛ{Í8ß—¨™ÿœñBY#t-–¹oÓñ&0îtS¸Ã$»­“ìT˽e±Üe0D¢Ä‰”N8¢@%çÓ,‹Žé$o’`FéjÂßérV5€)z ‹&Ž;Vq° žÛƢ߆.¼´"C€)]«âkT1þ˜¹XO¿áhE?9èyÇZNÙ«õ2Ö“²M¼á¨¦gL¶÷3 š~¾…Õü¦>fÃï <¸èŒc5™–— r•‰»<ý–AªhÃ^?,Í/¥€ê-3„ ÑJRÜ!«Çç‘Ë‘#šfß{ N»Ðg?Þ%܋۷0u}Zj’A(WŸá,èxòîý³'ù2PoÏ`æÄlÍÓsh=n Áåò^›¥ª¼ª¨éò:%Ù—S•mͰkÊ¢À> }NNðh¥#Ú¬ä ƒªÌCÔÄ>’ëÿg›Gr-ô~2&ë7¡¤’=,z)tåm>"Ù ¼À‘VÉÅ´>cgî ^ád^3´&u$ô×ùp(Ms(LæÖãÞ^ Éoî]‚Ä=„Ky»z¯€+ Qjy‹iOšúÿô°º°&yrØð_ã®<äæa±¼¬cå9Oá˜ûgÃ9€ó /yâsYè~s]Ïj‡Q0šÖw.°}@/íÎT¿Œ$U’B}8̓õÑí~£±?âÉû”Œ{¢5<Ä)Ü8²á—–žšÑ»×û¾˜œ-ƒÑ<¸h B!o´­¿´WÐÚ‘zK~ÜÇùÆ;pƸXñ<‰äú||Œ7 F•Ýñç‚:–äK†G†ù)|@©‰êð=÷€Ïo[ÿ>8:8r¼n PSw.çXwÃý<«à·¼¡Ñ¶š~@:µCˆI°Ë õ4M]~,7•‚› " =·Á#T~ X/’Y…1º¤Lït¾Œ!œ¢üB,=¨áÒÌ#‰8< ™2<3ñ÷ñVÒ³ÄP‰¿Ÿ¼ÿ b õìÔ2z0¾ÈƦ–KºP,qþÐÒVÙÙÜÑdÇÁ°¶7±U<èÚÄÀª•Ã=¦$ïe–ë'vƒn S÷m¸î‹±¦ÅSLy¡f +Žp-‡ë?D?åµT”À—ÆŽ, H R ‚£MKrõŽýÁÚm‡3ž9=|ö6¹¿ÀíÌ]Ç G5nx•–ü2ÀoœáÔ$ØnÐïd Å`%÷g–3§A’KûÞL¡Jë-­ õ­-Š žˆÏjz’í4Œ±;Ýz 5?2Ú·†N¶B¯Wá‰ÓU)Åí¬ŠŒ,s9BT±’èø´Pà6ŽxCÛâ3aGÅeÚâ §vo[í~Î (8m(Ú±žÃu·åWVY]ðT›m3âÔ}™.üìÑOŸú\Cη„óÇBGÀ9êüPßÕcCʨnA{\Ubµ ±ûHÒ5î0éý—“ÞÒÁ¼¬B…X둆{~q¾µÀš_ø e*œÐY$Øœ“¤O54™ ³ª.­AS–ŽI7M•7 8§]ºç ¹?úoþ³ `UAÿ}ˆÍXJ›Ç ÞD ÞÖë]–<†•~‘ÆÂ(ýÒs$ä€ú´4jä˜à»ŽW8&ô®c#§²lÐáwüºZYŸâ{œÍù¦RåÚ áD ürr¶3·ÖJ¦æ‡à¦Ó͇î$¡…Ù99…9üRC¡ dQ&@©†+'¤0Þz-ƒ¾Î­Í·®_m-å[‹ª p-µ¢#N^E6Vu™½‚f:Ó®dvX'ÉôLç'd:2|0BÛX??ƒÿaÓ®äg•ÕŸ–cBS3²´:,¢&»]Fqz‚,hoüàeÇÓ:Œ½Ì-Ðɹìcf.ëQ’¦F tD[?Yß…¬{Ý7úCe£àÁŒY̶á”p*ò+ó1Mw”k 'IpP£uÏÂQÐ[I÷"]98OÑ_c{ýTbÃN’ŠÇgØYãû˜›¹P%ç|N­HAºQ¦K@‹&ØÎh ü"öNf°ò­ªœ¹™«È¯bnælJòdn•fx5Ì›u¶r`=Öù¨¢âVZã6œ›Wΰž>†îÌÛFÏçY K™«Õ¦²ô !*alS{çaÛ’¬¤oºôS°›¥åfóцášn@ÔGãF„%¦·^ïÅ”žÂJzå«kÀU “mõ£€ºX+s¢(xefzñKR+™XP ­ý|FÂç#.¿ëéŸèyPv1¦ù R«;u4ø+cô>jþ̺MÅ៮L°;…ã )¦Dƒ½„¾tFÎðëÜâŒú¼B~FÉ*@cCù9&v%y1¯<£kcçzgêF èéÆÚìÉ«Ìú*óéBüJ®’Ýp—¿xʦ¥ðž¶8ÒK›qðQ&¾Ý>ûªÿ>xÈÿaÃ×|ü’Ã#q1ê8•[]–óÀß{:nêñE¯u›‚—¯[-D;‘§£ŽžËH>¬R¾¸ìo?ߞý —Á¿ð.†ÜÆŒ«Éý^`'µÎë½­²Tsc¤ ¡Vef„´Z“=œüуGMƒÇ…Ç›ÆÀ‡àtúqI{j½²2ÄFqŸÜ<~´÷ÜÜOö^X¢Š‹$ÈԉɬÙ΃wŽu¾¨kÇBÜ£ÔQII„©z—|Ò;;\ô½Éî=|÷î:t€ŸqÐîxÈÈFø-3>hzé‹Xpü#6ÜC̃þœjPTPhîûÌ9ìN1d¥à(‰9ˆ… XÈæ\~VQNiÇ€Q;(€däÙ}`1µdÿ–M©*s}‚ ®AQŽçM‰F—$>.¿÷øvóéSÂÑcuÁ}p[9p*dÄ«=_‡­ö oþÌ‚/÷Ãýì‹ôއǮ€ËÔ©èþÃÑrEL\½²·ÄœŸ_"0{E‰)3Ny0RMÊÿÅÛŒ-´Ã(«ë+Xô9›öß‚U‚ÁS¹Íïz @sÁ[Ík†úBÎɯbMùâÓoðD?{Û'ˆ I1ŠÝ…Íp žû ¸ª‰Ba¨Ÿ÷ôÔv àiûw¬9ø5\qº´ÅnÖU·—uOöÐL;}ç.‹>ˆq?Ááx E5™Ã7j-ç&pPàÄWŽ×80z|þdø[Æg¶8]¸¿ã.”Þô¹©Ó$ïû³ÝCàõéöË‹kÃî•ïÙ$¿¬p?CÄúäø]sß|´ :Âé®ÿô æ$šñ¹ mïŽ_cð¦Þ;|ø½Íþcý ~h츀ûàäÞ걨Qp;6øþá^ÒÃ^bAVÙ9Œ¹Ð0L;´°Ú¾d_JæAËyÛ“?ß¿_Ð…‚ëWÀç½°úuä‚fÞÞ‰óCÕƒgahb‹ÐÒD ^‚ã<š¡ÏÊ#µQ€Út¾p±úr]“°Æ~´¢P=¶½#ÄUÙ0²YpÂKHJŒ—Mjmo:Úڞؔ DÈ῞áLöôŸad;}Ùºþ9ÜÝ¿¢Wy7»†/ÿÉ1nNì¿AÀ.0c¸ü‚>ú!+ûïÍé+púu8÷:‹n§_æMäï¡ó9h®ÃÔ< L‚tW?û‚ÐeÏa.V5‰9zÇÏð;kxßpV)»cï(ÿnñ±îƆ®…-ÊdÌË3 Œ¹•i*)ílé9×+„rÛÿâmlåø;ëÄS6<Ù”ìöVô"€žˆÛûÆß5%Ÿ‰«¬vÞø¾´[vOþ¡þ>øübù´ùjËÕ¶žÛcÔo›À §ì>´ƒw÷D[Sïè­¿vOj\8;ò»5ÏÂN$ÖûuèÒ?voŽnÙÒ¸»b3X^ÓºÊöKÝ¢¶{b0]Á­èÐu,¸Î†°Ÿ‡¤{ É®C”^GR(ÛÃa† +ýz§Á:}ÿÀ:éZ;½–wÝ瀽z­w.f™•ô.ÒVav…Ÿ>%¿-Îñ¢†à3öeý¶\¬·€(h%A=h(8Kq—f:rC$9á.hÕ0Œ¶×T ÷·-À¦mŒ±É,ÌìVg­© hyw³gb`”0!LiÚHíÄù.d æWbù ÙÿH¹€[÷GÖ¹Gþ'Ȧ\ÃÃãÜ»¹ÍN?>†صï:è·y7Ñu8lJÒdŒ†\Iîo¼[¢©£üz ¿’ PîÇ0r Š åà EGfdæé³u&¾2C“•²€¡P]LqUH%%r—C X.H8bˆÈ^C¹qà×/#3œŸëNÔWfÊjÜx©Í’ÑàbÆ=½˜¹^ }ßgõÃ.À« œ9(í_;op˜.Ö4îÔÄjüZ¾eD¨+Ï„ª"£=½ÅˆœD!«=wÜ»7ä´| œÃ5=Ýcçû à †®„¯¢9‚œDÞãcȉ‘8D´vmȯ0Fƒì§Â)Yz¶ê>9£|„¹ ؽÒQCVWÔškаL­+SÇ ™;¢ØLEz†:C“ÊH¡V+ùN~z98ÁDsfªöµC;§íù'/´•NŸþ¤vú ‚ø½É¿ endstream endobj 38 0 obj <> endobj 181 0 obj <>stream xœMSiPSW~!Ë{B’øFP›dFFÁ*°eP)­K[ª¸Ð::¥ˆ)A!([’œ$ ÊbJ Px­,Ô¡UëÒ—¶#Ö¢SgJ7Å¥8÷ÑËFøÓ93wîsÏ÷Ýï;çržÁápIÉ»‘¯v«Øåv…û°sV9k⃠BÞ™ü<12¢ƒ(u1ÁåpròMI¹Úb]V¦J/Í“+bccÖÊ£"#cå ¥.+#=Gžœ®W)5ézïA-ß›‘¥ÔËC7©ôz톈ƒÁž®ÉÏÕe¾¶VnÈÒ«ä»”ùJ]¡ò|KnŽ^þAºF)Ÿ\øüš”«Ñè•:yrî!¥.‡ _m‘2*z}\h˜‚ Rˆ}ÄBB,!hBJ,"| 1áçUHðˆF·œK>1>Ü(®ž{“ÆÓ³;†S´ÖÃé:Ä/o¡Ï,•Í70½^* L ™™gŒxd¥M-0I&è—/P$’^»Oƒ‘@q‹0?!"j÷8[zš;‹;UUV°Y¤'o\ì»ÔÔÙ ±÷mÚ“,Ã{ða£¬,™aIÁi”È× j4Õi•¥ÕUÛ¡^‘œ¾ƒ(rù>û¾C<„xo>G±T‚ tÍГ§®Ü†Ÿ©Éè˯¯ŠßØSÔŸ=]# TÊ\oîjðR¢Âš’—&ÓìϲèlU¶|k5TÙªmPIIæŒÆzh‘žLöm Ã+¶ä¤}²¹åªJÖSÃx ŸêÕºsò³ËÒ"ÿØŽ8H45õ\ꥆÉ÷ï­l›w…AË6ÝÍAÃ\4Œbéõd¦Õ˜ -T c0äoaÐòªo…d‚µ±Wöè)³šÝOº¡®ÄdŠ*)¾6—å~ÔÁÐØ"@v_4û @áœË³ë¸³Iè>íj·»Êds ÊŠÀ`h—Œý §Ò'2¥²¹?¥ ™2„mÿîvs¦¯pÑC\MƒŸµà@$ÎF)€ÖJìG‰(67yÇØIÕ×:Í•Š­X²Súö©À\ˆ¸Fì ó¯á€_ßmªîˆ³þШċ=„œ4Ü3"ÿ=ˆ¿¼q£(sm­ÙÖÖbÙ“Ðïp4`%àøOñÛx1—Wx»n¡ÌGjë~»‰$W¥£È§ qáá‚¡lsÎ ­ýÉëçìºziò{Ñ Þ¿à†«0ÖI͹ÈLK…Bš­ô8Á€7AϼËÙ¤ÂÒØ'xؤ”ÓXçàƒM$ã{ËOêË‹q yŽ …·ÜB‚øHŒ–§ endstream endobj 36 0 obj <> endobj 182 0 obj <>stream xœU’_HSaÆÏçæ<Ú4•F‚Çyº4†"+%’E(+lATs;žÜ&Ç3§¥“é@Ÿ;VSR†މX¤Fƒ,-¢E”wÝuc^AÙ…t‘¤£Ò…/ßûûÞž‡—Qú !”TQSyµ¸h»=¬f#•IPst˜ÙXVDZQ‡úéÍöLˆeÀÌ~PÒ)!¯/twôɫަ6ÁÅ;E6Ϟϗ–ž°°G‹ŠJÙ3nNpÙm¶Æ&:9·MÔ ‘½ìµ»8±Í;åŦ²ÂB¿ß_`s7xþt¾…õ»D'[Ë5sB ç`«¼‘½`ssìn‚ݧÂënò‰œÀÖxœài² œ§‘kϺø~'õ‚Í~‹·Gõ.þ?îL·Ùå9^°5Š\«HQTbzÆS-…úFý(ŒdÊ m‡ÒS] lTŽ^ª½iêfóÐcXGPýY§>`6-¦pß`°ÓŠAŽôHaIÎåç¯Äk P`µÆÂê%È„¨€JYÆ2ÓC!Üpw—ÙRÊ’Ü*Âß$Y")Á¤ “às2Ť}Úñ´÷ uŽÜ‰„è=îß5÷c×MbÕðžÔ¯#Úð¬y"tOÂL»p°g ',åÎ:ßZ—IPÄ#“þSEŒd”T“s’$õâ^:8ˆ‡†Ã82h^û²¹Ÿ€_€Žc(ÑLj—gè_qr\²†&ˆÆ‰CûGœD {4‚¦ LÄa…(“|ƒD­àÐ4[?•É•Íõ•I½[U®ê`jKo “üõZ-³[nˆLâzÆ7Üê÷Ü×^;?¬-}ý¦˜‡ßÌÃBtcÎó³S/ð~,)­žžnwÐ%06® ?Zª›»hq)¿mî´òd®‹¤ÒiþqCÚÆ“S÷™Sôm^crì¾Ñ¨U*Eýó ] endstream endobj 29 0 obj <> endobj 183 0 obj <>stream xœ-Ð]HSað÷¸é޶´‚em'ATÄ¢/„>ŒÊÓ,Æ<ÛŽs;6[¤gócϾÒyšl&•µjèèBÝH7…ÝvUttž3OA'/ïûÀóÂÿ÷Dza¦jïìºÙ¢ÿÿ¬ª1áh‰pLb¬x©è/µÔÊçźüÀ~þNß½)0Œ™h§‡¸ŽWçyoû"ðŠbïÕ¤æ!ö€W÷g¹Ìë·{R:9÷v×®®Vti÷¾9À¹d’‹Btê¡Nü±}a2œ…ìßφ ”Ûý(˜*-ƒsÂ_ äŸ|à±i„sãÀÖé»ÛÂZQ÷ƒÍ4`u¡‘°\ìP6k®'ÆÊ lÛ¹bß`W¤3ÀíÔ¢»ïÉŽ¾ &å^$ðêxÊ–ue³ôÌçǹUx‡W/Ï/”‰Õœ*_‘ߣ­P4©ËóÔjéìEè·D.Ü endstream endobj 27 0 obj <> endobj 184 0 obj <>stream xœuSylužaËv„¶ ¦É6à̆ Gˆ¤4!mÑ¥…nÛa{°Ýköì¹;{¿ÝÙsö¢-Ý-e©l)g9å†*Š‚ ‰D5f`ŠqÄ?Lüç—÷ûã}ïû¾÷=É›€ (*.+/_µèyõ&? å§OàßPûôÆ“e¡@yCÓÅ¢©œz W÷*·ñ5$E[µV[™ReÒ4É)é¬úÙÒ·/^8Gº`þüÅÒR©iª—µJËeT#©Q¹Ï6ée}I™¤³–6R”jɼyƒa®L¡«ÔÈß›=Gjh¢¥•¤–ÔèÉé e+%­)Hé vs_¼eJ…JG‘i¹²Ô´ªšÉ×Måk%²ÙˆT!s‘w‘ Ôƒ`9mHÒ.E0&Òäѧò®">aÉðDå&_çdŠx;ßÚ N­R­n Û"4ž6$”ЂլZ„üÚÆôåsC·ÆðØ& ½ØÙ-ŸÔ̪ftYCÎpÜd|ðÇѽ#€%#V-e¥,¢J˜êl†m˜€Þ3\à–\áNÝÄ‹ø–Lò>W~?™A÷<äî<ñn~fñ–nw81]O{*5Ü3záƒí[«Éæ ×YßW9Œ aŠòù°˜/cˆô±§;Ч”ºjèÂÖª[à0v¬“Ër*@ úı(/–6%tªVЬ9©>ufßÁ=½xªêu0ã¦dXš±u¹maXY¹î#Àšµ©Ã;‚éÄy"4”ú)З8½wô(`ú;°">%Ôf²?zaÞç³WE|’/,ö'À!l§!Øå <êNÜí°ÊúÕ‚MâqÕ.YX•yäèÅá{é‹»“‰ä<·eè´Pzš²™ˆºæ PÍþV}éøpzïXci 75×Õ«7ƒHÆ0tÆï? AlÀ×ëÍUÕ£õß<âòïr…CxÑÓdùgYôéþbö€/r °t ú`"„b± \Ði/ÿZ²€ÿ̹ÓãS@‰0MlêcG¶gwC›ÓãquÂëãÁ®jÝJw‰½Ëc²h–ðCš¹·!éŠÀ§3ýIè‹uûl„Ü«Â^0 ðrÓùƒ¯ø·qŽióv' $¡h0ÂÏ{"–x=ÂŒñËVÒM—B‰± ŒNÆù¼ â ñ/0à L¼`kv™Àg&ô>7ø‚Þ@nçÔ=>ˆòøGÅ‘¯ï`7ÅÅš7jìµáîöXÁuF\Á¨?ØÇ໸'&ºÀ„ÿ Ö€lú ¡Tâv¸h°ctÈŽú=Aü÷Îî7'.ñ×C»œ“$ã)+éêž %-0z ³g×Uvß‹3pÍ•,œÓŸè•–Ÿšhaœ~ˆƒßëgX¬èé²—×ödÉÕÀuWÍ-*f’ì~&ɰÁë d¿<’̆s8ûvèj´vª{3aS›WÚUí«%³MŒê£ã½ÉX,„3Áðp ?}Oò%q¼élöäñ#_6Ò¯¯^+ò›‹Úe#Vy\9úû!Nÿ7ë¸v‘Œ$k£Ùt:œbwÉýG¸|8}·~¤lE¥¼|3Nßy¿w=Ô@EÛÚ2ÛØ¦L54‚FonÒV[ªr¶¾L­0„ÇNö^: Ø`P£39Œt3á¦Û?tvlÞ’8ÛíšÜUÿ—xnw-ýÜã+(7~NÄíãÑb._¬Ÿo*…R°AmT—±t/`©tß@ÈãsFý±öpÒp8yã ÅÛ! l8eâ¹ðg4`Æ…gb=PµI.WÖ€-GÐf}L8ˆŸ¼~)׈]·ÎøX(ܺÜÖn1€Ó÷X¶ïg÷ßíÇ‹ôý|rª~±PËæï™tk2>)oᎂW2þ‚ù)4ëè endstream endobj 15 0 obj <> endobj 185 0 obj <>stream xœYXW»„ë ¨™Ylö®hÁbÁ.aÁUšÀ"EÚ²K;À²ì²€4»¢b¬1ÖØ+F£1ÑÄÿ?hÊï!÷¹g–²ãÍÿÜç>eçÌ)_y¿ï{¿³V„MÂÊʪ›Ÿ2b*Ö7*rîЊ0•0æÆ÷¶âûtà?³ÈÿéÃ ÏØÀÎØÙlé3°ÑþËîFvåukÙ¤Àj›ÕA«_,Xâ:xðéQщ1ʰµqò‘'N”¯I”·¾‘{)b•a‘òøC¼"<*:B7L¾P¡Ç­UÈC•á ùôyþËfùùÈùøÈ}‘Š˜ p¹¿jM¸2X>W¬ˆŒU¸ÊC£bäá-òà¨Èeœ2*2v˜Ü3V$V+ñ"EB°"Zx1D­ˆ‰PÆÆâÏre¬<,&(2N"‹’+#ƒÃU!Âñx<4*2N…ßGà7x+ÿ¨Ø¸ØàetœŸèïåÝ"cÜÚ 8áÜX%~- Å3C¢‚U‚6íïâ‚”‘±ò8EBœpÎ…W.86<(æ£A‹—þ~#b©gäÒiQ˦G/÷Ú0#Æ;Ö'n¦jVüìAsÖÌM öM ñSÌ õ›¿vráºEëÂG,Ù4jô˜±ãÆ;Oè;±Ÿ{fÒ€É9Ÿ»”Mäêá¶bðUC‡ 1’ úóˆ‰D?Ÿp'úó‰IÄb1XH¸‹ˆADáJ,&܈%Ä`b)1B,#¦C‰å„1ŒXAÌ †ÞćIÌ$F³ˆÑÄlb 1‡KÌ%ƾÄx˜@¬"dD‚&ˆž„#Ñp"¬‰^„ Ñ››ˆ>I|F¤ AiKt$:΄-1™°#:Sˆ.DW¢Ѱ'–á8ÀËâ­:X;tîàÛ᜵—õ%[›µ6ÿ”¬•4’3Éÿ’¥¿QIÔóŽƒ:nêx·ÓˆNõ¶¶÷ìfÚ•t–vNêüG—ñ]övµëº¦ëñnƒ»åuïÑý+{ûӲ޲³=¨+z|OO£Óè;î9üÖó‹žï}ë¦9©®÷òìÛ õÞÓûEŸã}^}Öﳆd&0*ÆÄ@v«gŸÉãåÿtþÌ9ØYã|Ôùóo}ûö½Ç¿í¿tá`gÅ{@úr‘L%ÕšÔ£¦Š…#È*cqy™º8‘ %Ñ2¾Z‚V‹^ۑȧY%  ¶¬˜Lž‡.¨ãh”FB6H6“-'¾ä¢9ûïÖpp×SvNâÇÒ–Md7êÐË@2¡å¹š…=-›B=¹+1m6Q²ýÛvï)9Üû¨Ön߸#¾,DP(¥i¬, •°˜üÆçò(Fæï ÂæÏ¡Ì²ð*Î >† }`*P˜_®Ö'²ÈÚ"P=‰†¢Uh8\%9nÙu°ÌØLB”Šü$‰¬!| 'Àup"Z'ñÍp´ÌHjµN2X¨n‚[ò9kü­¸L¾zxçù­Õ§Æ×²—÷í<ÎRÏ<¯¹1Èd™tžä‚† 8AúàBà|_¿/ƱÈí a2t•^uš} ‡T[ׂ0Êwá2/œÌñVœÕ}Žï„ü’ŸN#Ù@ÔuoeÐþÝ¿`(sùêÁ®±ùýÙ¤¾ƒÆ»÷ï7á黦ïžþÆZLY‡W7ÂbzÁ-’íd…ÉPVšaHŒÕîÏ£$rEѸQrXdN‘Á· æÜ"Aʾ"gê,sû’-S5°·®Æþš vOÙ[>†×Ñ+ëçmŸfÿ +V¯Ššü¨ RÔvE]aŸëWw4œfvn/­%”)Ó¦ÉÎÍÔ0s–nðêäòÚ±÷¤Ðö§·ÐúÇoûé˜üäâÌR@•±6RYŸ*15E£N̆ü88œƒDo…]0¤y,ÈkØCzˆÒ?žo® e¯-êXÔ‘½rÅÚ5̺ñ  `×7?C[VÖ Ú}…ºM]í½˜‘-‡Þ_ÑMßxº ›äá:hrã›_ž4þKd÷Î<ÉYóZ8[ŠåKIÑbù3º¥“§üjÉ%ô–&«ËËŠÕ ì·æHÑÓæÕiû.¿qðgýG+Ex‹&!Ým`_ö‚:„6ˆfP´eBÉ5«h^ûIσC›¬=ºfÇ<°˜š&uí:øßmtƒ¸5°KOÙ>¾¤WÞ\T;¾ÛÈÙ¾»²²Á…Ú†›Ò¹JÅ“ݳ˜ì~ìÏW—Êú¶!e+‘O4ÝôØl§A.Ÿ7¾yóøá¯l[`=¯ù³"k÷'·;ê¿áDˆ£b@ ™Øbš*ö”¶;8 ÀAÔ€¥(V—•›ß²Íž™Î7«D¡)ûÀ/†cÈöÕX¡[Ò¶ŒŽ6g»| D‘I™i©%™•l#z9Øòï•%†Ò2sÐøÀ ¤lDìp`[}ï­ &}{\±¦ ¯P¥Fc©QkH*g7lU#ÁR°T_yüî¯Ñ-ÍOÒg–µúJ*ëR­5¤˜ÕI‘Ê.ªšÕ{˜´lE¸'ð¢<¥hÔà„¯oVÕ]n• G¡}öœ5ôÃP¶Òö]­§´=>D[I«+~ù’‡÷”¨K2²rò²2™8¯¤…€ò^½ÿ, k`´´=<>*/ö܆Æ, ” 3gŒ¿Ö«xÕQ‚¶¤@Û âü§ßîhVµ–ïaG¹eVLnEŒæPÜÅ­y×SÂÏqheÄ%o‡¤¤HÙrRV?o‘_ÄÌÞKƒžaáWCDÅo'¹ä[Fæu\ÝýÕyQqß‡íø€¯¦!+òì}±g›Uˆ=ßá›mg,VðGèÎÛI8‘΢ ›Äö¥$Dnð¥ä†è¬Á–I '7ÛIZŠ—–³zÞ’µWcñþ!Úö´HšÑèùùl_÷e+·6,e ƒ%9!WéýÙçb®êý÷ÏZjA ®>V¼¯ÃwXÎêO—Ö3¤7z0 $KɱÈ0>|ùé‰Õäs,êYòGXú •JÚ¼æÅAGÎþ>/·¤ñ~¼Ã‘ã6ÀòžÏ¹a¡³S1éÙZ È Òê’]~¾Žy¼}·þ  œY·œõ’úW„­ÂÕ}¬ÿV¦žzmÉë»Wv^¼ÊÈö×Û̜녑XwîÚýãœ8¥U•¼næ¼¶~úàMø‰yÐN/,r,$=à¢Ð³nGa \îX!rG' ¨0¯Û $° Ñ$h ùw‰oŠe{.žÁ(އ#%ş憧Hôr@½ ƒ¤áÓü²˜„ãÑH4yKÔ¢óœ,ûùp$t…Ñ«dö§Ù£º=®Þ™=d!#‹0iÂdD¶ÔR©o®ÆtyûÈ›—¤ì䌓W”zC»·M°;ì6ä-êÂÊŽ{‚…ë—yQÐûkú×Çîý\Ý'º±²úA“Ÿ55=y*¦ ¸ÊïÄ.Ùøo`N±Èûf•(ø%p•¨XŽA;¥-da Î{vœ½ÌpmñB$HöqýiÛ|6 |•¤üÓ†?O¢±è9šŸKΉfH-3*È6æ‹‹Æbs}ïãíè5ªý00{ ‚Q_†Ãž7oÖ8Ëî®Þ\J©µ1C““›™É,Yá; Ï´šÒø;ìð¨vx~/h±Ž-HÒkD¥`Bu‹¦ L»Å0Ùî‚ ö~Ncïþ!14«™…ÍæóÌ ¥ätrù½ùû=°ilÝ„N¢ëÛ¯iWœ¼Kz#g EÃw˜¹MêÇÊö»Lrwu™Œ› Œ0‹½Þsð–9ׯ®¬«¨J4“èŸ/qþ(ˆÅvq³”7>˜„—šó[”‚Ó8膳g%\Aƒ†œê”ýaßM:;‹ßÃeêŒþì{¿‚v'˜°ï³²s³4lðúi*_€0éNÊïgSõóýKoÁ[ðпbÕ­bÎê,Ç17Uh„Šz¢‰h"Äÿ! ?Ã̺' § : gÜ\AêgD¢þè³!HŠ$ˆ ¥På¯ñ?)Ûæ@8û0 oºcΡ{þUòLý›[ääìbNi(È¢¡´D[œVÈ®×{+5rÎâi¬ßhK}‰^ºŠŒt×b$äÝšêp°°­-o2Û[Hþøðõë ÷Y¤ùÛ¼ù1a„É6°w¡On,ôñž¾` û¿;„Û:„¹ðxkq­_’aC…¨×C ³ÿûàüÞzÜ!ÐRŸ€E> æÓ–óHò 8TyàPí¾²½€£p›0è· “£é7p›àá1l˜n§6þb–DÊmâøo9{³ÓzÊà%¬(£ûöp…û÷°'öb›Ÿ£nŒaFÎÖdekÆ)ͨ6 •îxé-S-Ø*rveP²²Œ=¦œŠÞǾÞ[Sª6¦ggçåd³á“4ñ dÄ)Y‚)"NÙ;$çFh©ÓüKúÿ€s šÝ²CpË¿asõª–§M¥¹\´9"HSÙùë»1uu: ¶j¶%ìØX¶Á|ƒ(bƒ=-•3’Og]æç·´%*a(‡Qh>½Þ y´öDÀK4Žºhe8é£Í=þz_Ã)pƒz>ᾃòÿo¥êl`*½+`ÞœIK\Û£àýoö°ôrˆ¹á~ d²Ñ¢-:¡—hŒèù/ Dz¦\þ[Ú¿ßåö¦Th~æOÀ#œê„ÐÀÌçËé .°ÊP}'ÏT²?Ž­UL»›v/m‹v{2n`v'U®_ÛËsFÀ°žgoæ0yZC* „¤É"›–¦¥§Úƒ{˜•Üå˜;¸Àþp÷íÙ¸ã!ÛÙˆê(Ô2ß’ÆJÜÃÄT¦ìÞÛëæÕÓÏ_ œ×rõÐVCÞãæßnø*l+/â´8g‚æARŸåËg1¨‡Hqš|uýÆk–Ÿˆ«†ùmêc[þUó«Pà$ºcG>䄆Þ}š4Ù˜^Z®7T3l^‰HK‡øNjVýÍíŒ(IÙ_hˆà_8“n8gbŽõé²@ÃÕ¸ö”œ%4Ëì­ØmȹHx[ù—½n!3M¢‘7d;Çnj©Ù˜–ԵЯL}‚¹jdjSÌÓ¢ŸÈ‘3fŒå}÷'þôð£Ú’¨nU‡g!ò‡1Pm^¼€dk]ªno1šh4J¤YÇæêÿ€?3[çóð⇋$TðÕ’½"r膥k]ø˜DÍ*I‰üx•äѤÁáöŠoVMMÁGááG¸n°Á!¢²«u襒”Ýi©nIÛ³½ì–ÓwÙ‹‡÷í8лnïÚÝDx{øRívÌàì]§ÀÔG/—Wžž¿f]ü²Fujý¶Õ€’ùÈDå Ê¢q‡k$”òý¡MsÉuòk¬ûf‘ÃÝD„‘\Ьø>_Ù4÷YÈGK^‹¸¼¨f™ï–áþ£‚'ÒB ˜ï6þgƬ™Û´8l?gÏÏàB1(¿‡]9ºPzñÔµË/~€Ý`çš=à2õjìSW¿…Q+B™„ØÔH©qïbÒëËuLÅ•¯ëoêÑ-?ïÀ„¨¡ÃYÏIîÓ¨yRÙžùÆBfÃ&‹ëÈ)|ÖL.A8‹ïŸÒùùùàÊŽÃçÀEêÅÔ[.K–%G„0ñÉ)‰¸IÊÀ¥ÃPXPZÂì©;]sPß\^>;(*$(‘ÏHÎ[¨¤Œ6/ÞyûlÖçS¦Î±paõÁ•¬¦(»@ ¨ µ:#µ,u{.êdêi@Aé/Ïšn¯¼òù¾Ö¨ìÁÁ?0ɱÃa‡y'ê1UâË{%ìaŽ–²òþÉŒ'QB³£d¥nçh´‰„!˜±n'Íz§œŒëÊÁ-8ðWh¨¹éÚJ¦Ô¦¢¢})slë ÓQ@=¿ä;ªïÌé®ó¶XÁjŠÍ ¤«Õéiå›öųgÖIþ+`ûýOÐÚx<ðE¨:~)ÛràúÔ”ã#û:h8fÑyÕy›\¯üz÷‰ÓON>Õ *§$Ë¥Ë+”É`4U‡U¬ž=;fÕbÖsîÚ±Qšþê;½}ô=$ÞM}>Â+ xâF@o?aªÚ\±ïàÑ-‡u÷â¬ÉcÌòôYZÿ<šÍ1‚|í6Ðy5<ÇYýÎÁÂU ŒFcšKò²ó´ Ç ·¼¦’¢¢Š"Žç+$7š;NµÜ¿€'Ì©±‚¯°µò @ ¨­I•ª¸¤$U6ÈÊ×0UèI~‚^]œÊÞ_CE£!tDœ**j›ª–-#k·oݽ;vkD«IjŽÂÎJ¸p „›¯ÃmáMúÞÁ½ÇÁUê'÷ÆÁƒÝ=Fúí[}7Œ‘½Ÿ¦ Y2«—ÛóÉïß?òöfÈ—Óö3²·°’~te¡×l¿%Sç»y÷Ê…G¬ì=:dóݵS=gÍ›ä>ûJãÃë—ž §Â8éÕ!}aŒ1½ 8U£©pGÔ;žsøÔh‹œJÁ{Øq¼}6pü.¶P òôq‚ãà|Úã8èyú•õ£È “_©1¥§T ÍÌKJF*Çäf(§ E‡DD…†ÖFaKÈ#µ»ŠØÂfÀóx<Ò2¾çСˆ=!‚ƒ†C_~>ìˆlj`$r¸ƒêŽq îÇ½ÈøŒyþ`:PT…íÙ¸3yOÆõ´[©YÛR·o*xà\ßÀqŒ¬ÑÌ<”}<·*«¸ÖL–¶w‹Ò‘çãrq‹g„¶Òw¦ëÁp<ñ`ø–¨ê †éåþ¦xýÆRJv#¾,eûÎ^/œ¼sõbx˜pM«×¶Qœ+RÙ*­p+‘Ç„íŽ+ ÔQ礲¹bÞèDgê³ [ÓEfa¶1“~—b­>§8 7ãz²ŠXA°ïŒmÐ3P!Íí”\–ZQfÐW—0p¬’Lƒ;þ΀i‡¡Ça(þZAéá×ã_køL§g.ZèãóÕÂÛ7/\¸y/à+oVasêbù*EȪ•Š#'O>ÜÀ¢nüHñÄ[wECB…‰'i`¡ %Òeº"=0R%™F5nò²™æ®¨Kjv–d8áÚ`((Ê/(LpÖÑüX´©(§(·8é^W¨/(t,(Ì×B ÏÖ«‹„Ë!þc,Òƒ(Ì+ÌÅ¿ŽÙz­ç'€Øs(|ùAF#Gd•¡ÉÉ™æ¼U¤/,*fþ|ùçèâ,³QMF£É«K7šÃ~’_›³vØ6û‹.vüQÿO¿ãœñ·®üUÆáž™÷᥵XH­6"qMÚj° ¬­Š>QŸt\¡à)t¼þúÕíÓËf0+¥ò=ýÀxÊëRÀã’’ü‚æ˜MZNJ^: æ8såâñÇfly?›FNÇ4¹ùÙÀIã ƒÚÇêò S;²Ñd`=³ÌÍ´¡HWb`žB›gÈÆ®Ë1´ ±(GŸ]̺Cètls‘^LNÂw 9š¼Ü,9­–@VúQ»ÔûÐ ×ýB{ƒKî[ãdþ ‡ŒåÙÍL%ËÛˆ=,§áL›™ùѵå*4SÄ f"Ñ7‰f´ÿ§Ä#Ël” ´™û¦¤š÷b>„#ÆòüÖ,• ‡F.4‰ÂkàGáµé? ûñ:Ó:N:ªÇCFVvІAßþ57[“—…%ÌÀF,)Ò•3m Ö W¡»ç“áo4=,ÖSF3.R4ê" 5´~ 8ŠA?¼¡ÁõªÛµ [ëêªc .a«²vU•˜C’‚9‰>«TJeBÊjU]DCâmp½åëš6çð4òŸœ…ó^:–'ÃX`(È×1Ðñ˜¤¹ ±RËK÷E>dÉ.kø±5†<Þ‰³=ngÇÙu&ˆÿ2àÊ endstream endobj 145 0 obj <> endobj 186 0 obj <>stream xœcœþCMR6$øøø‹‹ø®ù-‹ ‹ ­÷Ó÷W÷T÷Qmq€Copyright (c) 1997, 2009 American Mathematical Society (), with Reserved Font Name CMR6.CMR6Computer Modern2wøˆø÷‹å÷ç÷ä²Ó÷÷•ðø®÷Of‰}I|€„…:‹}‹ûO÷ó­¦ä˪©©©´¾‹Ò÷ûÔûû16-X¶…–¤¨¯¡~«_¢¾È±Ð‹ôÃ=:D\BEKûûmŠŠ‹løGv øCš÷o•“ ûa–² Þ  7Ÿ «’ Þœ (±‰% endstream endobj 2 0 obj <>endobj xref 0 196 0000000000 65535 f 0000100470 00000 n 0000150604 00000 n 0000100208 00000 n 0000095620 00000 n 0000000015 00000 n 0000002332 00000 n 0000100518 00000 n 0000106022 00000 n 0000128466 00000 n 0000104768 00000 n 0000120291 00000 n 0000103921 00000 n 0000111101 00000 n 0000108455 00000 n 0000142307 00000 n 0000100559 00000 n 0000100589 00000 n 0000095780 00000 n 0000002352 00000 n 0000003442 00000 n 0000100652 00000 n 0000100682 00000 n 0000095942 00000 n 0000003463 00000 n 0000006903 00000 n 0000108018 00000 n 0000140539 00000 n 0000107507 00000 n 0000139545 00000 n 0000100723 00000 n 0000100753 00000 n 0000096104 00000 n 0000006924 00000 n 0000011854 00000 n 0000107059 00000 n 0000138460 00000 n 0000106726 00000 n 0000136748 00000 n 0000100827 00000 n 0000100857 00000 n 0000096266 00000 n 0000011875 00000 n 0000016557 00000 n 0000100931 00000 n 0000100961 00000 n 0000096428 00000 n 0000016578 00000 n 0000020896 00000 n 0000101024 00000 n 0000101054 00000 n 0000096590 00000 n 0000020917 00000 n 0000025848 00000 n 0000101106 00000 n 0000101136 00000 n 0000096752 00000 n 0000025869 00000 n 0000028770 00000 n 0000101199 00000 n 0000101229 00000 n 0000096914 00000 n 0000028791 00000 n 0000031248 00000 n 0000101292 00000 n 0000101322 00000 n 0000097076 00000 n 0000031269 00000 n 0000035287 00000 n 0000101363 00000 n 0000101393 00000 n 0000097238 00000 n 0000035308 00000 n 0000039075 00000 n 0000105568 00000 n 0000126367 00000 n 0000101445 00000 n 0000101475 00000 n 0000097400 00000 n 0000039096 00000 n 0000043479 00000 n 0000101549 00000 n 0000101579 00000 n 0000097562 00000 n 0000043500 00000 n 0000046496 00000 n 0000101653 00000 n 0000101683 00000 n 0000097724 00000 n 0000046517 00000 n 0000049620 00000 n 0000101724 00000 n 0000101754 00000 n 0000097886 00000 n 0000049641 00000 n 0000054059 00000 n 0000105363 00000 n 0000124879 00000 n 0000104511 00000 n 0000117470 00000 n 0000101795 00000 n 0000101826 00000 n 0000098050 00000 n 0000054080 00000 n 0000057445 00000 n 0000101912 00000 n 0000101943 00000 n 0000098216 00000 n 0000057467 00000 n 0000062422 00000 n 0000102029 00000 n 0000102060 00000 n 0000098382 00000 n 0000062444 00000 n 0000067705 00000 n 0000102135 00000 n 0000102166 00000 n 0000098548 00000 n 0000067727 00000 n 0000071549 00000 n 0000102252 00000 n 0000102283 00000 n 0000098714 00000 n 0000071571 00000 n 0000074380 00000 n 0000102347 00000 n 0000102378 00000 n 0000098880 00000 n 0000074402 00000 n 0000077321 00000 n 0000102431 00000 n 0000102462 00000 n 0000099046 00000 n 0000077343 00000 n 0000080835 00000 n 0000102515 00000 n 0000102546 00000 n 0000099212 00000 n 0000080857 00000 n 0000084198 00000 n 0000103433 00000 n 0000110235 00000 n 0000103252 00000 n 0000109287 00000 n 0000109132 00000 n 0000149942 00000 n 0000102599 00000 n 0000102630 00000 n 0000099378 00000 n 0000084220 00000 n 0000086425 00000 n 0000102777 00000 n 0000102808 00000 n 0000099544 00000 n 0000086447 00000 n 0000089546 00000 n 0000102872 00000 n 0000102903 00000 n 0000099710 00000 n 0000089568 00000 n 0000092080 00000 n 0000102967 00000 n 0000102998 00000 n 0000099876 00000 n 0000092102 00000 n 0000095035 00000 n 0000103073 00000 n 0000103104 00000 n 0000100042 00000 n 0000095057 00000 n 0000095599 00000 n 0000103168 00000 n 0000103199 00000 n 0000109529 00000 n 0000110470 00000 n 0000111488 00000 n 0000117779 00000 n 0000120621 00000 n 0000125157 00000 n 0000126607 00000 n 0000128978 00000 n 0000137028 00000 n 0000138742 00000 n 0000139800 00000 n 0000140765 00000 n 0000142926 00000 n 0000150153 00000 n 0000103810 00000 n 0000104427 00000 n 0000105279 00000 n 0000105928 00000 n 0000106625 00000 n 0000107346 00000 n 0000107882 00000 n 0000108370 00000 n 0000109030 00000 n trailer << /Size 196 /Root 1 0 R /Info 2 0 R /ID [<0BD6FB3D5F897533AA0DBEF863FD5FF9><0BD6FB3D5F897533AA0DBEF863FD5FF9>] >> startxref 150727 %%EOF orpie-release-1.6.0/doc/manual.tex.in000066400000000000000000002104231334120314700174230ustar00rootroot00000000000000% Orpie documentation % % Notes: % This document is designed to be processed with the 'latex2man' perl % script. This script has a preprocessor, which is the source of the % IF LATEX ... ELSE ... END IF comments. % "latex2man -CLATEX -L" will output pure tex source, which can be % handled either via latex or hevea. (Also, latex2man sucks. If you know % of a better tool that does the same job, I'd love to hear about it.) % Hevea has its own preprocessor, which is the source of the BEGIN LATEX % and HEVEA comments. (Hevea doesn't suck.) \documentclass[11pt,notitlepage]{article} %@% IF LATEX %@% \usepackage{times} \usepackage{amsmath} \usepackage{fullpage} %@% ELSE %@% \usepackage{latex2man} \setVersion{1.4} %@% END-IF %@% % End preamble %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{document} %@% IF LATEX %@% \title{Orpie v1.6 User Manual} \author{Paul J. Pelzl} \date{August 27, 2018} \maketitle \begin{center} \emph{``Because the equals key is for the weak.''} \end{center} %@% IF LATEX %@% \tableofcontents \clearpage %@% ELSE %@% %@% END-IF %@% \section{Introduction} Orpie is a console-based RPN (reverse polish notation) desktop calculator. The interface is similar to that of modern Hewlett-Packard${}^{TM}$ calculators, but has been optimized for efficiency on a PC keyboard. The design is also influenced to some degree by the %BEGIN LATEX Mutt email client\footnote{http://www.mutt.org} %END LATEX %HEVEA \begin{rawhtml} Mutt email client \end{rawhtml} and the %BEGIN LATEX Vim editor\footnote{http://vim.sf.net}. %END LATEX %HEVEA \begin{rawhtml} Vim editor. \end{rawhtml} Orpie does not have graphing capability, nor does it offer much in the way of a programming interface; other applications such as %BEGIN LATEX GNU Octave\footnote{http://www.octave.org} %END LATEX %HEVEA \begin{rawhtml} GNU Octave. \end{rawhtml} are already very effective for such tasks. Orpie focuses specifically on helping you to crunch numbers quickly. Orpie is written in %BEGIN LATEX OCaml\footnote{http://ocaml.org/}. %END LATEX %HEVEA \begin{rawhtml} OCaml). \end{rawhtml} \section{Installation} The recommended method to install Orpie is through the %BEGIN LATEX OPAM package manager.\footnote{https://opam.ocaml.org/} %END LATEX %HEVEA \begin{rawhtml} OPAM package manager. \end{rawhtml} ``\texttt{opam install orpie}'' should get the job done. If you want to install without using OPAM, you will need the following OCaml packages installed: \begin{itemize} \item OCaml 4.03+ \item dune \item camlp5 \item ocamlfind \item curses (registered with \texttt{ocamlfind}) \item gsl (registered with \texttt{ocamlfind}) \end{itemize} If you have satisfied all the dependencies, then use the Makefile to build: \begin{verbatim} # optionally set an installation prefix (default is /usr/local) $ export PREFIX=/usr # optionally set a staging directory (useful if you're creating a package) $ export DESTDIR=/tmp/orpie # build $ make # install build products (use 'sudo' if installing to a root-owned location) $ make install \end{verbatim} %@% ELSE %@% %@% IF ORPIERC %@% \begin{Name}{5}{orpierc}{Paul J. Pelzl}{configuration file for the Orpie calculator}{orpierc manpage} \texttt{orpierc} is the configuration textfile for the \Cmd{orpie}{1} console calculator. \end{Name} %@% ELSE %@% \begin{Name}{1}{orpie}{Paul J. Pelzl}{a console-based RPN calculator}{Orpie 1.6 Manpage} \Prog{orpie} is a console-based RPN calculator with an interactive visual stack. \end{Name} \section{Synopsis} \Prog{orpie} %@% END-IF %@% %@% END-IF %@% %@% IF !ORPIERC %@% \section{Quick Start} %@% IF !LATEX %@% CAUTION: while this manpage should be suitable as a quick reference, it may be subject to miscellaneous shortcomings in typesetting. The definitive documentation is the user manual provided with Orpie in PDF format. %@% END-IF %@% This section describes how to use Orpie in its default configuration. After familiarizing yourself with the basic operations as outlined in this section, you may wish to consult %@% IF LATEX %@% Section \ref{advanced} %@% ELSE %@% the \Cmd{orpierc}{5} manpage %@% END-IF %@% to see how Orpie can be configured to better fit your needs. \subsection{Overview} %@% IF LATEX %@% You can start the calculator by executing \texttt{orpie}. %@% END-IF %@% The interface has two panels. The left panel combines status information with context-sensitive help; the right panel represents the calculator's stack. (Note that the left panel will be hidden if Orpie is run in a terminal with less than 80 columns.) In general, you perform calculations by first entering data on to the stack, then executing functions that operate on the stack data. As an example, you can hit \texttt{12+} in order to add 1 and 2. %@% IF LATEX %@% \subsection{Entering Data} \subsubsection{Entering Real Numbers} %@% ELSE %@% \subsection{Entering Real Numbers} %@% END-IF %@% To enter a real number, just type the desired digits and hit enter. The space bar will begin entry of a scientific notation exponent. The '\texttt{n}' key is used for negation. Here are some examples: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|r|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Keypresses & Resulting Entry \\ \hline \texttt{1.23} & \texttt{1.23} \\ \texttt{1.2323n} & \texttt{1.23e-23} \\ \texttt{1.23n23} & \texttt{-1.23e23} \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \subsubsection{Entering Complex Numbers} %@% ELSE %@% \subsection{Entering Complex Numbers} %@% END-IF %@% Orpie can represent complex numbers using either cartesian (rectangular) or polar coordinates. See %@% IF LATEX %@% Section \ref{rectpolar} %@% ELSE %@% PERFORMING BASIC COMMAND OPERATIONS %@% END-IF %@% to see how to change the complex number display mode. A complex number is entered by first pressing '\texttt{(}', then entering the real part, then pressing '\texttt{,}' followed by the imaginary part. Alternatively, you can press '\texttt{(}' followed by the magnitude, then '\texttt{<}' followed by the phase angle. The angle will be interpreted in degrees or radians, depending on the current setting of the angle mode %@% IF LATEX %@% (see Section \ref{anglemode}). %@% ELSE %@% (see PERFORMING BASIC COMMAND OPERATIONS). %@% END-IF %@% Examples: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|r|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Keypresses & Resulting Entry \\ \hline \texttt{(1.23, 4.56} & \texttt{(1.23, 4.56)} \\ \texttt{(0.7072<45} & \texttt{(0.500065915655126, 0.50006591...} \\ \texttt{(1.23n,4.5610} & \texttt{(-1.23, 45600000000)} \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \subsubsection{Entering Matrices} %@% ELSE %@% \subsection{Entering Matrices} %@% END-IF %@% You can enter matrices by pressing '\texttt{[}'. The elements of the matrix may then be entered as described in the previous sections, and should be separated using '\texttt{,}'. To start a new row of the matrix, press '\texttt{[}' again. On the stack, each row of the matrix is enclosed in a set of brackets; for example, the matrix %@% IF LATEX %@% %BEGIN LATEX \begin{displaymath} \left[ \begin{matrix} 1 & 2 \\ 3 & 4 \end{matrix} \right] \end{displaymath} %END LATEX %HEVEA \begin{center}\begin{tabular}{|cc|}1 & 2 \\ 3 & 4 \end{tabular}\end{center} %@% ELSE %@% \begin{Table}{2} 1 & 2 \\ 3 & 4 \end{Table} %@% END-IF %@% would appear on the stack as \texttt{[[1, 2][3, 4]]}. Examples of matrix entry: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|r|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Keypresses & Resulting Entry \\ \hline \texttt{[1,2[3,4} & \texttt{[[1, 2][3, 4]]} \\ \texttt{[1.210,0[3n,5n} & \texttt{[[ 12000000000, 0 ][ -3, -5 ]]} \\ \texttt{[(1,2,3,4[5,6,7,8} & \texttt{[[ (1, 2), (3, 4) ][ (5, 6), (...} \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \subsubsection{Entering Data With Units} %@% ELSE %@% \subsection{Entering Data With Units} %@% END-IF %@% Real and complex scalars and matrices can optionally be labeled with units. After typing in the numeric portion of the data, press '\texttt{\_}' followed by a units string. The format of units strings is described in %@% IF LATEX %@% Section \ref{unitsformat}. %@% ELSE %@% the UNITS FORMATTING section. %@% END-IF %@% Examples of entering dimensioned data: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|r|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Keypresses & Resulting Entry \\ \hline %@% IF LATEX %@% \texttt{1.234\_N*mm\^{}2/s} & \texttt{1.234\_N*mm\^{}2*s\^{}-1} \\ \texttt{(2.3,5\_s\^{}-4} & \texttt{(2.3, 5)\_s\^{}-4} \\ %@% ELSE %@% \texttt{1.234\_N*mm\Circum2/s} & \texttt{1.234\_N*mm\Circum2*s\Circum-1} \\ \texttt{(2.3,5\_s\Circum-4} & \texttt{(2.3, 5)\_s\Circum-4} \\ %@% END-IF %@% \texttt{[1,2[3,4\_lbf*in} & \texttt{[[ 1, 2 ][ 3, 4 ]]\_lbf*in} \\ \texttt{\_nm} & \texttt{1\_nm} \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \subsubsection{Entering Exact Integers} %@% ELSE %@% \subsection{Entering Exact Integers} %@% END-IF %@% An exact integer may be entered by pressing '\texttt{\#}' followed by the desired digits. The base of the integer will be assumed to be the same as the current calculator base mode (see %@% IF LATEX %@% Section \ref{basemode} %@% ELSE %@% PERFORMING BASIC COMMAND OPERATIONS %@% END-IF %@% to see how to set this mode). Alternatively, the desired base may be specified by pressing space and appending one of \{\texttt{b, o, d, h}\}, to represent binary, octal, decimal, or hexadecimal, respectively. On the stack, the representation of the integer will be changed to match the current base mode. Examples: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|r|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Keypresses & Resulting Entry \\ \hline %@% IF LATEX %@% \texttt{\#123456} & \texttt{\# 123456\`{}d} \\ \texttt{\#ffffh} & \texttt{\# 65535\`{}d} \\ \texttt{\#10101nb} & \texttt{\# -21\`{}d} \\ %@% ELSE %@% \texttt{\#123456} & \texttt{\# 123456`d} \\ \texttt{\#ffffh} & \texttt{\# 65535`d} \\ \texttt{\#10101nb} & \texttt{\# -21`d} \\ %@% END-IF %@% \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% Note that exact integers may have unlimited length, and the basic arithmetic operations (addition, subtraction, multiplication, division) will be performed using exact arithmetic when both arguments are integers. %@% IF LATEX %@% \subsubsection{Entering Variable Names} %@% ELSE %@% \subsection{Entering Variable Names} %@% END-IF %@% A variable name may be entered by pressing '\texttt{@}' followed by the desired variable name string. The string may contain alphanumeric characters, dashes, and underscores. Example: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|r|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Keypresses & Resulting Entry \\ \hline \texttt{@myvar} & \texttt{@ myvar}\\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% Orpie also supports autocompletion of variable names. The help panel displays a list of pre-existing variables that partially match the name currently being entered. You can press '\texttt{}' to iterate through the list of matching variables. As a shortcut, keys \texttt{-} will enter the variables (``registers'') \texttt{@ r01} through \texttt{@ r04}. %@% IF LATEX %@% \subsubsection{Entering Physical Constants} %@% ELSE %@% \subsection{Entering Physical Constants} %@% END-IF %@% Orpie includes definitions for a number of fundamental physical constants. To enter a constant, press '\texttt{C}', followed by the first few letters/digits of the constant's symbol, then hit enter. Orpie offers an autocompletion feature for physical constants, so you only need to type enough of the constant to identify it uniquely. A list of matching constants will appear in the left panel of the display, to assist you in finding the desired choice. The following is a list of Orpie's physical constant symbols: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Symbol & Physical Constant \\ \hline \texttt{NA} & Avagadro's number \\ \texttt{k} & Boltzmann constant \\ \texttt{Vm} & molar volume \\ \texttt{R} & universal gas constant \\ \texttt{stdT} & standard temperature \\ \texttt{stdP} & standard pressure \\ \texttt{sigma} & Stefan-Boltzmann constant \\ \texttt{c} & speed of light \\ \texttt{eps0} & permittivity of free space \\ \texttt{u0} & permeability of free space \\ \texttt{g} & acceleration of gravity \\ \texttt{G} & Newtonian gravitational constant \\ \texttt{h} & Planck's constant \\ \texttt{hbar} & Dirac's constant \\ \texttt{e} & electron charge \\ \texttt{me} & electron mass \\ \texttt{mp} & proton mass \\ \texttt{alpha} & fine structure constant \\ \texttt{phi} & magnetic flux quantum \\ \texttt{F} & Faraday's constant \\ \texttt{Rinf} & ``infinity'' Rydberg constant \\ \texttt{a0} & Bohr radius \\ \texttt{uB} & Bohr magneton \\ \texttt{uN} & nuclear magneton \\ \texttt{lam0} & wavelength of a 1eV photon \\ \texttt{f0} & frequency of a 1eV photon \\ \texttt{lamc} & Compton wavelength \\ \texttt{c3} & Wien's constant \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% All physical constants are defined in the Orpie run-configuration file; consult %@% IF LATEX %@% Section \ref{advanced} %@% ELSE %@% the \Cmd{orpierc}{5} manpage %@% END-IF %@% if you wish to define your own constants or change the existing definitions. %@% IF LATEX %@% \subsubsection{Entering Data With an External Editor} \label{editor} %@% ELSE %@% \subsection{Entering Data With an External Editor} %@% END-IF %@% Orpie can also parse input entered via an external editor. You may find this to be a convenient method for entering large matrices. Pressing '\texttt{E}' will launch the external editor, and the various data types may be entered as illustrated by the examples below: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Data Type & Sample Input String \\ \hline %@% IF LATEX %@% exact integer & \texttt{\#12345678\`{}d}, where the trailing letter is one of the base characters \{b, o, d, h\} \\ %@% ELSE %@% exact integer & \texttt{\#12345678`d}, where the trailing letter is one of the base characters \{b, o, d, h\} \\ %@% END-IF %@% real number & \texttt{-123.45e67} \\ complex number & \texttt{(1e10, 2)} or \texttt{(1 <90)} \\ real matrix & \texttt{[[1, 2][3.1, 4.5e10]]} \\ complex matrix & \texttt{[[(1, 0), 5][1e10, (2 <90)]]} \\ variable & \texttt{@myvar} \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% Real and complex numbers and matrices may have units appended; just add a units string such as ``\texttt{\_N*m/s}'' immediately following the numeric portion of the expression. Notice that the complex matrix input parser is quite flexible; real and complex matrix elements may be mixed, and cartesian and polar complex formats may be mixed as well. Multiple stack entries may be specified in the same file, if they are separated by whitespace. For example, entering \texttt{(1, 2) 1.5} into the editor will cause the complex value \texttt{(1, 2)} to be placed on the stack, followed by the real value \texttt{1.5}. The input parser will discard whitespace where possible, so feel free to add any form of whitespace between matrix rows, matrix elements, real and complex components, etc. \subsection{Executing Basic Function Operations} %@% IF LATEX %@% \label{functionops} %@% END-IF %@% Once some data has been entered on the stack, you can apply operations to that data. For example, '\texttt{+}' will add the last two elements on the stack. By default, the following keys have been bound to such operations: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Keys & Operations \\ \hline \texttt{+} & add last two stack elements \\ \texttt{-} & subtract element 1 from element 2 \\ \texttt{*} & multiply last two stack elements \\ \texttt{/} & divide element 2 by element 1 \\ %@% IF LATEX %@% \texttt{\^{}} & raise element 2 to the power of element 1 \\ %@% ELSE %@% \texttt{\Circum} & raise element 2 to the power of element 1 \\ %@% END-IF %@% \texttt{n} & negate last element \\ \texttt{i} & invert last element \\ \texttt{s} & square root function \\ \texttt{a} & absolute value function \\ \texttt{e} & exponential function \\ \texttt{l} & natural logarithm function \\ \texttt{c} & complex conjugate function \\ \texttt{!} & factorial function \\ \texttt{\%} & element 2 mod element 1 \\ \texttt{S} & store element 2 in (variable) element 1 \\ \texttt{;} & evaluate variable to obtain contents \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% As a shortcut, function operators will automatically enter any data that you were in the process of entering. So instead of the sequence \texttt{22+}, you could type simply \texttt{22+} and the second number would be entered before the addition operation is applied. As an additional shortcut, any variable names used as function arguments will be evaluated before application of the function. In other words, it is not necessary to evaluate variables before performing arithmetic operations on them. \subsection{Executing Function Abbreviations} %@% IF LATEX %@% \label{abbrevfunctions} %@% END-IF %@% One could bind nearly all calculator operations to specific keypresses, but this would rapidly get confusing since the PC keyboard is not labeled as nicely as a calculator keyboard is. For this reason, Orpie includes an \emph{abbreviation} syntax. To activate an abbreviation, press '\texttt{'}' (quote key), followed by the first few letters/digits of the abbreviation, then hit enter. Orpie offers an autocompletion feature for abbreviations, so you only need to type enough of the operation to identify it uniquely. The matching abbreviations will appear in the left panel of the display, to assist you in finding the appropriate operation. To avoid interface conflicts, abbreviations may be entered only when the entry buffer (the bottom line of the screen) is empty. The following functions are available as abbreviations: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|l|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Abbreviations & Functions \\ \hline \texttt{inv} & inverse function \\ \texttt{pow} & raise element 2 to the power of element 1 \\ \texttt{sq} & square last element \\ \texttt{sqrt} & square root function \\ \texttt{abs} & absolute value function \\ \texttt{exp} & exponential function \\ \texttt{ln} & natural logarithm function \\ %@% IF LATEX %@% \texttt{10\^{}} & base 10 exponential function \\ %@% ELSE %@% \texttt{10\Circum} & base 10 exponential function \\ %@% END-IF %@% \texttt{log10} & base 10 logarithm function \\ \texttt{conj} & complex conjugate function \\ \texttt{sin} & sine function \\ \texttt{cos} & cosine function \\ \texttt{tan} & tangent function \\ \texttt{sinh} & hyperbolic sine function \\ \texttt{cosh} & hyperbolic cosine function \\ \texttt{tanh} & hyperbolic tangent function \\ \texttt{asin} & arcsine function \\ \texttt{acos} & arccosine function \\ \texttt{atan} & arctangent function \\ \texttt{asinh} & inverse hyperbolic sine function \\ \texttt{acosh} & inverse hyperbolic cosine function \\ \texttt{atanh} & inverse hyperbolic tangent function \\ \texttt{re} & real part of complex number \\ \texttt{im} & imaginary part of complex number \\ \texttt{gamma} & Euler gamma function \\ \texttt{lngamma} & natural log of Euler gamma function \\ \texttt{erf} & error function \\ \texttt{erfc} & complementary error function \\ \texttt{fact} & factorial function \\ \texttt{gcd} & greatest common divisor function \\ \texttt{lcm} & least common multiple function \\ \texttt{binom} & binomial coefficient function \\ \texttt{perm} & permutation function \\ %@% IF LATEX %@% %BEGIN LATEX \hline \end{tabular} \end{center} \begin{center} \begin{tabular}[t]{|l|l|} \hline Abbreviations (con't) & Functions \\ \hline %END LATEX %@% END-IF %@% \texttt{trans} & matrix transpose \\ \texttt{trace} & trace of a matrix \\ \texttt{solvelin} & solve a linear system of the form Ax = b \\ \texttt{mod} & element 2 mod element 1 \\ \texttt{floor} & floor function \\ \texttt{ceil} & ceiling function \\ \texttt{toint} & convert a real number to an integer type \\ \texttt{toreal} & convert an integer type to a real number \\ \texttt{add} & add last two elements \\ \texttt{sub} & subtract element 1 from element 2 \\ \texttt{mult} & multiply last two elements \\ \texttt{div} & divide element 2 by element 1 \\ \texttt{neg} & negate last element \\ \texttt{store} & store element 2 in (variable) element 1 \\ \texttt{eval} & evaluate variable to obtain contents \\ \texttt{purge} & delete a variable \\ \texttt{total} & sum the columns of a real matrix \\ \texttt{mean} & compute the sample means of the columns of a real matrix \\ \texttt{sumsq} & sum the squares of the columns of a real matrix \\ \texttt{var} & compute the unbiased sample variances of the columns of a real matrix \\ \texttt{varbias} & compute the biased (population) sample variances of the columns of a real matrix \\ \texttt{stdev} & compute the unbiased sample standard deviations of the columns of a real matrix \\ \texttt{stdevbias} & compute the biased (pop.) sample standard deviations of the columns of a matrix \\ \texttt{min} & find the minima of the columns of a real matrix \\ \texttt{max} & find the maxima of the columns of a real matrix \\ \texttt{utpn} & compute the upper tail probability of a normal distribution \\ \texttt{uconvert} & convert element 2 to an equivalent expression with units matching element 1 \\ \texttt{ustand} & convert to equivalent expression using SI standard base units \\ \texttt{uvalue} & drop the units of the last element \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% Entering abbreviations can become tedious when performing repetitive calculations. To save some keystrokes, Orpie will automatically bind recently-used operations with no prexisting binding to keys \texttt{-}. The current autobindings can be viewed by pressing \texttt{'h'} to cycle between the various pages of the help panel. \subsection{Executing Basic Command Operations} %@% IF LATEX %@% \label{rectpolar} \label{anglemode} \label{basemode} %@% END-IF %@% In addition to the function operations listed in %@% IF LATEX %@% Section \ref{functionops}, %@% ELSE %@% the section EXECUTING BASIC FUNCTION OPERATIONS, %@% END-IF %@% a number of basic calculator commands have been bound to single keypresses: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Keys & Operations \\ \hline %@% IF LATEX %@% \texttt{$\backslash$} & drop last element \\ %@% ELSE %@% \texttt{\Bs} & drop last element \\ %@% END-IF %@% \texttt{|} & clear all stack elements \\ \texttt{} & swap last two elements \\ \texttt{} & duplicate last element (when entry buffer is empty) \\ \texttt{u} & undo last operation \\ \texttt{r} & toggle angle mode between degrees and radians \\ \texttt{p} & toggle complex display mode between rectangular and polar \\ \texttt{b} & cycle base display mode between binary, octal, decimal, hex \\ \texttt{h} & cycle through multiple help windows \\ \texttt{v} & view last stack element in a fullscreen editor \\ \texttt{E} & create a new stack element using an external editor \\ %@% IF LATEX %@% %BEGIN LATEX \texttt{P} & enter $\pi$ on the stack \\ %END LATEX %HEVEA \texttt{P} & enter 3.1415\dots on the stack \\ %@% ELSE %@% \texttt{P} & enter 3.14159265 on the stack \\ %@% END-IF %@% \texttt{C-L} & refresh the display \\ \texttt{} & begin stack browsing mode \\ \texttt{Q} & quit Orpie \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% \subsection{Executing Command Abbreviations} In addition to the function operations listed in %@% IF LATEX %@% Section \ref{abbrevfunctions}, %@% ELSE %@% the section EXECUTING FUNCTION ABBREVIATIONS, %@% END-IF %@% there are a large number of calculator commands that have been implemented using the abbreviation syntax: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|l|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Abbreviations & Calculator Operation \\ \hline \texttt{drop} & drop last element \\ \texttt{clear} & clear all stack elements \\ \texttt{swap} & swap last two elements \\ \texttt{dup} & duplicate last element \\ \texttt{undo} & undo last operation \\ \texttt{rad} & set angle mode to radians \\ \texttt{deg} & set angle mode to degrees \\ \texttt{rect} & set complex display mode to rectangular \\ \texttt{polar} & set complex display mode to polar \\ \texttt{bin} & set base display mode to binary \\ \texttt{oct} & set base display mode to octal \\ \texttt{dec} & set base display mode to decimal \\ \texttt{hex} & set base display mode to hexidecimal \\ \texttt{view} & view last stack element in a fullscreen editor \\ \texttt{edit} & create a new stack element using an external editor \\ %@% IF LATEX %@% %BEGIN LATEX \texttt{pi} & enter $\pi$ on the stack \\ %END LATEX %HEVEA \texttt{pi} & enter 3.1415\dots on the stack \\ %@% ELSE %@% \texttt{pi} & enter 3.14159265 on the stack \\ %@% END-IF %@% \texttt{rand} & generate a random number between 0 and 1 (uniformly distributed) \\ \texttt{refresh} & refresh the display \\ \texttt{about} & display a nifty ``About Orpie'' screen \\ \texttt{quit} & quit Orpie \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% \subsection{Browsing the Stack} Orpie offers a \emph{stack browsing mode} to assist in viewing and manipulating stack data. Press \texttt{} to enter stack browsing mode; this should highlight the last stack element. You can use the up and down arrow keys to select different stack elements. The following keys are useful in stack browsing mode: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline Keys & Operations \\ \hline \texttt{q} & quit stack browsing mode \\ \texttt{} & scroll selected entry to the left \\ \texttt{} & scroll selected entry to the right \\ \texttt{r} & cyclically ``roll'' stack elements downward, below the selected element (inclusive) \\ \texttt{R} & cyclically ``roll'' stack elements upward, below the selected element (inclusive) \\ \texttt{v} & view the currently selected element in a fullscreen editor \\ \texttt{E} & edit the currently selected element with an external editor \\ \texttt{} & duplicate the currently selected element \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% The left and right scrolling option may prove useful for viewing very lengthy stack entries, such as large matrices. The edit option provides a convenient way to correct data after it has been entered on the stack. \subsection{Units Formatting} A units string is a list of units separated by '\texttt{*}' to indicate multiplication and '\texttt{/}' to indicate division. Units may be raised to real-valued powers %@% IF LATEX %@% \label{unitsformat} using the '\texttt{\^{}}' character. A contrived example of a valid unit string would be "\texttt{N*nm\^{}2*kg/s/in\^{}-3*GHz\^{}2.34}". %@% ELSE %@% using the '\texttt{\Circum}' character. A contrived example of a valid unit string would be "\texttt{N*nm\Circum2*kg/s/in\Circum-3*GHz\Circum2.34}". %@% END-IF %@% Orpie supports the standard SI prefix set, \texttt{\{y, z, a, f, p, n, u, m, c, d, da, h, k, M, G, T, P, E, Z, Y\}} (note the use of '\texttt{u}' for micro-). These prefixes may be applied to any of the following exhaustive sets of units: %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline String & Length Unit \\ \hline \texttt{m} & meter \\ \texttt{ft} & foot \\ \texttt{in} & inch \\ \texttt{yd} & yard \\ \texttt{mi} & mile \\ \texttt{pc} & parsec \\ \texttt{AU} & astronomical unit \\ \texttt{Ang} & angstrom \\ \texttt{furlong} & furlong \\ \texttt{pt} & PostScript point \\ \texttt{pica} & PostScript pica \\ \texttt{nmi} & nautical mile \\ \texttt{lyr} & lightyear \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline String & Mass Unit \\ \hline \texttt{g} & gram \\ \texttt{lb} & pound mass \\ \texttt{oz} & ounce \\ \texttt{slug} & slug \\ \texttt{lbt} & Troy pound \\ \texttt{ton} & (USA) short ton \\ \texttt{tonl} & (UK) long ton \\ \texttt{tonm} & metric ton \\ \texttt{ct} & carat \\ \texttt{gr} & grain \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline String & Time Unit \\ \hline \texttt{s} & second \\ \texttt{min} & minute \\ \texttt{hr} & hour \\ \texttt{day} & day \\ \texttt{yr} & year \\ \texttt{Hz} & Hertz \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline String & Temperature Unit \\ \hline \texttt{K} & Kelvin \\ \texttt{R} & Rankine \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% Note: No, Celsius and Fahrenheit will not be supported. Because these temperature units do not share a common zero point, their behavior is ill-defined under many operations. %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline String & ``Amount of Substance'' Unit \\ \hline \texttt{mol} & Mole \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline String & Force Unit \\ \hline \texttt{N} & Newton \\ \texttt{lbf} & pound force \\ \texttt{dyn} & dyne \\ \texttt{kip} & kip \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline String & Energy Unit \\ \hline \texttt{J} & Joule \\ \texttt{erg} & erg \\ \texttt{cal} & calorie \\ \texttt{BTU} & british thermal unit \\ \texttt{eV} & electron volt \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline String & Electrical Unit \\ \hline \texttt{A} & Ampere \\ \texttt{C} & Coulomb \\ \texttt{V} & volt \\ \texttt{Ohm} & Ohm \\ \texttt{F} & Farad \\ \texttt{H} & Henry \\ \texttt{T} & Tesla \\ \texttt{G} & Gauss \\ \texttt{Wb} & Weber \\ \texttt{Mx} & Maxwell \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline String & Power Unit \\ \hline \texttt{W} & Watt \\ \texttt{hp} & horsepower \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline String & Pressure Unit \\ \hline \texttt{Pa} & Pascal \\ \texttt{atm} & atmosphere \\ \texttt{bar} & bar \\ \texttt{Ohm} & Ohm \\ \texttt{mmHg} & millimeters of mercury \\ \texttt{inHg} & inches of mercury \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline String & Luminance Unit \\ \hline \texttt{cd} & candela \\ \texttt{lm} & lumen \\ \texttt{lx} & lux \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% Note: Although the lumen is defined by \texttt{1\_lm = 1\_cd * sr}, Orpie drops the steridian because it is a dimensionless unit and therefore is of questionable use to a calculator. %@% IF LATEX %@% \begin{center} \begin{tabular}[t]{|r|l|} %@% ELSE %@% \begin{Table}{2} %@% END-IF %@% \hline String & Volume Unit \\ \hline \texttt{ozfl} & fluid ounce (US) \\ \texttt{cup} & cup (US) \\ \texttt{pt} & pint (US) \\ \texttt{qt} & quart (US) \\ \texttt{gal} & gallon (US) \\ \texttt{L} & liter \\ \hline %@% IF LATEX %@% \end{tabular} \end{center} %@% ELSE %@% \end{Table} %@% END-IF %@% All units are defined in the Orpie run-configuration file; consult %@% IF LATEX %@% Section \ref{advanced} %@% ELSE %@% the \Cmd{orpierc}{5} manpage %@% END-IF %@% if you wish to define your own units or change the existing definitions. %@% END-IF %@% % (!ORPIERC) %@% IF LATEX || ORPIERC %@% %@% IF LATEX %@% \section{Advanced Configuration} \label{advanced} %@% ELSE %@% \section{Introduction} CAUTION: while this manpage should be suitable as a quick reference, it may be subject to miscellaneous shortcomings in typesetting. The definitive documentation is the user manual provided with Orpie in PDF format. %@% END-IF %@% Orpie reads a run-configuration textfile (generally \texttt{/etc/orpierc} or \texttt{/usr/local/etc/orpierc}) to determine key and command bindings. You can create a personalized configuration file in \texttt{\$HOME/.orpierc}, and select bindings that match your usage patterns. The recommended procedure is to ``include'' the \texttt{orpierc} file provided with Orpie %@% IF LATEX %@% (see Section \ref{include}), %@% ELSE %@% (see INCLUDING OTHER RCFILES), %@% END-IF %@% and add or remove settings as desired. %@% IF LATEX %@% \subsection{\texttt{orpierc} Syntax} %@% ELSE %@% \section{\texttt{orpierc} Syntax} %@% END-IF %@% You may notice that the \texttt{orpierc} syntax is similar to the syntax used in the configuration file for the Mutt email client (muttrc). Within the \texttt{orpierc} file, strings should be enclosed in double quotes (\texttt{"}). A double quote character inside a string may be represented by %@% IF LATEX %@% \texttt{$\backslash$"} . %@% ELSE %@% \Bs " . %@% END-IF %@% The backslash character must be represented by doubling it %@% IF LATEX %@% (\texttt{$\backslash\backslash$}). %@% ELSE %@% (\Bs\Bs). %@% END-IF %@% %@% IF LATEX %@% \subsubsection{Including Other Rcfiles} \label{include} Syntax: \texttt{include \emph{filename\_string}} \\ \\ %@% ELSE %@% \subsection{Including Other Rcfiles} Syntax: include \emph{filename\_string} \\ \\ %@% END-IF %@% This syntax can be used to include one run-configuration file within another. This command could be used to load the default \texttt{orpierc} file (probably found in \texttt{/etc/orpierc}) within your personalized rcfile, \texttt{\~{}/.orpierc}. The filename string should be enclosed in quotes. %@% IF LATEX %@% \subsubsection{Setting Configuration Variables} \label{setvar} Syntax: \texttt{set \emph{variable}=\emph{value\_string}} \\ \\ %@% ELSE %@% \subsection{Setting Configuration Variables} Syntax: set \emph{variable}=\emph{value\_string} \\ \\ %@% END-IF %@% Several configuration variables can be set using this syntax; check %@% IF LATEX %@% Section \ref{variables} %@% ELSE %@% the CONFIGURATION VARIABLES description %@% END-IF %@% to see a list. The variables are unquoted, but the values should be quoted strings. %@% IF LATEX %@% \subsubsection{Creating Key Bindings} \label{bindings} %@% ELSE %@% \subsection{Creating Key Bindings} %@% END-IF %@% Syntax: \texttt{bind \emph{key\_identifier operation}} \\ \\ This command will bind a keypress to execute a calculator operation. The various operations, which should not be enclosed in quotes, may be found in %@% IF LATEX %@% Section \ref{operationslist}. %@% ELSE %@% the section on CALCULATOR OPERATIONS. %@% END-IF %@% Key identifiers may be specified by strings that represent a single keypress, for example \texttt{"m"} (quotes included). The key may be prefixed with %@% IF LATEX %@% \texttt{"$\backslash\backslash$C"} or \texttt{"$\backslash\backslash$M"} %@% ELSE %@% "\Bs\Bs C" or "\Bs\Bs M" %@% END-IF %@% to represent Control or Meta (Alt) modifiers, respectively; note that the backslash must be doubled. A number of special keys lack single-character representations, so the following strings may be used to represent them: \begin{itemize} \item \texttt{""} \item \texttt{""} \item \texttt{""} \item \texttt{""} \item \texttt{""} \item \texttt{""} \item \texttt{""} \item \texttt{""} \item \texttt{""} \item \texttt{""} \item \texttt{""} \item \texttt{""} \item \texttt{""} \item \texttt{""} \item \texttt{""} to \texttt{""} \end{itemize} Due to differences between various terminal emulators, this key identifier syntax may not be adequate to describe every keypress. As a workaround, Orpie will also accept key identifiers in octal notation. As an example, you could use %@% IF LATEX %@% \texttt{$\backslash$024} %@% ELSE %@% \Bs 024 %@% END-IF %@% (do \emph{not} enclose it in quotes) to represent Ctrl-T. Orpie includes a secondary executable, \texttt{orpie-curses-keys}, that prints out the key identifiers associated with keypresses. You may find it useful when customizing \texttt{orpierc}. Multiple keys may be bound to the same operation, if desired. %@% IF LATEX %@% \subsubsection{Removing Key Bindings} \label{unbindings} %@% ELSE %@% \subsection{Removing Key Bindings} %@% END-IF %@% Syntax: \\ \texttt{unbind\_function \emph{key\_identifier}} \\ \texttt{unbind\_command \emph{key\_identifier}} \\ \texttt{unbind\_edit \emph{key\_identifier}} \\ \texttt{unbind\_browse \emph{key\_identifier}} \\ \texttt{unbind\_abbrev \emph{key\_identifier}} \\ \texttt{unbind\_variable \emph{key\_identifier}} \\ \texttt{unbind\_integer \emph{key\_identifier}} \\ \\ These commands will remove key bindings associated with the various entry modes (functions, commands, editing operations, etc.). The key identifiers should be defined using the syntax described in the previous section. %@% IF LATEX %@% \subsubsection{Creating Key Auto-Bindings} \label{autobindings} %@% ELSE %@% \subsection{Creating Key Auto-Bindings} %@% END-IF %@% Syntax: \texttt{autobind \emph{key\_identifier}} \\ \\ In order to make repetitive calculations more pleasant, Orpie offers an automatic key binding feature. When a function or command is executed using its abbreviation, one of the keys selected by the \texttt{autobind} syntax will be automatically bound to that operation (unless the operation has already been bound to a key). The current set of autobindings can be viewed in the help panel by executing \texttt{command\_cycle\_help} (bound to \texttt{'h'} by default). The syntax for the key identifiers is provided in the previous section. %@% IF LATEX %@% \subsubsection{Creating Operation Abbreviations} \label{abbreviations} %@% ELSE %@% \subsection{Creating Operation Abbreviations} %@% END-IF %@% Syntax: \texttt{abbrev \emph{operation\_abbreviation operation}} \\ \\ You can use this syntax to set the abbreviations used within Orpie to represent the various functions and commands. A list of available operations may be found in %@% IF LATEX %@% Section \ref{operationslist}. %@% ELSE %@% the CALCULATOR OPERATIONS section. %@% END-IF %@% The operation abbreviations should be quoted strings, for example \texttt{"sin"} or \texttt{"log"}. Orpie performs autocompletion on these abbreviations, allowing you to type usually just a few letters in order to select the desired command. The order of the autocompletion matches will be the same as the order in which the abbreviations are registered by the rcfile--so you may wish to place the more commonly used operation abbreviations earlier in the list. Multiple abbreviations may be bound to the same operation, if desired. %@% IF LATEX %@% \subsubsection{Removing Operation Abbreviations} \label{unabbreviations} %@% ELSE %@% \subsection{Removing Operation Abbreviations} %@% END-IF %@% Syntax: \texttt{unabbrev \emph{operation\_abbreviation}} \\ \\ This syntax can be used to remove an operation abbreviation. The operation abbreviations should be quoted strings, as described in the previous section. %@% IF LATEX %@% \subsubsection{Creating Macros} \label{macros} %@% ELSE %@% \subsection{Creating Macros} %@% END-IF %@% Syntax: \texttt{macro \emph{key\_identifier macro\_string}} \\ \\ You can use this syntax to cause a single keypress (the \emph{key\_identifier}) to be interpreted as the series of keypresses listed in \emph{macro\_string}. The syntax for defining a keypress is the same as that defined in %@% IF LATEX %@% Section \ref{bindings}. %@% ELSE %@% the section on CREATING KEY BINDINGS. %@% END-IF %@% The macro string should be a list of whitespace-separated keypresses, e.g. \texttt{"2 2 +"} (including quotes). This macro syntax provides a way to create small programs; by way of example, the default orpierc file includes macros for the base 2 logarithm and the binary entropy function (bound to \texttt{L} and \texttt{H}, respectively), as well as ``register'' variable shortcuts (\texttt{} to \texttt{}). Macros may call other macros recursively. However, take care that a macro does not call \emph{itself} recursively; Orpie will not trap the infinite loop. Note that operation abbreviations may be accessed within macros. For example, \texttt{macro "A" "' a b o u t "} would bind \texttt{A} to display the ``about Orpie'' screen. %@% IF LATEX %@% \subsubsection{Creating Units} \label{units} %@% ELSE %@% \subsection{Creating Units} %@% END-IF %@% Syntax: \\ \texttt{base\_unit \emph{unit\_symbol preferred\_prefix}} \\ \texttt{unit \emph{unit\_symbol unit\_definition}} \\ \\ Units are defined in a two-step process: \begin{enumerate} \item Define a set of orthogonal ``base units.'' All other units must be expressible in terms of these base units. The base units can be given a preferred SI prefix, which will be used whenever the units are standardized (e.g. via \texttt{ustand}). The unit symbols and preferred prefixes should all be quoted strings; to prefer \emph{no} prefix, use the empty string (\texttt{""}). It is expected that most users will use the fundamental SI units for base units. \item Define all other units in terms of either base units or previously-defined units. Again, the unit symbol and unit definition should be quoted strings. The definition should take the form of a numeric value followed by a units string, e.g. \texttt{"2.5\_kN*m/s"}. See %@% IF LATEX %@% Section \ref{unitsformat} %@% ELSE %@% the UNITS FORMATTING section %@% END-IF %@% for more details on the unit string format. \end{enumerate} %@% IF LATEX %@% \subsubsection{Creating Constants} \label{constants} %@% ELSE %@% \subsection{Creating Constants} %@% END-IF %@% Syntax: \texttt{constant \emph{constant\_symbol constant\_definition}} \\ \\ This syntax can be used to define a physical constant. Both the constant symbol and definition must be quoted strings. The constant definition should be a numeric constant followed by a units string e.g. \texttt{"1.60217733e-19\_C"}. All units used in the constant definition must already have been defined. %@% IF LATEX %@% \subsection{Configuration Variables} \label{variables} The following configuration variables may be set as described in Section \ref{setvar}: %@% ELSE %@% \section{Configuration Variables} The following configuration variables may be set as described in the SETTING CONFIGURATION VARIABLES section. %@% END-IF %@% \begin{itemize} \item \texttt{datadir} \\ This variable should be set to the full path of the Orpie data directory, which will contain the calculator state save file, temporary buffers, etc. The default directory is %@% IF LATEX %@% \texttt{"\~{}/.orpie/"}. %@% ELSE %@% "\Bs\Tilde /.orpie/". %@% END-IF %@% \item \texttt{editor} \\ This variable may be set to the fullscreen editor of your choice. The default value is \texttt{"vi"}. It is recommended that you choose an editor that offers horizontal scrolling in place of word wrapping, so that the columns of large matrices can be properly aligned. (The Vim editor could be used in this fashion by setting \texttt{editor} to \texttt{"vim -c 'set nowrap'"}.) \item \texttt{hide\_help} \\ Set this variable to \texttt{"true"} to hide the left help/status panel, or leave it on the default of \texttt{"false"} to display the help panel. \item \texttt{conserve\_memory} \\ Set this variable to \texttt{"true"} to minimize memory usage, or leave it on the default of \texttt{"false"} to improve rendering performance. (By default, Orpie caches multiple string representations of all stack elements. Very large integers in particular require significant computation for string representation, so caching these strings can make display updates much faster.) \end{itemize} %@% IF LATEX %@% \subsection{Calculator Operations} \label{operationslist} %@% ELSE %@% \section{Calculator Operations} %@% END-IF %@% Every calculator operation can be made available to the interface using the syntax described in %@% IF LATEX %@% Sections \ref{bindings} and \ref{abbreviations}. %@% ELSE %@% the sections on CREATING KEY BINDINGS and CREATING OPERATION ABBREVIATIONS. %@% END-IF %@% The following is a list of every available operation. %@% IF LATEX %@% \subsubsection{Functions} \label{functions} %@% ELSE %@% \subsection{Functions} %@% END-IF %@% The following operations are functions--that is, they will consume at least one argument from the stack. Orpie will generally abort the computation and provide an informative error message if a function cannot be successfully applied (for example, if you try to compute the transpose of something that is not a matrix). For the exact integer data type, basic arithmetic operations will yield an exact integer result. Division of two exact integers will yield the quotient of the division. The more complicated functions will generally promote the integer to a real number, and as such the arithmetic will no longer be exact. \begin{itemize} \item \texttt{function\_10\_x} \\ Raise 10 to the power of the last stack element (inverse of function\_log10). \item \texttt{function\_abs} \\ Compute the absolute value of the last stack element. \item \texttt{function\_acos} \\ Compute the inverse cosine of the last stack element. For real numbers, The result will be provided either in degrees or radians, depending on the angle mode of the calculator. \item \texttt{function\_acosh} \\ Compute the inverse hyperbolic cosine of the last stack element. \item \texttt{function\_add} \\ Add last two stack elements. \item \texttt{function\_arg} \\ Compute the argument (phase angle of complex number) of the last stack element. The value will be provided in either degrees or radians, depending on the current angle mode of the calculator. \item \texttt{function\_asin} \\ Compute the inverse sine of the last stack element. For real numbers, The result will be provided either in degrees or radians, depending on the angle mode of the calculator. \item \texttt{function\_asinh} \\ Compute the inverse hyperbolic sine of the last stack element. \item \texttt{function\_atan} \\ Compute the inverse tangent of the last stack element. For real numbers, The result will be provided either in degrees or radians, depending on the angle mode of the calculator. \item \texttt{function\_atanh} \\ Compute the inverse hyperbolic tangent of the last stack element. \item \texttt{function\_binomial\_coeff} \\ Compute the binomial coefficient (``n choose k'') formed by the last two stack elements. If these arguments are real, the coefficient is computed using a fast approximation to the log of the gamma function, and therefore the result is subject to rounding errors. For exact integer arguments, the coefficient is computed using exact arithmetic; this has the potential to be a slow operation. \item \texttt{function\_ceiling} \\ Compute the ceiling of the last stack element. \item \texttt{function\_convert\_units} \\ Convert stack element 2 to an equivalent expression in the units of element 1. Element 1 should be real-valued, and its magnitude will be ignored when computing the conversion. \item \texttt{function\_cos} \\ Compute the cosine of the last stack element. If the argument is real, it will be assumed to be either degrees or radians, depending on the angle mode of the calculator. \item \texttt{function\_cosh} \\ Compute the hyperbolic cosine of the last stack element. \item \texttt{function\_conj} \\ Compute the complex conjugate of the last stack element. \item \texttt{function\_div} \\ Divide element 2 by element 1. \item \texttt{function\_erf} \\ Compute the error function of the last stack element. \item \texttt{function\_erfc} \\ Compute the complementary error function of the last stack element. \item \texttt{function\_eval} \\ Obtain the contents of the variable in the last stack position. \item \texttt{function\_exp} \\ Evaluate the exponential function of the last stack element. \item \texttt{function\_factorial} \\ Compute the factorial of the last stack element. For a real argument, this is computed using a fast approximation to the gamma function, and therefore the result may be subject to rounding errors (or overflow). For an exact integer argument, the factorial is computed using exact arithmetic; this has the potential to be a slow operation. \item \texttt{function\_floor} \\ Compute the floor of the last stack element. \item \texttt{function\_gamma} \\ Compute the Euler gamma function of the last stack element. \item \texttt{function\_gcd} \\ Compute the greatest common divisor of the last two stack elements. This operation may be applied only to integer type data. \item \texttt{function\_im} \\ Compute the imaginary part of the last stack element. \item \texttt{function\_inv} \\ Compute the multiplicative inverse of the last stack element. \item \texttt{function\_lcm} \\ Compute the least common multiple of the last two stack elements. This operation may be applied only to integer type data. \item \texttt{function\_ln} \\ Compute the natural logarithm of the last stack element. \item \texttt{function\_lngamma} \\ Compute the natural logarithm of the Euler gamma function of the last stack element. \item \texttt{function\_log10} \\ Compute the base-10 logarithm of the last stack element. \item \texttt{function\_maximum} \\ Find the maximum values of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. \item \texttt{function\_minimum} \\ Find the minimum values of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. \item \texttt{function\_mean} \\ Compute the sample means of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. \item \texttt{function\_mod} \\ Compute element 2 mod element 1. This operation can be applied only to integer type data. \item \texttt{function\_mult} \\ Multiply last two stack elements. \item \texttt{function\_neg} \\ Negate last stack element. \item \texttt{function\_permutation} \\ Compute the permutation coefficient determined by the last two stack elements 'n' and 'k': the number of ways of obtaining an ordered subset of k elements from a set of n elements. If these arguments are real, the coefficient is computed using a fast approximation to the log of the gamma function, and therefore the result is subject to rounding errors. For exact integer arguments, the coefficient is computed using exact arithmetic; this has the potential to be a slow operation. \item \texttt{function\_pow} \\ Raise element 2 to the power of element 1. \item \texttt{function\_purge} \\ Delete the variable in the last stack position. \item \texttt{function\_re} \\ Compute the real part of the last stack element. \item \texttt{function\_sin} \\ Compute the sine of the last stack element. If the argument is real, it will be assumed to be either degrees or radians, depending on the angle mode of the calculator. \item \texttt{function\_sinh} \\ Compute the hyperbolic sine of the last stack element. \item \texttt{function\_solve\_linear} \\ Solve a linear system of the form Ax = b, where A and b are the last two elements on the stack. A must be a square matrix and b must be a matrix with one column. This function does not compute inv(A), but obtains the solution by a more efficient LU decomposition method. This function is recommended over explicitly computing the inverse, especially when solving linear systems with relatively large dimension or with poorly conditioned matrices. \item \texttt{function\_sq} \\ Square the last stack element. \item \texttt{function\_sqrt} \\ Compute the square root of the last stack element. \item \texttt{function\_standardize\_units} \\ Convert the last stack element to an equivalent expression using the SI standard base units (kg, m, s, etc.). \item \texttt{function\_stdev\_unbiased} \\ Compute the unbiased sample standard deviation of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. (Compare to HP48's \texttt{sdev} function.) \item \texttt{function\_stdev\_biased} \\ Compute the biased (population) sample standard deviation of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. (Compare to HP48's \texttt{psdev} function.) \item \texttt{function\_store} \\ Store element 2 in (variable) element 1. \item \texttt{function\_sub} \\ Subtract element 1 from element 2. \item \texttt{function\_sumsq} \\ Sum the squares of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. \item \texttt{function\_tan} \\ Compute the tangent of the last stack element. If the argument is real, it will be assumed to be either degrees or radians, depending on the angle mode of the calculator. \item \texttt{function\_tanh} \\ Compute the hyperbolic tangent of the last stack element. \item \texttt{function\_to\_int} \\ Convert a real number to an integer type. \item \texttt{function\_to\_real} \\ Convert an integer type to a real number. \item \texttt{function\_total} \\ Sum each of the columns of a real NxM matrix, returning a 1xM matrix as a result. \item \texttt{function\_trace} \\ Compute the trace of a square matrix. \item \texttt{function\_transpose} \\ Compute the matrix transpose of the last stack element. \item \texttt{function\_unit\_value} \\ Drop the units of the last stack element. \item \texttt{function\_utpn} \\ Compute the upper tail probability of a normal distribution. \\ %@% IF LATEX %@% %BEGIN LATEX $utpn(m, v, x) = \int_x^\infty \frac{1}{\sqrt{2 \pi v}} \exp \left(- \frac{(m - y)^2}{2 v} \right) \,dy$ %END LATEX %HEVEA UTPN(m, v, x) = Integrate[ 1/Sqrt[2 Pi v] Exp[-(m-y)${}^2$/(2 v)], \{y, x, Infinity\}] %@% ELSE %@% UTPN(m, v, x) = Integrate[ 1/Sqrt[2 Pi v] Exp[-(m-y)^2/(2 v)], {y, x, Infinity}] %@% END-IF %@% \item \texttt{function\_var\_unbiased} \\ Compute the unbiased sample variance of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. (Compare to HP48's \texttt{var} function.) \item \texttt{function\_var\_biased} \\ Compute the biased (population) sample variance of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. (Compare to HP48's \texttt{pvar} function.) \end{itemize} %@% IF LATEX %@% \subsubsection{Commands} \label{commands} %@% ELSE %@% \subsection{Commands} %@% END-IF %@% The following operations are referred to as commands; they differ from functions because they do not take an argument. Many calculator interface settings are implemented as commands. \begin{itemize} \item \texttt{command\_about} \\ Display a nifty ``about Orpie'' credits screen. \item \texttt{command\_begin\_abbrev} \\ Begin entry of an operation abbreviation. \item \texttt{command\_begin\_browsing} \\ Enter stack browsing mode. \item \texttt{command\_begin\_constant} \\ Begin entry of a physical constant. \item \texttt{command\_begin\_variable} \\ Begin entry of a variable name. \item \texttt{command\_bin} \\ Set the base of exact integer representation to 2 (binary). \item \texttt{command\_clear} \\ Clear all elements from the stack. \item \texttt{command\_cycle\_base} \\ Cycle the base of exact integer representation between 2, 8, 10, and 16 (bin, oct, dec, and hex). \item \texttt{command\_cycle\_help} \\ Cycle through multiple help pages. The first page displays commonly used bindings, and the second page displays the current autobindings. \item \texttt{command\_dec} \\ Set the base of exact integer representation to 10 (decimal). \item \texttt{command\_deg} \\ Set the angle mode to degrees. \item \texttt{command\_drop} \\ Drop the last element off the stack. \item \texttt{command\_dup} \\ Duplicate the last stack element. \item \texttt{command\_enter\_pi} \\ %@% IF LATEX %@% %BEGIN LATEX Enter $\pi$ on the stack. %END LATEX %HEVEA Enter 3.1415\dots on the stack. %@% ELSE %@% Enter 3.1415\Dots on the stack. %@% END-IF %@% \item \texttt{command\_hex} \\ Set the base of exact integer representation to 16 (hexadecimal). \item \texttt{command\_oct} \\ Set the base of exact integer representation to 8 (octal). \item \texttt{command\_polar} \\ Set the complex display mode to polar. \item \texttt{command\_rad} \\ Set the angle mode to radians. \item \texttt{command\_rand} \\ Generate a random real-valued number between 0 (inclusive) and 1 (exclusive). The deviates are uniformly distributed. \item \texttt{command\_rect} \\ Set the complex display mode to rectangular (cartesian). \item \texttt{command\_refresh} \\ Refresh the display. \item \texttt{command\_swap} \\ Swap stack elements 1 and 2. \item \texttt{command\_quit} \\ Quit Orpie. \item \texttt{command\_toggle\_angle\_mode} \\ Toggle the angle mode between degrees and radians. \item \texttt{command\_toggle\_complex\_mode} \\ Toggle the complex display mode between rectangular and polar. \item \texttt{command\_undo} \\ Undo the last calculator operation. \item \texttt{command\_view} \\ View the last stack element in an external fullscreen editor. \item \texttt{command\_edit\_input} \\ Create a new stack element using an external editor. \end{itemize} %@% IF LATEX %@% \subsubsection{Edit Operations} \label{edits} %@% ELSE %@% \subsection{Edit Operations} %@% END-IF %@% The following operations are related to editing during data entry. These commands cannot be made available as operation abbreviations, since abbreviations are not accessible while entering data. These operations should be made available as single keypresses using the \texttt{bind} keyword. \begin{itemize} \item \texttt{edit\_angle} \\ Begin entering the phase angle of a complex number. (Orpie will assume the angle is in either degrees or radians, depending on the current angle mode.) \item \texttt{edit\_backspace} \\ Delete the last character entered. \item \texttt{edit\_begin\_integer} \\ Begin entering an exact integer. \item \texttt{edit\_begin\_units} \\ Begin appending units to a numeric expression. \item \texttt{edit\_complex} \\ Begin entering a complex number. \item \texttt{edit\_enter} \\ Enter the data that is currently being edited. \item \texttt{edit\_matrix} \\ Begin entering a matrix, or begin entering the next row of a matrix. \item \texttt{edit\_minus} \\ Enter a minus sign in input. \item \texttt{edit\_scientific\_notation\_base} \\ Begin entering the scientific notation exponent of a real number, or the base of an exact integer. \item \texttt{edit\_separator} \\ Begin editing the next element of a complex number or matrix. (This will insert a comma between elements.) \end{itemize} %@% IF LATEX %@% \subsubsection{Browsing Operations} \label{browse} %@% ELSE %@% \subsection{Browsing Operations} %@% END-IF %@% The following list of operations is available only in stack browsing mode. As abbreviations are unavailable while browsing the stack, these operations should be bound to single keypresses using the \texttt{bind} keyword. \begin{itemize} \item \texttt{browse\_echo} \\ Echo the currently selected element to stack level 1. \item \texttt{browse\_end} \\ Exit stack browsing mode. \item \texttt{browse\_drop} \\ Drop the currently selected stack element. \item \texttt{browse\_dropn} \\ Drop all stack elements below the current selection (inclusive). \item \texttt{browse\_keep} \\ Drop all stack elements \emph{except} the current selection. (This is complementary to \texttt{browse\_drop}. \item \texttt{browse\_keepn} \\ Drop all stack elements above the current selection (non-inclusive). (This is complementary to \texttt{browse\_dropn}. \item \texttt{browse\_next\_line} \\ Move the selection cursor down one line. \item \texttt{browse\_prev\_line} \\ Move the selection cursor up one line. \item \texttt{browse\_rolldown} \\ Cyclically ``roll'' stack elements downward, below the selected element (inclusive). \item \texttt{browse\_rollup} \\ Cyclically ``roll'' stack elements upward, below the selected element (inclusive) . \item \texttt{browse\_scroll\_left} \\ Scroll the selected element to the left (for viewing very large entries such as matrices). \item \texttt{browse\_scroll\_right} \\ Scroll the selected element to the right. \item \texttt{browse\_view} \\ View the currently selected stack element in a fullscreen editor. \item \texttt{browse\_edit} \\ Edit the currently selected stack element using an external editor. \end{itemize} %@% IF LATEX %@% \subsubsection{Abbreviation Entry Operations} \label{abbrevoperations} %@% ELSE %@% \subsection{Abbreviation Entry Operations} %@% END-IF %@% The following list of operations is available only while entering a function or command abbreviation, or while entering a physical constant. These operations must be bound to single keypresses using the \texttt{bind} keyword. \begin{itemize} \item \texttt{abbrev\_backspace} \\ Delete a character from the abbreviation string. \item \texttt{abbrev\_enter} \\ Execute the operation associated with the selected abbreviation. \item \texttt{abbrev\_exit} \\ Cancel abbreviation entry. \end{itemize} %@% IF LATEX %@% \subsubsection{Variable Entry Operations} \label{variableoperations} %@% ELSE %@% \subsection{Variable Entry Operations} %@% END-IF %@% The following list of operations is available only while entering a variable name. As abbreviations are unavailable while entering variables, these operations should be bound to single keypresses using the \texttt{bind} keyword. \begin{itemize} \item \texttt{variable\_backspace} \\ Delete a character from the variable name. \item \texttt{variable\_cancel} \\ Cancel entry of the variable name. \item \texttt{variable\_complete} \\ Autocomplete the variable name. \item \texttt{variable\_enter} \\ Enter the variable name on the stack. \end{itemize} %@% IF LATEX %@% \subsubsection{Integer Entry Operations} \label{integeroperations} %@% ELSE %@% \subsection{Integer Entry Operations} %@% END-IF %@% The following operation is available only while entering an integer; it can be made accessible by binding it to a single keypress using the \texttt{bind} keyword. \begin{itemize} \item \texttt{integer\_cancel} \\ Cancel entry of an integer. \end{itemize} % end ORPIERC %@% END-IF %@% %@% IF !ORPIERC %@% \section{Licensing} Orpie is Free Software; you can redistribute it and/or modify it under the terms of the GNU General Public License (GPL), Version 3, as published by the Free Software Foundation. You should have received a copy of the GPL along with this program, in the file ``LICENSE.md''. \section{Contact info} Orpie author: Paul Pelzl \texttt{} \\ %@% IF LATEX %@% Orpie website: \texttt{https://github.com/pelzlpj/orpie} \\ %@% ELSE %@% Orpie website: \URL{https://github.com/pelzlpj/orpie} \\ %@% END-IF %@% %@% IF !LATEX && !ORPIERC %@% \section{See Also} \Cmd{orpierc}{5}, \Cmd{orpie-curses-keys}{1} %@% END-IF %@% %@% END-IF %@% % !ORPIERC %@% IF !LATEX %@% %@% IF ORPIERC %@% \section{See Also} \Cmd{orpie}{1}, \Cmd{orpie-curses-keys}{1} \section{Author} This manpage is written by Paul J. Pelzl . %@% END-IF %@% \LatexManEnd %@% END-IF %@% \end{document} % arch-tag: DO_NOT_CHANGE_db7ed8b2-8ea4-4e32-b0f6-50482487cb00 orpie-release-1.6.0/doc/orpie-curses-keys.1000066400000000000000000000017151334120314700204740ustar00rootroot00000000000000'\" t .\" Manual page created with latex2man on Mon Aug 27 22:53:47 PDT 2018 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW .nf .. .de Ve .ft R .fi .. .TH "ORPIE\-CURSES\-KEYS" "1" "27 August 2018" "a configuration utility for orpierc " "a configuration utility for orpierc " .SH NAME orpie\-curses\-keys is a small utility designed to assist in the creation of an \fIorpierc\fP(5) file. .PP .SH SYNOPSIS orpie\-curses\-keys .PP .SH DESCRIPTION orpie\-curses\-keys is a small interactive utility designed to help users of the \fIorpie\fP(1) calculator to create a customized \fIorpierc\fP(5) file. orpie\-curses\-keys translates keypresses into octal and string representations that can be used with the \&'bind\&' command inside orpierc. Ctrl\-C will exit the program. .PP .SH SEE ALSO \fIorpie\fP(1), \fIorpierc\fP(5) .PP .SH AUTHOR This manpage is written by Paul J. Pelzl . .PP .\" NOTE: This file is generated, DO NOT EDIT. orpie-release-1.6.0/doc/orpie-curses-keys.tex000066400000000000000000000021611334120314700211300ustar00rootroot00000000000000% orpie-curses-keys manpage % % Notes: % This document is designed to be processed with the 'latex2man' perl % script. \documentclass[11pt,notitlepage]{article} \usepackage{latex2man} \setVersion{1.0} % end preamble %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{Name}{1}{orpie-curses-keys}{Paul J. Pelzl}{a configuration utility for orpierc}{orpie-curses-keys manpage} \Prog{orpie-curses-keys} is a small utility designed to assist in the creation of an \Cmd{orpierc}{5} file. \end{Name} \section{Synopsis} \Prog{orpie-curses-keys} \section{Description} \Prog{orpie-curses-keys} is a small interactive utility designed to help users of the \Cmd{orpie}{1} calculator to create a customized \Cmd{orpierc}{5} file. \Prog{orpie-curses-keys} translates keypresses into octal and string representations that can be used with the 'bind' command inside orpierc. Ctrl-C will exit the program. \section{See Also} \Cmd{orpie}{1}, \Cmd{orpierc}{5} \section{Author} This manpage is written by Paul J. Pelzl . \LatexManEnd % arch-tag: DO_NOT_CHANGE_d7f0503b-18fb-4c3a-b7cc-1a96db5322a3 orpie-release-1.6.0/doc/orpie.1000066400000000000000000000547341334120314700162320ustar00rootroot00000000000000'\" t .\" Manual page created with latex2man on Tue Aug 28 01:19:13 PDT 2018 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW .nf .. .de Ve .ft R .fi .. .TH "ORPIE" "1" "28 August 2018" "a console\-based RPN calculator " "a console\-based RPN calculator " .SH NAME orpie is a console\-based RPN calculator with an interactive visual stack. .PP .SH SYNOPSIS orpie .PP .SH QUICK START CAUTION: while this manpage should be suitable as a quick reference, it may be subject to miscellaneous shortcomings in typesetting. The definitive documentation is the user manual provided with Orpie in PDF format. .PP This section describes how to use Orpie in its default configuration. After familiarizing yourself with the basic operations as outlined in this section, you may wish to consult the \fIorpierc\fP(5) manpage to see how Orpie can be configured to better fit your needs. .PP .SS OVERVIEW The interface has two panels. The left panel combines status information with context\-sensitive help; the right panel represents the calculator\&'s stack. (Note that the left panel will be hidden if Orpie is run in a terminal with less than 80 columns.) .PP In general, you perform calculations by first entering data on to the stack, then executing functions that operate on the stack data. As an example, you can hit 12+ in order to add 1 and 2. .PP .SS ENTERING REAL NUMBERS To enter a real number, just type the desired digits and hit enter. The space bar will begin entry of a scientific notation exponent. The \&'n\&' key is used for negation. Here are some examples: .PP .TS tab(&); l l. T{ _ Keypresses T}&T{ Resulting Entry T} T{ _ 1.23 T}&T{ 1.23 T} T{ 1.2323n T}&T{ 1.23e\-23 T} T{ 1.23n23 T}&T{ \-1.23e23 T} T{ _ .TE .PP .SS ENTERING COMPLEX NUMBERS Orpie can represent complex numbers using either cartesian (rectangular) or polar coordinates. See PERFORMING BASIC COMMAND OPERATIONS to see how to change the complex number display mode. .PP A complex number is entered by first pressing \&'(\&', then entering the real part, then pressing \&',\&' followed by the imaginary part. Alternatively, you can press \&'(\&' followed by the magnitude, then \&'<\&' followed by the phase angle. The angle will be interpreted in degrees or radians, depending on the current setting of the angle mode (see PERFORMING BASIC COMMAND OPERATIONS). Examples: .PP .TS tab(&); l l. T{ _ Keypresses T}&T{ Resulting Entry T} T{ _ (1.23, 4.56 T}&T{ (1.23, 4.56) T} T{ (0.7072<45 T}&T{ (0.500065915655126, 0.50006591... T} T{ (1.23n,4.5610 T}&T{ (\-1.23, 45600000000) T} T{ _ .TE .PP .SS ENTERING MATRICES You can enter matrices by pressing \&'[\&'\&. The elements of the matrix may then be entered as described in the previous sections, and should be separated using \&',\&'\&. To start a new row of the matrix, press \&'[\&' again. On the stack, each row of the matrix is enclosed in a set of brackets; for example, the matrix .PP .TS tab(&); l l. T{ 1 T}&T{ 2 T} T{ 3 T}&T{ 4 .TE .PP would appear on the stack as [[1, 2][3, 4]]. .PP Examples of matrix entry: .PP .TS tab(&); l l. T{ _ Keypresses T}&T{ Resulting Entry T} T{ _ [1,2[3,4 T}&T{ [[1, 2][3, 4]] T} T{ [1.210,0[3n,5n T}&T{ [[ 12000000000, 0 ][ \-3, \-5 ]] T} T{ [(1,2,3,4[5,6,7,8 T}&T{ [[ (1, 2), (3, 4) ][ (5, 6), (... T} T{ _ .TE .PP .SS ENTERING DATA WITH UNITS Real and complex scalars and matrices can optionally be labeled with units. After typing in the numeric portion of the data, press \&'_\&' followed by a units string. The format of units strings is described in the UNITS FORMATTING section. .PP Examples of entering dimensioned data: .PP .TS tab(&); l l. T{ _ Keypresses T}&T{ Resulting Entry T} T{ _ 1.234_N*mm^2/s T}&T{ 1.234_N*mm^2*s^\-1 T} T{ (2.3,5_s^\-4 T}&T{ (2.3, 5)_s^\-4 T} T{ [1,2[3,4_lbf*in T}&T{ [[ 1, 2 ][ 3, 4 ]]_lbf*in T} T{ _nm T}&T{ 1_nm T} T{ _ .TE .PP .SS ENTERING EXACT INTEGERS An exact integer may be entered by pressing \&'#\&' followed by the desired digits. The base of the integer will be assumed to be the same as the current calculator base mode (see PERFORMING BASIC COMMAND OPERATIONS to see how to set this mode). Alternatively, the desired base may be specified by pressing space and appending one of {b, o, d, h}, to represent binary, octal, decimal, or hexadecimal, respectively. On the stack, the representation of the integer will be changed to match the current base mode. Examples: .PP .TS tab(&); l l. T{ _ Keypresses T}&T{ Resulting Entry T} T{ _ #123456 T}&T{ #123456`d T} T{ #ffffh T}&T{ #65535`d T} T{ #10101nb T}&T{ #\-21`d T} T{ _ .TE .PP Note that exact integers may have unlimited length, and the basic arithmetic operations (addition, subtraction, multiplication, division) will be performed using exact arithmetic when both arguments are integers. .PP .SS ENTERING VARIABLE NAMES A variable name may be entered by pressing \&'@\&' followed by the desired variable name string. The string may contain alphanumeric characters, dashes, and underscores. Example: .PP .TS tab(&); l l. T{ _ Keypresses T}&T{ Resulting Entry T} T{ _ @myvar T}&T{ @ myvar T} T{ _ .TE .PP Orpie also supports autocompletion of variable names. The help panel displays a list of pre\-existing variables that partially match the name currently being entered. You can press \&'\&' to iterate through the list of matching variables. .PP As a shortcut, keys \- will enter the variables (``registers\&'') @ r01 through @ r04. .PP .SS ENTERING PHYSICAL CONSTANTS Orpie includes definitions for a number of fundamental physical constants. To enter a constant, press \&'C\&', followed by the first few letters/digits of the constant\&'s symbol, then hit enter. Orpie offers an autocompletion feature for physical constants, so you only need to type enough of the constant to identify it uniquely. A list of matching constants will appear in the left panel of the display, to assist you in finding the desired choice. .PP The following is a list of Orpie\&'s physical constant symbols: .PP .TS tab(&); l l. T{ _ Symbol T}&T{ Physical Constant T} T{ _ NA T}&T{ Avagadro\&'s number T} T{ k T}&T{ Boltzmann constant T} T{ Vm T}&T{ molar volume T} T{ R T}&T{ universal gas constant T} T{ stdT T}&T{ standard temperature T} T{ stdP T}&T{ standard pressure T} T{ sigma T}&T{ Stefan\-Boltzmann constant T} T{ c T}&T{ speed of light T} T{ eps0 T}&T{ permittivity of free space T} T{ u0 T}&T{ permeability of free space T} T{ g T}&T{ acceleration of gravity T} T{ G T}&T{ Newtonian gravitational constant T} T{ h T}&T{ Planck\&'s constant T} T{ hbar T}&T{ Dirac\&'s constant T} T{ e T}&T{ electron charge T} T{ me T}&T{ electron mass T} T{ mp T}&T{ proton mass T} T{ alpha T}&T{ fine structure constant T} T{ phi T}&T{ magnetic flux quantum T} T{ F T}&T{ Faraday\&'s constant T} T{ Rinf T}&T{ ``infinity\&'' Rydberg constant T} T{ a0 T}&T{ Bohr radius T} T{ uB T}&T{ Bohr magneton T} T{ uN T}&T{ nuclear magneton T} T{ lam0 T}&T{ wavelength of a 1eV photon T} T{ f0 T}&T{ frequency of a 1eV photon T} T{ lamc T}&T{ Compton wavelength T} T{ c3 T}&T{ Wien\&'s constant T} T{ _ .TE .PP All physical constants are defined in the Orpie run\-configuration file; consult the \fIorpierc\fP(5) manpage if you wish to define your own constants or change the existing definitions. .PP .SS ENTERING DATA WITH AN EXTERNAL EDITOR Orpie can also parse input entered via an external editor. You may find this to be a convenient method for entering large matrices. Pressing \&'E\&' will launch the external editor, and the various data types may be entered as illustrated by the examples below: .PP .TS tab(&); l l. T{ _ Data Type T}&T{ Sample Input String T} T{ _ exact integer T}&T{ #12345678`d, where the trailing letter is one of the base characters {b, o, d, h} T} T{ real number T}&T{ \-123.45e67 T} T{ complex number T}&T{ (1e10, 2) or (1 <90) T} T{ real matrix T}&T{ [[1, 2][3.1, 4.5e10]] T} T{ complex matrix T}&T{ [[(1, 0), 5][1e10, (2 <90)]] T} T{ variable T}&T{ @myvar T} T{ _ .TE .PP Real and complex numbers and matrices may have units appended; just add a units string such as ``_N*m/s\&'' immediately following the numeric portion of the expression. .PP Notice that the complex matrix input parser is quite flexible; real and complex matrix elements may be mixed, and cartesian and polar complex formats may be mixed as well. .PP Multiple stack entries may be specified in the same file, if they are separated by whitespace. For example, entering (1, 2) 1.5 into the editor will cause the complex value (1, 2) to be placed on the stack, followed by the real value 1.5. .PP The input parser will discard whitespace where possible, so feel free to add any form of whitespace between matrix rows, matrix elements, real and complex components, etc. .PP .SS EXECUTING BASIC FUNCTION OPERATIONS Once some data has been entered on the stack, you can apply operations to that data. For example, \&'+\&' will add the last two elements on the stack. By default, the following keys have been bound to such operations: .PP .TS tab(&); l l. T{ _ Keys T}&T{ Operations T} T{ _ + T}&T{ add last two stack elements T} T{ \- T}&T{ subtract element 1 from element 2 T} T{ * T}&T{ multiply last two stack elements T} T{ / T}&T{ divide element 2 by element 1 T} T{ ^T}&T{ raise element 2 to the power of element 1 T} T{ n T}&T{ negate last element T} T{ i T}&T{ invert last element T} T{ s T}&T{ square root function T} T{ a T}&T{ absolute value function T} T{ e T}&T{ exponential function T} T{ l T}&T{ natural logarithm function T} T{ c T}&T{ complex conjugate function T} T{ ! T}&T{ factorial function T} T{ %T}&T{ element 2 mod element 1 T} T{ S T}&T{ store element 2 in (variable) element 1 T} T{ ; T}&T{ evaluate variable to obtain contents T} T{ _ .TE .PP As a shortcut, function operators will automatically enter any data that you were in the process of entering. So instead of the sequence 22+, you could type simply 22+ and the second number would be entered before the addition operation is applied. .PP As an additional shortcut, any variable names used as function arguments will be evaluated before application of the function. In other words, it is not necessary to evaluate variables before performing arithmetic operations on them. .PP .SS EXECUTING FUNCTION ABBREVIATIONS One could bind nearly all calculator operations to specific keypresses, but this would rapidly get confusing since the PC keyboard is not labeled as nicely as a calculator keyboard is. For this reason, Orpie includes an \fIabbreviation\fP syntax. .PP To activate an abbreviation, press \&''' (quote key), followed by the first few letters/digits of the abbreviation, then hit enter. Orpie offers an autocompletion feature for abbreviations, so you only need to type enough of the operation to identify it uniquely. The matching abbreviations will appear in the left panel of the display, to assist you in finding the appropriate operation. .PP To avoid interface conflicts, abbreviations may be entered only when the entry buffer (the bottom line of the screen) is empty. .PP The following functions are available as abbreviations: .PP .TS tab(&); l l. T{ _ Abbreviations T}&T{ Functions T} T{ _ inv T}&T{ inverse function T} T{ pow T}&T{ raise element 2 to the power of element 1 T} T{ sq T}&T{ square last element T} T{ sqrt T}&T{ square root function T} T{ abs T}&T{ absolute value function T} T{ exp T}&T{ exponential function T} T{ ln T}&T{ natural logarithm function T} T{ 10^ T}&T{ base 10 exponential function T} T{ log10 T}&T{ base 10 logarithm function T} T{ conj T}&T{ complex conjugate function T} T{ sin T}&T{ sine function T} T{ cos T}&T{ cosine function T} T{ tan T}&T{ tangent function T} T{ sinh T}&T{ hyperbolic sine function T} T{ cosh T}&T{ hyperbolic cosine function T} T{ tanh T}&T{ hyperbolic tangent function T} T{ asin T}&T{ arcsine function T} T{ acos T}&T{ arccosine function T} T{ atan T}&T{ arctangent function T} T{ asinh T}&T{ inverse hyperbolic sine function T} T{ acosh T}&T{ inverse hyperbolic cosine function T} T{ atanh T}&T{ inverse hyperbolic tangent function T} T{ re T}&T{ real part of complex number T} T{ im T}&T{ imaginary part of complex number T} T{ gamma T}&T{ Euler gamma function T} T{ lngamma T}&T{ natural log of Euler gamma function T} T{ erf T}&T{ error function T} T{ erfc T}&T{ complementary error function T} T{ fact T}&T{ factorial function T} T{ gcd T}&T{ greatest common divisor function T} T{ lcm T}&T{ least common multiple function T} T{ binom T}&T{ binomial coefficient function T} T{ perm T}&T{ permutation function T} T{ trans T}&T{ matrix transpose T} T{ trace T}&T{ trace of a matrix T} T{ solvelin T}&T{ solve a linear system of the form Ax = b T} T{ mod T}&T{ element 2 mod element 1 T} T{ floor T}&T{ floor function T} T{ ceil T}&T{ ceiling function T} T{ toint T}&T{ convert a real number to an integer type T} T{ toreal T}&T{ convert an integer type to a real number T} T{ add T}&T{ add last two elements T} T{ sub T}&T{ subtract element 1 from element 2 T} T{ mult T}&T{ multiply last two elements T} T{ div T}&T{ divide element 2 by element 1 T} T{ neg T}&T{ negate last element T} T{ store T}&T{ store element 2 in (variable) element 1 T} T{ eval T}&T{ evaluate variable to obtain contents T} T{ purge T}&T{ delete a variable T} T{ total T}&T{ sum the columns of a real matrix T} T{ mean T}&T{ compute the sample means of the columns of a real matrix T} T{ sumsq T}&T{ sum the squares of the columns of a real matrix T} T{ var T}&T{ compute the unbiased sample variances of the columns of a real matrix T} T{ varbias T}&T{ compute the biased (population) sample variances of the columns of a real matrix T} T{ stdev T}&T{ compute the unbiased sample standard deviations of the columns of a real matrix T} T{ stdevbias T}&T{ compute the biased (pop.) sample standard deviations of the columns of a matrix T} T{ min T}&T{ find the minima of the columns of a real matrix T} T{ max T}&T{ find the maxima of the columns of a real matrix T} T{ utpn T}&T{ compute the upper tail probability of a normal distribution T} T{ uconvert T}&T{ convert element 2 to an equivalent expression with units matching element 1 T} T{ ustand T}&T{ convert to equivalent expression using SI standard base units T} T{ uvalue T}&T{ drop the units of the last element T} T{ _ .TE .PP Entering abbreviations can become tedious when performing repetitive calculations. To save some keystrokes, Orpie will automatically bind recently\-used operations with no prexisting binding to keys \-. The current autobindings can be viewed by pressing \&'h\&' to cycle between the various pages of the help panel. .PP .SS EXECUTING BASIC COMMAND OPERATIONS In addition to the function operations listed in the section EXECUTING BASIC FUNCTION OPERATIONS, a number of basic calculator commands have been bound to single keypresses: .PP .TS tab(&); l l. T{ _ Keys T}&T{ Operations T} T{ _ \\T}&T{ drop last element T} T{ | T}&T{ clear all stack elements T} T{ T}&T{ swap last two elements T} T{ T}&T{ duplicate last element (when entry buffer is empty) T} T{ u T}&T{ undo last operation T} T{ r T}&T{ toggle angle mode between degrees and radians T} T{ p T}&T{ toggle complex display mode between rectangular and polar T} T{ b T}&T{ cycle base display mode between binary, octal, decimal, hex T} T{ h T}&T{ cycle through multiple help windows T} T{ v T}&T{ view last stack element in a fullscreen editor T} T{ E T}&T{ create a new stack element using an external editor T} T{ P T}&T{ enter 3.14159265 on the stack T} T{ C\-L T}&T{ refresh the display T} T{ T}&T{ begin stack browsing mode T} T{ Q T}&T{ quit Orpie T} T{ _ .TE .PP .SS EXECUTING COMMAND ABBREVIATIONS In addition to the function operations listed in the section EXECUTING FUNCTION ABBREVIATIONS, there are a large number of calculator commands that have been implemented using the abbreviation syntax: .PP .TS tab(&); l l. T{ _ Abbreviations T}&T{ Calculator Operation T} T{ _ drop T}&T{ drop last element T} T{ clear T}&T{ clear all stack elements T} T{ swap T}&T{ swap last two elements T} T{ dup T}&T{ duplicate last element T} T{ undo T}&T{ undo last operation T} T{ rad T}&T{ set angle mode to radians T} T{ deg T}&T{ set angle mode to degrees T} T{ rect T}&T{ set complex display mode to rectangular T} T{ polar T}&T{ set complex display mode to polar T} T{ bin T}&T{ set base display mode to binary T} T{ oct T}&T{ set base display mode to octal T} T{ dec T}&T{ set base display mode to decimal T} T{ hex T}&T{ set base display mode to hexidecimal T} T{ view T}&T{ view last stack element in a fullscreen editor T} T{ edit T}&T{ create a new stack element using an external editor T} T{ pi T}&T{ enter 3.14159265 on the stack T} T{ rand T}&T{ generate a random number between 0 and 1 (uniformly distributed) T} T{ refresh T}&T{ refresh the display T} T{ about T}&T{ display a nifty ``About Orpie\&'' screen T} T{ quit T}&T{ quit Orpie T} T{ _ .TE .PP .SS BROWSING THE STACK Orpie offers a \fIstack browsing mode\fP to assist in viewing and manipulating stack data. Press to enter stack browsing mode; this should highlight the last stack element. You can use the up and down arrow keys to select different stack elements. The following keys are useful in stack browsing mode: .PP .TS tab(&); l l. T{ _ Keys T}&T{ Operations T} T{ _ q T}&T{ quit stack browsing mode T} T{ T}&T{ scroll selected entry to the left T} T{ T}&T{ scroll selected entry to the right T} T{ r T}&T{ cyclically ``roll\&'' stack elements downward, below the selected element (inclusive) T} T{ R T}&T{ cyclically ``roll\&'' stack elements upward, below the selected element (inclusive) T} T{ v T}&T{ view the currently selected element in a fullscreen editor T} T{ E T}&T{ edit the currently selected element with an external editor T} T{ T}&T{ duplicate the currently selected element T} T{ _ .TE .PP The left and right scrolling option may prove useful for viewing very lengthy stack entries, such as large matrices. The edit option provides a convenient way to correct data after it has been entered on the stack. .PP .SS UNITS FORMATTING A units string is a list of units separated by \&'*\&' to indicate multiplication and \&'/\&' to indicate division. Units may be raised to real\-valued powers using the \&'^\&'character. A contrived example of a valid unit string would be "N*nm^2*kg/s/in^\-3*GHz^2.34". .PP Orpie supports the standard SI prefix set, {y, z, a, f, p, n, u, m, c, d, da, h, k, M, G, T, P, E, Z, Y} (note the use of \&'u\&' for micro\-). These prefixes may be applied to any of the following exhaustive sets of units: .PP .TS tab(&); l l. T{ _ String T}&T{ Length Unit T} T{ _ m T}&T{ meter T} T{ ft T}&T{ foot T} T{ in T}&T{ inch T} T{ yd T}&T{ yard T} T{ mi T}&T{ mile T} T{ pc T}&T{ parsec T} T{ AU T}&T{ astronomical unit T} T{ Ang T}&T{ angstrom T} T{ furlong T}&T{ furlong T} T{ pt T}&T{ PostScript point T} T{ pica T}&T{ PostScript pica T} T{ nmi T}&T{ nautical mile T} T{ lyr T}&T{ lightyear T} T{ _ .TE .PP .TS tab(&); l l. T{ _ String T}&T{ Mass Unit T} T{ _ g T}&T{ gram T} T{ lb T}&T{ pound mass T} T{ oz T}&T{ ounce T} T{ slug T}&T{ slug T} T{ lbt T}&T{ Troy pound T} T{ ton T}&T{ (USA) short ton T} T{ tonl T}&T{ (UK) long ton T} T{ tonm T}&T{ metric ton T} T{ ct T}&T{ carat T} T{ gr T}&T{ grain T} T{ _ .TE .PP .TS tab(&); l l. T{ _ String T}&T{ Time Unit T} T{ _ s T}&T{ second T} T{ min T}&T{ minute T} T{ hr T}&T{ hour T} T{ day T}&T{ day T} T{ yr T}&T{ year T} T{ Hz T}&T{ Hertz T} T{ _ .TE .PP .TS tab(&); l l. T{ _ String T}&T{ Temperature Unit T} T{ _ K T}&T{ Kelvin T} T{ R T}&T{ Rankine T} T{ _ .TE .PP Note: No, Celsius and Fahrenheit will not be supported. Because these temperature units do not share a common zero point, their behavior is ill\-defined under many operations. .PP .TS tab(&); l l. T{ _ String T}&T{ ``Amount of Substance\&'' Unit T} T{ _ mol T}&T{ Mole T} T{ _ .TE .PP .TS tab(&); l l. T{ _ String T}&T{ Force Unit T} T{ _ N T}&T{ Newton T} T{ lbf T}&T{ pound force T} T{ dyn T}&T{ dyne T} T{ kip T}&T{ kip T} T{ _ .TE .PP .TS tab(&); l l. T{ _ String T}&T{ Energy Unit T} T{ _ J T}&T{ Joule T} T{ erg T}&T{ erg T} T{ cal T}&T{ calorie T} T{ BTU T}&T{ british thermal unit T} T{ eV T}&T{ electron volt T} T{ _ .TE .PP .TS tab(&); l l. T{ _ String T}&T{ Electrical Unit T} T{ _ A T}&T{ Ampere T} T{ C T}&T{ Coulomb T} T{ V T}&T{ volt T} T{ Ohm T}&T{ Ohm T} T{ F T}&T{ Farad T} T{ H T}&T{ Henry T} T{ T T}&T{ Tesla T} T{ G T}&T{ Gauss T} T{ Wb T}&T{ Weber T} T{ Mx T}&T{ Maxwell T} T{ _ .TE .PP .TS tab(&); l l. T{ _ String T}&T{ Power Unit T} T{ _ W T}&T{ Watt T} T{ hp T}&T{ horsepower T} T{ _ .TE .PP .TS tab(&); l l. T{ _ String T}&T{ Pressure Unit T} T{ _ Pa T}&T{ Pascal T} T{ atm T}&T{ atmosphere T} T{ bar T}&T{ bar T} T{ Ohm T}&T{ Ohm T} T{ mmHg T}&T{ millimeters of mercury T} T{ inHg T}&T{ inches of mercury T} T{ _ .TE .PP .TS tab(&); l l. T{ _ String T}&T{ Luminance Unit T} T{ _ cd T}&T{ candela T} T{ lm T}&T{ lumen T} T{ lx T}&T{ lux T} T{ _ .TE .PP Note: Although the lumen is defined by 1_lm = 1_cd * sr, Orpie drops the steridian because it is a dimensionless unit and therefore is of questionable use to a calculator. .PP .TS tab(&); l l. T{ _ String T}&T{ Volume Unit T} T{ _ ozfl T}&T{ fluid ounce (US) T} T{ cup T}&T{ cup (US) T} T{ pt T}&T{ pint (US) T} T{ qt T}&T{ quart (US) T} T{ gal T}&T{ gallon (US) T} T{ L T}&T{ liter T} T{ _ .TE .PP All units are defined in the Orpie run\-configuration file; consult the \fIorpierc\fP(5) manpage if you wish to define your own units or change the existing definitions. .PP .SH LICENSING Orpie is Free Software; you can redistribute it and/or modify it under the terms of the GNU General Public License (GPL), Version 3, as published by the Free Software Foundation. You should have received a copy of the GPL along with this program, in the file ``LICENSE.md\&''\&. .PP .SH CONTACT INFO Orpie author: Paul Pelzl .br Orpie website: \fBhttps://github.com/pelzlpj/orpie\fP .br .PP .SH SEE ALSO \fIorpierc\fP(5), \fIorpie\-curses\-keys\fP(1) .PP .\" NOTE: This file is generated, DO NOT EDIT. orpie-release-1.6.0/doc/orpierc.5000066400000000000000000000630051334120314700165520ustar00rootroot00000000000000'\" t .\" Manual page created with latex2man on Tue Aug 28 01:19:13 PDT 2018 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW .nf .. .de Ve .ft R .fi .. .TH "ORPIERC" "5" "28 August 2018" "configuration file for the Orpie calculator " "configuration file for the Orpie calculator " .SH NAME orpierc is the configuration textfile for the \fIorpie\fP(1) console calculator. .PP .SH INTRODUCTION CAUTION: while this manpage should be suitable as a quick reference, it may be subject to miscellaneous shortcomings in typesetting. The definitive documentation is the user manual provided with Orpie in PDF format. .PP Orpie reads a run\-configuration textfile (generally /etc/orpierc or /usr/local/etc/orpierc) to determine key and command bindings. You can create a personalized configuration file in $HOME/.orpierc, and select bindings that match your usage patterns. The recommended procedure is to ``include\&'' the orpierc file provided with Orpie (see INCLUDING OTHER RCFILES), and add or remove settings as desired. .PP .SH ORPIERC SYNTAX You may notice that the orpierc syntax is similar to the syntax used in the configuration file for the Mutt email client (muttrc). .PP Within the orpierc file, strings should be enclosed in double quotes ("). A double quote character inside a string may be represented by \\" \&. The backslash character must be represented by doubling it (\\\\). .PP .SS INCLUDING OTHER RCFILES Syntax: include \fIfilename_string\fP .br .br This syntax can be used to include one run\-configuration file within another. This command could be used to load the default orpierc file (probably found in /etc/orpierc) within your personalized rcfile, {/.orpierc}. The filename string should be enclosed in quotes. .PP .SS SETTING CONFIGURATION VARIABLES Syntax: set \fIvariable\fP=\fIvalue_string\fP .br .br Several configuration variables can be set using this syntax; check the CONFIGURATION VARIABLES description to see a list. The variables are unquoted, but the values should be quoted strings. .PP .SS CREATING KEY BINDINGS Syntax: bind \fIkey_identifier operation\fP .br .br This command will bind a keypress to execute a calculator operation. The various operations, which should not be enclosed in quotes, may be found in the section on CALCULATOR OPERATIONS. Key identifiers may be specified by strings that represent a single keypress, for example "m" (quotes included). The key may be prefixed with "\\\\C" or "\\\\M" to represent Control or Meta (Alt) modifiers, respectively; note that the backslash must be doubled. A number of special keys lack single\-character representations, so the following strings may be used to represent them: .TP .B * "" .TP .B * "" .TP .B * "" .TP .B * "" .TP .B * "" .TP .B * "" .TP .B * "" .TP .B * "" .TP .B * "" .TP .B * "" .TP .B * "" .TP .B * "" .TP .B * "" .TP .B * "" .TP .B * "" to "" .PP Due to differences between various terminal emulators, this key identifier syntax may not be adequate to describe every keypress. As a workaround, Orpie will also accept key identifiers in octal notation. As an example, you could use \\024 (do \fInot\fP enclose it in quotes) to represent Ctrl\-T. .PP Orpie includes a secondary executable, orpie\-curses\-keys, that prints out the key identifiers associated with keypresses. You may find it useful when customizing orpierc. .PP Multiple keys may be bound to the same operation, if desired. .PP .SS REMOVING KEY BINDINGS Syntax: .br unbind_function \fIkey_identifier\fP .br unbind_command \fIkey_identifier\fP .br unbind_edit \fIkey_identifier\fP .br unbind_browse \fIkey_identifier\fP .br unbind_abbrev \fIkey_identifier\fP .br unbind_variable \fIkey_identifier\fP .br unbind_integer \fIkey_identifier\fP .br .br These commands will remove key bindings associated with the various entry modes (functions, commands, editing operations, etc.). The key identifiers should be defined using the syntax described in the previous section. .PP .SS CREATING KEY AUTO\-BINDINGS Syntax: autobind \fIkey_identifier\fP .br .br In order to make repetitive calculations more pleasant, Orpie offers an automatic key binding feature. When a function or command is executed using its abbreviation, one of the keys selected by the autobind syntax will be automatically bound to that operation (unless the operation has already been bound to a key). The current set of autobindings can be viewed in the help panel by executing command_cycle_help (bound to \&'h\&' by default). .PP The syntax for the key identifiers is provided in the previous section. .PP .SS CREATING OPERATION ABBREVIATIONS Syntax: abbrev \fIoperation_abbreviation operation\fP .br .br You can use this syntax to set the abbreviations used within Orpie to represent the various functions and commands. A list of available operations may be found in the CALCULATOR OPERATIONS section. The operation abbreviations should be quoted strings, for example "sin" or "log". .PP Orpie performs autocompletion on these abbreviations, allowing you to type usually just a few letters in order to select the desired command. The order of the autocompletion matches will be the same as the order in which the abbreviations are registered by the rcfile\-\-so you may wish to place the more commonly used operation abbreviations earlier in the list. .PP Multiple abbreviations may be bound to the same operation, if desired. .PP .SS REMOVING OPERATION ABBREVIATIONS Syntax: unabbrev \fIoperation_abbreviation\fP .br .br This syntax can be used to remove an operation abbreviation. The operation abbreviations should be quoted strings, as described in the previous section. .PP .SS CREATING MACROS Syntax: macro \fIkey_identifier macro_string\fP .br .br You can use this syntax to cause a single keypress (the \fIkey_identifier\fP) to be interpreted as the series of keypresses listed in \fImacro_string\fP\&. The syntax for defining a keypress is the same as that defined in the section on CREATING KEY BINDINGS. The macro string should be a list of whitespace\-separated keypresses, e.g. "2 2 +" (including quotes). .PP This macro syntax provides a way to create small programs; by way of example, the default orpierc file includes macros for the base 2 logarithm and the binary entropy function (bound to L and H, respectively), as well as ``register\&'' variable shortcuts ( to ). .PP Macros may call other macros recursively. However, take care that a macro does not call \fIitself\fP recursively; Orpie will not trap the infinite loop. .PP Note that operation abbreviations may be accessed within macros. For example, macro "A" "\&' a b o u t " would bind A to display the ``about Orpie\&'' screen. .PP .SS CREATING UNITS Syntax: .br base_unit \fIunit_symbol preferred_prefix\fP .br unit \fIunit_symbol unit_definition\fP .br .br Units are defined in a two\-step process: .TP 1. Define a set of orthogonal ``base units.\&'' All other units must be expressible in terms of these base units. The base units can be given a preferred SI prefix, which will be used whenever the units are standardized (e.g. via ustand). The unit symbols and preferred prefixes should all be quoted strings; to prefer \fIno\fP prefix, use the empty string (""). .PP It is expected that most users will use the fundamental SI units for base units. .TP 2. Define all other units in terms of either base units or previously\-defined units. Again, the unit symbol and unit definition should be quoted strings. The definition should take the form of a numeric value followed by a units string, e.g. "2.5_kN*m/s". See the UNITS FORMATTING section for more details on the unit string format. .PP .SS CREATING CONSTANTS Syntax: constant \fIconstant_symbol constant_definition\fP .br .br This syntax can be used to define a physical constant. Both the constant symbol and definition must be quoted strings. The constant definition should be a numeric constant followed by a units string e.g. "1.60217733e\-19_C". All units used in the constant definition must already have been defined. .PP .SH CONFIGURATION VARIABLES The following configuration variables may be set as described in the SETTING CONFIGURATION VARIABLES section. .TP .B * datadir .br This variable should be set to the full path of the Orpie data directory, which will contain the calculator state save file, temporary buffers, etc. The default directory is "\\~/.orpie/". .TP .B * editor .br This variable may be set to the fullscreen editor of your choice. The default value is "vi". It is recommended that you choose an editor that offers horizontal scrolling in place of word wrapping, so that the columns of large matrices can be properly aligned. (The Vim editor could be used in this fashion by setting editor to "vim \-c \&'set nowrap\&'".) .TP .B * hide_help .br Set this variable to "true" to hide the left help/status panel, or leave it on the default of "false" to display the help panel. .TP .B * conserve_memory .br Set this variable to "true" to minimize memory usage, or leave it on the default of "false" to improve rendering performance. (By default, Orpie caches multiple string representations of all stack elements. Very large integers in particular require significant computation for string representation, so caching these strings can make display updates much faster.) .PP .SH CALCULATOR OPERATIONS Every calculator operation can be made available to the interface using the syntax described in the sections on CREATING KEY BINDINGS and CREATING OPERATION ABBREVIATIONS. The following is a list of every available operation. .PP .SS FUNCTIONS The following operations are functions\-\-that is, they will consume at least one argument from the stack. Orpie will generally abort the computation and provide an informative error message if a function cannot be successfully applied (for example, if you try to compute the transpose of something that is not a matrix). .PP For the exact integer data type, basic arithmetic operations will yield an exact integer result. Division of two exact integers will yield the quotient of the division. The more complicated functions will generally promote the integer to a real number, and as such the arithmetic will no longer be exact. .TP .B * function_10_x .br Raise 10 to the power of the last stack element (inverse of function_log10). .TP .B * function_abs .br Compute the absolute value of the last stack element. .TP .B * function_acos .br Compute the inverse cosine of the last stack element. For real numbers, The result will be provided either in degrees or radians, depending on the angle mode of the calculator. .TP .B * function_acosh .br Compute the inverse hyperbolic cosine of the last stack element. .TP .B * function_add .br Add last two stack elements. .TP .B * function_arg .br Compute the argument (phase angle of complex number) of the last stack element. The value will be provided in either degrees or radians, depending on the current angle mode of the calculator. .TP .B * function_asin .br Compute the inverse sine of the last stack element. For real numbers, The result will be provided either in degrees or radians, depending on the angle mode of the calculator. .TP .B * function_asinh .br Compute the inverse hyperbolic sine of the last stack element. .TP .B * function_atan .br Compute the inverse tangent of the last stack element. For real numbers, The result will be provided either in degrees or radians, depending on the angle mode of the calculator. .TP .B * function_atanh .br Compute the inverse hyperbolic tangent of the last stack element. .TP .B * function_binomial_coeff .br Compute the binomial coefficient (``n choose k\&'') formed by the last two stack elements. If these arguments are real, the coefficient is computed using a fast approximation to the log of the gamma function, and therefore the result is subject to rounding errors. For exact integer arguments, the coefficient is computed using exact arithmetic; this has the potential to be a slow operation. .TP .B * function_ceiling .br Compute the ceiling of the last stack element. .TP .B * function_convert_units .br Convert stack element 2 to an equivalent expression in the units of element 1. Element 1 should be real\-valued, and its magnitude will be ignored when computing the conversion. .TP .B * function_cos .br Compute the cosine of the last stack element. If the argument is real, it will be assumed to be either degrees or radians, depending on the angle mode of the calculator. .TP .B * function_cosh .br Compute the hyperbolic cosine of the last stack element. .TP .B * function_conj .br Compute the complex conjugate of the last stack element. .TP .B * function_div .br Divide element 2 by element 1. .TP .B * function_erf .br Compute the error function of the last stack element. .TP .B * function_erfc .br Compute the complementary error function of the last stack element. .TP .B * function_eval .br Obtain the contents of the variable in the last stack position. .TP .B * function_exp .br Evaluate the exponential function of the last stack element. .TP .B * function_factorial .br Compute the factorial of the last stack element. For a real argument, this is computed using a fast approximation to the gamma function, and therefore the result may be subject to rounding errors (or overflow). For an exact integer argument, the factorial is computed using exact arithmetic; this has the potential to be a slow operation. .TP .B * function_floor .br Compute the floor of the last stack element. .TP .B * function_gamma .br Compute the Euler gamma function of the last stack element. .TP .B * function_gcd .br Compute the greatest common divisor of the last two stack elements. This operation may be applied only to integer type data. .TP .B * function_im .br Compute the imaginary part of the last stack element. .TP .B * function_inv .br Compute the multiplicative inverse of the last stack element. .TP .B * function_lcm .br Compute the least common multiple of the last two stack elements. This operation may be applied only to integer type data. .TP .B * function_ln .br Compute the natural logarithm of the last stack element. .TP .B * function_lngamma .br Compute the natural logarithm of the Euler gamma function of the last stack element. .TP .B * function_log10 .br Compute the base\-10 logarithm of the last stack element. .TP .B * function_maximum .br Find the maximum values of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. .TP .B * function_minimum .br Find the minimum values of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. .TP .B * function_mean .br Compute the sample means of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. .TP .B * function_mod .br Compute element 2 mod element 1. This operation can be applied only to integer type data. .TP .B * function_mult .br Multiply last two stack elements. .TP .B * function_neg .br Negate last stack element. .TP .B * function_permutation .br Compute the permutation coefficient determined by the last two stack elements \&'n\&' and \&'k\&': the number of ways of obtaining an ordered subset of k elements from a set of n elements. If these arguments are real, the coefficient is computed using a fast approximation to the log of the gamma function, and therefore the result is subject to rounding errors. For exact integer arguments, the coefficient is computed using exact arithmetic; this has the potential to be a slow operation. .TP .B * function_pow .br Raise element 2 to the power of element 1. .TP .B * function_purge .br Delete the variable in the last stack position. .TP .B * function_re .br Compute the real part of the last stack element. .TP .B * function_sin .br Compute the sine of the last stack element. If the argument is real, it will be assumed to be either degrees or radians, depending on the angle mode of the calculator. .TP .B * function_sinh .br Compute the hyperbolic sine of the last stack element. .TP .B * function_solve_linear .br Solve a linear system of the form Ax = b, where A and b are the last two elements on the stack. A must be a square matrix and b must be a matrix with one column. This function does not compute inv(A), but obtains the solution by a more efficient LU decomposition method. This function is recommended over explicitly computing the inverse, especially when solving linear systems with relatively large dimension or with poorly conditioned matrices. .TP .B * function_sq .br Square the last stack element. .TP .B * function_sqrt .br Compute the square root of the last stack element. .TP .B * function_standardize_units .br Convert the last stack element to an equivalent expression using the SI standard base units (kg, m, s, etc.). .TP .B * function_stdev_unbiased .br Compute the unbiased sample standard deviation of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. (Compare to HP48\&'s sdev function.) .TP .B * function_stdev_biased .br Compute the biased (population) sample standard deviation of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. (Compare to HP48\&'s psdev function.) .TP .B * function_store .br Store element 2 in (variable) element 1. .TP .B * function_sub .br Subtract element 1 from element 2. .TP .B * function_sumsq .br Sum the squares of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. .TP .B * function_tan .br Compute the tangent of the last stack element. If the argument is real, it will be assumed to be either degrees or radians, depending on the angle mode of the calculator. .TP .B * function_tanh .br Compute the hyperbolic tangent of the last stack element. .TP .B * function_to_int .br Convert a real number to an integer type. .TP .B * function_to_real .br Convert an integer type to a real number. .TP .B * function_total .br Sum each of the columns of a real NxM matrix, returning a 1xM matrix as a result. .TP .B * function_trace .br Compute the trace of a square matrix. .TP .B * function_transpose .br Compute the matrix transpose of the last stack element. .TP .B * function_unit_value .br Drop the units of the last stack element. .TP .B * function_utpn .br Compute the upper tail probability of a normal distribution. .br UTPN(m, v, x) = Integrate[ 1/Sqrt[2 Pi v] Exp[\-(m\-y)^2/(2 v)], {y, x, Infinity}] .TP .B * function_var_unbiased .br Compute the unbiased sample variance of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. (Compare to HP48\&'s var function.) .TP .B * function_var_biased .br Compute the biased (population) sample variance of each of the columns of a real NxM matrix, returning a 1xM matrix as a result. (Compare to HP48\&'s pvar function.) .PP .SS COMMANDS The following operations are referred to as commands; they differ from functions because they do not take an argument. Many calculator interface settings are implemented as commands. .TP .B * command_about .br Display a nifty ``about Orpie\&'' credits screen. .TP .B * command_begin_abbrev .br Begin entry of an operation abbreviation. .TP .B * command_begin_browsing .br Enter stack browsing mode. .TP .B * command_begin_constant .br Begin entry of a physical constant. .TP .B * command_begin_variable .br Begin entry of a variable name. .TP .B * command_bin .br Set the base of exact integer representation to 2 (binary). .TP .B * command_clear .br Clear all elements from the stack. .TP .B * command_cycle_base .br Cycle the base of exact integer representation between 2, 8, 10, and 16 (bin, oct, dec, and hex). .TP .B * command_cycle_help .br Cycle through multiple help pages. The first page displays commonly used bindings, and the second page displays the current autobindings. .TP .B * command_dec .br Set the base of exact integer representation to 10 (decimal). .TP .B * command_deg .br Set the angle mode to degrees. .TP .B * command_drop .br Drop the last element off the stack. .TP .B * command_dup .br Duplicate the last stack element. .TP .B * command_enter_pi .br Enter 3.1415\&... on the stack. .TP .B * command_hex .br Set the base of exact integer representation to 16 (hexadecimal). .TP .B * command_oct .br Set the base of exact integer representation to 8 (octal). .TP .B * command_polar .br Set the complex display mode to polar. .TP .B * command_rad .br Set the angle mode to radians. .TP .B * command_rand .br Generate a random real\-valued number between 0 (inclusive) and 1 (exclusive). The deviates are uniformly distributed. .TP .B * command_rect .br Set the complex display mode to rectangular (cartesian). .TP .B * command_refresh .br Refresh the display. .TP .B * command_swap .br Swap stack elements 1 and 2. .TP .B * command_quit .br Quit Orpie. .TP .B * command_toggle_angle_mode .br Toggle the angle mode between degrees and radians. .TP .B * command_toggle_complex_mode .br Toggle the complex display mode between rectangular and polar. .TP .B * command_undo .br Undo the last calculator operation. .TP .B * command_view .br View the last stack element in an external fullscreen editor. .TP .B * command_edit_input .br Create a new stack element using an external editor. .PP .SS EDIT OPERATIONS The following operations are related to editing during data entry. These commands cannot be made available as operation abbreviations, since abbreviations are not accessible while entering data. These operations should be made available as single keypresses using the bind keyword. .TP .B * edit_angle .br Begin entering the phase angle of a complex number. (Orpie will assume the angle is in either degrees or radians, depending on the current angle mode.) .TP .B * edit_backspace .br Delete the last character entered. .TP .B * edit_begin_integer .br Begin entering an exact integer. .TP .B * edit_begin_units .br Begin appending units to a numeric expression. .TP .B * edit_complex .br Begin entering a complex number. .TP .B * edit_enter .br Enter the data that is currently being edited. .TP .B * edit_matrix .br Begin entering a matrix, or begin entering the next row of a matrix. .TP .B * edit_minus .br Enter a minus sign in input. .TP .B * edit_scientific_notation_base .br Begin entering the scientific notation exponent of a real number, or the base of an exact integer. .TP .B * edit_separator .br Begin editing the next element of a complex number or matrix. (This will insert a comma between elements.) .PP .SS BROWSING OPERATIONS The following list of operations is available only in stack browsing mode. As abbreviations are unavailable while browsing the stack, these operations should be bound to single keypresses using the bind keyword. .TP .B * browse_echo .br Echo the currently selected element to stack level 1. .TP .B * browse_end .br Exit stack browsing mode. .TP .B * browse_drop .br Drop the currently selected stack element. .TP .B * browse_dropn .br Drop all stack elements below the current selection (inclusive). .TP .B * browse_keep .br Drop all stack elements \fIexcept\fP the current selection. (This is complementary to browse_drop. .TP .B * browse_keepn .br Drop all stack elements above the current selection (non\-inclusive). (This is complementary to browse_dropn. .TP .B * browse_next_line .br Move the selection cursor down one line. .TP .B * browse_prev_line .br Move the selection cursor up one line. .TP .B * browse_rolldown .br Cyclically ``roll\&'' stack elements downward, below the selected element (inclusive). .TP .B * browse_rollup .br Cyclically ``roll\&'' stack elements upward, below the selected element (inclusive) \&. .TP .B * browse_scroll_left .br Scroll the selected element to the left (for viewing very large entries such as matrices). .TP .B * browse_scroll_right .br Scroll the selected element to the right. .TP .B * browse_view .br View the currently selected stack element in a fullscreen editor. .TP .B * browse_edit .br Edit the currently selected stack element using an external editor. .PP .SS ABBREVIATION ENTRY OPERATIONS The following list of operations is available only while entering a function or command abbreviation, or while entering a physical constant. These operations must be bound to single keypresses using the bind keyword. .TP .B * abbrev_backspace .br Delete a character from the abbreviation string. .TP .B * abbrev_enter .br Execute the operation associated with the selected abbreviation. .TP .B * abbrev_exit .br Cancel abbreviation entry. .PP .SS VARIABLE ENTRY OPERATIONS The following list of operations is available only while entering a variable name. As abbreviations are unavailable while entering variables, these operations should be bound to single keypresses using the bind keyword. .TP .B * variable_backspace .br Delete a character from the variable name. .TP .B * variable_cancel .br Cancel entry of the variable name. .TP .B * variable_complete .br Autocomplete the variable name. .TP .B * variable_enter .br Enter the variable name on the stack. .PP .SS INTEGER ENTRY OPERATIONS The following operation is available only while entering an integer; it can be made accessible by binding it to a single keypress using the bind keyword. .TP .B * integer_cancel .br Cancel entry of an integer. .PP .SH SEE ALSO \fIorpie\fP(1), \fIorpie\-curses\-keys\fP(1) .PP .SH AUTHOR This manpage is written by Paul J. Pelzl . .\" NOTE: This file is generated, DO NOT EDIT. orpie-release-1.6.0/doc/remove-tt.py000066400000000000000000000007651334120314700173210ustar00rootroot00000000000000#!/usr/bin/env python # Strip out all the \texttt{} occurrences (not perfect, but good enough) import re, sys infilename = sys.argv[1] outfilename = sys.argv[2] infile = open(infilename, "r").read() tt_regex = re.compile("\\\\texttt\{([^}]+)}") def tt_replace(m): return m.group(1) replaced_string = re.sub(tt_regex, tt_replace, infile) outfile = open(outfilename, "w") outfile.write(replaced_string) outfile.close() # arch-tag: DO_NOT_CHANGE_97d4155d-0a4e-42ae-ae0a-0d064c1a7b71 orpie-release-1.6.0/dune-project000066400000000000000000000000351334120314700165700ustar00rootroot00000000000000(lang dune 1.1) (name orpie) orpie-release-1.6.0/etc/000077500000000000000000000000001334120314700150235ustar00rootroot00000000000000orpie-release-1.6.0/etc/dune000066400000000000000000000000551334120314700157010ustar00rootroot00000000000000(install (section etc) (files orpierc)) orpie-release-1.6.0/etc/orpierc000066400000000000000000000265361334120314700164250ustar00rootroot00000000000000# orpierc # default key bindings and other settings for the Orpie calculator # directory for storing Orpie data set datadir="~/.orpie/" # editor used for fullscreen viewing of stack elements set editor="vi" # whether or not to hide the help panel set hide_help="false" # whether or not to conserve memory in favor of faster rendering set conserve_memory="false" # keys for "edit" operations, which affect the data that # is currently being entered bind "n" edit_minus bind "" edit_backspace bind "" edit_enter bind "`" edit_scientific_notation_base bind "" edit_scientific_notation_base bind "#" edit_begin_integer bind "(" edit_complex bind "[" edit_matrix bind "," edit_separator bind "<" edit_angle bind "_" edit_begin_units # keys for "integer edit" operations bind "#" integer_cancel # keys for "function" operations, which operate on an argument bind "+" function_add bind "-" function_sub bind "*" function_mult bind "/" function_div bind "n" function_neg bind "i" function_inv bind "^" function_pow bind "s" function_sqrt bind "a" function_abs bind "\\Ca" function_arg bind "e" function_exp bind "l" function_ln bind "c" function_conj bind "!" function_factorial bind "%" function_mod bind "S" function_store bind ";" function_eval # keys for "command" operations, which do not take an argument bind "\\" command_drop bind "|" command_clear bind "" command_swap bind "" command_swap bind "" command_dup bind "u" command_undo bind "" command_begin_browsing bind "'" command_begin_abbrev bind "C" command_begin_constant bind "@" command_begin_variable bind "r" command_toggle_angle_mode bind "p" command_toggle_complex_mode bind "b" command_cycle_base bind "v" command_view bind "\\Cl" command_refresh bind "P" command_enter_pi bind "E" command_edit_input bind "h" command_cycle_help bind "Q" command_quit # keys for "browse" operations, which are active during # stack browsing mode bind "q" browse_end bind "" browse_scroll_left bind "" browse_scroll_right bind "" browse_prev_line bind "" browse_next_line bind "r" browse_rolldown bind "R" browse_rollup bind "v" browse_view bind "" browse_echo bind "\\" browse_drop bind "d" browse_drop bind "D" browse_dropn bind "k" browse_keep bind "K" browse_keepn bind "E" browse_edit # keys for abbrev mode operations bind "'" abbrev_exit bind "" abbrev_enter bind "" abbrev_backspace # keys for variable edit mode operations bind "@" variable_cancel bind "" variable_enter bind "" variable_backspace bind "" variable_complete # autobound keys autobind "" autobind "" autobind "" autobind "" autobind "" autobind "" autobind "" autobind "" # abbrev command abbreviations # these should be given in the desired order of matching precedence abbrev "inv" function_inv abbrev "pow" function_pow abbrev "sq" function_sq abbrev "sqrt" function_sqrt abbrev "abs" function_abs abbrev "arg" function_arg abbrev "exp" function_exp abbrev "ln" function_ln abbrev "10^" function_10_x abbrev "log10" function_log10 abbrev "conj" function_conj abbrev "sin" function_sin abbrev "cos" function_cos abbrev "tan" function_tan abbrev "sinh" function_sinh abbrev "cosh" function_cosh abbrev "tanh" function_tanh abbrev "asinh" function_asinh abbrev "acosh" function_acosh abbrev "atanh" function_atanh abbrev "asin" function_asin abbrev "acos" function_acos abbrev "atan" function_atan abbrev "re" function_re abbrev "im" function_im abbrev "gamma" function_gamma abbrev "lngamma" function_lngamma abbrev "erf" function_erf abbrev "erfc" function_erfc abbrev "fact" function_factorial abbrev "trans" function_transpose abbrev "mod" function_mod abbrev "floor" function_floor abbrev "ceil" function_ceiling abbrev "toint" function_to_int abbrev "toreal" function_to_real abbrev "solvelin" function_solve_linear abbrev "eval" function_eval abbrev "store" function_store abbrev "purge" function_purge abbrev "gcd" function_gcd abbrev "lcm" function_lcm abbrev "binom" function_binomial_coeff abbrev "perm" function_permutation abbrev "total" function_total abbrev "mean" function_mean abbrev "sumsq" function_sumsq abbrev "var" function_var_unbiased abbrev "varbias" function_var_biased abbrev "stdev" function_stdev_unbiased abbrev "stdevbias" function_stdev_biased abbrev "min" function_minimum abbrev "max" function_maximum abbrev "utpn" function_utpn abbrev "ustand" function_standardize_units abbrev "uconvert" function_convert_units abbrev "uvalue" function_unit_value abbrev "trace" function_trace abbrev "drop" command_drop abbrev "clear" command_clear abbrev "swap" command_swap abbrev "dup" command_dup abbrev "undo" command_undo abbrev "quit" command_quit abbrev "rad" command_rad abbrev "deg" command_deg abbrev "rect" command_rect abbrev "polar" command_polar abbrev "bin" command_bin abbrev "oct" command_oct abbrev "dec" command_dec abbrev "hex" command_hex abbrev "view" command_view abbrev "refresh" command_refresh abbrev "pi" command_enter_pi abbrev "rand" command_rand abbrev "edit" command_edit_input abbrev "add" function_add abbrev "sub" function_sub abbrev "mult" function_mult abbrev "div" function_div abbrev "neg" function_neg abbrev "about" command_about # fundamental SI units base_unit "m" "" base_unit "g" "k" base_unit "s" "" base_unit "A" "" base_unit "K" "" base_unit "mol" "" base_unit "cd" "" # derived distance units unit "in" "2.54_cm" unit "ft" "12_in" unit "yd" "3_ft" unit "mi" "1760_yd" unit "pc" "3.085678e16_m" unit "AU" "1.49598e11_m" unit "Ang" "1e-10_m" unit "furlong" "660_ft" unit "point" "0.0138888888888888_in" unit "pica" "12_point" unit "nmi" "1852_m" unit "lyr" "63239.7139591_AU" # derived mass units unit "gr" "0.06479891_g" unit "oz" "437.5_gr" unit "lb" "16_oz" unit "slug" "14593.9029_g" unit "lbt" "5760_gr" unit "ton" "2000_lb" unit "tonl" "2240_lb" unit "tonm" "1e6_g" unit "ct" "0.2_g" # derived time units unit "min" "60_s" unit "hr" "60_min" unit "day" "24_hr" unit "yr" "365.242199_day" # derived temperature units unit "R" "0.555555555555556_K" # derived force units unit "N" "1_kg*m/s^2" unit "lbf" "4.44822162_N" unit "dyne" "1e-5_N" unit "kip" "1000_lbf" # derived energy units unit "J" "1_N*m" unit "erg" "1e-7_J" unit "cal" "4.1868_J" unit "BTU" "1055.05585252_J" unit "eV" "1.602176487e-19_J" # derived frequency units unit "Hz" "1_s^-1" # derived power units unit "W" "1_J/s" unit "hp" "33000_lbf*ft/min" # derived pressure units unit "Pa" "1_N/m^2" unit "bar" "1e5_Pa" unit "inHg" "3386_Pa" unit "mmHg" "133.307086614173_Pa" unit "atm" "760_mmHg" # various derived electrical units unit "C" "1_A*s" unit "V" "1_W/A" unit "Ohm" "1_V/A" unit "F" "1_C/V" unit "Wb" "1_V*s" unit "H" "1_Wb/A" unit "T" "1_Wb/m^2" unit "G" "1e-4_T" unit "Mw" "1e-8_Wb" # derived units of luminous flux and illuminance # (steridian is dropped because it is dimensionless) unit "lm" "1_cd" unit "lx" "1_lm/m^2" # derived units of (fluid) volume unit "L" "0.001_m^3" unit "ozfl" "29.573529562_mL" unit "cup" "8_ozfl" unit "pt" "2_cup" unit "qt" "2_pt" unit "gal" "4_qt" # various physical constants # Avagadro's number constant "NA" "6.0221367e23_mol^-1" # Boltzmann's constant constant "k" "1.380658e-23_J/K" # molar volume constant "Vm" "0.0224141_m^3/mol" # universal gas constant constant "R" "8.31451_J/mol/K" # standard temperature constant "stdT" "273.15_K" # standard pressure constant "stdP" "101.325_kPa" # Stephan-Boltzmann constant constant "sigma" "1_W/m^2/K^4" # speed of light constant "c" "299792458.0_m/s" # permittivity of free space constant "eps0" "8.85418781761e-12_F/m" # permeability of free space constant "u0" "1.25663706144e-6_H/m" # acceleration of gravity constant "g" "9.80665_m/s^2" # gravitational constant constant "G" "6.67259e-11_m^3/s^2/kg" # Planck's constant constant "h" "6.6260755e-34_J*s" # Dirac's constant constant "hbar" "1.05457266e-34_J*s" # electronic charge constant "e" "1.60217733e-19_C" # electronic mass constant "me" "9.1093897e-31_kg" # proton mass constant "mp" "1.6726231e-17_kg" # fine structure constant constant "alpha" "0.00729735308" # magnetic flux quantum constant "phi" "2.06783461e-15_Wb" # Faraday's constant constant "F" "96485.309_C/mol" # "infinity" Rydberg constant constant "Rinf" "10973731.534_m^-1" # Bohr radius constant "a0" "0.0529177249_nm" # Bohr magneton constant "uB" "9.2740154e-24_J/T" # nuclear magneton constant "uN" "5.0507866e-27_J/T" # 1eV photon wavelength constant "lam0" "1239.8425_nm" # 1eV photon frequency constant "f0" "2.4179883e14_Hz" # Compton wavelength constant "lamc" "0.00242631058_nm" # Wien's constant constant "c3" "0.002897756_m*K" # base 2 logarithm macro "L" "l 2 l /" # or alternatively, using abbrev command syntax: #macro "L" "' l n 2 ' l n /" # binary entropy function (makes use of the "L" macro) macro "H" " 1 - n L * L * + n" # registers macro "" "@ r 0 1 " macro "" "@ r 0 2 " macro "" "@ r 0 3 " macro "" "@ r 0 4 " # arch-tag: DO_NOT_CHANGE_bb2181f2-1288-4d0f-849b-36482daf59c5 orpie-release-1.6.0/orpie.opam000066400000000000000000000007311334120314700162450ustar00rootroot00000000000000opam-version: "1.2" name: "orpie" version: "1.6.0" maintainer: "Paul Pelzl " authors: "Paul Pelzl " homepage: "https://github.com/pelzlpj/orpie" bug-reports: "https://github.com/pelzlpj/orpie/issues" license: "GPL-3" dev-repo: "https://github.com/pelzlpj/orpie.git" build: ["dune" "build" "-p" "orpie"] depends: [ "ocamlfind" {build} "camlp5" {build} "dune" {build & >= "1.1"} "curses" { >= "1.0.3" } "gsl" { >= "1.22.0" } ] orpie-release-1.6.0/scripts/000077500000000000000000000000001334120314700157375ustar00rootroot00000000000000orpie-release-1.6.0/scripts/compute_prefix000077500000000000000000000043001334120314700207130ustar00rootroot00000000000000#!/usr/bin/env ocaml (* * This script computes an installation prefix based on * 1) $PREFIX, if set, or * 2) "opam config var prefix", if available, or * 3) "/usr/local" as a fallback. * The result gets substituted for @prefix@ in the input file, in much * the same way that autoconf does things. *) #use "topfind";; #require "unix";; let opam_prefix () : string option = try let ic = UnixLabels.open_process_in "opam config var prefix" in let prefix = input_line ic in let status = UnixLabels.close_process_in ic in begin match status with | UnixLabels.WEXITED 0 -> Some prefix | _ -> None end with _ -> None let installation_prefix () = match Sys.getenv_opt "PREFIX" with | Some prefix -> prefix | None -> begin match opam_prefix () with | Some prefix -> prefix | None -> "/usr/local" end let sed_escape (input : string) : string = let buf = Buffer.create (String.length input) in for i = 0 to String.length input - 1 do begin if input.[i] == '\\' || input.[i] == '/' then Buffer.add_char buf '\\' end; Buffer.add_char buf input.[i] done; Buffer.contents buf let subst_prefix (in_filename : string) (out_filename : string) : unit = let prefix = installation_prefix () in let cmd = Printf.sprintf "sed 's/@prefix@/%s/g' \"%s\" > \"%s\"" (sed_escape prefix) in_filename out_filename in let ec = Sys.command cmd in begin if ec <> 0 then Printf.fprintf stderr "sed substitution failed with exit code %d.\n" ec end; exit ec let abort_usage () = Printf.fprintf stderr "Usage:\n"; Printf.fprintf stderr " compute_prefix eval\n"; Printf.fprintf stderr " compute_prefix subst in_filename out_filename\n"; exit 2 let () = begin if Array.length Sys.argv < 2 then abort_usage () end; let command = Sys.argv.(1) in if String.equal command "eval" && Array.length Sys.argv = 2 then print_string (installation_prefix ()) else if String.equal command "subst" && Array.length Sys.argv = 4 then let in_filename = Sys.argv.(2) in let out_filename = Sys.argv.(3) in subst_prefix in_filename out_filename else abort_usage () orpie-release-1.6.0/scripts/prep_release000077500000000000000000000000411334120314700203260ustar00rootroot00000000000000#!/bin/sh set -e cd doc && make orpie-release-1.6.0/src/000077500000000000000000000000001334120314700150375ustar00rootroot00000000000000orpie-release-1.6.0/src/orpie-curses-keys/000077500000000000000000000000001334120314700204305ustar00rootroot00000000000000orpie-release-1.6.0/src/orpie-curses-keys/curses_assist.ml000066400000000000000000000062421334120314700236600ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* curses_assist.ml * Miscellaneous helper functions functions for dealing with the * Curses module *) open Curses (* translate a curses key to a printable string *) let string_of_chtype ch = let display_string_of_keyname str = match str with |"KEY_LEFT" -> "" |"KEY_RIGHT" -> "" |"KEY_UP" -> "" |"KEY_DOWN" -> "" |"KEY_BACKSPACE" -> "" |"KEY_IC" -> "" |"KEY_DC" -> "" |"KEY_HOME" -> "" |"KEY_END" -> "" |"KEY_PPAGE" -> "" |"KEY_NPAGE" -> "" |" " -> "" |"KEY_F(1)" -> "" |"KEY_F(2)" -> "" |"KEY_F(3)" -> "" |"KEY_F(4)" -> "" |"KEY_F(5)" -> "" |"KEY_F(6)" -> "" |"KEY_F(7)" -> "" |"KEY_F(8)" -> "" |"KEY_F(9)" -> "" |"KEY_F(10)" -> "" |"KEY_F(11)" -> "" |"KEY_F(12)" -> "" |"KEY_ENTER" -> "" |"\\" -> "\\\\" |_ -> str in (* regexp to check for meta and/or ctrl prefixes * matches either "M-^" or "M-" or "^" followed by some character string *) let mc_re = Str.regexp "^\\(\\(M-\\^\\)\\|\\(M-\\)\\|\\(\\^\\)\\)?\\(.+\\)" and key_string = (keyname ch) in if Str.string_match mc_re key_string 0 then let has_meta_ctrl = try let _ = Str.matched_group 2 key_string in true with Not_found -> false and has_meta = try let _ = Str.matched_group 3 key_string in true with Not_found -> false and has_ctrl = try let _ = Str.matched_group 4 key_string in true with Not_found -> false and main_key = Str.matched_group 5 key_string in if has_meta_ctrl then "\\\\M\\\\C" ^ (display_string_of_keyname main_key) else if has_meta then "\\\\M" ^ (display_string_of_keyname main_key) else if has_ctrl then if main_key = "J" then "" else if main_key = "I" then "" else "\\\\C" ^ (display_string_of_keyname main_key) else display_string_of_keyname main_key else Printf.sprintf "\\%.3o" ch (* arch-tag: DO_NOT_CHANGE_833e814a-273c-4faa-b6d0-123eca3c608d *) orpie-release-1.6.0/src/orpie-curses-keys/curses_keys.ml000066400000000000000000000027001334120314700233200ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) open Curses;; begin let std = initscr () in assert (keypad std true); assert (cbreak ()); assert (noecho ()); assert (mvaddstr 1 0 "Type some keys to see the corresponding octal and string representations:"); assert (refresh ()) end; while true do let k = getch () in let s1 = Printf.sprintf " octal: \\%.3o" k and s2 = Printf.sprintf "string: \"%s\"" (Curses_assist.string_of_chtype k) in (assert (move 2 0); clrtoeol (); assert (addstr s1); assert (move 3 0); clrtoeol (); assert (addstr s2); assert (refresh ())) done; endwin ();; (* arch-tag: DO_NOT_CHANGE_31cbf03e-49f9-4f66-844c-ceb584edb920 *) orpie-release-1.6.0/src/orpie-curses-keys/dune000066400000000000000000000001361334120314700213060ustar00rootroot00000000000000(executable (name curses_keys) (public_name orpie-curses-keys) (libraries curses str)) orpie-release-1.6.0/src/orpie/000077500000000000000000000000001334120314700161555ustar00rootroot00000000000000orpie-release-1.6.0/src/orpie/add.ml000066400000000000000000000213521334120314700172420ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) open Rpc_stack open Gsl_assist open Big_int let add (stack : rpc_stack) (evaln : int -> unit) = evaln 2; let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in match gen_el1 with |RpcInt el1 -> ( match gen_el2 with |RpcInt el2 -> stack#push (RpcInt (add_big_int el1 el2)) |RpcFloatUnit (el2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "inconsistent units" end else let (f, u) = funit_of_float ((float_of_big_int el1) +. el2) in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "inconsistent units" end else let c_el1 = cmpx_of_int el1 in let (c, u) = cunit_of_cpx (Complex.add c_el1 el2) in stack#push (RpcComplexUnit (c, u)) |_ -> (* if the elements are incompatible, we have to put them back on the stack *) (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for addition")) ) |RpcFloatUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> if uu1 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "inconsistent units" end else let (f, u) = funit_of_float (el1 +. float_of_big_int el2) in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el2, uu2) -> begin try let conv = Units.conversion_factor uu1 uu2 !Rcfile.unit_table in stack#push (RpcFloatUnit (el1 *. conv +. el2, uu2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s end |RpcComplexUnit (el2, uu2) -> begin try let conv = Units.conversion_factor uu1 uu2 !Rcfile.unit_table in let c_el1 = c_of_f (el1 *. conv) in stack#push (RpcComplexUnit (Complex.add c_el1 el2, uu2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s end |_ -> (* if the elements are incompatible, we have to put them back on the stack *) (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for addition")) ) |RpcComplexUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> if uu1 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "inconsistent units" end else let c_el2 = cmpx_of_int el2 in let (c, u) = cunit_of_cpx (Complex.add el1 c_el2) in stack#push (RpcComplexUnit (c, u)) |RpcFloatUnit (el2, uu2) -> begin try let conv = c_of_f (Units.conversion_factor uu1 uu2 !Rcfile.unit_table) in let c_el1 = Complex.mul conv el1 in let c_el2 = c_of_f el2 in stack#push (RpcComplexUnit (Complex.add c_el1 c_el2, uu2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s end |RpcComplexUnit (el2, uu2) -> begin try let conv = c_of_f (Units.conversion_factor uu1 uu2 !Rcfile.unit_table) in let c_el1 = Complex.mul conv el1 in stack#push (RpcComplexUnit (Complex.add c_el1 el2, uu2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s end |_ -> (* if the elements are incompatible, we have to put them back on the stack *) (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for addition")) ) |RpcFloatMatrixUnit (el1, u1) -> ( match gen_el2 with |RpcFloatMatrixUnit (el2, u2) -> let dim1 = (Gsl.Matrix.dims el1) and dim2 = (Gsl.Matrix.dims el2) in if dim1 = dim2 then try let conv = Units.conversion_factor u1 u2 !Rcfile.unit_table in let result = Gsl.Matrix.copy el1 in Gsl.Matrix.scale result conv; Gsl.Matrix.add result el2; stack#push (RpcFloatMatrixUnit (result, u2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s else begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for addition") end |RpcComplexMatrixUnit (el2, u2) -> let dim1 = (Gsl.Matrix.dims el1) and dim2 = (Gsl.Matrix_complex.dims el2) in if dim1 = dim2 then try let conv = c_of_f (Units.conversion_factor u1 u2 !Rcfile.unit_table) in let c_el1 = cmat_of_fmat el1 in Gsl.Matrix_complex.scale c_el1 conv; Gsl.Matrix_complex.add c_el1 el2; stack#push (RpcComplexMatrixUnit (c_el1, u2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for addition")) |_ -> (* if the elements are incompatible, we have to put them back on the stack *) (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for addition")) ) |RpcComplexMatrixUnit (el1, u1) -> ( match gen_el2 with |RpcFloatMatrixUnit (el2, u2) -> let dim1 = (Gsl.Matrix_complex.dims el1) and dim2 = (Gsl.Matrix.dims el2) in if dim1 = dim2 then try let conv = c_of_f (Units.conversion_factor u1 u2 !Rcfile.unit_table) in let c_el2 = cmat_of_fmat el2 in let copy = Gsl.Matrix_complex.copy el1 in Gsl.Matrix_complex.scale copy conv; Gsl.Matrix_complex.add copy c_el2; stack#push (RpcComplexMatrixUnit (copy, u2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for addition")) |RpcComplexMatrixUnit (el2, u2) -> let dim1 = (Gsl.Matrix_complex.dims el1) and dim2 = (Gsl.Matrix_complex.dims el2) in if dim1 = dim2 then try let conv = c_of_f (Units.conversion_factor u1 u2 !Rcfile.unit_table) in let copy = Gsl.Matrix_complex.copy el1 in Gsl.Matrix_complex.scale copy conv; Gsl.Matrix_complex.add copy el2; stack#push (RpcComplexMatrixUnit (copy, u2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for addition")) |_ -> (* if the elements are incompatible, we have to put them back on the stack *) (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for addition")) ) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for addition")) (* arch-tag: DO_NOT_CHANGE_f59cab0f-755e-4d09-9d30-114114945b38 *) orpie-release-1.6.0/src/orpie/big_int_str.ml000066400000000000000000000214021334120314700210110ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* string coercion functions for Big_int * * These functions accept a base parameter, unlike the functions in the Big_int * module itself. The algorithms are simple, but appear to be fast enough. *) open Big_int;; exception Big_int_string_failure of string;; let digits = "0123456789abcdefghijklmnopqrstuvwxyz";; (* 'values' maps characters to integer values; '0' = 0, '1' = 0, ... 'a' = 10, etc. *) let values = Hashtbl.create 36;; for i = 0 to pred (String.length digits) do Hashtbl.add values digits.[i] i done;; (* Represent an integer in a given base. (Continually divide the integer * by the base until the remainder is zero.) This uses ordinary integer * division, so it can be done in constant time (< Sys.word_size divisions). * This is not designed to be used externally; no error checking is performed * in order to keep it fast. *) let string_of_positive_int_base (num : int) (base : int) = let rec get_digit quotient zero_str str = if quotient = 0 then str else let quot = quotient / base and rem = quotient mod base in let digit = digits.[rem] in begin match rem with |0 -> get_digit quot ((String.make 1 digit) ^ zero_str) str |_ -> get_digit quot "" ((String.make 1 digit) ^ zero_str ^ str) end in get_digit num "" "" (* Divide a bignum by a power of 2, yielding a quotient and a remainder. * Faster than general case division because it can be done using bit blitting * and shifting. *) let quomod_power_2 bignum bits = let shift_words = bits / Sys.word_size and shift_bits = bits mod Sys.word_size in let quot = nat_of_big_int bignum in let quot_len = Nat.length_nat quot in let rem = Nat.create_nat (succ shift_words) in (* copy the 'shift_words' least significant words of quot over to rem *) Nat.blit_nat rem 0 quot 0 shift_words; Nat.set_to_zero_nat rem shift_words 1; (* shift over the remaining 'shift_bits' bits from quot to rem *) Nat.shift_right_nat quot shift_words (quot_len - shift_words) rem shift_words shift_bits; (* adjust the most significant word of rem *) let dummy = Nat.create_nat 1 in Nat.shift_right_nat rem shift_words 1 dummy 0 (Sys.word_size - shift_bits); let big_quot = big_int_of_nat (Nat.copy_nat quot shift_words (quot_len - shift_words)) and big_rem = big_int_of_nat rem in (big_quot, big_rem) (* Divide-and-conquer algorithm for conversion to bases that are powers of two * (binary, octal, and hex, for example). The big_int is divided by a power * of two that is also a power of 'base'; the power is chosen to approximately * cut the big_int in half. This division can be performed using only blits * and bit shifts, so it is much faster than ordinary big_int division. * The two pieces are recursively subdivided and the resulting strings are * concatenated together. O(n*log(n)) time. This function runs significantly * faster than string_of_big_int_base_gen. *) let string_of_big_int_base (num : big_int) (base : int) = let log2_base = match base with |2 -> 1 |4 -> 2 |8 -> 3 |16 -> 4 |32 -> 5 |_ -> let err_str = Printf.sprintf "Error: called string_of_big_int_base() with illegal base %d" base in raise (Big_int_string_failure err_str) in let rec str_of_big_int_aux (ival : big_int) = if is_int_big_int ival then string_of_positive_int_base (int_of_big_int ival) base else begin let num_words = num_digits_big_int ival in let half_binary_digits = num_words * Sys.word_size / 2 in (* rounded_digits mod log2_base = 0, therefore * division by (2^rounded_digits) will split ival * perfectly with respect to the base 'base'. *) let rounded_digits = half_binary_digits - (half_binary_digits mod log2_base) in let upper, lower = quomod_power_2 ival rounded_digits in let upper_string = str_of_big_int_aux upper and lower_string = str_of_big_int_aux lower in (* pad the lower_string with zeros as necessary *) let adj_len_lower = rounded_digits / log2_base in let zeros = String.make (adj_len_lower - (String.length lower_string)) '0' in upper_string ^ zeros ^ lower_string end in let s = str_of_big_int_aux (abs_big_int num) in match sign_big_int num with |0 -> "0" |1 -> s |(-1) -> "-" ^ s |x -> raise (Big_int_string_failure ("unmatched sign: " ^ (string_of_int x))) (* Divide-and-conquer algorithm for string representation of big_ints in a * desired base (general case--not necessarily a power of two). The big_int * is split approximately in half using this divisor: * base^(num_words * log_base(2^word_size) / 2) * Each half is split recursively, and the pieces are concatenated together. * Should run in O(n*log(n)) time, a big improvement on the standard O(n^2) * algorithm that requires one long division for every digit output. *) (* Note 1: This runs in logarithmic stack space, so we should be able to handle * some pretty large big_ints before worrying about blowing the stack. *) (* Note 2: A faster method for computing a divisor could make this go a * lot quicker yet; gprof indicates that most of the time is spent in * multiplication ==> the power_int_positive_int_base call. *) (* Note 3: This routine actually appears to outperform string_of_big_int() * for computing decimal representations. CPU time decrease looks to be around * a third. *) let string_of_big_int_base_gen (num : big_int) (base : int) = if base >= 2 && base <= 36 then let rec str_of_big_int_aux (ival : big_int) = if is_int_big_int ival then string_of_positive_int_base (int_of_big_int ival) base else begin let num_words = num_digits_big_int ival in let size_factor = (log (2.0 ** (float_of_int Sys.word_size))) /. (log (float_of_int base)) in let log_div = (num_words * (int_of_float size_factor)) / 2 in let divisor = power_int_positive_int base log_div in let (upper, lower) = quomod_big_int ival divisor in let upper_string = str_of_big_int_aux upper and lower_string = str_of_big_int_aux lower in (* pad the lower_string with zeros as necessary *) let zeros = String.make (log_div - (String.length lower_string)) '0' in upper_string ^ zeros ^ lower_string end in let s = str_of_big_int_aux (abs_big_int num) in match sign_big_int num with |0 -> "0" |1 -> s |(-1) -> "-" ^ s |x -> raise (Big_int_string_failure ("unmatched sign: " ^ (string_of_int x))) else raise (Big_int_string_failure ("unsupported base: " ^ (string_of_int base))) (* convert a string to a big_int, assuming base 'base' *) (* The algorithm is simple... add up the values of the digits. *) let big_int_of_string_base (str : string) (base : int) = let multiplier = ref unit_big_int and sum = ref zero_big_int in for i = pred (String.length str) downto 0 do match str.[i] with |'-' -> sum := minus_big_int !sum |'+' -> () |_ -> try let digit_value = (Hashtbl.find values str.[i]) in if digit_value < base then (let diff = mult_int_big_int digit_value !multiplier in sum := add_big_int !sum diff; multiplier := mult_int_big_int base !multiplier) else raise (Big_int_string_failure ("invalid digits for base " ^ (string_of_int base) ^ " integer data" )) with Not_found -> raise (Big_int_string_failure ("invalid digits for base " ^ (string_of_int base) ^ " integer data" )) done; !sum;; (* arch-tag: DO_NOT_CHANGE_16f12562-9499-46c4-8e3a-519da76c1622 *) orpie-release-1.6.0/src/orpie/calc_test.ml000066400000000000000000000414421334120314700204550ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* calc_test.ml * a testing framework for the rpc_calc object *) (* The basic testing procedure is as follows: * - load some data on the stack using load_data(), * which supports the string syntax described in the manual * under "Entering Data With an External Editor." * - perform desired operations on the data using various * calculator functions provided by rpc_calc.ml * - compute a distortion metric between the calculator * result and the expected result, and raise an error * if it's not within tolerance * - repeat many many times * * Use "make test.opt" to build the testing executable. * * Writing test cases is a big job, and I would really appreciate * some help with this. It's a good project for other developers * to chip away on, because * 1) it doesn't require very deep knowledge of the calculator * internals * 2) testing development can be done in parallel with development * of the calculator itself * 3) since the calculator object handles most of the dirty work, * relatively little OCaml knowledge is necessary * *) open Rpc_calc;; open Rpc_stack;; let calc = new Rpc_calc.rpc_calc false in (* load data into the calculator using its string representation *) let load_data (data_str : string) = let lexbuf = Lexing.from_string data_str in let data = (* need to call completely different parsers when using degrees * or when using radians *) begin match (calc#get_modes ()).angle with |Rad -> Txtin_parser.decode_data_rad Txtin_lexer.token lexbuf |Deg -> Txtin_parser.decode_data_deg Txtin_lexer.token lexbuf end in List.iter calc#push data in let get_result () = calc#get_display_line 1 in let underscore = Str.regexp "_" in (* grab only the numeric portion of a value with units *) let num_part (num_and_units : string) = List.hd (Str.split underscore num_and_units) in (* grab only the units portion of a value with units *) let units_part (num_and_units : string) = List.hd (List.tl (Str.split underscore num_and_units)) in let print_indent s = print_endline (" " ^ s) in (* test whether the calculator result exactly matches the given * string, or raise an exception *) let test_result_exact (expected : string) (test_stage : string) = print_indent test_stage; let result = get_result () in if result <> expected then begin print_endline ("calculator result: \"" ^ result ^ "\""); print_endline (" expected: \"" ^ expected ^ "\""); failwith test_stage end else () in let test_result_float (expected : float) (tol : float) (test_stage : string) (normalized : bool) = print_indent test_stage; let result = get_result () in let ff = float_of_string (num_part result) in let test = if normalized then (abs_float ((ff -. expected) /. expected)) > tol else (abs_float (ff -. expected)) > tol in if test then begin print_endline ("calculator result: " ^ (num_part result)); print_endline (" expected: " ^ (string_of_float expected)); failwith test_stage end else () in (* test whether the calculator result is a floating-point value * which is within the specified tolerance (normalized) *) let test_result_float_tolnorm (expected : float) (tol : float) (test_stage : string) = test_result_float expected tol test_stage true in (* test whether the calculator result is a floating-point value * which is within the specified tolerance (not normalized) *) let test_result_float_tol (expected : float) (tol : float) (test_stage : string) = test_result_float expected tol test_stage false in (* machine precision tolerance *) let mprec = 1e-15 in (* unit conversion tolerance *) let uprec = 1e-6 in (* ad-hoc matrix norm *) let mat_norm () = calc#dup (); calc#transpose (); calc#conj (); calc#mult (); calc#trace (); calc#abs (); calc#sqrt () in (* get a normlized error metric for a matrix result. *) (* Assumes the last two stack elements are result matrix * and expected result matrix. *) let mat_error () = calc#dup (); calc#rolldown 3; calc#sub (); mat_norm (); calc#swap (); mat_norm (); calc#div () in let cpx_error () = calc#dup (); calc#rolldown 3; calc#sub (); calc#swap (); calc#div (); calc#abs () in (************************************************) (* ADDITION *) (************************************************) print_endline "testing add()..."; load_data "#10`d #20`d"; calc#add (); test_result_exact "# 30`d" "add-int-int-1"; load_data "#10`o #20`h"; calc#add (); test_result_exact "# 40`d" "add-int-int-2"; load_data "#10`d 10.0"; calc#add (); test_result_float_tolnorm 20.0 mprec "add-int-float-1"; load_data "(20.0, 20.0) #10`d (10.0, 20.0)"; calc#add (); calc#sub (); calc#abs (); test_result_float_tol 0.0 mprec "add-int-complex-1"; load_data "10.0 #10`d"; calc#add (); test_result_float_tolnorm 20.0 mprec "add-float-int-1"; load_data "10.0 20.0"; calc#add (); test_result_float_tolnorm 30.0 mprec "add-float-float-1"; load_data "10.0_kg*m/s 20.0_ft*lb/min"; calc#add (); test_result_float_tolnorm 4359.80831073 uprec "add-float-float-2"; load_data "(20.0, 20.0) 10.0 (10.0, 20.0)"; calc#add (); calc#sub (); calc#abs (); test_result_float_tol 0.0 mprec "add-float-complex-1"; load_data "(76.66666666667, 20.0)_yd^2/min 10.0_ft^2/s (10.0, 20.0)_yd^2/min"; calc#add (); calc#sub (); calc#abs (); test_result_float_tol 0.0 uprec "add-float-complex-2"; load_data "(20.0, 20.0) (10.0, 20.0) #10`d"; calc#add (); calc#sub (); calc#abs (); test_result_float_tol 0.0 mprec "add-complex-int-1"; load_data "(20.0, 20.0) (10.0, 20.0) 10.0"; calc#add (); calc#sub (); calc#abs (); test_result_float_tol 0.0 mprec "add-complex-float-1"; load_data "(40.0, 60.0) (10.0, 20.0) (30.0, 40.0)"; calc#add (); calc#sub (); calc#abs (); test_result_float_tol 0.0 mprec "add-complex-complex-1"; load_data "(10.254, 20.508)_m (10.0, 20.0)_in (10.0, 20.0)_m"; calc#add (); calc#sub (); calc#abs (); test_result_float_tol 0.0 uprec "add-complex-complex-2"; load_data "[[6, 8][10, 12]] [[1, 2][3, 4]] [[5, 6][7, 8]]"; calc#add (); mat_error (); test_result_float_tol 0.0 mprec "add-fmat-fmat-1"; load_data "[[55.1676416, 106.3352832][157.5029248, 208.6705664]]_m^2/min [[1, 2][3, 4]]_yd^2/s [[5, 6][7, 8]]_m^2/min"; calc#add (); mat_error (); test_result_float_tol 0.0 uprec "add-fmat-fmat-2"; load_data "[[(6, 6), (9, 8)][(12, 10), (15, 12)]] [[1, 2][3, 4]] [[(5, 6), (7, 8)][(9, 10), (11, 12)]]"; calc#add (); mat_error (); test_result_float_tol 0.0 mprec "add-fmat-cmat-1"; load_data "[[(55.1676416, 10.0), (106.3352832, 20.0)] [(157.5029248, 30.0), (208.6705664, 40.0)]]_m^2/min [[1, 2][3, 4]]_yd^2/s [[(5, 10.0), (6, 20.0)][(7, 30.0), (8, 40.0)]]_m^2/min"; calc#add (); mat_error (); test_result_float_tol 0.0 uprec "add_fmat_cmat_2"; load_data "[[(6, 6), (9, 8)][(12, 10), (15, 12)]] [[(5, 6), (7, 8)][(9, 10), (11, 12)]] [[1, 2][3, 4]]"; calc#add (); mat_error (); test_result_float_tol 0.0 mprec "add-cmat-fmat-1"; load_data "[[(55.1676416, 10.0), (106.3352832, 20.0)] [(157.5029248, 30.0), (208.6705664, 40.0)]]_m^2/min [[(5, 10.0), (6, 20.0)][(7, 30.0), (8, 40.0)]]_m^2/min [[1, 2][3, 4]]_yd^2/s"; calc#add (); load_data "1_m^2/min"; calc#convert_units (); mat_error (); test_result_float_tol 0.0 uprec "add-cmat-fmat-2"; load_data "[[(-4, 12), (17, 24)][(105, 9), (-5, -7)]] [[(1, 2), (3, 4)][(5, 6), (7, 8)]] [[(-5, 10), (14, 20)][(100, 3), (-12, -15)]]"; calc#add (); mat_error (); test_result_float_tol 0.0 mprec "add-cmat-cmat-1"; load_data "[[(12.0231131092, 20.0462262185), (9.22773573109, 144.092452437)] [(65.4323583529, 76.1386786555), (8.63698097479, 58.184904874)]]_lb [[(5, 10), (6, 20)][(7, 30), (8, 40)]]_kg [[(1, -2), (-4, 100)][(50, 10), (-9, -30)]]_lb"; calc#add (); mat_error (); test_result_float_tol 0.0 uprec "add-cmat-cmat-2"; calc#clear (); (************************************************) (* SUBTRACTION *) (************************************************) print_endline "testing sub()..."; load_data "#10`d #20`d"; calc#sub (); test_result_exact "# -10`d" "sub-int-int-1"; load_data "#60`o #20`h"; calc#sub (); test_result_exact "# 16`d" "sub-int-int-2"; load_data "#50`d 10.0"; calc#sub (); test_result_float_tolnorm 40.0 mprec "sub-int-float-1"; load_data "(20.0, -20.0) #30`d (10.0, 20.0)"; calc#sub (); cpx_error (); test_result_float_tol 0.0 mprec "sub-int-complex-1"; load_data "30.0 #10`d"; calc#sub (); test_result_float_tolnorm 20.0 mprec "sub-float-int-1"; load_data "50.0 20.0"; calc#sub (); test_result_float_tolnorm 30.0 mprec "sub-float-float-1"; load_data "10.0_kg*m/s 4359.80831073_ft*lb/min"; calc#sub (); test_result_float_tolnorm (-20.0000000041) uprec "sub-float-float-2"; load_data "(20.0, -20.0) 30.0 (10.0, 20.0)"; calc#sub (); cpx_error (); test_result_float_tol 0.0 mprec "sub-float-complex-1"; load_data "(36.6666666667, -20)_yd^2/min 10.0_ft^2/s (30, 20)_yd^2/min"; calc#sub (); cpx_error (); test_result_float_tol 0.0 uprec "sub-float-complex-2"; load_data "(20.0, 20.0) (30.0, 20.0) #10`d"; calc#sub (); cpx_error (); test_result_float_tol 0.0 mprec "sub-complex-int-1"; load_data "(20.0, 20.0) (30.0, 20.0) 10.0"; calc#sub (); cpx_error (); test_result_float_tol 0.0 mprec "sub-complex-float-1"; load_data "(-20.0, -30.0) (10.0, 20.0) (30.0, 50.0)"; calc#sub (); cpx_error (); test_result_float_tol 0.0 mprec "sub-complex-complex-1"; load_data "(-10.0, -20.0)_m (10.0, 20.0)_in (10.254, 20.508)_m"; calc#sub (); cpx_error (); test_result_float_tol 0.0 uprec "sub-complex-complex-2"; load_data "[[-4, -1][2, 6]] [[1, 5][9, 14]] [[5, 6][7, 8]]"; calc#sub (); mat_error (); test_result_float_tol 0.0 mprec "sub-fmat-fmat-1"; load_data "[[-5, -6][-7, -8]]_m^2/min [[1, 2][3, 4]]_yd^2/s [[55.1676416, 106.3352832][157.5029248, 208.6705664]]_m^2/min"; calc#sub (); mat_error (); test_result_float_tol 0.0 uprec "sub-fmat-fmat-2"; load_data "[[(-5, -6), (-7, -8)][(-9, -10), (-11, -12)]] [[1, 2][3, 4]] [[(6, 6), (9, 8)][(12, 10), (15, 12)]]"; calc#sub (); mat_error (); test_result_float_tol 0.0 mprec "sub-fmat-cmat-1"; load_data "[[(-5, -10.0), (-6, -20.0)][(-7, -30.0), (-8, -40.0)]]_m^2/min [[1, 2][3, 4]]_yd^2/s [[(55.1676416, 10.0), (106.3352832, 20.0)] [(157.5029248, 30.0), (208.6705664, 40.0)]]_m^2/min"; calc#sub (); mat_error (); test_result_float_tol 0.0 uprec "sub_fmat_cmat_2"; load_data "[[(4, 6), (5, 8)][(6, 10), (7, 12)]] [[(5, 6), (7, 8)][(9, 10), (11, 12)]] [[1, 2][3, 4]]"; calc#sub (); mat_error (); test_result_float_tol 0.0 mprec "sub-cmat-fmat-1"; load_data "[[(5, 10), (6, 20)][(7, 30), (8, 40)]]_m^2/min [[(55.1676416, 10), (106.3352832, 20)] [(157.5029248, 30), (208.6705664, 40)]]_m^2/min [[1, 2][3, 4]]_yd^2/s"; calc#sub (); load_data "1_m^2/min"; calc#convert_units (); mat_error (); test_result_float_tol 0.0 uprec "sub-cmat-fmat-2"; load_data "[[(6, -8), (-11, -16)][(-95, 3), (19, 23)]] [[(1, 2), (3, 4)][(5, 6), (7, 8)]] [[(-5, 10), (14, 20)][(100, 3), (-12, -15)]]"; calc#sub (); mat_error (); test_result_float_tol 0.0 mprec "sub-cmat-cmat-1"; load_data "[[(-1, 2), (4, -100)][(-50, -10), (9, 30)]]_lb [[(5, 10), (6, 20)][(7, 30), (8, 40)]]_kg [[(12.0231131092, 20.0462262185), (9.22773573109, 144.092452437)] [(65.4323583529, 76.1386786555), (8.63698097479, 58.184904874)]]_lb"; calc#sub (); mat_error (); test_result_float_tol 0.0 uprec "sub-cmat-cmat-2"; calc#clear (); (************************************************) (* MULTIPLICATION *) (************************************************) print_endline "testing mult()..."; load_data "#15`d #-5`d"; calc#mult (); test_result_exact "# -75`d" "mult-int-int-1"; load_data "#65`o #9f`h"; calc#mult (); test_result_exact "# 8427`d" "mult-int-int-2"; load_data "#10`d 20"; calc#mult (); test_result_float_tolnorm 200.0 mprec "mult-int-float-1"; load_data "#10`d 20_m^2/s"; calc#mult (); test_result_float_tolnorm 200.0 mprec "mult-int-float-1"; load_data "(200, -300)_ft^3*s #10`d (20, -30)_ft^3*s"; calc#mult (); cpx_error (); test_result_float_tol 0.0 mprec "mult-int-complex-1"; load_data "30 #15`d"; calc#mult (); test_result_float_tolnorm 450.0 mprec "mult-float-int-1"; load_data "30_ft^2 #15`d"; calc#mult (); test_result_float_tolnorm 450.0 mprec "mult-float-int-2"; load_data "20 30"; calc#mult (); test_result_float_tolnorm 600.0 mprec "mult-float-float-1"; load_data "50_m/s 60_kg/hr"; calc#mult (); test_result_float_tolnorm 0.833333333333333 mprec "mult-float-float-2"; load_data "(-800, 160) -20 (40, -8)"; calc#mult (); cpx_error (); test_result_float_tol 0.0 mprec "mult-float-complex-1"; load_data "(-74.322432, 14.8644864)_m^3 -20_m/s (40, -8)_ft^2*s"; calc#mult (); cpx_error (); test_result_float_tol 0.0 uprec "mult-float-complex-2"; load_data "(300, -600) (10, -20) #30`d"; calc#mult (); cpx_error (); test_result_float_tol 0.0 mprec "mult-complex-int-1"; load_data "(300, -600)_m^2 (10, -20)_m^2 #30`d"; calc#mult (); cpx_error (); test_result_float_tol 0.0 mprec "mult-complex-int-2"; load_data "(-300, 450) (-20, 30) 15"; calc#mult (); cpx_error (); test_result_float_tol 0.0 mprec "mult-complex-float-1"; load_data "(-661.386786555, 992.080179832)_lb^3/s (-20, 30)_lb^2 15_kg/s"; calc#mult (); cpx_error (); test_result_float_tol 0.0 uprec "mult-complex-float-2"; load_data "(1100, 200) (10, 20) (30, -40)"; calc#mult (); cpx_error (); test_result_float_tol 0.0 mprec "mult-complex-complex-1"; load_data "(43307.0866143, 7874.0157481)_kg*in^2/s (10, 20)_in/s^2 (30, -40)_kg*m*s"; calc#mult (); cpx_error (); test_result_float_tol 0.0 uprec "mult-complex-complex-2"; calc#clear (); (************************************************) (* DIVISION *) (************************************************) print_endline "testing div()..."; load_data "#75`d #-5`d"; calc#div (); test_result_exact "# -15`d" "div-int-int-1"; load_data "#20353`o #9f`h"; calc#div (); test_result_exact "# 53`d" "div-int-int-2"; load_data "#10`d 20"; calc#div (); test_result_float_tolnorm 0.5 mprec "div-int-float-1"; load_data "#10`d 20_m^2/s"; calc#div (); test_result_float_tolnorm 0.5 mprec "div-int-float-1"; load_data "(4, 2)_ft^-3*s^-1 #10`d (2, -1)_ft^3*s"; calc#div (); cpx_error (); test_result_float_tol 0.0 mprec "div-int-complex-1"; load_data "30 #15`d"; calc#div (); test_result_float_tolnorm 2.0 mprec "div-float-int-1"; load_data "30_ft^2 #15`d"; calc#div (); test_result_float_tolnorm 2.0 mprec "div-float-int-2"; load_data "30 20"; calc#div (); test_result_float_tolnorm 1.5 mprec "div-float-float-1"; load_data "50_m/s 60_kg/hr"; calc#div (); test_result_float_tolnorm 3000.0 mprec "div-float-float-2"; load_data "(-4, -2) -20 (4, -2)"; calc#div (); cpx_error (); test_result_float_tol 0.0 mprec "div-float-complex-1"; load_data "(-1.5773268726, -0.315465374521)_ft^-1*s^-2 -20_m/s (40, -8)_ft^2*s"; calc#div (); cpx_error (); test_result_float_tol 0.0 uprec "div-float-complex-2"; load_data "(1, -2) (30, -60) #30`d"; calc#div (); cpx_error (); test_result_float_tol 0.0 mprec "div-complex-int-1"; load_data "(1, -2)_m^2 (30, -60)_m^2 #30`d"; calc#div (); cpx_error (); test_result_float_tol 0.0 mprec "div-complex-int-2"; load_data "(-2, 3) (-30, 45) 15"; calc#div (); cpx_error (); test_result_float_tol 0.0 mprec "div-complex-float-1"; load_data "(-0.274328050829, 0.411492076245)_kg*s (-20, 30)_lb^2 15_kg/s"; calc#div (); cpx_error (); test_result_float_tol 0.0 uprec "div-complex-float-2"; load_data "(-0.2, 0.4) (10, 20) (30, -40)"; calc#div (); cpx_error (); test_result_float_tol 0.0 mprec "div-complex-complex-1"; load_data "(-0.00508, 0.01016)_kg^-1*s^-3 (10, 20)_in/s^2 (30, -40)_kg*m*s"; calc#div (); cpx_error (); test_result_float_tol 0.0 uprec "div-complex-complex-2"; print_endline "rpc_calc tested OK!";; (* arch-tag: DO_NOT_CHANGE_f6a7a71b-838a-4128-8858-14708a0c2f69 *) orpie-release-1.6.0/src/orpie/div.ml000066400000000000000000000237431334120314700173020ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) open Rpc_stack open Gsl.Error open Gsl_assist open Big_int let div (stack : rpc_stack) (evaln : int -> unit) = evaln 2; let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in match gen_el1 with |RpcInt el1 -> ( match gen_el2 with |RpcInt el2 -> stack#push (RpcInt (div_big_int el1 el2)) |RpcFloatUnit (el2, uu2) -> let f_el1 = float_of_big_int el1 in stack#push (RpcFloatUnit (f_el1 /. el2, Units.div Units.empty_unit uu2)) |RpcComplexUnit (el2, uu2) -> let c_el1 = cmpx_of_int el1 in stack#push (RpcComplexUnit (Complex.div c_el1 el2, Units.div Units.empty_unit uu2)) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for division")) ) |RpcFloatUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> let f_el2 = float_of_big_int el2 in stack#push (RpcFloatUnit (el1 /. f_el2, uu1)) |RpcFloatUnit (el2, uu2) -> stack#push (RpcFloatUnit (el1 /. el2, Units.div uu1 uu2)) |RpcComplexUnit (el2, uu2) -> let c_el1 = c_of_f el1 in stack#push (RpcComplexUnit (Complex.div c_el1 el2, Units.div uu1 uu2)) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for division")) ) |RpcComplexUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> let c_el2 = cmpx_of_int el2 in stack#push (RpcComplexUnit (Complex.div el1 c_el2, uu1)) |RpcFloatUnit (el2, uu2) -> let c_el2 = c_of_f el2 in stack#push (RpcComplexUnit (Complex.div el1 c_el2, Units.div uu1 uu2)) |RpcComplexUnit (el2, uu2) -> stack#push (RpcComplexUnit (Complex.div el1 el2, Units.div uu1 uu2)) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for division")) ) |RpcFloatMatrixUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> let f_el2 = float_of_big_int el2 in let result = Gsl.Matrix.copy el1 in (Gsl.Matrix.scale result (1.0 /. f_el2)); stack#push (RpcFloatMatrixUnit (result, uu1)) |RpcFloatUnit (el2, uu2) -> let result = Gsl.Matrix.copy el1 in (Gsl.Matrix.scale result (1.0 /. el2)); stack#push (RpcFloatMatrixUnit (result, Units.div uu1 uu2)) |RpcComplexUnit (el2, uu2) -> let c_el1 = cmat_of_fmat el1 in Gsl.Matrix_complex.scale c_el1 (Complex.inv el2); stack#push (RpcComplexMatrixUnit (c_el1, Units.div uu1 uu2)) |RpcFloatMatrixUnit (el2, uu2) -> let n1, m1 = (Gsl.Matrix.dims el1) and n2, m2 = (Gsl.Matrix.dims el2) in if n2 = m2 then if m1 = n2 then let copy_el2 = Gsl.Vectmat.mat_convert ~protect:true (`M el2) and perm = Gsl.Permut.create m1 and inv = Gsl.Matrix.create m1 m1 in try let _ = Gsl.Linalg._LU_decomp copy_el2 perm in Gsl.Linalg._LU_invert copy_el2 perm (`M inv); let result = Gsl.Matrix.create n1 m2 in Gsl.Blas.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:1.0 ~a:el1 ~b:inv ~beta:0.0 ~c:result; stack#push (RpcFloatMatrixUnit (result, Units.div uu1 uu2)) with Gsl_exn _ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "divisor matrix is singular")) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible dimensions for division")) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "divisor matrix is non-square")) |RpcComplexMatrixUnit (el2, uu2) -> let n1, m1 = (Gsl.Matrix.dims el1) and n2, m2 = (Gsl.Matrix_complex.dims el2) in if n2 = m2 then if m1 = n2 then let copy_el2 = Gsl.Matrix_complex.copy el2 and perm = Gsl.Permut.create m1 and inv = Gsl.Matrix_complex.create m1 m1 in try let _ = Gsl.Linalg.complex_LU_decomp (`CM copy_el2) perm in Gsl.Linalg.complex_LU_invert (`CM copy_el2) perm (`CM inv); let result = Gsl.Matrix_complex.create n1 m2 in Gsl.Blas.Complex.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:Complex.one ~a:(cmat_of_fmat el1) ~b:inv ~beta:Complex.zero ~c:result; stack#push (RpcComplexMatrixUnit (result, Units.div uu1 uu2)) with Gsl_exn _ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "divisor matrix is singular")) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for division")) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "divisor matrix is non-square")) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for division")) ) |RpcComplexMatrixUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> let c_el2 = cmpx_of_int el2 in let result = Gsl.Matrix_complex.copy el1 in Gsl.Matrix_complex.scale result (Complex.inv c_el2); stack#push (RpcComplexMatrixUnit (result, uu1)) |RpcFloatUnit (el2, uu2) -> let result = Gsl.Matrix_complex.copy el1 in Gsl.Matrix_complex.scale result (Complex.inv (c_of_f el2)); stack#push (RpcComplexMatrixUnit (result, Units.div uu1 uu2)) |RpcComplexUnit (el2, uu2) -> let result = Gsl.Matrix_complex.copy el1 in Gsl.Matrix_complex.scale result (Complex.inv el2); stack#push (RpcComplexMatrixUnit (result, Units.div uu1 uu2)) |RpcFloatMatrixUnit (el2, uu2) -> let n1, m1 = (Gsl.Matrix_complex.dims el1) and n2, m2 = (Gsl.Matrix.dims el2) in if n2 = m2 then if m1 = n2 then let copy_el2 = Gsl.Matrix.copy el2 and perm = Gsl.Permut.create m1 and inv = Gsl.Matrix.create m1 m1 in try let _ = Gsl.Linalg._LU_decomp (`M copy_el2) perm in Gsl.Linalg._LU_invert (`M copy_el2) perm (`M inv); let result = Gsl.Matrix_complex.create n1 m2 in Gsl.Blas.Complex.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:Complex.one ~a:el1 ~b:(cmat_of_fmat inv) ~beta:Complex.zero ~c:result; stack#push (RpcComplexMatrixUnit (result, Units.div uu1 uu2)) with Gsl_exn _ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "divisor matrix is singular")) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for division")) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "divisor matrix is non-square")) |RpcComplexMatrixUnit (el2, uu2) -> let n1, m1 = (Gsl.Matrix_complex.dims el1) and n2, m2 = (Gsl.Matrix_complex.dims el2) in if n2 = m2 then if m1 = n2 then (* FIXME: do we need to use Gsl.Vectmat.cmat_convert here? *) let copy_el2 = Gsl.Matrix_complex.copy el2 and perm = Gsl.Permut.create m1 and inv = Gsl.Matrix_complex.create m1 m1 in try let _ = Gsl.Linalg.complex_LU_decomp (`CM copy_el2) perm in Gsl.Linalg.complex_LU_invert (`CM copy_el2) perm (`CM inv); let result = Gsl.Matrix_complex.create n1 m2 in Gsl.Blas.Complex.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:Complex.one ~a:el1 ~b:inv ~beta:Complex.zero ~c:result; stack#push (RpcComplexMatrixUnit (result, Units.div uu1 uu2)) with Gsl_exn _ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "divisor matrix is singular")) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for division")) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "divisor matrix is non-square")) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for division")) ) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for division")) (* arch-tag: DO_NOT_CHANGE_c2535853-756a-4574-8f36-1103a81d053b *) orpie-release-1.6.0/src/orpie/dune000066400000000000000000000013231334120314700170320ustar00rootroot00000000000000(executable (name main) (public_name orpie) (preprocess (action (system "camlp5o %{input-file}"))) (flags (:standard -w -27-35-52 -thread -unsafe-string)) (libraries curses gsl num str threads)) ; Use orpie.opam as the authoritative source of version number (rule (targets version.ml) (action (system "grep '^version:' %{deps} | sed -E 's/^version: *\"([^\"]*)\"/let version = \"\\1\"/' > %{targets}")) (deps (file %{project_root}/orpie.opam))) ; Support $PREFIX for overriding installation location (rule (targets install.ml) (action (run %{project_root}/scripts/compute_prefix subst %{deps} %{targets})) (deps (file install.ml.in))) (ocamllex txtin_lexer) (ocamlyacc txtin_parser) orpie-release-1.6.0/src/orpie/gsl_assist.ml000066400000000000000000000042701334120314700206650ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* gsl_assist.ml * * Contains a bunch of helper functions that make ocamlgsl a bit easier * to work with. *) (* simplifying functions for use with ocamlgsl bindings *) let cmpx_of_int i = {Complex.re=Big_int.float_of_big_int i; Complex.im=0.0} let cmpx_of_float f = {Complex.re=f; Complex.im=0.0} let cmat_of_fmat fm = let rows, cols = Gsl.Matrix.dims fm and f_array = Gsl.Matrix.to_array fm in let c_array = Array.map cmpx_of_float f_array in Gsl.Matrix_complex.of_array c_array rows cols (* 1-norm of matrix *) let one_norm mat = let n, m = Gsl.Matrix.dims mat in let maxval = ref (-1.0) in let sum = ref 0.0 in for j = 0 to pred m do sum := 0.0; for i = 0 to pred n do sum := !sum +. (abs_float mat.{i, j}) done; if !sum > !maxval then maxval := !sum else () done; !maxval (* solve a complex linear system using LU decomposition *) let solve_complex_LU ?(protect=true) mat b = let mA = Gsl.Vectmat.cmat_convert ~protect mat in let vB = (`CV (Gsl.Vector_complex.copy b)) in let (len, _) = Gsl.Vectmat.dims mA in let p = Gsl.Permut.create len in let _ = Gsl.Linalg.complex_LU_decomp mA p in let x = Gsl.Vector_complex_flat.create len in Gsl.Linalg.complex_LU_solve mA p ~b:vB ~x:(`CVF x); Gsl.Vector_complex_flat.to_complex_array x (* arch-tag: DO_NOT_CHANGE_a19e0df2-6d6b-4925-87eb-be2a2926ffbb *) orpie-release-1.6.0/src/orpie/install.ml.in000066400000000000000000000002101334120314700205530ustar00rootroot00000000000000(* Hard-code the installation prefix and sysconfdir. *) let prefix = "@prefix@" let sysconfdir = Filename.concat prefix "etc/orpie" orpie-release-1.6.0/src/orpie/interface.ml000066400000000000000000000170351334120314700204550ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* interface.ml * This file defines the data structure (a record) that stores the * interface state. The state includes information on the curses screen, * the current editing mode, the data stored in entry string buffers, etc. *) open Curses;; open Rpc_calc;; exception Not_handled;; (* help_win is provided as an option, because it may be dropped if * the screen width is too small *) type screen_t = {stdscr:window; mutable lines:int; mutable cols:int; mutable help_win:window option; mutable hw_lines:int; mutable hw_cols:int; mutable stack_win:window; mutable sw_lines:int; mutable sw_cols:int; mutable entry_win:window; mutable ew_lines:int; mutable ew_cols:int};; type entry_t = | IntEntry | FloatEntry | ComplexEntry | FloatMatrixEntry | ComplexMatrixEntry | VarEntry;; type interface_mode_t = | StandardEditMode | IntEditMode | AbbrevEditMode | VarEditMode | UnitEditMode | BrowsingMode;; type abbrev_const_t = IsAbbrev | IsConst;; type complex_entry_element_t = {mutable re_mantissa : string; mutable re_exponent : string; mutable im_mantissa : string; mutable im_exponent : string; mutable is_polar : bool};; let max_matrix_size = 1000;; let all_taglines = [| "RPN for the masses"; "'=' is for the weak"; "swap drop dup view"; "I hate the mouse"; "now w/ 800% more stack"; "powered by OCaml"; "compute _this_"; "interface as art"; "kick that data's ass"; "Nice."; "configurability is key"; ":wq"; "the \"Mutt\" of calcs"|]; (* everything you need to know about the interface state goes in this variable *) type interface_state_t = {version : string; (* program version string *) tagline : string; calc : rpc_calc; mutable scr : screen_t; (* curses screen with two or three subwindows *) mutable run_calc : bool; (* exit when run_true becomes false *) mutable stack_bottom_row : int; (* controls what portion of the stack is viewable *) mutable stack_selection : int; (* in stack browsing mode, this item is selected *) mutable interface_mode : interface_mode_t; (* standard mode or stack browsing mode *) mutable horiz_scroll : int; (* controls how far an element is scrolled left/right *) mutable help_page : int; (* which help page is being viewed *) mutable has_entry : bool; (* whether or not the entry buffer has anything in it *) mutable entry_type : entry_t; (* the current type of data being entered *) mutable int_entry_buffer : string; (* holds characters entered for int data type *) mutable is_entering_base : bool; (* is the user is entering a base *) mutable int_base_string : string; (* one-character representation of the base *) mutable is_entering_exponent : bool; (* is the user entering a scientific notation exponent *) mutable abbrev_entry_buffer : string; (* stores characters entered in abbrev entry mode *) mutable matched_abbrev_list : string list; (* stores the list of all possible command completions *) gen_buffer : complex_entry_element_t array; (* storage for floating-point (array)-based types *) mutable abbrev_or_const : abbrev_const_t; (* in AbbrevEntryMode, are we entering an abbrev or a constant? *) mutable variable_entry_buffer : string; (* stores characters entered in variable entry mode *) mutable variable_entry_buffer_back : string; (* used in variable completion *) mutable matched_variables : string list; (* stores the list of all matching variable completions *) mutable sorted_variables : string list; (* stores an alphabetically sorted list of all variables *) mutable completion : int option; (* which one of the list elements to complete variables with *) mutable curr_buf : int; (* which element of gen_buffer is being edited *) mutable is_entering_imag : bool; (* is the imaginary component being edited *) mutable matrix_cols : int; (* how many cols in the matrix being entered *) mutable has_multiple_rows : bool; (* does the matrix being entered have >1 row *) mutable units_entry_buffer : string; (* stores unit characters entered *) mutable is_entering_units : bool} (* is the user appending units *) (* create and initialize an interface with default settings *) let make (c : rpc_calc) (std : screen_t) = Random.self_init (); let tagline_index = Random.int (Array.length all_taglines) in let iface = { version = Version.version; tagline = all_taglines.(tagline_index); calc = c; scr = std; run_calc = true; stack_bottom_row = 1; stack_selection = 1; interface_mode = StandardEditMode; horiz_scroll = 0; help_page = 0; has_entry = false; entry_type = FloatEntry; int_entry_buffer = ""; is_entering_base = false; int_base_string = ""; is_entering_exponent = false; abbrev_entry_buffer = ""; matched_abbrev_list = []; abbrev_or_const = IsAbbrev; variable_entry_buffer = ""; variable_entry_buffer_back = ""; matched_variables = []; sorted_variables = []; completion = None; gen_buffer = Array.make max_matrix_size {re_mantissa = ""; re_exponent = ""; im_mantissa = ""; im_exponent = ""; is_polar = false}; curr_buf = 0; is_entering_imag = false; matrix_cols = 1; has_multiple_rows = false; units_entry_buffer = ""; is_entering_units = false } in iface (* arch-tag: DO_NOT_CHANGE_2e912989-cdb2-498a-9bb3-b6d76e94f3a5 *) orpie-release-1.6.0/src/orpie/interface_draw.ml000066400000000000000000000744741334120314700215040ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* interface_draw.ml * This file has all code concerned with rendering the stack, help panel, * and data entry line. *) open Curses;; open Printf;; open Rpc_stack;; open Operations;; open Interface;; type abbrev_help_display_t = {functions : string list; modes : string list; misc : string list} (* display the stack, where the bottom line of the display * corresponds to stack level 'stack_bottom_row' *) let draw_stack (iface : interface_state_t) = let print_numbered_line l_num = let num_len = String.length (string_of_int (pred (iface.stack_bottom_row + iface.scr.sw_lines))) in if num_len <= 2 then (sprintf "%2d: %s" l_num) else if num_len = 3 then (sprintf "%3d: %s" l_num) else if num_len = 4 then (sprintf "%4d: %s" l_num) else (* if the line number is really huge, truncate to least * significant digits *) let l_num_str = string_of_int l_num in let str_len = String.length l_num_str in let trunc_num = Str.string_after l_num_str (str_len - 4) in (sprintf "%s: %s" trunc_num) in (* if there is no help window, then print the calculator mode * information above the stack *) let num_stack_lines = begin match iface.scr.help_win with |Some win -> iface.scr.sw_lines |None -> let modes = iface.calc#get_modes () in assert (wmove iface.scr.stack_win 0 0); wclrtoeol iface.scr.stack_win; wattron iface.scr.stack_win WA.bold; assert (mvwaddstr iface.scr.stack_win 0 2 "angle: base: complex:"); wattroff iface.scr.stack_win WA.bold; let angle_str = match modes.angle with |Rad -> "RAD" |Deg -> "DEG" in assert (mvwaddstr iface.scr.stack_win 0 9 angle_str); let base_str = match modes.base with |Bin -> "BIN" |Oct -> "OCT" |Hex -> "HEX" |Dec -> "DEC" in assert (mvwaddstr iface.scr.stack_win 0 20 base_str); let complex_str = match modes.complex with |Rect -> "REC" |Polar -> "POL" in assert (mvwaddstr iface.scr.stack_win 0 34 complex_str); assert (mvwaddstr iface.scr.stack_win 1 0 (String.make (iface.scr.sw_cols) '-')); iface.scr.sw_lines - 2 end in (* display the stack data itself *) for line = iface.stack_bottom_row to pred (iface.stack_bottom_row + num_stack_lines) do let s = iface.calc#get_display_line line in let len = String.length s in assert (wmove iface.scr.stack_win (iface.scr.sw_lines + iface.stack_bottom_row - 1 - line) 0); wclrtoeol iface.scr.stack_win; if line = iface.stack_selection && iface.interface_mode = BrowsingMode then wattron iface.scr.stack_win WA.reverse else (); if len > iface.scr.sw_cols - 7 then begin (* need to truncate the string *) let line_string = if line = iface.stack_selection && iface.interface_mode = BrowsingMode then let sub_s = if iface.horiz_scroll < len - iface.scr.sw_cols + 7 then String.sub s iface.horiz_scroll (iface.scr.sw_cols - 7) else String.sub s (len - iface.scr.sw_cols + 7) (iface.scr.sw_cols - 7) in print_numbered_line line sub_s else let sub_s = String.sub s 0 (iface.scr.sw_cols - 10) in print_numbered_line line (sub_s ^ "...") in assert (waddstr iface.scr.stack_win line_string) end else begin let spacer = String.make (iface.scr.sw_cols - 7 - len) ' ' in let line_string = print_numbered_line line (spacer ^ s) in assert (waddstr iface.scr.stack_win line_string) end; if line = iface.stack_selection && iface.interface_mode = BrowsingMode then wattroff iface.scr.stack_win WA.reverse else (); done; assert (wnoutrefresh iface.scr.stack_win); assert (wmove iface.scr.entry_win (iface.scr.ew_lines - 1) (iface.scr.ew_cols - 1)) let draw_update_stack iface = draw_stack iface; assert (doupdate ()) (* display the data that the user is in the process of entering *) let draw_entry (iface : interface_state_t) = assert (mvwaddstr iface.scr.entry_win 0 0 (String.make iface.scr.ew_cols '-')); assert (wmove iface.scr.entry_win 1 0); wclrtoeol iface.scr.entry_win; (* Safely draw a string into the entry window, with "..." when * truncation occurs. Highlight the first 'highlight_len' * characters. *) let draw_entry_string str highlight_len = let len_str = String.length str in begin if len_str > iface.scr.ew_cols - 1 then let trunc_str = String.sub str (len_str - iface.scr.ew_cols + 4) (iface.scr.ew_cols - 4) in assert (mvwaddstr iface.scr.entry_win 1 0 ("..." ^ trunc_str)) else if highlight_len <= len_str then begin (* highlight the first 'highlight_len' characters *) wattron iface.scr.entry_win WA.bold; assert (mvwaddstr iface.scr.entry_win 1 (iface.scr.ew_cols - len_str - 1) (Str.string_before str (highlight_len))); wattroff iface.scr.entry_win WA.bold; assert (mvwaddstr iface.scr.entry_win 1 (iface.scr.ew_cols - len_str - 1 + highlight_len) (Str.string_after str (highlight_len))) end else assert (mvwaddstr iface.scr.entry_win 1 (iface.scr.ew_cols - len_str - 1) str) end; assert (wnoutrefresh iface.scr.entry_win) in (* draw a string for a single floating-point number *) let get_float_str is_current mantissa exponent = let sign_space = if String.length exponent > 0 then match exponent.[0] with |'-' -> "" |'+' -> "" |_ -> " " else " " in if (is_current && iface.is_entering_exponent) || String.length exponent > 0 then mantissa ^ " x10^" ^ sign_space ^ exponent else if is_current || String.length mantissa > 0 then mantissa else "0" in (* get a string representation of the data that is in the entry buffer *) let data_string = match iface.entry_type with |IntEntry -> if iface.is_entering_base then "# " ^ iface.int_entry_buffer ^ "`" ^ iface.int_base_string else "# " ^ iface.int_entry_buffer |FloatEntry -> let mantissa_str = iface.gen_buffer.(0).re_mantissa and exponent_str = iface.gen_buffer.(0).re_exponent in let ff = get_float_str true mantissa_str exponent_str in if iface.is_entering_units then ff ^ "_" ^ iface.units_entry_buffer else ff |ComplexEntry -> let buffer = iface.gen_buffer.(0) in let cc = if iface.is_entering_imag then let temp = get_float_str false buffer.re_mantissa buffer.re_exponent in let re_str = if String.length temp > 0 then temp else "0" in let im_str = get_float_str true buffer.im_mantissa buffer.im_exponent in match buffer.is_polar with |false -> "(" ^ re_str ^ ", " ^ im_str ^ ")" |true -> "(" ^ re_str ^ " <" ^ im_str ^ ")" else let re_str = get_float_str true buffer.re_mantissa buffer.re_exponent in "(" ^ re_str ^ ")" in if iface.is_entering_units then cc ^ "_" ^ iface.units_entry_buffer else cc |FloatMatrixEntry -> let ss = ref "[[" in for el = 0 to pred iface.curr_buf do let temp_re = get_float_str false iface.gen_buffer.(el).re_mantissa iface.gen_buffer.(el).re_exponent in if iface.has_multiple_rows && ((succ el) mod iface.matrix_cols) = 0 then ss := !ss ^ temp_re ^ "][" else ss := !ss ^ temp_re ^ ", " done; let temp_re = get_float_str true iface.gen_buffer.(iface.curr_buf).re_mantissa iface.gen_buffer.(iface.curr_buf).re_exponent in ss := !ss ^ temp_re ^ "]]"; if iface.is_entering_units then !ss ^ "_" ^ iface.units_entry_buffer else !ss |ComplexMatrixEntry -> let ss = ref "[[" in for el = 0 to pred iface.curr_buf do let temp_re = get_float_str false iface.gen_buffer.(el).re_mantissa iface.gen_buffer.(el).re_exponent and temp_im = get_float_str false iface.gen_buffer.(el).im_mantissa iface.gen_buffer.(el).im_exponent in (if iface.has_multiple_rows && ((succ el) mod iface.matrix_cols) = 0 then match iface.gen_buffer.(el).is_polar with |false -> ss := !ss ^ "(" ^ temp_re ^ ", " ^ temp_im ^ ")][" |true -> ss := !ss ^ "(" ^ temp_re ^ " <" ^ temp_im ^ ")][" else match iface.gen_buffer.(el).is_polar with |false -> ss := !ss ^ "(" ^ temp_re ^ ", " ^ temp_im ^ "), " |true -> ss := !ss ^ "(" ^ temp_re ^ " <" ^ temp_im ^ "), ") done; (if iface.is_entering_imag then let temp_re = get_float_str false iface.gen_buffer.(iface.curr_buf).re_mantissa iface.gen_buffer.(iface.curr_buf).re_exponent and temp_im = get_float_str true iface.gen_buffer.(iface.curr_buf).im_mantissa iface.gen_buffer.(iface.curr_buf).im_exponent in match iface.gen_buffer.(iface.curr_buf).is_polar with |false -> ss := !ss ^ "(" ^ temp_re ^ ", " ^ temp_im ^ ")]]" |true -> ss := !ss ^ "(" ^ temp_re ^ " <" ^ temp_im ^ ")]]" else let temp_re = get_float_str true iface.gen_buffer.(iface.curr_buf).re_mantissa iface.gen_buffer.(iface.curr_buf).re_exponent in ss := !ss ^ "(" ^ temp_re ^ ")]]"); if iface.is_entering_units then !ss ^ "_" ^ iface.units_entry_buffer else !ss |VarEntry -> "@ " ^ iface.variable_entry_buffer in begin match iface.interface_mode with |StandardEditMode -> draw_entry_string data_string 0 |IntEditMode -> draw_entry_string data_string 0 |AbbrevEditMode -> let first_abbrev_match = if iface.matched_abbrev_list = [] then "" else List.hd iface.matched_abbrev_list in let highlight_len = String.length iface.abbrev_entry_buffer in if highlight_len = 0 then begin match iface.abbrev_or_const with |IsAbbrev -> draw_entry_string "" 0 |IsConst -> draw_entry_string "" 0 end else begin match iface.abbrev_or_const with |IsAbbrev -> let is_function = match (Rcfile.translate_abbrev first_abbrev_match) with |Function ff -> true |_ -> false in if is_function then draw_entry_string (first_abbrev_match ^ "( )") highlight_len else draw_entry_string first_abbrev_match highlight_len |IsConst -> draw_entry_string first_abbrev_match highlight_len end |BrowsingMode -> () |VarEditMode -> if String.length iface.variable_entry_buffer = 0 then draw_entry_string "" 0 else draw_entry_string data_string 0 |UnitEditMode -> draw_entry_string data_string 0 end; assert (wmove iface.scr.entry_win (iface.scr.ew_lines - 1) (iface.scr.ew_cols - 1)) let draw_update_entry iface = draw_entry iface; assert (doupdate ()) (* create the lists of abbreviations to display in the abbrev command * help screen *) let generate_abbrev_help () = let rec trunc_list lst n = if n = 0 then [] else match lst with |[] -> [] |head :: tail -> head :: (trunc_list tail (pred n)) in let get_abbr op = try Rcfile.abbrev_of_operation op with Not_found -> "" in let functions_str = (get_abbr (Function Sin)) ^ " " ^ (get_abbr (Function Asin)) ^ " " ^ (get_abbr (Function Cos)) ^ " " ^ (get_abbr (Function Acos)) ^ " " ^ (get_abbr (Function Tan)) ^ " " ^ (get_abbr (Function Atan)) ^ " " ^ (get_abbr (Function Exp)) ^ " " ^ (get_abbr (Function Ln)) ^ " " ^ (get_abbr (Function Ten_x)) ^ " " ^ (get_abbr (Function Log10)) ^ " " ^ (get_abbr (Function Sq)) ^ " " ^ (get_abbr (Function Sqrt)) ^ " " ^ (get_abbr (Function Inv)) ^ " " ^ (get_abbr (Function Gamma)) ^ " " ^ (get_abbr (Function LnGamma)) ^ " " ^ (get_abbr (Function Erf)) ^ " " ^ (get_abbr (Function Erfc)) ^ " " ^ (get_abbr (Function Transpose)) ^ " " ^ (get_abbr (Function Re)) ^ " " ^ (get_abbr (Function Im)) ^ " " ^ (get_abbr (Function Mod)) ^ " " ^ (get_abbr (Function Floor)) ^ " " ^ (get_abbr (Function Ceiling)) ^ " " ^ (get_abbr (Function ToInt)) ^ " " ^ (get_abbr (Function ToFloat)) ^ " " ^ (get_abbr (Function Eval)) ^ " " ^ (get_abbr (Function Store)) ^ " " ^ (get_abbr (Function Purge)) in let functions_str_wrap = trunc_list (Utility.wordwrap_nspace functions_str 34 2) 5 in let modes_str = (get_abbr (Command SetRadians)) ^ " " ^ (get_abbr (Command SetDegrees)) ^ " " ^ (get_abbr (Command SetBin)) ^ " " ^ (get_abbr (Command SetOct)) ^ " " ^ (get_abbr (Command SetDec)) ^ " " ^ (get_abbr (Command SetHex)) ^ " " ^ (get_abbr (Command SetRect)) ^ " " ^ (get_abbr (Command SetPolar)) in let modes_str_wrap = trunc_list (Utility.wordwrap_nspace modes_str 34 2) 2 in let misc_str = (get_abbr (Command EnterPi)) ^ " " ^ (get_abbr (Command Undo)) ^ " " ^ (get_abbr (Command View)) in let misc_str_wrap = trunc_list (Utility.wordwrap_nspace misc_str 34 2) 1 in {functions = functions_str_wrap; modes = modes_str_wrap; misc = misc_str_wrap} (* create the list of constants to display in the abbrev command * help screen *) let generate_const_help () = let rec trunc_list lst n = if n = 0 then [] else match lst with |[] -> [] |head :: tail -> head :: (trunc_list tail (pred n)) in let rec make_symbols_string symbols_list symbols_str = match symbols_list with | [] -> symbols_str | head :: tail -> make_symbols_string tail (head ^ " " ^ symbols_str) in let symbols = make_symbols_string !Rcfile.constant_symbols "" in trunc_list (Utility.wordwrap_nspace symbols 34 2) 5 (* draw the help page in standard entry mode *) let draw_help_standard iface win mvwaddstr_safe try_find = if iface.help_page = 0 then begin wattron win WA.bold; assert (mvwaddstr win 5 0 "Common Operations:"); wattroff win WA.bold; mvwaddstr_safe win 6 2 ("enter : " ^ try_find (Edit Enter)); mvwaddstr_safe win 7 2 ("drop : " ^ try_find (Command Drop)); mvwaddstr_safe win 8 2 ("swap : " ^ try_find (Command Swap)); mvwaddstr_safe win 9 2 ("backspace: " ^ try_find (Edit Backspace)); mvwaddstr_safe win 10 2 ("add : " ^ try_find (Function Add)); mvwaddstr_safe win 11 2 ("subtract : " ^ try_find (Function Sub)); mvwaddstr_safe win 12 2 ("multiply : " ^ try_find (Function Mult)); mvwaddstr_safe win 13 2 ("divide : " ^ try_find (Function Div)); mvwaddstr_safe win 14 2 ("y^x : " ^ try_find (Function Pow)); mvwaddstr_safe win 15 2 ("negation : " ^ try_find (Function Neg)); wattron win WA.bold; mvwaddstr_safe win 16 0 "Miscellaneous:"; wattroff win WA.bold; mvwaddstr_safe win 17 2 ("scientific notation : " ^ try_find (Edit SciNotBase)); mvwaddstr_safe win 18 2 ("abbreviation entry mode : " ^ try_find (Command BeginAbbrev)); mvwaddstr_safe win 19 2 ("stack browsing mode : " ^ try_find (Command BeginBrowse)); mvwaddstr_safe win 20 2 ("refresh display : " ^ try_find (Command Refresh)); mvwaddstr_safe win 21 2 ("quit : " ^ try_find (Command Quit)); assert (wnoutrefresh win) end else begin let adjust_len s len = if String.length s < len then s ^ (String.make (len - (String.length s)) ' ') else Str.string_before s len in let make_string colon_pos key_string abbr = (adjust_len key_string colon_pos) ^ ": " ^ abbr in wattron win WA.bold; mvwaddstr_safe win 5 0 "Autobindings:"; wattroff win WA.bold; if Array.length !Rcfile.autobind_keys <= 0 then mvwaddstr_safe win 6 2 "(none)" else for i = 0 to pred (min (iface.scr.hw_lines - 6) (Array.length !Rcfile.autobind_keys)) do let (key, key_string, bound_f, age) = !Rcfile.autobind_keys.(i) in let abbr = match bound_f with |None -> "(none)" |Some op -> Rcfile.abbrev_of_operation op in mvwaddstr_safe win (i + 6) 2 (make_string 12 key_string abbr) done; assert (wnoutrefresh win) end (* draw help page in integer editing mode *) let draw_help_intedit iface win mvwaddstr_safe try_find = wattron win WA.bold; mvwaddstr_safe win 5 0 "Integer Editing Operations:"; wattroff win WA.bold; mvwaddstr_safe win 6 2 ("enter : " ^ try_find (Edit Enter)); mvwaddstr_safe win 7 2 ("set base : " ^ try_find (Edit SciNotBase)); mvwaddstr_safe win 8 2 ("cancel : " ^ try_find (IntEdit IntEditExit)); assert (wnoutrefresh win) (* draw help page in abbrev/constant entry mode *) let draw_help_abbrev iface win mvwaddstr_safe try_find = if String.length iface.abbrev_entry_buffer = 0 then begin let abbr_strings = generate_abbrev_help () in let const_strings = generate_const_help () in let rec print_help_lines lines v_pos = begin match lines with |[] -> () |head :: tail -> mvwaddstr_safe win v_pos 2 head; print_help_lines tail (succ v_pos) end in begin match iface.abbrev_or_const with |IsAbbrev -> wattron win WA.bold; mvwaddstr_safe win 5 0 "Abbreviations:"; wattroff win WA.bold; mvwaddstr_safe win 6 1 "Common Functions:"; print_help_lines abbr_strings.functions 7; mvwaddstr_safe win 13 1 "Change Modes:"; print_help_lines abbr_strings.modes 14; mvwaddstr_safe win 17 1 "Miscellaneous:"; print_help_lines abbr_strings.misc 18; mvwaddstr_safe win 20 1 ("execute abbreviation : " ^ try_find (Abbrev AbbrevEnter)); mvwaddstr_safe win 21 1 ("cancel abbreviation : " ^ try_find (Abbrev AbbrevExit)); |IsConst -> wattron win WA.bold; mvwaddstr_safe win 5 0 "Constants:"; wattroff win WA.bold; print_help_lines const_strings 7; mvwaddstr_safe win 12 1 ("enter constant : " ^ try_find (Abbrev AbbrevEnter)); end; assert (wnoutrefresh win) end else begin wattron win WA.bold; assert (mvwaddstr win 5 0 "Matched Abbreviations:"); wattroff win WA.bold; let highlight_len = String.length iface.abbrev_entry_buffer in let rec draw_matches v_pos match_list = if v_pos < iface.scr.hw_lines then begin match match_list with |[] -> () |m :: tail -> begin (* highlight the first 'highlight_len' characters *) wattron win WA.bold; mvwaddstr_safe win v_pos 2 (Str.string_before m (highlight_len)); wattroff win WA.bold; mvwaddstr_safe win v_pos (2 + highlight_len) (Str.string_after m (highlight_len)); draw_matches (succ v_pos) tail end end else () in draw_matches 6 iface.matched_abbrev_list; assert (wnoutrefresh win) end (* draw help page in variable editing mode *) let draw_help_varedit iface win mvwaddstr_safe try_find = wattron win WA.bold; mvwaddstr_safe win 5 0 "Variable Mode Commands:"; wattroff win WA.bold; mvwaddstr_safe win 6 2 ("enter variable : " ^ try_find (VarEdit VarEditEnter)); mvwaddstr_safe win 7 2 ("complete variable: " ^ try_find (VarEdit VarEditComplete)); mvwaddstr_safe win 8 2 ("cancel entry : " ^ try_find (VarEdit VarEditExit)); wattron win WA.bold; mvwaddstr_safe win 10 0 "Matched variables:"; wattroff win WA.bold; let highlight_len = begin match iface.completion with |None -> String.length iface.variable_entry_buffer |Some _ -> 0 end in let rec draw_matches v_pos match_list count = if v_pos < iface.scr.hw_lines then begin match match_list with |[] -> () |m :: tail -> begin match iface.completion with |None -> (* highlight the first 'highlight_len' characters *) wattron win WA.bold; mvwaddstr_safe win v_pos 2 (Str.string_before m (highlight_len)); wattroff win WA.bold; mvwaddstr_safe win v_pos (2 + highlight_len) (Str.string_after m (highlight_len)); |Some num -> (* highlight the entire selected match *) if count = num then begin wattron win WA.bold; mvwaddstr_safe win v_pos 2 m; wattroff win WA.bold; end else mvwaddstr_safe win v_pos 2 m; end; draw_matches (succ v_pos) tail (succ count) end else () in if List.length iface.matched_variables = 0 then mvwaddstr_safe win 11 2 "(none)" else draw_matches 11 iface.matched_variables 0; assert (wnoutrefresh win) (* draw help page in stack browsing mode *) let draw_help_browsing iface win mvwaddstr_safe try_find = wattron win WA.bold; mvwaddstr_safe win 5 0 "Browsing Operations:"; wattroff win WA.bold; mvwaddstr_safe win 6 2 ("prev : " ^ try_find (Browse PrevLine)); mvwaddstr_safe win 7 2 ("next : " ^ try_find (Browse NextLine)); mvwaddstr_safe win 8 2 ("scroll left : " ^ try_find (Browse ScrollLeft)); mvwaddstr_safe win 9 2 ("scroll right: " ^ try_find (Browse ScrollRight)); mvwaddstr_safe win 10 2 ("roll down : " ^ try_find (Browse RollDown)); mvwaddstr_safe win 11 2 ("roll up : " ^ try_find (Browse RollUp)); mvwaddstr_safe win 12 2 ("dup : " ^ try_find (Command Dup)); mvwaddstr_safe win 13 2 ("view : " ^ try_find (Browse ViewEntry)); mvwaddstr_safe win 14 2 ("edit : " ^ try_find (Browse EditEntry)); mvwaddstr_safe win 15 2 ("drop : " ^ try_find (Browse Drop1)); mvwaddstr_safe win 16 2 ("dropn : " ^ try_find (Browse DropN)); mvwaddstr_safe win 17 2 ("keep : " ^ try_find (Browse Keep)); mvwaddstr_safe win 18 2 ("keepn : " ^ try_find (Browse KeepN)); mvwaddstr_safe win 20 1 ("exit browsing mode: " ^ try_find (Browse EndBrowse)); assert (wnoutrefresh win) (* display the help window *) let draw_help (iface : interface_state_t) = let mvwaddstr_safe w vert horiz st = let st_trunc = if String.length st > 36 then Str.string_before st 36 else st in assert (mvwaddstr w vert horiz st_trunc) in let modes = iface.calc#get_modes () in begin match iface.scr.help_win with |None -> () |Some win -> wclear win; wattron win WA.bold; let s = sprintf "Orpie v%s" iface.version in mvwaddstr_safe win 0 0 s; wattroff win WA.bold; let h_pos = String.length s in mvwaddstr_safe win 0 (h_pos + 1) ("-- " ^ iface.tagline); assert (mvwaddstr win 1 0 "--------------------------------------"); for i = 0 to pred iface.scr.hw_lines do assert (mvwaddch win i 38 (int_of_char '|')) done; wattron win WA.bold; assert (mvwaddstr win 2 0 "Calculator Modes:"); assert (mvwaddstr win 3 2 "angle: base: complex:"); wattroff win WA.bold; let angle_str = match modes.angle with |Rad -> "RAD" |Deg -> "DEG" in assert (mvwaddstr win 3 9 angle_str); let base_str = match modes.base with |Bin -> "BIN" |Oct -> "OCT" |Hex -> "HEX" |Dec -> "DEC" in assert (mvwaddstr win 3 20 base_str); let complex_str = match modes.complex with |Rect -> "REC" |Polar -> "POL" in assert (mvwaddstr win 3 34 complex_str); let try_find op = try Rcfile.key_of_operation op with Not_found -> "(N/A)" in begin match iface.interface_mode with |StandardEditMode | UnitEditMode -> draw_help_standard iface win mvwaddstr_safe try_find |IntEditMode -> draw_help_intedit iface win mvwaddstr_safe try_find |AbbrevEditMode -> draw_help_abbrev iface win mvwaddstr_safe try_find |VarEditMode -> draw_help_varedit iface win mvwaddstr_safe try_find |BrowsingMode -> draw_help_browsing iface win mvwaddstr_safe try_find end end; assert (wmove iface.scr.entry_win (iface.scr.ew_lines - 1) (iface.scr.ew_cols - 1)) let draw_message (iface : interface_state_t) msg = draw_stack iface; let error_lines = Utility.wordwrap msg (iface.scr.sw_cols-2) in let trunc_error_lines = if List.length error_lines > 4 then (List.nth error_lines 0) :: (List.nth error_lines 1) :: (List.nth error_lines 2) :: (List.nth error_lines 3) :: [] else error_lines in let top_line = begin match iface.scr.help_win with |Some win -> 0 |None -> 2 end in for i = 0 to pred (List.length trunc_error_lines) do assert (wmove iface.scr.stack_win (i + top_line) 0); wclrtoeol iface.scr.stack_win; assert (mvwaddstr iface.scr.stack_win (i + top_line) 1 (List.nth trunc_error_lines i)) done; let s = String.make iface.scr.sw_cols '-' in assert (mvwaddstr iface.scr.stack_win ((List.length trunc_error_lines) + top_line) 0 s); assert (wnoutrefresh iface.scr.stack_win); assert (wmove iface.scr.entry_win (iface.scr.ew_lines - 1) (iface.scr.ew_cols - 1)) (* write an error message to the stack window *) let draw_error (iface : interface_state_t) msg = draw_message iface ("Error: " ^ msg) (* display the "about" screen *) let draw_about (iface : interface_state_t) = erase (); (* draw the box outline *) let horiz_line = String.make iface.scr.cols '*' in let vert_line_piece = String.make iface.scr.cols ' ' in vert_line_piece.[0] <- '*'; vert_line_piece.[pred iface.scr.cols] <- '*'; assert (mvaddstr 0 0 horiz_line); assert (mvaddstr (iface.scr.lines - 2) 0 horiz_line); for i = 1 to iface.scr.lines - 3 do assert (mvaddstr i 0 vert_line_piece) done; (* draw the text *) let vert_center = (iface.scr.lines - 2) / 2 and horiz_center = iface.scr.cols / 2 in let left_shift = 17 in attron A.bold; assert (mvaddstr (vert_center - 6) (horiz_center - left_shift) ("Orpie v" ^ iface.version)); attroff A.bold; assert (mvaddstr (vert_center - 5) (horiz_center - left_shift) "Copyright (C) 2004 Paul Pelzl"); assert (mvaddstr (vert_center - 3) (horiz_center - left_shift) "\"Because, frankly, GUI calculator"); assert (mvaddstr (vert_center - 2) (horiz_center - left_shift) " programs are pure evil. "); attron A.bold; assert (mvaddstr (vert_center - 2) (horiz_center - left_shift + 26) "Orpie"); attroff A.bold; assert (mvaddstr (vert_center - 2) (horiz_center - left_shift + 31) ", on"); assert (mvaddstr (vert_center - 1) (horiz_center - left_shift) " the other hand, is only a little"); assert (mvaddstr (vert_center + 0) (horiz_center - left_shift) " bit evil.\""); assert (mvaddstr (vert_center + 2) (horiz_center - left_shift) "Orpie comes with ABSOLUTELY NO"); assert (mvaddstr (vert_center + 3) (horiz_center - left_shift) "WARRANTY. This is free software,"); assert (mvaddstr (vert_center + 4) (horiz_center - left_shift) "and you are welcome to redistribute"); assert (mvaddstr (vert_center + 5) (horiz_center - left_shift) "it under certain conditions; see"); assert (mvaddstr (vert_center + 6) (horiz_center - left_shift) "'COPYING' for details."); assert (mvaddstr (iface.scr.lines - 4) (horiz_center - 12) "Press any key to continue."); assert (move (iface.scr.lines - 1) (iface.scr.cols - 1)); assert (refresh ()) (* arch-tag: DO_NOT_CHANGE_044cbd96-d20b-48c6-92e7-62709c2aa3df *) orpie-release-1.6.0/src/orpie/interface_main.ml000066400000000000000000002117011334120314700214550ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* interface_main.ml * This file has the bulk of the implementation of the curses-based interface, * including the main program loop that grabs keypresses and processes them. * The screen rendering code is found in interface_draw.ml . *) open Curses;; open Rpc_stack;; open Complex;; open Operations;; open Interface;; open Interface_draw;; exception Interrupt_exception;; (*******************************************************************) (* RESIZE HANDLER *) (*******************************************************************) (* create the (new) windows corresponding to the different areas of the screen *) let create_windows screen = let height, width = get_size () in if height >= 24 then if width >= 80 && not !Rcfile.hide_help then (* full two-pane window provided *) let left_win = Some (newwin (height - 2) 40 0 0) and right_win = newwin (height - 2) 40 0 40 and bottom_win = newwin 2 80 (height - 2) 0 in {stdscr = screen; lines = height; cols = width; help_win = left_win; hw_lines = (height - 2); hw_cols = 40; stack_win = right_win; sw_lines = (height - 2); sw_cols = 40; entry_win = bottom_win; ew_lines = 2; ew_cols = 80} else if width >= 40 then (* only the stack window is provided *) let right_win = newwin (height - 2) 40 0 0 and bottom_win = newwin 2 width (height - 2) 0 in {stdscr = screen; lines = height; cols = width; help_win = None; hw_lines = 0; hw_cols = 0; stack_win = right_win; sw_lines = (height - 2); sw_cols = 40; entry_win = bottom_win; ew_lines = 2; ew_cols = 40} else (endwin (); failwith "Orpie requires at least a 40 column window.") else (endwin (); failwith "Orpie requires at least a 24 line window.");; (* resize the various windows to fit the new terminal size *) let resize_subwins scr = let height, width = get_size () in if height >= 24 then if width >= 80 && not !Rcfile.hide_help then (* full two-pane window provided *) begin scr.lines <- height; scr.cols <- width; begin match scr.help_win with |None -> scr.help_win <- Some (newwin (height - 2) 40 0 0) |Some win -> assert (wresize win (height - 2) 40); end; scr.hw_lines <- height - 2; scr.hw_cols <- 40; assert (wresize scr.stack_win (height - 2) 40); assert (mvwin scr.stack_win 0 40); scr.sw_lines <- height - 2; scr.sw_cols <- 40; assert (wresize scr.entry_win 2 80); assert (mvwin scr.entry_win (height - 2) 0); scr.ew_lines <- 2; scr.ew_cols <- 80 end else if width >= 40 then (* only the stack window is provided *) begin scr.lines <- height; scr.cols <- width; begin match scr.help_win with |None -> () |Some win -> assert (delwin win); scr.help_win <- None; end; scr.hw_lines <- 0; scr.hw_cols <- 0; assert (wresize scr.stack_win (height - 2) 40); assert (mvwin scr.stack_win 0 0); scr.sw_lines <- height - 2; scr.sw_cols <- 40; assert (wresize scr.entry_win 2 40); assert (mvwin scr.entry_win (height - 2) 0); scr.ew_lines <- 2; scr.ew_cols <- 40 end else (endwin (); failwith "Orpie requires at least a 40 column window.") else (endwin (); failwith "Orpie requires at least a 24 line window.");; (* refresh the screen *) let handle_refresh (iface : interface_state_t) = begin match iface.scr.help_win with |None -> () |Some win -> let _ = touchwin win in () end; let _ = touchwin iface.scr.stack_win in let _ = touchwin iface.scr.entry_win in draw_help iface; draw_stack iface; draw_update_entry iface; draw_update_stack iface (* handle a terminal resize *) let handle_resize (iface : interface_state_t) = (* reset ncurses *) endwin (); assert (refresh ()); resize_subwins iface.scr; handle_refresh iface;; (*******************************************************************) (* OBTAINING AN OBJECT FROM THE ENTRY BUFFER *) (*******************************************************************) (* parse the entry buffers to obtain a stack object *) let get_entry_from_buffer (iface : interface_state_t) = (* get a float from strings representing the mantissa and exponent *) let get_float_el mantissa exponent = if String.length mantissa > 0 then if String.length exponent > 0 then if exponent = "-" || exponent = "+" then float_of_string mantissa else float_of_string (mantissa ^ "e" ^ exponent) else float_of_string mantissa else 0.0 in match iface.entry_type with |IntEntry -> let base_mode = if iface.is_entering_base then match iface.int_base_string with |"b" -> Bin |"o" -> Oct |"d" -> Dec |"h" -> Hex |_ -> (iface.calc#get_modes ()).base else (iface.calc#get_modes ()).base in let base = match base_mode with |Bin -> 2 |Oct -> 8 |Dec -> 10 |Hex -> 16 in begin try let ival = Big_int_str.big_int_of_string_base iface.int_entry_buffer base in RpcInt ival with Big_int_str.Big_int_string_failure error_msg -> raise (Invalid_argument error_msg) end |FloatEntry -> begin let buffer = iface.gen_buffer.(0) in try let ff = get_float_el buffer.re_mantissa buffer.re_exponent in let uu = Units.units_of_string iface.units_entry_buffer !Rcfile.unit_table in RpcFloatUnit (ff, uu) with |Failure "float_of_string" -> raise (Invalid_argument "improperly formatted floating-point data") |Units.Units_error ss -> raise (Invalid_argument ss) end |ComplexEntry -> begin let buffer = iface.gen_buffer.(0) in try begin match buffer.is_polar with |false -> let real_part = get_float_el buffer.re_mantissa buffer.re_exponent and imag_part = get_float_el buffer.im_mantissa buffer.im_exponent in let cc = {re = real_part; im = imag_part} and uu = Units.units_of_string iface.units_entry_buffer !Rcfile.unit_table in RpcComplexUnit (cc, uu) (* if is_polar==true, then the data in the buffer represents * polar data... so convert it to rect notation before storing * it as a complex number. *) |true -> let r = get_float_el buffer.re_mantissa buffer.re_exponent and theta = match (iface.calc#get_modes ()).angle with |Rad -> get_float_el buffer.im_mantissa buffer.im_exponent |Deg -> (pi /. 180.0 *. (get_float_el buffer.im_mantissa buffer.im_exponent)) in let cc = {re = r *. (cos theta); im = r *. (sin theta)} and uu = Units.units_of_string iface.units_entry_buffer !Rcfile.unit_table in RpcComplexUnit (cc, uu) end with |Failure "float_of_string" -> raise (Invalid_argument "improperly formatted complex floating-point data") |Units.Units_error ss -> raise (Invalid_argument ss) end |FloatMatrixEntry -> begin let matrix_rows = if iface.has_multiple_rows then succ (iface.curr_buf / iface.matrix_cols) else 1 in let temp_arr = Array.make (matrix_rows * iface.matrix_cols) 0.0 in try for i = 0 to iface.curr_buf do temp_arr.(i) <- (get_float_el iface.gen_buffer.(i).re_mantissa iface.gen_buffer.(i).re_exponent) done; let uu = Units.units_of_string iface.units_entry_buffer !Rcfile.unit_table in RpcFloatMatrixUnit (Gsl.Matrix.of_array temp_arr matrix_rows iface.matrix_cols, uu) with Failure "float_of_string" -> raise (Invalid_argument "improperly formatted floating-point matrix data") end |ComplexMatrixEntry -> begin let matrix_rows = if iface.has_multiple_rows then succ (iface.curr_buf / iface.matrix_cols) else 1 in let temp_arr = Array.make (matrix_rows * iface.matrix_cols) {re = 0.0; im = 0.0} in try for i = 0 to iface.curr_buf do begin match iface.gen_buffer.(i).is_polar with |false -> let re_part = get_float_el iface.gen_buffer.(i).re_mantissa iface.gen_buffer.(i).re_exponent and im_part = get_float_el iface.gen_buffer.(i).im_mantissa iface.gen_buffer.(i).im_exponent in temp_arr.(i) <- {re = re_part; im = im_part} (* if is_polar==true, then the data in the buffer represents * polar data... so convert it to rect notation before storing * it as a complex number. *) |true -> let r = get_float_el iface.gen_buffer.(i).re_mantissa iface.gen_buffer.(i).re_exponent and theta = match (iface.calc#get_modes ()).angle with |Rad -> get_float_el iface.gen_buffer.(i).im_mantissa iface.gen_buffer.(i).im_exponent |Deg -> (pi /. 180.0 *. (get_float_el iface.gen_buffer.(i).im_mantissa iface.gen_buffer.(i).im_exponent)) in temp_arr.(i) <- {re = r *. (cos theta); im = r *. (sin theta)} end done; let uu = Units.units_of_string iface.units_entry_buffer !Rcfile.unit_table in RpcComplexMatrixUnit (Gsl.Matrix_complex.of_array temp_arr matrix_rows iface.matrix_cols, uu) with Failure "float_of_string" -> raise (Invalid_argument "improperly formatted complex floating-point matrix data") end |VarEntry -> RpcVariable iface.variable_entry_buffer (* parse the entry buffers to obtain a stack object, then stack#push it *) let push_entry (iface : interface_state_t) = (* perform this cleanup routine after every entry, so we are prepared to receive a new object. *) let post_entry_cleanup () = iface.has_entry <- false; iface.entry_type <- FloatEntry; iface.int_entry_buffer <- ""; iface.int_base_string <- ""; iface.is_entering_base <- false; iface.is_entering_exponent <- false; for i = 0 to pred max_matrix_size do iface.gen_buffer.(i) <- {re_mantissa = ""; re_exponent = ""; im_mantissa = ""; im_exponent = ""; is_polar = false} done; iface.curr_buf <- 0; iface.is_entering_imag <- false; iface.matrix_cols <- 1; iface.has_multiple_rows <- false; iface.variable_entry_buffer <- ""; iface.units_entry_buffer <- ""; iface.is_entering_units <- false; draw_update_entry iface in begin try iface.calc#push (get_entry_from_buffer iface) with Invalid_argument error_msg -> (post_entry_cleanup (); (* raise the EXN again, so it can be caught within do_main_loop *) raise (Invalid_argument error_msg)) end; post_entry_cleanup () (***********************************************************************) (* HANDLERS FOR EDITING KEYSTROKES *) (***********************************************************************) (* handle an 'enter' keypress *) let handle_enter (iface : interface_state_t) = iface.interface_mode <- StandardEditMode; draw_help iface; try if iface.has_entry then push_entry iface else raise Not_handled; draw_update_stack iface with Invalid_argument error_msg -> draw_error iface error_msg; assert (doupdate ()) (* handle a 'begin_int' keypress *) let handle_begin_int (iface : interface_state_t) = if iface.entry_type = FloatEntry then (iface.entry_type <- IntEntry; iface.interface_mode <- IntEditMode; iface.int_entry_buffer <- ""; draw_help iface; draw_update_entry iface) else () (* handle a 'begin_complex' keypress *) let handle_begin_complex (iface : interface_state_t) = iface.has_entry <- true; match iface.entry_type with |FloatEntry -> (iface.entry_type <- ComplexEntry; draw_update_entry iface) |FloatMatrixEntry -> (iface.entry_type <- ComplexMatrixEntry; draw_update_entry iface) |_ -> () (* handle a 'begin_matrix' keypress *) let handle_begin_matrix (iface : interface_state_t) = iface.has_entry <- true; match iface.entry_type with |FloatEntry -> (iface.entry_type <- FloatMatrixEntry; draw_update_entry iface) |ComplexEntry -> (iface.entry_type <- ComplexMatrixEntry; draw_update_entry iface) |FloatMatrixEntry -> if not iface.has_multiple_rows then (iface.has_multiple_rows <- true; iface.curr_buf <- succ iface.curr_buf; iface.matrix_cols <- iface.curr_buf; iface.is_entering_exponent <- false; draw_update_entry iface) else if (succ iface.curr_buf) mod iface.matrix_cols = 0 then (iface.curr_buf <- succ iface.curr_buf; iface.is_entering_exponent <- false; (*FIXME: any other items to reset here?*) draw_update_entry iface) else () |ComplexMatrixEntry -> if not iface.has_multiple_rows then (iface.has_multiple_rows <- true; iface.curr_buf <- succ iface.curr_buf; iface.matrix_cols <- iface.curr_buf; iface.is_entering_exponent <- false; iface.is_entering_imag <- false; draw_update_entry iface) else if (succ iface.curr_buf) mod iface.matrix_cols = 0 then (iface.curr_buf <- succ iface.curr_buf; iface.is_entering_exponent <- false; iface.is_entering_imag <- false; (*FIXME: any other items to reset here?*) draw_update_entry iface) else () |_ -> () (* handle a 'separator' keypress *) let handle_separator (iface : interface_state_t) = match iface.entry_type with |ComplexEntry -> if not iface.is_entering_imag then (iface.is_entering_imag <- true; iface.is_entering_exponent <- false; draw_update_entry iface) else () |FloatMatrixEntry -> if iface.curr_buf < pred max_matrix_size then (iface.curr_buf <- succ iface.curr_buf; iface.is_entering_exponent <- false; (if not iface.has_multiple_rows then iface.matrix_cols <- succ iface.curr_buf else ()); (*FIXME: any other items to reset here?*) draw_update_entry iface) else () |ComplexMatrixEntry -> if iface.is_entering_imag then if iface.curr_buf < pred max_matrix_size then (iface.curr_buf <- succ iface.curr_buf; iface.is_entering_exponent <- false; iface.is_entering_imag <- false; (if not iface.has_multiple_rows then iface.matrix_cols <- succ iface.curr_buf else ()); (*FIXME: any other items to reset here?*) draw_update_entry iface) else () else (iface.is_entering_imag <- true; iface.is_entering_exponent <- false; draw_update_entry iface) |_ -> () (* handle an 'angle' keypress *) let handle_angle (iface : interface_state_t) = match iface.entry_type with |ComplexEntry -> if not iface.is_entering_imag then (iface.is_entering_imag <- true; iface.gen_buffer.(iface.curr_buf).is_polar <- true; iface.is_entering_exponent <- false; draw_update_entry iface) else () |ComplexMatrixEntry -> if iface.is_entering_imag then () else (iface.is_entering_imag <- true; iface.gen_buffer.(iface.curr_buf).is_polar <- true; iface.is_entering_exponent <- false; draw_update_entry iface) |_ -> () (* cancel integer entry mode *) let handle_exit_int (iface : interface_state_t) = iface.interface_mode <- StandardEditMode; iface.entry_type <- FloatEntry; iface.is_entering_base <- false; iface.int_base_string <- ""; if (String.length iface.gen_buffer.(0).re_mantissa > 0) || (String.length iface.gen_buffer.(0).re_exponent > 0) || (String.length iface.gen_buffer.(0).im_mantissa > 0) || (String.length iface.gen_buffer.(0).im_exponent > 0) then iface.has_entry <- true else iface.has_entry <- false; draw_help iface; draw_update_entry iface (* handle a 'backspace' keypress *) let handle_backspace (iface : interface_state_t) = let buffer = iface.gen_buffer.(iface.curr_buf) in begin match iface.entry_type with |IntEntry -> if iface.is_entering_base then begin iface.is_entering_base <- false; iface.int_base_string <- "" end else if String.length iface.int_entry_buffer > 0 then begin let len = String.length iface.int_entry_buffer in iface.int_entry_buffer <- String.sub iface.int_entry_buffer 0 (len - 1) end else handle_exit_int iface |FloatEntry -> if iface.is_entering_exponent then if String.length buffer.re_exponent > 0 then (let len = String.length buffer.re_exponent in buffer.re_exponent <- String.sub buffer.re_exponent 0 (len - 1)) else (iface.is_entering_exponent <- false; draw_update_entry iface) else if String.length buffer.re_mantissa > 1 then (let len = String.length buffer.re_mantissa in buffer.re_mantissa <- String.sub buffer.re_mantissa 0 (len - 1)) else (iface.has_entry <- false; buffer.re_mantissa <- "") |ComplexEntry -> if iface.is_entering_imag then if iface.is_entering_exponent then if String.length buffer.im_exponent > 0 then (let len = String.length buffer.im_exponent in buffer.im_exponent <- String.sub buffer.im_exponent 0 (len - 1)) else iface.is_entering_exponent <- false else if String.length buffer.im_mantissa > 0 then (let len = String.length buffer.im_mantissa in buffer.im_mantissa <- String.sub buffer.im_mantissa 0 (len - 1)) else begin iface.is_entering_imag <- false; buffer.is_polar <- false; (if String.length buffer.re_exponent > 0 then iface.is_entering_exponent <- true else ()) end (* not entering imag/angle *) else if iface.is_entering_exponent then if String.length buffer.re_exponent > 0 then (let len = String.length buffer.re_exponent in buffer.re_exponent <- String.sub buffer.re_exponent 0 (len - 1)) else iface.is_entering_exponent <- false else if String.length buffer.re_mantissa > 0 then (let len = String.length buffer.re_mantissa in buffer.re_mantissa <- String.sub buffer.re_mantissa 0 (len - 1)) else (iface.entry_type <- FloatEntry; iface.has_entry <- false) |FloatMatrixEntry -> if iface.is_entering_exponent then if String.length buffer.re_exponent > 0 then (let len = String.length buffer.re_exponent in buffer.re_exponent <- String.sub buffer.re_exponent 0 (len - 1)) else iface.is_entering_exponent <- false else if String.length buffer.re_mantissa > 0 then (let len = String.length buffer.re_mantissa in buffer.re_mantissa <- String.sub buffer.re_mantissa 0 (len - 1)) else if iface.curr_buf > 0 then begin iface.curr_buf <- pred iface.curr_buf; (if String.length iface.gen_buffer.(iface.curr_buf).re_exponent > 0 then iface.is_entering_exponent <- true else ()); (if succ iface.curr_buf <= iface.matrix_cols then (iface.matrix_cols <- succ iface.curr_buf; iface.has_multiple_rows <- false) else ()) end else begin iface.entry_type <- FloatEntry; iface.has_entry <- false end |ComplexMatrixEntry -> if iface.is_entering_imag then if iface.is_entering_exponent then if String.length buffer.im_exponent > 0 then (let len = String.length buffer.im_exponent in buffer.im_exponent <- String.sub buffer.im_exponent 0 (len - 1)) else iface.is_entering_exponent <- false else if String.length buffer.im_mantissa > 0 then (let len = String.length buffer.im_mantissa in buffer.im_mantissa <- String.sub buffer.im_mantissa 0 (len - 1)) else begin iface.is_entering_imag <- false; buffer.is_polar <- false; (if String.length buffer.re_exponent > 0 then iface.is_entering_exponent <- true else ()) end (* not entering imag/angle *) else if iface.is_entering_exponent then if String.length buffer.re_exponent > 0 then (let len = String.length buffer.re_exponent in buffer.re_exponent <- String.sub buffer.re_exponent 0 (len - 1)) else iface.is_entering_exponent <- false else if String.length buffer.re_mantissa > 0 then (let len = String.length buffer.re_mantissa in buffer.re_mantissa <- String.sub buffer.re_mantissa 0 (len - 1)) else if iface.curr_buf > 0 then begin iface.curr_buf <- pred iface.curr_buf; iface.is_entering_imag <- true; (if String.length iface.gen_buffer.(iface.curr_buf).im_exponent > 0 then iface.is_entering_exponent <- true else ()); (if succ iface.curr_buf <= iface.matrix_cols then (iface.matrix_cols <- succ iface.curr_buf; iface.has_multiple_rows <- false) else ()) end else begin iface.entry_type <- FloatEntry; iface.has_entry <- false end |_ -> failwith "caught unhandled backspace" end; draw_update_entry iface (* handle a 'scientific_notation' (or base change) keypress *) let handle_scientific_notation (iface : interface_state_t) = begin match iface.entry_type with |IntEntry -> if String.length iface.int_entry_buffer > 0 then (iface.is_entering_base <- true; draw_update_entry iface) else () |FloatEntry | FloatMatrixEntry -> if String.length iface.gen_buffer.(iface.curr_buf).re_mantissa > 0 then (iface.is_entering_exponent <- true; draw_update_entry iface) else () |ComplexEntry | ComplexMatrixEntry -> if iface.is_entering_imag then if String.length iface.gen_buffer.(iface.curr_buf).im_mantissa > 0 then (iface.is_entering_exponent <- true; draw_update_entry iface) else () else if String.length iface.gen_buffer.(0).re_mantissa > 0 then (iface.is_entering_exponent <- true; draw_update_entry iface) else () |_ -> failwith "caught unhandled scientific notation" end; draw_update_entry iface (* handle a 'minus' keypress *) let handle_minus (iface : interface_state_t) = if iface.has_entry then match iface.entry_type with |IntEntry -> (begin match iface.int_entry_buffer.[0] with |'-' -> iface.int_entry_buffer.[0] <- '+' |'+' -> iface.int_entry_buffer.[0] <- '-' |_ -> iface.int_entry_buffer <- "-" ^ iface.int_entry_buffer end; draw_update_entry iface) |FloatEntry | FloatMatrixEntry -> begin let buffer = iface.gen_buffer.(iface.curr_buf) in if iface.is_entering_exponent then if String.length buffer.re_exponent > 0 then match buffer.re_exponent.[0] with |'-' -> buffer.re_exponent.[0] <- '+' |'+' -> buffer.re_exponent.[0] <- '-' |_ -> buffer.re_exponent <- "-" ^ buffer.re_exponent else () else if String.length buffer.re_mantissa > 0 then match buffer.re_mantissa.[0] with |'-' -> buffer.re_mantissa.[0] <- '+' |'+' -> buffer.re_mantissa.[0] <- '-' |_ -> buffer.re_mantissa <- "-" ^ buffer.re_mantissa else () end; draw_update_entry iface |ComplexEntry | ComplexMatrixEntry -> begin let buffer = iface.gen_buffer.(iface.curr_buf) in let mantissa = if iface.is_entering_imag then buffer.im_mantissa else buffer.re_mantissa and exponent = if iface.is_entering_imag then buffer.im_exponent else buffer.re_exponent in if iface.is_entering_exponent then if String.length exponent > 0 then match exponent.[0] with |'-' -> exponent.[0] <- '+' |'+' -> exponent.[0] <- '-' |_ -> if iface.is_entering_imag then buffer.im_exponent <- "-" ^ buffer.im_exponent else buffer.re_exponent <- "-" ^ buffer.re_exponent else () else if String.length mantissa > 0 then match mantissa.[0] with |'-' -> mantissa.[0] <- '+' |'+' -> mantissa.[0] <- '-' |_ -> if iface.is_entering_imag then buffer.im_mantissa <- "-" ^ buffer.im_mantissa else buffer.re_mantissa <- "-" ^ buffer.re_mantissa else () end; draw_update_entry iface |_ -> failwith "caught unhandled minus key" else raise Not_handled (* handle entry of a digit *) let handle_digit (iface : interface_state_t) key = let process_float_digit buffer is_imag = let exponent_buffer = if is_imag then buffer.im_exponent else buffer.re_exponent and mantissa_buffer = if is_imag then buffer.im_mantissa else buffer.re_mantissa in let has_decimal = let dot_regex = Str.regexp "^.*\\..*$" in Str.string_match dot_regex mantissa_buffer 0 in begin if iface.is_entering_exponent then let explen = (if String.length exponent_buffer > 0 then match exponent_buffer.[0] with |'-' | '+'-> 4 |_ -> 3 else 3) in if String.length exponent_buffer < explen then let digits = "0123456789" in try let c = char_of_int key in if String.contains digits c then (* need this hack to be able to perform * the mutable field assignment... *) (if is_imag then buffer.im_exponent <- exponent_buffer ^ (String.make 1 c) else buffer.re_exponent <- exponent_buffer ^ (String.make 1 c); draw_update_entry iface) else () with Invalid_argument "char_of_int" -> () else () else if String.length mantissa_buffer < 17 then let digits = if has_decimal then "0123456789" else "0123456789." in try let c = char_of_int key in if String.contains digits c then begin (if is_imag then buffer.im_mantissa <- mantissa_buffer ^ (String.make 1 c) else buffer.re_mantissa <- mantissa_buffer ^ (String.make 1 c)); iface.has_entry <- true; draw_update_entry iface end else () with Invalid_argument "char_of_int" -> () else () end in match iface.entry_type with |IntEntry -> begin if iface.is_entering_base then let base_chars = "bodh" in try let c = char_of_int key in if String.contains base_chars c then (iface.int_base_string <- String.make 1 c; draw_update_entry iface) else () with Invalid_argument "char_of_int" -> () else let digits = "0123456789abcdefABCDEF" in try let c = char_of_int key in if String.contains digits c then (iface.int_entry_buffer <- iface.int_entry_buffer ^ (String.make 1 c); iface.has_entry <- true; draw_update_entry iface) else () with Invalid_argument "char_of_int" -> () end |FloatEntry | FloatMatrixEntry -> let buffer = iface.gen_buffer.(iface.curr_buf) in process_float_digit buffer false |ComplexEntry | ComplexMatrixEntry -> let buffer = iface.gen_buffer.(iface.curr_buf) in if iface.is_entering_imag then process_float_digit buffer true else process_float_digit buffer false |_ -> failwith "caught unhandled digit" (* begin abbrev entry *) let handle_begin_abbrev (iface : interface_state_t) = if iface.interface_mode <> AbbrevEditMode then begin iface.interface_mode <- AbbrevEditMode; iface.abbrev_or_const <- IsAbbrev; draw_help iface; draw_update_entry iface (* do other cleanup stuff *) end else () (* begin constant entry *) let handle_begin_const (iface : interface_state_t) = if iface.interface_mode <> AbbrevEditMode then begin iface.interface_mode <- AbbrevEditMode; iface.abbrev_or_const <- IsConst; draw_help iface; draw_update_entry iface (* do other cleanup stuff *) end else () (* begin entry of a variable name *) let handle_begin_variable (iface : interface_state_t) = if iface.interface_mode <> VarEditMode then begin iface.interface_mode <- VarEditMode; iface.entry_type <- VarEntry; (* FIXME: generation of the sorted variable list can be done far * more efficiently *) let add_variable var_str var_binding var_list = var_str :: var_list in let all_variables = Hashtbl.fold add_variable (iface.calc#get_variables ()) [] in iface.sorted_variables <- List.stable_sort String.compare all_variables; iface.matched_variables <- iface.sorted_variables; draw_help iface; draw_update_entry iface end else () (* begin entry of units *) let handle_begin_units (iface : interface_state_t) = match iface.entry_type with |FloatEntry -> if iface.gen_buffer.(0).re_mantissa = "" then iface.gen_buffer.(0).re_mantissa <- "1" else (); iface.has_entry <- true; iface.interface_mode <- UnitEditMode; iface.is_entering_units <- true; draw_update_entry iface |ComplexEntry -> if iface.gen_buffer.(0).re_mantissa = "" then iface.gen_buffer.(0).re_mantissa <- "0" else (); if iface.gen_buffer.(0).im_mantissa = "" && iface.is_entering_imag then iface.gen_buffer.(0).im_mantissa <- "0" else (); iface.has_entry <- true; iface.interface_mode <- UnitEditMode; iface.is_entering_units <- true; draw_update_entry iface |FloatMatrixEntry | ComplexMatrixEntry -> iface.has_entry <- true; iface.interface_mode <- UnitEditMode; iface.is_entering_units <- true; draw_update_entry iface (* FIXME: add other matches as units code gets filled out *) |_ -> () (***********************************************************************) (* HANDLERS FOR BROWSING-RELATED KEYSTROKES *) (***********************************************************************) (* begin stack browsing *) let handle_begin_browse (iface : interface_state_t) = if iface.calc#get_stack_size () > 0 then ( (* fprintf stderr "beginning browse\n"; flush stderr; *) iface.interface_mode <- BrowsingMode; iface.calc#backup (); draw_help iface; draw_update_stack iface) else () (* handle exit of browsing mode *) let handle_end_browse (iface : interface_state_t) = iface.horiz_scroll <- 0; iface.stack_selection <- 1; iface.stack_bottom_row <- 1; iface.interface_mode <- StandardEditMode; draw_help iface; draw_update_stack iface (* handle scrolling left in browsing mode *) let handle_scroll_left (iface : interface_state_t) = (if iface.horiz_scroll > 0 then iface.horiz_scroll <- pred iface.horiz_scroll else ()); draw_update_stack iface (* handle scrolling right in browsing mode *) let handle_scroll_right (iface : interface_state_t) = let s = iface.calc#get_display_line iface.stack_selection in let len = String.length s in (if iface.horiz_scroll < len - iface.scr.sw_cols + 7 then iface.horiz_scroll <- succ iface.horiz_scroll else ()); draw_update_stack iface (* handle cyclic rolldown in browsing mode *) let handle_rolldown (iface : interface_state_t) = iface.calc#rolldown iface.stack_selection; draw_update_stack iface (* handle cyclic rollup in browsing mode *) let handle_rollup (iface : interface_state_t) = iface.calc#rollup iface.stack_selection; draw_update_stack iface (* handle moving up a line in browsing mode *) let handle_prev_line (iface : interface_state_t) = if iface.stack_selection < iface.calc#get_stack_size () then (iface.stack_selection <- succ iface.stack_selection; iface.horiz_scroll <- 0; let num_stack_lines = begin match iface.scr.help_win with |Some win -> iface.scr.sw_lines |None -> iface.scr.sw_lines - 2 end in (if iface.stack_selection > pred (iface.stack_bottom_row + num_stack_lines) then iface.stack_bottom_row <- iface.stack_selection - num_stack_lines + 1 else ()); draw_update_stack iface) else () (* handle moving down a line in browsing mode *) let handle_next_line (iface : interface_state_t) = if iface.stack_selection > 1 then (iface.stack_selection <- pred iface.stack_selection; iface.horiz_scroll <- 0; (if iface.stack_selection < iface.stack_bottom_row then iface.stack_bottom_row <- iface.stack_selection else ()); draw_update_stack iface) else () (* handle echoing stack selection (browsing mode) *) let handle_browse_echo (iface : interface_state_t) = iface.calc#echo iface.stack_selection; handle_prev_line iface (* handle fullscreen viewing of a selected stack element *) let handle_browse_view (iface : interface_state_t) = try let fs_string = iface.calc#get_fullscreen_display iface.stack_selection in let fs_file = Utility.join_path !(Rcfile.datadir) "fullscreen" in let buf = Utility.open_or_create_out_ascii fs_file in output_string buf fs_string; close_out buf; let _ = Sys.command (!(Rcfile.editor) ^ " " ^ fs_file) in (); draw_help iface; draw_stack iface; draw_update_entry iface with Sys_error ss -> draw_error iface ss; assert (doupdate ()) (* drop a particular stack element *) let handle_browse_drop1 (iface : interface_state_t) = iface.calc#delete iface.stack_selection; (if iface.stack_selection > iface.calc#get_stack_size () then iface.stack_selection <- iface.calc#get_stack_size () else ()); (if iface.stack_selection < iface.stack_bottom_row then iface.stack_bottom_row <- iface.stack_selection else ()); if iface.calc#get_stack_size () < 1 then handle_end_browse iface else draw_update_stack iface (* drop all elements below selected element (inclusive) *) let handle_browse_dropn (iface : interface_state_t) = iface.calc#deleteN iface.stack_selection; iface.stack_selection <- 1; iface.stack_bottom_row <- 1; if iface.calc#get_stack_size () < 1 then handle_end_browse iface else draw_update_stack iface (* keep only the selected stack element *) let handle_browse_keep (iface : interface_state_t) = iface.calc#keep iface.stack_selection; iface.stack_selection <- 1; iface.stack_bottom_row <- 1; draw_update_stack iface (* keep only stack elements below the current selection (inclusive) *) let handle_browse_keepn (iface : interface_state_t) = iface.calc#keepN iface.stack_selection; draw_update_stack iface (* call !Rcfile.editor to edit the input textfile buffer, then * parse it to generate one or more stack elements. * If is_browsing = true, then the stack element will be * bumped up to the selected stack level. *) let edit_parse_input_textfile (iface : interface_state_t) (is_browsing) = try let input_file = Utility.join_path !(Rcfile.datadir) "input" in let _ = Sys.command (!(Rcfile.editor) ^ " " ^ input_file) in (); (* wake up curses again, and check for resize *) assert (refresh ()); resize_subwins iface.scr; handle_refresh iface; let edited_buf = Utility.expand_open_in_ascii input_file in let lexbuf = Lexing.from_channel edited_buf in let data = (* need to call completely different parsers when using degrees * or when using radians *) begin match (iface.calc#get_modes ()).angle with |Rad -> Txtin_parser.decode_data_rad Txtin_lexer.token lexbuf |Deg -> Txtin_parser.decode_data_deg Txtin_lexer.token lexbuf end in let push_data el = if is_browsing then begin iface.calc#delete iface.stack_selection; iface.calc#push el; iface.calc#rolldown iface.stack_selection end else iface.calc#push el; in List.iter push_data data; close_in edited_buf; handle_refresh iface with |Parsing.Parse_error -> draw_help iface; draw_stack iface; draw_error iface "syntax error in input"; draw_update_entry iface |Utility.Txtin_error ss -> draw_help iface; draw_stack iface; draw_error iface ss; draw_update_entry iface |Big_int_str.Big_int_string_failure ss -> draw_help iface; draw_stack iface; draw_error iface ss; draw_update_entry iface |Units.Units_error ss -> draw_help iface; draw_stack iface; draw_error iface ss; draw_update_entry iface |Failure ss -> draw_help iface; draw_stack iface; draw_error iface "syntax error in input"; draw_update_entry iface (* edit the selected stack element with an external editor *) let handle_browse_edit (iface : interface_state_t) = try let fs_string = iface.calc#get_fullscreen_display iface.stack_selection in let input_file = Utility.join_path !(Rcfile.datadir) "input" in let buf = Utility.open_or_create_out_ascii input_file in output_string buf fs_string; close_out buf; edit_parse_input_textfile iface true with Sys_error ss -> (draw_help iface; draw_stack iface; draw_error iface ss; draw_update_entry iface) (* view the last stack element in fullscreen *) let handle_view (iface : interface_state_t) = try let fs_string = iface.calc#get_fullscreen_display 1 in let fs_file = Utility.join_path !(Rcfile.datadir) "fullscreen" in let buf = Utility.open_or_create_out_ascii fs_file in output_string buf fs_string; close_out buf; let _ = Sys.command (!(Rcfile.editor) ^ " " ^ fs_file) in (); draw_help iface; draw_stack iface; draw_update_entry iface with Sys_error ss -> draw_error iface ss; assert (doupdate ()) (* handle a call to edit a new input buffer *) let handle_edit_input (iface : interface_state_t) = try edit_parse_input_textfile iface false with Sys_error ss -> (draw_help iface; draw_stack iface; draw_error iface ss; draw_update_entry iface) (* display an "about" screen *) let handle_about (iface : interface_state_t) = draw_about iface; let _ = getch () in (); erase (); assert (refresh ()); handle_refresh iface (* cycle the help screen *) let handle_cycle_help (iface : interface_state_t) = if iface.help_page < 1 then iface.help_page <- succ iface.help_page else iface.help_page <- 0; draw_help iface; assert (doupdate ()) (* quit the calculator *) let handle_quit (iface : interface_state_t) = Statefile.save_state (iface.calc#get_state ()); iface.run_calc <- false (***********************************************************************) (* HANDLERS FOR STANDARD CALCULATOR FUNCTIONS AND COMMANDS *) (***********************************************************************) (* register an autobinding. * The autobindings are stored both in an array for quick ordered * access, and in the standard keybinding hashtables. *) let register_autobinding (op : operation_t) = (* find oldest autobinding *) let oldest = ref 0 in let oldest_age = ref (-1) in for i = 0 to pred (Array.length !Rcfile.autobind_keys) do let (key, key_string, bound_f, age) = !Rcfile.autobind_keys.(i) in if age > !oldest_age then begin oldest := i; oldest_age := age end else (); (* make all autobindings "older" *) !Rcfile.autobind_keys.(i) <- (key, key_string, bound_f, (succ age)) done; if Array.length !Rcfile.autobind_keys > 0 then begin let (key, key_string, bound_f, age) = !Rcfile.autobind_keys.(!oldest) in begin match bound_f with |None -> () |Some op -> Rcfile.remove_binding key op end; Rcfile.register_binding_internal key key_string op; !Rcfile.autobind_keys.(!oldest) <- (key, key_string, Some op, 0) end else () (* handle a call to a function (which first pushes the item in the * entry buffer) *) let handle_function_call (iface : interface_state_t) calc_function = try if iface.has_entry then push_entry iface else (); calc_function (); draw_update_stack iface with Invalid_argument error_msg -> draw_error iface error_msg; assert (doupdate ()) (* handle a call to the simple commands that require no argument *) let handle_command_call (iface : interface_state_t) calc_command = try calc_command (); draw_update_stack iface with Invalid_argument error_msg -> draw_error iface error_msg; assert (doupdate ()) (* handle a call to an interruptible function *) let handle_interr_function_call (iface : interface_state_t) calc_function = try if iface.has_entry then push_entry iface else (); draw_message iface "Working... (press a key to abort)"; assert (doupdate ()); assert (nodelay iface.scr.entry_win true); while not (calc_function ()) do let key = wgetch iface.scr.entry_win in if key <> ~-1 then raise Interrupt_exception else () done; assert (nodelay iface.scr.entry_win false); draw_update_stack iface with |Invalid_argument error_msg -> assert (nodelay iface.scr.entry_win false); draw_error iface error_msg; assert (doupdate ()) |Interrupt_exception -> assert (nodelay iface.scr.entry_win false); iface.calc#abort_computation (); draw_message iface "Computation aborted."; assert (doupdate ()) let process_function (iface : interface_state_t) ff = begin match ff with |Add -> handle_function_call iface iface.calc#add |Sub -> handle_function_call iface iface.calc#sub |Mult -> handle_function_call iface iface.calc#mult |Div -> handle_function_call iface iface.calc#div |Neg -> handle_function_call iface iface.calc#neg |Inv -> handle_function_call iface iface.calc#inv |Pow -> handle_function_call iface iface.calc#pow |Sq -> handle_function_call iface iface.calc#sq |Sqrt -> handle_function_call iface iface.calc#sqrt |Abs -> handle_function_call iface iface.calc#abs |Arg -> handle_function_call iface iface.calc#arg |Exp -> handle_function_call iface iface.calc#exp |Ln -> handle_function_call iface iface.calc#ln |Ten_x -> handle_function_call iface iface.calc#ten_pow_x |Log10 -> handle_function_call iface iface.calc#log10 |Conj -> handle_function_call iface iface.calc#conj |Sin -> handle_function_call iface iface.calc#sin |Cos -> handle_function_call iface iface.calc#cos |Tan -> handle_function_call iface iface.calc#tan |Sinh -> handle_function_call iface iface.calc#sinh |Cosh -> handle_function_call iface iface.calc#cosh |Tanh -> handle_function_call iface iface.calc#tanh |Asin -> handle_function_call iface iface.calc#asin |Acos -> handle_function_call iface iface.calc#acos |Atan -> handle_function_call iface iface.calc#atan |Asinh -> handle_function_call iface iface.calc#asinh |Acosh -> handle_function_call iface iface.calc#acosh |Atanh -> handle_function_call iface iface.calc#atanh |Re -> handle_function_call iface iface.calc#re |Im -> handle_function_call iface iface.calc#im |Gamma -> handle_function_call iface iface.calc#gamma |LnGamma -> handle_function_call iface iface.calc#lngamma |Erf -> handle_function_call iface iface.calc#erf |Erfc -> handle_function_call iface iface.calc#erfc |Fact -> handle_interr_function_call iface iface.calc#fact |Transpose -> handle_function_call iface iface.calc#transpose |Mod -> handle_function_call iface iface.calc#mod_int |Floor -> handle_function_call iface iface.calc#floor |Ceiling -> handle_function_call iface iface.calc#ceiling |ToInt -> handle_function_call iface iface.calc#to_int |ToFloat -> handle_function_call iface iface.calc#to_float |SolveLin -> handle_function_call iface iface.calc#solve_linear |Eval -> handle_function_call iface iface.calc#eval |Store -> handle_function_call iface iface.calc#store |Purge -> handle_function_call iface iface.calc#purge |Gcd -> handle_interr_function_call iface iface.calc#gcd |Lcm -> handle_interr_function_call iface iface.calc#lcm |Binom -> handle_interr_function_call iface iface.calc#binom |Perm -> handle_interr_function_call iface iface.calc#permutations |Total -> handle_function_call iface iface.calc#total |Mean -> handle_function_call iface iface.calc#mean |Sumsq -> handle_function_call iface iface.calc#sum_squares |Var -> handle_function_call iface iface.calc#variance_unbiased |VarBias -> handle_function_call iface iface.calc#variance_biased |Stdev -> handle_function_call iface iface.calc#standard_deviation_unbiased |StdevBias -> handle_function_call iface iface.calc#standard_deviation_biased |Min -> handle_function_call iface iface.calc#minimum |Max -> handle_function_call iface iface.calc#maximum |Utpn -> handle_function_call iface iface.calc#upper_tail_prob_normal |StandardizeUnits -> handle_function_call iface iface.calc#standardize_units |ConvertUnits -> handle_function_call iface iface.calc#convert_units |UnitValue -> handle_function_call iface iface.calc#unit_value |Trace -> handle_function_call iface iface.calc#trace end let process_command (iface : interface_state_t) cc = begin match cc with |Drop -> handle_command_call iface iface.calc#drop |Clear -> handle_command_call iface iface.calc#clear |Swap -> handle_command_call iface iface.calc#swap |Dup -> handle_command_call iface iface.calc#dup |Undo -> handle_command_call iface iface.calc#undo |BeginBrowse -> handle_begin_browse iface |BeginAbbrev -> handle_begin_abbrev iface |BeginConst -> handle_begin_const iface |BeginVar -> handle_begin_variable iface |Quit -> handle_quit iface |SetRadians -> handle_command_call iface iface.calc#mode_rad; draw_help iface; draw_update_stack iface |SetDegrees -> handle_command_call iface iface.calc#mode_deg; draw_help iface; draw_update_stack iface |SetRect -> handle_command_call iface iface.calc#mode_rect; draw_help iface; draw_update_stack iface |SetPolar -> handle_command_call iface iface.calc#mode_polar; draw_help iface; draw_update_stack iface |SetBin -> handle_command_call iface iface.calc#mode_bin; draw_help iface; draw_update_stack iface |SetOct -> handle_command_call iface iface.calc#mode_oct; draw_help iface; draw_update_stack iface |SetDec -> handle_command_call iface iface.calc#mode_dec; draw_help iface; draw_update_stack iface |SetHex -> handle_command_call iface iface.calc#mode_hex; draw_help iface; draw_update_stack iface |ToggleAngleMode -> handle_command_call iface iface.calc#toggle_angle_mode; draw_help iface; draw_update_stack iface |ToggleComplexMode -> handle_command_call iface iface.calc#toggle_complex_mode; draw_help iface; draw_update_stack iface |CycleBase -> handle_command_call iface iface.calc#cycle_base; draw_help iface; draw_update_stack iface |View -> handle_view iface |About -> handle_about iface |Refresh -> handle_refresh iface |EnterPi -> handle_command_call iface iface.calc#enter_pi |Rand -> handle_command_call iface iface.calc#rand |EditInput -> handle_edit_input iface |CycleHelp -> handle_cycle_help iface end (****************************************************************) (* IMPLEMENTATION OF ABBREVIATION AND CONSTANT ENTRY SYSTEM *) (****************************************************************) (* exit abbrev entry *) let handle_exit_abbrev (iface : interface_state_t) = if iface.interface_mode = AbbrevEditMode then begin iface.interface_mode <- StandardEditMode; iface.abbrev_entry_buffer <- ""; draw_help iface; draw_update_entry iface end else () (* search through a list of commands and find all that match * iface.abbrev_entry_buffer. * The list is built up in reverse order using Str.search_backward, * so the head of the list is actually the first match. *) let match_abbrev_buffer (iface : interface_state_t) buf = if String.length buf > 0 then let regex = Str.regexp_string buf in let find_matches prev_matches el = if Str.string_match regex el 0 then el :: prev_matches else prev_matches in let match_list = match iface.abbrev_or_const with |IsAbbrev -> List.fold_left find_matches [] !Rcfile.abbrev_commands |IsConst -> List.fold_left find_matches [] !Rcfile.constant_symbols in if List.length match_list = 0 then raise Not_found else match_list else raise Not_found (* backspace during abbrev entry *) let handle_abbrev_backspace (iface : interface_state_t) = let len = String.length iface.abbrev_entry_buffer in if len > 0 then begin iface.abbrev_entry_buffer <- Str.string_before iface.abbrev_entry_buffer (pred len); begin try iface.matched_abbrev_list <- match_abbrev_buffer iface iface.abbrev_entry_buffer with Not_found -> () end; draw_help iface; draw_update_entry iface end else () (* handle entry of an arbitrary character in abbrev mode *) let handle_abbrev_character (iface : interface_state_t) key = try let ch = char_of_int key in let test_buffer = iface.abbrev_entry_buffer ^ (String.make 1 ch) in (* search through the list of commands for the first one that matches * iface.abbrev_entry_buffer *) iface.matched_abbrev_list <- match_abbrev_buffer iface test_buffer; iface.abbrev_entry_buffer <- test_buffer; draw_help iface; draw_update_entry iface with Not_found | Invalid_argument "char_of_int" -> let _ = beep () in () (* enter an abbrev entry *) let handle_enter_abbrev (iface : interface_state_t) = if iface.interface_mode = AbbrevEditMode then begin iface.interface_mode <- StandardEditMode; begin try iface.matched_abbrev_list <- match_abbrev_buffer iface iface.abbrev_entry_buffer; let first_abbrev_match = if iface.matched_abbrev_list = [] then "" else List.hd iface.matched_abbrev_list in begin match iface.abbrev_or_const with |IsAbbrev -> let operation = Rcfile.translate_abbrev first_abbrev_match in begin match operation with |Function ff -> process_function iface ff |Command cc -> process_command iface cc |_ -> failwith "found abbrev command that is neither Function nor Command" end; (* check whether ff should be autobound *) begin try let _ = Rcfile.key_of_operation operation in () with Not_found -> register_autobinding operation end |IsConst -> let const = Rcfile.translate_constant first_abbrev_match in iface.calc#enter_const const.Units.coeff const.Units.comp_units; draw_update_stack iface end with Not_found -> () end; iface.abbrev_entry_buffer <- ""; draw_help iface; draw_update_entry iface end else () (****************************************************************) (* IMPLEMENTATION OF VARIABLE ENTRY SYSTEM *) (****************************************************************) (* exit variable entry *) let handle_exit_variable (iface : interface_state_t) = if iface.interface_mode = VarEditMode then begin iface.interface_mode <- StandardEditMode; iface.entry_type <- FloatEntry; iface.variable_entry_buffer <- ""; draw_help iface; draw_update_entry iface end else () (* search through a list of variables and find all that match * iface.variable_entry_buffer. *) let match_variable_buffer (iface : interface_state_t) buf = let buf_regex = Str.regexp_string buf in let find_matches prev_result el = if Str.string_match buf_regex el 0 then el :: prev_result else prev_result in List.fold_left find_matches [] (List.rev iface.sorted_variables) (* backspace during variable entry *) let handle_variable_backspace (iface : interface_state_t) = let len = String.length iface.variable_entry_buffer in if len > 0 then begin iface.completion <- None; iface.variable_entry_buffer <- Str.string_before iface.variable_entry_buffer (pred len); iface.matched_variables <- match_variable_buffer iface iface.variable_entry_buffer; draw_help iface; draw_update_entry iface end else () (* handle entry of an arbitrary character in variable entry mode *) let handle_variable_character (iface : interface_state_t) key = (* variables with long strings simply aren't useful, and * it's possible that deleting them could become difficult. *) if String.length iface.variable_entry_buffer < 25 then let allowable_regex = Str.regexp "[-a-zA-Z0-9_]" in let ch = char_of_int key in let ss = String.make 1 ch in if Str.string_match allowable_regex ss 0 then let test_buffer = iface.variable_entry_buffer ^ ss in (* search through the list of variables for the first one that matches * iface.variable_entry_buffer *) begin try iface.completion <- None; iface.matched_variables <- match_variable_buffer iface test_buffer; iface.variable_entry_buffer <- test_buffer; draw_help iface; draw_update_entry iface with Not_found -> iface.matched_variables <- []; iface.variable_entry_buffer <- test_buffer; draw_help iface; draw_update_entry iface end else let _ = beep () in () else () (* enter an variable entry *) let handle_enter_variable (iface : interface_state_t) = if iface.interface_mode = VarEditMode then begin if String.length iface.variable_entry_buffer > 0 then push_entry iface else iface.entry_type <- FloatEntry; iface.interface_mode <- StandardEditMode; iface.completion <- None; iface.variable_entry_buffer <- ""; draw_help iface; draw_stack iface; draw_update_entry iface end else () (* autocomplete a variable *) let handle_complete_variable (iface : interface_state_t) = if List.length iface.matched_variables > 0 then begin begin match iface.completion with |None -> iface.completion <- Some 0; iface.variable_entry_buffer_back <- iface.variable_entry_buffer; iface.variable_entry_buffer <- List.nth iface.matched_variables 0 |Some i -> if i < List.length iface.matched_variables - 1 then begin iface.completion <- Some (succ i); iface.variable_entry_buffer <- List.nth iface.matched_variables (succ i) end else begin iface.completion <- None; iface.variable_entry_buffer <- iface.variable_entry_buffer_back end end; draw_help iface; draw_update_entry iface end else let _ = beep () in () (****************************************************************) (* IMPLEMENTATION OF UNITS ENTRY SYSTEM *) (****************************************************************) (* exit units entry *) let handle_exit_units (iface : interface_state_t) = if iface.interface_mode = UnitEditMode then begin iface.interface_mode <- StandardEditMode; iface.units_entry_buffer <- ""; iface.is_entering_units <- false; draw_update_entry iface end else () (* backspace during units entry *) let handle_units_backspace (iface : interface_state_t) = let len = String.length iface.units_entry_buffer in if len > 0 then begin iface.units_entry_buffer <- Str.string_before iface.units_entry_buffer (pred len); draw_update_entry iface end else begin iface.interface_mode <- StandardEditMode; iface.is_entering_units <- false; draw_update_entry iface end (* handle entry of an arbitrary character in units entry mode *) let handle_units_character (iface : interface_state_t) key = try let allowable_regex = Str.regexp "[a-zA-Z0-9\\.\\^\\*/-]" in let ch = char_of_int key in let ss = String.make 1 ch in if Str.string_match allowable_regex ss 0 then begin iface.units_entry_buffer <- iface.units_entry_buffer ^ ss; draw_update_entry iface end else let _ = beep () in () with Invalid_argument "char_of_int" -> let _ = beep () in () (****************************************************************) (* MAIN LOOP *) (****************************************************************) (* handle a keypress in standard entry mode *) let handle_keypress_standard key (iface : interface_state_t) = (* check whether this keypress is a macro *) begin try let ch_list = Rcfile.macro_of_key key in let rec push_macro chars = match chars with | [] -> () | head :: tail -> let _ = ungetch head in push_macro tail in push_macro ch_list with Not_found -> (* if it's not a macro, go ahead and process it *) begin try (* editing operations take priority *) let edit_op = Rcfile.edit_of_key key in begin match edit_op with |Digit -> handle_digit iface key |Enter -> handle_enter iface |Backspace -> handle_backspace iface |Minus -> handle_minus iface |BeginInteger -> handle_begin_int iface |BeginComplex -> handle_begin_complex iface |BeginMatrix -> handle_begin_matrix iface |Separator -> handle_separator iface |Angle -> handle_angle iface |SciNotBase -> handle_scientific_notation iface |BeginUnits -> handle_begin_units iface end with Not_found | Not_handled -> (* next we try to match on functions *) try let function_op = Rcfile.function_of_key key in process_function iface function_op with Not_found -> if iface.has_entry then (* finally we try entry of digits *) handle_digit iface key else (* commands are only suitable when there is no entry *) try let command_op = Rcfile.command_of_key key in process_command iface command_op with Not_found -> handle_digit iface key end end (* handle a keypress in integer editing mode *) let handle_keypress_intedit key (iface : interface_state_t) = begin try let edit_op = Rcfile.edit_of_key key in begin match edit_op with |Digit -> handle_digit iface key |Enter -> handle_enter iface |Backspace -> handle_backspace iface |Minus -> handle_minus iface |SciNotBase -> handle_scientific_notation iface |_ -> raise Not_handled end with Not_found | Not_handled -> try let intedit_op = Rcfile.intedit_of_key key in begin match intedit_op with |IntEditExit -> handle_exit_int iface end with Not_found | Not_handled -> handle_digit iface key end (* handle a keypress in units entry mode *) let handle_keypress_unitedit key (iface : interface_state_t) = begin try let edit_op = Rcfile.edit_of_key key in begin match edit_op with |Enter -> handle_enter iface |Backspace -> handle_units_backspace iface |_ -> handle_units_character iface key end with Not_found | Not_handled -> handle_units_character iface key end (* handle a keypress in abbrev entry mode *) let handle_keypress_abbrev key (iface : interface_state_t) = begin try let abbrev_op = Rcfile.abbrev_of_key key in begin match abbrev_op with |AbbrevExit -> handle_exit_abbrev iface |AbbrevEnter -> handle_enter_abbrev iface |AbbrevBackspace -> handle_abbrev_backspace iface end with Not_found -> handle_abbrev_character iface key end (* handle a keypress in variable editing mode *) let handle_keypress_varedit key (iface : interface_state_t) = begin try let varedit_op = Rcfile.varedit_of_key key in begin match varedit_op with |VarEditExit -> handle_exit_variable iface |VarEditEnter -> handle_enter_variable iface |VarEditBackspace -> handle_variable_backspace iface |VarEditComplete -> handle_complete_variable iface end with Not_found -> handle_variable_character iface key end (* handle a keypress in stack browsing mode *) let handle_keypress_browse key (iface : interface_state_t) = try let browse_op = Rcfile.browse_of_key key in begin match browse_op with |EndBrowse -> handle_end_browse iface |ScrollLeft -> handle_scroll_left iface |ScrollRight -> handle_scroll_right iface |RollDown -> handle_rolldown iface |RollUp -> handle_rollup iface |PrevLine -> handle_prev_line iface |NextLine -> handle_next_line iface |Echo -> handle_browse_echo iface |ViewEntry -> handle_browse_view iface |Drop1 -> handle_browse_drop1 iface |DropN -> handle_browse_dropn iface |Keep -> handle_browse_keep iface |KeepN -> handle_browse_keepn iface |EditEntry -> handle_browse_edit iface end with Not_found | Not_handled -> () let do_main_loop (iface : interface_state_t) = while iface.run_calc do iface.calc#launch_fill_in_thread (); let key = wgetch iface.scr.entry_win in (* using the ncurses SIGWINCH handler to catch window resize events *) if key = Key.resize then handle_resize iface else match iface.interface_mode with |StandardEditMode -> handle_keypress_standard key iface |IntEditMode -> handle_keypress_intedit key iface |AbbrevEditMode -> handle_keypress_abbrev key iface |VarEditMode -> handle_keypress_varedit key iface |UnitEditMode -> handle_keypress_unitedit key iface |BrowsingMode -> handle_keypress_browse key iface done (* initialize the interface and begin the main loop *) let run (iface : interface_state_t) = iface.calc#backup (); assert (keypad iface.scr.entry_win true); (* initialize buffers for matrix entry *) for i = 1 to pred max_matrix_size do iface.gen_buffer.(i) <- {re_mantissa = ""; re_exponent = ""; im_mantissa = ""; im_exponent = ""; is_polar = false} done; begin try iface.calc#set_state (Statefile.load_state ()); draw_stack iface; draw_help iface; draw_update_entry iface; with Invalid_argument err -> draw_stack iface; draw_help iface; draw_error iface err; draw_update_entry iface end; do_main_loop iface (* arch-tag: DO_NOT_CHANGE_b4519dd2-7e94-4cbf-931a-bb5f97445cbf *) orpie-release-1.6.0/src/orpie/inv.ml000066400000000000000000000073721334120314700173140ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) open Rpc_stack open Gsl.Error let inv (stack : rpc_stack) (evaln : int -> unit) = evaln 1; let gen_el = stack#pop () in match gen_el with |RpcFloatUnit (el, uu) -> stack#push (RpcFloatUnit (1.0 /. el, Units.div Units.empty_unit uu)) |RpcComplexUnit (el, uu) -> stack#push (RpcComplexUnit (Complex.inv el, Units.div Units.empty_unit uu)) |RpcFloatMatrixUnit (el, uu) -> let new_unit = Units.pow uu (~-. 1.0) in let n, m = (Gsl.Matrix.dims el) in if n = m then let copy_el1 = Gsl.Matrix.copy el and copy_el2 = Gsl.Matrix.copy el and perm = Gsl.Permut.create m and inv = Gsl.Matrix.create m m and vv = Gsl.Matrix.create m m and ss = Gsl.Vector.create m and work = Gsl.Vector.create m in begin (* test for singular matrix *) (* first factor out matrix norm, since the GSL SVD algorithm has * issues with large magnitude matrices *) let norm = Gsl_assist.one_norm copy_el1 in Gsl.Matrix.scale copy_el1 (1.0 /. norm); (* now compute condition number as largest singular value * divided by smallest singular value *) Gsl.Linalg._SV_decomp ~a:(`M copy_el1) ~v:(`M vv) ~s:(`V ss) ~work:(`V work); let condition_number = (Gsl.Vector.get ss 0) /. (Gsl.Vector.get ss (pred m)) in (* if the condition number is too large for machine precision, * then abort with error *) if condition_number > 1e14 then (stack#push gen_el; raise (Invalid_argument "cannot invert ill-conditioned matrix")) else let _ = Gsl.Linalg._LU_decomp (`M copy_el2) perm in (Gsl.Linalg._LU_invert (`M copy_el2) perm (`M inv); stack#push (RpcFloatMatrixUnit (inv, new_unit))) end else (stack#push gen_el; raise (Invalid_argument "cannot invert non-square matrix")) |RpcComplexMatrixUnit (el, uu) -> let new_unit = Units.pow uu (~-. 1.0) in let n, m = (Gsl.Matrix_complex.dims el) in if n = m then let copy_el = Gsl.Vectmat.cmat_convert ~protect:true (`CM el) and perm = Gsl.Permut.create m and inv = Gsl.Matrix_complex.create m m in try let _ = Gsl.Linalg.complex_LU_decomp copy_el perm in Gsl.Linalg.complex_LU_invert copy_el perm (`CM inv); stack#push (RpcComplexMatrixUnit (inv, new_unit)) with Gsl_exn _ -> (stack#push gen_el; raise (Invalid_argument "cannot invert singular matrix")) else (stack#push gen_el; raise (Invalid_argument "cannot invert non-square matrix")) |_ -> (stack#push gen_el; raise (Invalid_argument "inversion is undefined for this data type")) (* arch-tag: DO_NOT_CHANGE_d8ce074c-3d77-4448-b3c6-9e239b853aad *) orpie-release-1.6.0/src/orpie/main.ml000066400000000000000000000031011334120314700174260ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) open Curses;; (* load orpierc *) Rcfile.process_rcfile None;; let initialize_screen () = let std = initscr () in assert (keypad std true); assert (cbreak ()); assert (noecho ()); Interface_main.create_windows std;; (* Global: this is the interface state variable used for the calculator *) let calc = new Rpc_calc.rpc_calc !Rcfile.conserve_memory;; let iface = Interface.make calc (initialize_screen ());; (* initialize the error handler *) Gsl.Error.init ();; try Interface_main.run iface with error -> endwin (); Printf.fprintf stderr "Caught error at toplevel:\n%s\n" (Printexc.to_string error);; (* For some reason this call fails if it is moved to interface_draw... *) endwin ();; (* arch-tag: DO_NOT_CHANGE_eeac13df-e93f-4359-8b70-44fefc40e225 *) orpie-release-1.6.0/src/orpie/mult.ml000066400000000000000000000205211334120314700174700ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) open Rpc_stack open Gsl_assist open Big_int let mult (stack : rpc_stack) (evaln : int -> unit) = evaln 2; let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in match gen_el1 with |RpcInt el1 -> ( match gen_el2 with |RpcInt el2 -> stack#push (RpcInt (mult_big_int el1 el2)) |RpcFloatUnit (el2, uu2) -> let f_el1 = float_of_big_int el1 in stack#push (RpcFloatUnit (f_el1 *. el2, uu2)) |RpcComplexUnit (el2, uu2) -> let c_el1 = { Complex.re = float_of_big_int el1; Complex.im = 0.0 } in stack#push (RpcComplexUnit (Complex.mul c_el1 el2, uu2)) |RpcFloatMatrixUnit (el2, uu) -> let result = Gsl.Matrix.copy el2 in Gsl.Matrix.scale result (float_of_big_int el1); stack#push (RpcFloatMatrixUnit (result, uu)) |RpcComplexMatrixUnit (el2, uu) -> let c_el1 = cmpx_of_int el1 in let result = Gsl.Matrix_complex.copy el2 in Gsl.Matrix_complex.scale result c_el1; stack#push (RpcComplexMatrixUnit (result, uu)) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for multiplication")) ) |RpcFloatUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> let f_el2 = float_of_big_int el2 in stack#push (RpcFloatUnit (el1 *. f_el2, uu1)) |RpcFloatUnit (el2, uu2) -> stack#push (RpcFloatUnit (el1 *. el2, Units.mult uu1 uu2)) |RpcComplexUnit (el2, uu2) -> let c_el1 = c_of_f el1 in stack#push (RpcComplexUnit (Complex.mul c_el1 el2, Units.mult uu1 uu2)) |RpcFloatMatrixUnit (el2, uu2) -> let uprod = Units.mult uu1 uu2 in let result = Gsl.Matrix.copy el2 in Gsl.Matrix.scale result el1; stack#push (RpcFloatMatrixUnit (result, uprod)) |RpcComplexMatrixUnit (el2, uu2) -> let uprod = Units.mult uu1 uu2 in let result = Gsl.Matrix_complex.copy el2 in Gsl.Matrix_complex.scale result (c_of_f el1); stack#push (RpcComplexMatrixUnit (result, uprod)) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for multiplication")) ) |RpcComplexUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> let c_el2 = cmpx_of_int el2 in stack#push (RpcComplexUnit (Complex.mul el1 c_el2, uu1)) |RpcFloatUnit (el2, uu2) -> let c_el2 = c_of_f el2 in stack#push (RpcComplexUnit (Complex.mul el1 c_el2, Units.mult uu1 uu2)) |RpcComplexUnit (el2, uu2) -> stack#push (RpcComplexUnit (Complex.mul el1 el2, Units.mult uu1 uu2)) |RpcFloatMatrixUnit (el2, uu2) -> let c_el2 = cmat_of_fmat el2 in Gsl.Matrix_complex.scale c_el2 el1; stack#push (RpcComplexMatrixUnit (c_el2, Units.mult uu1 uu2)) |RpcComplexMatrixUnit (el2, uu2) -> let result = Gsl.Matrix_complex.copy el2 in Gsl.Matrix_complex.scale result el1; stack#push (RpcComplexMatrixUnit (result, Units.mult uu1 uu2)) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for multiplication")) ) |RpcFloatMatrixUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> let result = Gsl.Matrix.copy el1 in Gsl.Matrix.scale result (float_of_big_int el2); stack#push (RpcFloatMatrixUnit (result, uu1)) |RpcFloatUnit (el2, uu2) -> let result = Gsl.Matrix.copy el1 in Gsl.Matrix.scale result el2; stack#push (RpcFloatMatrixUnit (result, Units.mult uu1 uu2)) |RpcComplexUnit (el2, uu2) -> let c_el1 = cmat_of_fmat el1 in Gsl.Matrix_complex.scale c_el1 el2; stack#push (RpcComplexMatrixUnit (c_el1, Units.mult uu1 uu2)) |RpcFloatMatrixUnit (el2, uu2) -> let n1, m1 = (Gsl.Matrix.dims el1) and n2, m2 = (Gsl.Matrix.dims el2) in if m1 = n2 then let result = Gsl.Matrix.create n1 m2 in Gsl.Blas.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:1.0 ~a:el1 ~b:el2 ~beta:0.0 ~c:result; stack#push (RpcFloatMatrixUnit (result, Units.mult uu1 uu2)) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for multiplication")) |RpcComplexMatrixUnit (el2, uu2) -> let n1, m1 = (Gsl.Matrix.dims el1) and n2, m2 = (Gsl.Matrix_complex.dims el2) in if m1 = n2 then let c_el1 = cmat_of_fmat el1 and result = Gsl.Matrix_complex.create n1 m2 in Gsl.Blas.Complex.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:Complex.one ~a:c_el1 ~b:el2 ~beta:Complex.zero ~c:result; stack#push (RpcComplexMatrixUnit (result, Units.mult uu1 uu2)) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for multiplication")) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for multiplication")) ) |RpcComplexMatrixUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> let c_el2 = cmpx_of_int el2 in let result = Gsl.Matrix_complex.copy el1 in Gsl.Matrix_complex.scale result c_el2; stack#push (RpcComplexMatrixUnit (result, uu1)) |RpcFloatUnit (el2, uu2) -> let result = Gsl.Matrix_complex.copy el1 in Gsl.Matrix_complex.scale result Complex.one; stack#push (RpcComplexMatrixUnit (result, Units.mult uu1 uu2)) |RpcComplexUnit (el2, uu2) -> let result = Gsl.Matrix_complex.copy el1 in Gsl.Matrix_complex.scale result Complex.one; stack#push (RpcComplexMatrixUnit (result, Units.mult uu1 uu2)) |RpcFloatMatrixUnit (el2, uu2) -> let n1, m1 = (Gsl.Matrix_complex.dims el1) and n2, m2 = (Gsl.Matrix.dims el2) in if m1 = n2 then let c_el2 = cmat_of_fmat el2 and result = Gsl.Matrix_complex.create n1 m2 in Gsl.Blas.Complex.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:Complex.one ~a:el1 ~b:c_el2 ~beta:Complex.zero ~c:result; stack#push (RpcComplexMatrixUnit (result, Units.mult uu1 uu2)) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for multiplication")) |RpcComplexMatrixUnit (el2, uu2) -> let n1, m1 = (Gsl.Matrix_complex.dims el1) and n2, m2 = (Gsl.Matrix_complex.dims el2) in if m1 = n2 then let result = Gsl.Matrix_complex.create n1 m2 in Gsl.Blas.Complex.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:Complex.one ~a:el1 ~b:el2 ~beta:Complex.zero ~c:result; stack#push (RpcComplexMatrixUnit (result, Units.mult uu1 uu2)) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for multiplication")) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for multiplication")) ) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for multiplication")) (* arch-tag: DO_NOT_CHANGE_5fc03e41-d1d3-40da-8b68-9a85d96148d0 *) orpie-release-1.6.0/src/orpie/operations.ml000066400000000000000000000070451334120314700207000ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* A function will push any item in the entry buffer before performing the * operation. A command does not take input, so it is not allowed when data is * in the entry buffer. An edit is an operation that acts on the data in the * entry buffer (e.g. backspace). *) type function_operation_t = | Add | Sub | Mult | Div | Neg | Inv | Pow | Sqrt | Sq | Abs | Arg | Exp | Ln | Ten_x | Log10 | Conj | Sin | Cos | Tan | Asin | Acos | Atan | Sinh | Cosh | Tanh | Asinh | Acosh | Atanh | Re | Im | Gamma | LnGamma | Erf | Erfc | Fact | Transpose | Mod | Floor | Ceiling | ToInt | ToFloat | SolveLin | Eval | Store | Purge | Gcd | Lcm | Binom | Perm | Total | Mean | Sumsq | Var |VarBias | Stdev | StdevBias | Min | Max | Utpn | StandardizeUnits | ConvertUnits | UnitValue | Trace;; type command_operation_t = | Drop | Clear | Swap | Dup | Undo | BeginBrowse | BeginAbbrev | BeginVar | Quit | SetRadians | SetDegrees | SetRect | SetPolar | SetBin | SetOct | SetDec | SetHex | ToggleAngleMode | ToggleComplexMode | CycleBase | View | About | Refresh | EnterPi | Rand | EditInput | CycleHelp | BeginConst;; type edit_operation_t = | Digit | Enter | Backspace | Minus | SciNotBase | BeginInteger | BeginComplex | BeginMatrix | Separator | Angle | BeginUnits;; type browse_operation_t = | EndBrowse | ScrollLeft | ScrollRight | RollDown | RollUp | PrevLine | NextLine | Echo | ViewEntry | Drop1 | DropN | Keep | KeepN | EditEntry;; type abbrev_operation_t = | AbbrevExit | AbbrevEnter | AbbrevBackspace;; type integer_edit_operation_t = | IntEditExit;; type var_edit_operation_t = | VarEditExit | VarEditEnter | VarEditBackspace | VarEditComplete;; type operation_t = | Function of function_operation_t | Command of command_operation_t | Edit of edit_operation_t | Browse of browse_operation_t | Abbrev of abbrev_operation_t | IntEdit of integer_edit_operation_t | VarEdit of var_edit_operation_t;; (* arch-tag: DO_NOT_CHANGE_e761ca10-6bfd-4edf-a3de-53778a07ca21 *) orpie-release-1.6.0/src/orpie/pow.ml000066400000000000000000000132401334120314700173140ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) open Rpc_stack open Gsl_assist open Big_int let pow (stack : rpc_stack) (evaln : int -> unit) = evaln 2; let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in match gen_el1 with |RpcInt el1 -> ( match gen_el2 with |RpcInt el2 -> if (sign_big_int el2) <> (-1) then stack#push (RpcInt (power_big_int_positive_big_int el1 el2)) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "integer power function requires nonnegative power")) |RpcFloatUnit (el2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "cannot raise to a dimensioned power" end else let f_el1 = float_of_big_int el1 in let f_el2 = el2 in (* if power is nonnegative or if power is integer *) if f_el1 >= 0.0 || f_el2 = float_of_int (int_of_float f_el2) then let (f, u) = funit_of_float (f_el1 ** f_el2) in stack#push (RpcFloatUnit (f, u)) else let c_el1 = cmpx_of_float f_el1 in let c_el2 = cmpx_of_float f_el2 in let (c, u) = cunit_of_cpx (Complex.pow c_el1 c_el2) in stack#push (RpcComplexUnit (c, u)) |RpcComplexUnit (el2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "cannot raise to a dimensioned power" end else let c_el1 = cmpx_of_int el1 in let (c, u) = cunit_of_cpx (Complex.pow c_el1 el2) in stack#push (RpcComplexUnit (c, u)) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types")) ) |RpcFloatUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> let f_el2 = float_of_big_int el2 in stack#push (RpcFloatUnit (el1 ** f_el2, Units.pow uu1 f_el2)) |RpcFloatUnit (el2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "cannot raise to a dimensioned power" end else if el2 > 0.0 then begin stack#push (RpcFloatUnit (el1 ** el2, Units.pow uu1 el2)) end else let c_el1 = c_of_f el1 and c_el2 = c_of_f el2 in let c_prod = Complex.pow c_el1 c_el2 in if c_prod.Complex.im <> 0.0 then stack#push (RpcComplexUnit (c_prod, Units.pow uu1 el2)) else stack#push (RpcFloatUnit (c_prod.Complex.re, Units.pow uu1 el2)) |RpcComplexUnit (el2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "cannot raise to a dimensioned power" end else if uu1 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "cannot raise dimensioned value to complex power" end else let c_el1 = c_of_f el1 in let (c, u) = cunit_of_cpx (Complex.pow c_el1 el2) in stack#push (RpcComplexUnit (c, u)) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types")) ) |RpcComplexUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> let f_el2 = float_of_big_int el2 in let c_el2 = cmpx_of_int el2 in let c_prod = Complex.pow el1 c_el2 in stack#push (RpcComplexUnit (c_prod, Units.pow uu1 f_el2)) |RpcFloatUnit (el2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "cannot raise to a dimensioned power" end else let c_el2 = c_of_f el2 in let c_prod = Complex.pow el1 c_el2 in stack#push (RpcComplexUnit (c_prod, Units.pow uu1 el2)) |RpcComplexUnit (el2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "cannot raise to a dimensioned power" end else if uu1 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "cannot raise dimensioned value to complex power" end else let (c, u) = cunit_of_cpx (Complex.pow el1 el2) in stack#push (RpcComplexUnit (c, u)) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types")) ) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "invalid argument")) (* arch-tag: DO_NOT_CHANGE_55f98700-eb1e-4457-ae73-18170a816984 *) orpie-release-1.6.0/src/orpie/rcfile.ml000066400000000000000000001142741334120314700177640ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* rcfile.ml * This file includes everything associated with processing the orpierc file. * In particular, this includes a number of hashtables used to store the * bindings of curses keypresses to calculator operations. *) open Genlex;; open Curses;; open Operations;; exception Config_failure of string;; let config_failwith s = raise (Config_failure s);; (* These hashtables store conversions between curses keys and the operations * they are associated with. *) let table_key_function = Hashtbl.create 20;; let table_function_key = Hashtbl.create 20;; let table_key_command = Hashtbl.create 20;; let table_command_key = Hashtbl.create 20;; let table_key_edit = Hashtbl.create 20;; let table_edit_key = Hashtbl.create 20;; let table_key_browse = Hashtbl.create 20;; let table_browse_key = Hashtbl.create 20;; let table_abbrev_key = Hashtbl.create 20;; let table_key_abbrev = Hashtbl.create 20;; let table_intedit_key = Hashtbl.create 20;; let table_key_intedit = Hashtbl.create 20;; let table_key_macro = Hashtbl.create 20;; let table_key_varedit = Hashtbl.create 20;; let table_varedit_key = Hashtbl.create 20;; (* Default directory for orpie data *) let datadir = ref "~/.orpie" (* Default editor for fullscreen viewing *) let editor = ref "vi";; (* Whether or not to hide the help panel *) let hide_help = ref false;; (* Whether or not to conserve memory in favor of faster display *) let conserve_memory = ref false;; (* Autobinding keys *) let autobind_keys_list : (int * string * operation_t option * int) list ref = ref [];; let autobind_keys = ref (Array.make 1 (0, "", None, 0));; (* List of included rc files *) let included_rcfiles : (string list) ref = ref [];; (* Unit definition table *) let unit_table = ref Units.empty_unit_table;; let function_of_key key = Hashtbl.find table_key_function key;; let key_of_function f = Hashtbl.find table_function_key f;; let command_of_key key = Hashtbl.find table_key_command key;; let key_of_command command = Hashtbl.find table_command_key command;; let edit_of_key key = Hashtbl.find table_key_edit key;; let key_of_edit edit_op = Hashtbl.find table_edit_key edit_op;; let browse_of_key key = Hashtbl.find table_key_browse key;; let key_of_browse browse_op = Hashtbl.find table_browse_key browse_op;; let abbrev_of_key key = Hashtbl.find table_key_abbrev key;; let key_of_abbrev ex_op = Hashtbl.find table_abbrev_key ex_op;; let intedit_of_key key = Hashtbl.find table_key_intedit key;; let key_of_intedit edit_op = Hashtbl.find table_intedit_key edit_op;; let macro_of_key key = Hashtbl.find table_key_macro key;; let varedit_of_key key = Hashtbl.find table_key_varedit key;; let key_of_varedit edit_op = Hashtbl.find table_varedit_key edit_op;; let key_of_operation (op : operation_t) = match op with |Function x -> Hashtbl.find table_function_key x |Command x -> Hashtbl.find table_command_key x |Edit x -> Hashtbl.find table_edit_key x |Browse x -> Hashtbl.find table_browse_key x |Abbrev x -> Hashtbl.find table_abbrev_key x |IntEdit x -> Hashtbl.find table_intedit_key x |VarEdit x -> Hashtbl.find table_varedit_key x (* abbreviations used in abbreviation entry mode *) let abbrev_commands = ref [];; let abbrev_command_table = Hashtbl.create 50;; let command_abbrev_table = Hashtbl.create 50;; (* Register an abbreviation for an operation * This updates the string used in regexp matching, and * updates the hashtable used to find the corresponding operation. * Note: this list is generated in reverse order, so that * the list of matches can be generated rapidly in the opposite * order. *) let register_abbrev abbr op = (* Dummyproofing: if an abbreviation is a prefix of another * abbreviation, then it *must* lie earlier in the search order. * If not, it becomes impossible to execute the prefix command. *) let regex = Str.regexp_string abbr in let check_match (prev_result : bool) el = if prev_result then true else Str.string_match regex el 0 in if List.fold_left check_match false !abbrev_commands then (* if abbr is a prefix of some element, then it must * go at the end of the list *) abbrev_commands := !abbrev_commands @ [abbr] else (* if it has no prefix, then prepend it *) abbrev_commands := abbr :: !abbrev_commands; Hashtbl.add abbrev_command_table abbr op; Hashtbl.add command_abbrev_table op abbr;; (* tables used in for constant entry *) let constant_symbols = ref [];; let constants_table = Hashtbl.create 50;; (* Register a constant string. * This updates the string used in regexp matching, and * adds the constant definition to a lookup table. * Note: this list is generated in reverse order, so that * the list of matches can be generated rapidly in the opposite * order. *) let register_constant (const : string) (unit_def : Units.unit_def_t) = (* Dummyproofing: if a constant is a prefix of another * constant, then it *must* lie earlier in the search order. * If not, it becomes impossible to execute the prefix command. *) let regex = Str.regexp_string const in let check_match (prev_result : bool) el = if prev_result then true else Str.string_match regex el 0 in if List.fold_left check_match false !constant_symbols then (* if const is a prefix of some element, then it must * go at the end of the list *) constant_symbols := !constant_symbols @ [const] else (* if it has no prefix, then prepend it *) constant_symbols := const :: !constant_symbols; Hashtbl.add constants_table const unit_def;; (* remove an abbreviation for a command. *) let unregister_abbrev abbr = let remove_matching out_list el = if el = abbr then out_list else el :: out_list in let sublist = List.fold_left remove_matching [] !abbrev_commands in abbrev_commands := List.rev sublist; try let op = Hashtbl.find abbrev_command_table abbr in Hashtbl.remove abbrev_command_table abbr; Hashtbl.remove command_abbrev_table op with Not_found -> ();; let translate_abbrev abb = Hashtbl.find abbrev_command_table abb;; let abbrev_of_operation op = Hashtbl.find command_abbrev_table op;; let translate_constant const = Hashtbl.find constants_table const;; let decode_single_key_string key_string = let decode_alias str = match str with |"" -> 27 |"" -> 9 |"" -> Key.enter |"" -> 10 |"" -> Key.ic |"" -> Key.dc |"" -> Key.home |"" -> Key.end_ |"" -> Key.ppage |"" -> Key.npage |"" -> 32 |"" -> Key.backspace |"" -> Key.left |"" -> Key.right |"" -> Key.up |"" -> Key.down |"" -> (Key.f 1) |"" -> (Key.f 2) |"" -> (Key.f 3) |"" -> (Key.f 4) |"" -> (Key.f 5) |"" -> (Key.f 6) |"" -> (Key.f 7) |"" -> (Key.f 8) |"" -> (Key.f 9) |"" -> (Key.f 10) |"" -> (Key.f 11) |"" -> (Key.f 12) |_ -> if String.length key_string = 1 then int_of_char str.[0] else config_failwith ("Unrecognized key \"" ^ str ^ "\"") in (* This regexp is used to extract the ctrl and meta characters from a string * representing a keypress. * It matches \\M\\C or \\C\\M or \\C or \\M (or no such characters) followed * by an arbitrary string. *) (* Note: is there a way to use raw strings here? Getting tired of those * backslashes...*) let cm_re = Str.regexp "^\\(\\(\\\\M\\\\C\\|\\\\C\\\\M\\)\\|\\(\\\\M\\)\\|\\(\\\\C\\)\\)?\\(<.+>\\|.\\)" in if Str.string_match cm_re key_string 0 then let has_meta_ctrl = try let _ = Str.matched_group 2 key_string in true with Not_found -> false and has_meta = try let _ = Str.matched_group 3 key_string in true with Not_found -> false and has_ctrl = try let _ = Str.matched_group 4 key_string in true with Not_found -> false and main_key = Str.matched_group 5 key_string in if has_meta_ctrl then if String.length main_key = 1 then let uc_main_key = String.uppercase_ascii main_key in let mc_chtype = ((int_of_char uc_main_key.[0]) + 64) in let mc_str = "M-C-" ^ uc_main_key in (mc_chtype, mc_str) else config_failwith ("Cannot apply \\\\M\\\\C to key \"" ^ main_key ^ "\";\n" ^ "octal notation might let you accomplish this.") else if has_meta then if String.length main_key = 1 then let m_chtype = ((int_of_char main_key.[0]) + 128) in let m_str = "M-" ^ main_key in (m_chtype, m_str) else config_failwith ("Cannot apply \\\\M to key \"" ^ main_key ^ "\";\n" ^ "octal notation might let you accomplish this.") else if has_ctrl then if String.length main_key = 1 then let uc_main_key = String.uppercase_ascii main_key in let c_chtype = ((int_of_char uc_main_key.[0]) - 64) in let c_str = "C-" ^ uc_main_key in (c_chtype, c_str) else config_failwith ("Cannot apply \\\\C to key \"" ^ main_key ^ "\";\n" ^ "octal notation might let you accomplish this.") else let octal_regex = Str.regexp "^0o" in try let _ = Str.search_forward octal_regex key_string 0 in ((int_of_string key_string), ("\\" ^ Str.string_after key_string 2)) with _ -> ((decode_alias main_key), main_key) else config_failwith ("Unable to match binding string with standard regular expression.") (* Register a key binding. This adds hash table entries for translation * between curses chtypes and commands (in both directions). *) let register_binding_internal k k_string op = match op with |Function x -> Hashtbl.add table_key_function k x; Hashtbl.add table_function_key x k_string |Command x -> Hashtbl.add table_key_command k x; Hashtbl.add table_command_key x k_string |Edit x -> Hashtbl.add table_key_edit k x; Hashtbl.add table_edit_key x k_string |Browse x -> Hashtbl.add table_key_browse k x; Hashtbl.add table_browse_key x k_string |Abbrev x -> Hashtbl.add table_key_abbrev k x; Hashtbl.add table_abbrev_key x k_string |IntEdit x -> Hashtbl.add table_key_intedit k x; Hashtbl.add table_intedit_key x k_string |VarEdit x -> Hashtbl.add table_key_varedit k x; Hashtbl.add table_varedit_key x k_string (* convenience routine for previous *) let register_binding key_string op = (* given a string that represents a character, find the associated * curses chtype *) let k, string_rep = decode_single_key_string key_string in register_binding_internal k string_rep op (* Unregister key bindings. *) let unregister_function_binding key_string = let k, _ = decode_single_key_string key_string in try let op = Hashtbl.find table_key_function k in Hashtbl.remove table_key_function k; Hashtbl.remove table_function_key op with Not_found -> () let unregister_command_binding key_string = let k, _ = decode_single_key_string key_string in try let op = Hashtbl.find table_key_command k in Hashtbl.remove table_key_command k; Hashtbl.remove table_command_key op with Not_found -> () let unregister_edit_binding key_string = let k, _ = decode_single_key_string key_string in try let op = Hashtbl.find table_key_edit k in Hashtbl.remove table_key_edit k; Hashtbl.remove table_edit_key op with Not_found -> () let unregister_browse_binding key_string = let k, _ = decode_single_key_string key_string in try let op = Hashtbl.find table_key_browse k in Hashtbl.remove table_key_browse k; Hashtbl.remove table_browse_key op with Not_found -> () let unregister_abbrev_binding key_string = let k, _ = decode_single_key_string key_string in try let op = Hashtbl.find table_key_abbrev k in Hashtbl.remove table_key_abbrev k; Hashtbl.remove table_abbrev_key op with Not_found -> () let unregister_intedit_binding key_string = let k, _ = decode_single_key_string key_string in try let op = Hashtbl.find table_key_intedit k in Hashtbl.remove table_key_intedit k; Hashtbl.remove table_intedit_key op with Not_found -> () let unregister_varedit_binding key_string = let k, _ = decode_single_key_string key_string in try let op = Hashtbl.find table_key_varedit k in Hashtbl.remove table_key_varedit k; Hashtbl.remove table_varedit_key op with Not_found -> () (* Remove a key binding. *) let remove_binding k op = match op with |Function x -> Hashtbl.remove table_key_function k; Hashtbl.remove table_function_key x |Command x -> Hashtbl.remove table_key_command k; Hashtbl.remove table_command_key x |Edit x -> Hashtbl.remove table_key_edit k; Hashtbl.remove table_edit_key x |Browse x -> Hashtbl.remove table_key_browse k; Hashtbl.remove table_browse_key x |Abbrev x -> Hashtbl.remove table_key_abbrev k; Hashtbl.remove table_abbrev_key x |IntEdit x -> Hashtbl.remove table_key_intedit k; Hashtbl.remove table_intedit_key x |VarEdit x -> Hashtbl.remove table_key_varedit k; Hashtbl.remove table_varedit_key x (* Register a macro. This parses the macro string and divides it into multiple * whitespace-separated keypresses, then stores the list of keypresses in the * appropriate hashtable. *) let register_macro key keys_string = let macro_ch = fst (decode_single_key_string key) in let split_regex = Str.regexp "[ \t]+" in let keys_list = Str.split split_regex keys_string in let ch_of_key_string k_string = fst (decode_single_key_string k_string) in let ch_list = List.rev_map ch_of_key_string keys_list in Hashtbl.add table_key_macro macro_ch ch_list (* translate a command string to the command type it represents *) let operation_of_string command_str = begin match command_str with |"function_add" -> (Function Add) |"function_sub" -> (Function Sub) |"function_mult" -> (Function Mult) |"function_div" -> (Function Div) |"function_neg" -> (Function Neg) |"function_inv" -> (Function Inv) |"function_pow" -> (Function Pow) |"function_sq" -> (Function Sq) |"function_sqrt" -> (Function Sqrt) |"function_abs" -> (Function Abs) |"function_arg" -> (Function Arg) |"function_exp" -> (Function Exp) |"function_ln" -> (Function Ln) |"function_10_x" -> (Function Ten_x) |"function_log10" -> (Function Log10) |"function_conj" -> (Function Conj) |"function_sin" -> (Function Sin) |"function_cos" -> (Function Cos) |"function_tan" -> (Function Tan) |"function_asin" -> (Function Asin) |"function_acos" -> (Function Acos) |"function_atan" -> (Function Atan) |"function_sinh" -> (Function Sinh) |"function_cosh" -> (Function Cosh) |"function_tanh" -> (Function Tanh) |"function_asinh" -> (Function Asinh) |"function_acosh" -> (Function Acosh) |"function_atanh" -> (Function Atanh) |"function_re" -> (Function Re) |"function_im" -> (Function Im) |"function_gamma" -> (Function Gamma) |"function_lngamma" -> (Function LnGamma) |"function_erf" -> (Function Erf) |"function_erfc" -> (Function Erfc) |"function_factorial" -> (Function Fact) |"function_transpose" -> (Function Transpose) |"function_mod" -> (Function Mod) |"function_floor" -> (Function Floor) |"function_ceiling" -> (Function Ceiling) |"function_to_int" -> (Function ToInt) |"function_to_real" -> (Function ToFloat) |"function_solve_linear" -> (Function SolveLin) |"function_eval" -> (Function Eval) |"function_store" -> (Function Store) |"function_purge" -> (Function Purge) |"function_gcd" -> (Function Gcd) |"function_lcm" -> (Function Lcm) |"function_binomial_coeff" -> (Function Binom) |"function_permutation" -> (Function Perm) |"function_total" -> (Function Total) |"function_mean" -> (Function Mean) |"function_sumsq" -> (Function Sumsq) |"function_var_unbiased" -> (Function Var) |"function_var_biased" -> (Function VarBias) |"function_stdev_unbiased" -> (Function Stdev) |"function_stdev_biased" -> (Function StdevBias) |"function_minimum" -> (Function Min) |"function_maximum" -> (Function Max) |"function_utpn" -> (Function Utpn) |"function_standardize_units" -> (Function StandardizeUnits) |"function_convert_units" -> (Function ConvertUnits) |"function_unit_value" -> (Function UnitValue) |"function_trace" -> (Function Trace) |"edit_begin_integer" -> (Edit BeginInteger) |"edit_complex" -> (Edit BeginComplex) |"edit_matrix" -> (Edit BeginMatrix) |"edit_separator" -> (Edit Separator) |"edit_angle" -> (Edit Angle) |"edit_minus" -> (Edit Minus) |"edit_backspace" -> (Edit Backspace) |"edit_enter" -> (Edit Enter) |"edit_scientific_notation_base" -> (Edit SciNotBase) |"edit_begin_units" -> (Edit BeginUnits) |"command_drop" -> (Command Drop) |"command_clear" -> (Command Clear) |"command_swap" -> (Command Swap) |"command_dup" -> (Command Dup) |"command_undo" -> (Command Undo) |"command_begin_browsing" -> (Command BeginBrowse) |"command_begin_abbrev" -> (Command BeginAbbrev) |"command_begin_constant" -> (Command BeginConst) |"command_begin_variable" -> (Command BeginVar) |"command_quit" -> (Command Quit) |"command_rad" -> (Command SetRadians) |"command_deg" -> (Command SetDegrees) |"command_rect" -> (Command SetRect) |"command_polar" -> (Command SetPolar) |"command_bin" -> (Command SetBin) |"command_oct" -> (Command SetOct) |"command_dec" -> (Command SetDec) |"command_hex" -> (Command SetHex) |"command_toggle_angle_mode" -> (Command ToggleAngleMode) |"command_toggle_complex_mode" -> (Command ToggleComplexMode) |"command_cycle_base" -> (Command CycleBase) |"command_view" -> (Command View) |"command_refresh" -> (Command Refresh) |"command_about" -> (Command About) |"command_enter_pi" -> (Command EnterPi) |"command_rand" -> (Command Rand) |"command_edit_input" -> (Command EditInput) |"command_cycle_help" -> (Command CycleHelp) |"browse_end" -> (Browse EndBrowse) |"browse_scroll_left" -> (Browse ScrollLeft) |"browse_scroll_right" -> (Browse ScrollRight) |"browse_prev_line" -> (Browse PrevLine) |"browse_next_line" -> (Browse NextLine) |"browse_echo" -> (Browse Echo) |"browse_rolldown" -> (Browse RollDown) |"browse_rollup" -> (Browse RollUp) |"browse_view" -> (Browse ViewEntry) |"browse_drop" -> (Browse Drop1) |"browse_dropn" -> (Browse DropN) |"browse_keep" -> (Browse Keep) |"browse_keepn" -> (Browse KeepN) |"browse_edit" -> (Browse EditEntry) |"abbrev_exit" -> (Abbrev AbbrevExit) |"abbrev_enter" -> (Abbrev AbbrevEnter) |"abbrev_backspace" -> (Abbrev AbbrevBackspace) |"integer_cancel" -> (IntEdit IntEditExit) |"variable_cancel" -> (VarEdit VarEditExit) |"variable_enter" -> (VarEdit VarEditEnter) |"variable_backspace" -> (VarEdit VarEditBackspace) |"variable_complete" -> (VarEdit VarEditComplete) |"function_rand" -> config_failwith "operation \"function_rand\" is deprecated; please replace with \"command_rand\"." |"command_begin_extended" -> config_failwith "operation \"command_begin_extended\" is deprecated; please replace with \"command_begin_abbrev\"." |"extended_exit" -> config_failwith "operation \"extended_exit\" is deprecated; please replace with \"abbrev_exit\"." |"extended_enter" -> config_failwith "operation \"extended_enter\" is deprecated; please replace with \"abbrev_enter\"." |"extended_backspace" -> config_failwith "operation \"extended_backspace\" is deprecated; please replace with \"abbrev_backspace\"." |_ -> config_failwith ("Unknown command name \"" ^ command_str ^ "\"") end (* Parse a line from an Orpie configuration file. This operates on a stream * corresponding to a non-empty line from the file. It will match commands * of the form * bind key command * macro key multiple_keys * where 'key' is either a quoted string containing a key specifier or an octal * key representation of the form \xxx (unquoted), and multiple_keys is a quoted * string containing a number of keypresses to simulate. *) let parse_line line_stream = match line_stream with parser | [< 'Kwd "include" >] -> begin match line_stream with parser | [< 'String include_file >] -> included_rcfiles := include_file :: !included_rcfiles | [< >] -> config_failwith ("Expected a filename string after \"include\"") end | [< 'Kwd "bind" >] -> let bind_key key = begin match line_stream with parser | [< 'Ident command_str >] -> let command = operation_of_string command_str in register_binding key command | [< >] -> config_failwith ("Expected a command name after \"bind \"" ^ key ^ "\"") end in begin match line_stream with parser | [< 'String k >] -> bind_key k | [< 'Ident "\\" >] -> begin match line_stream with parser | [< 'Int octal_int >] -> begin try let octal_digits = "0o" ^ (string_of_int octal_int) in bind_key octal_digits with (Failure "int_of_string") -> config_failwith "Expected octal digits after \"\\\"" end | [< >] -> config_failwith "Expected octal digits after \"\\\"" end | [< >] -> config_failwith "Expected a key string after keyword \"bind\"" end | [< 'Kwd "unbind_function" >] -> begin match line_stream with parser | [< 'String k >] -> unregister_function_binding k | [< >] -> config_failwith ("Expected a key string after keyword \"unbind_function\"") end | [< 'Kwd "unbind_command" >] -> begin match line_stream with parser | [< 'String k >] -> unregister_command_binding k | [< >] -> config_failwith ("Expected a key string after keyword \"unbind_command\"") end | [< 'Kwd "unbind_edit" >] -> begin match line_stream with parser | [< 'String k >] -> unregister_edit_binding k | [< >] -> config_failwith ("Expected a key string after keyword \"unbind_edit\"") end | [< 'Kwd "unbind_browse" >] -> begin match line_stream with parser | [< 'String k >] -> unregister_browse_binding k | [< >] -> config_failwith ("Expected a key string after keyword \"unbind_browse\"") end | [< 'Kwd "unbind_abbrev" >] -> begin match line_stream with parser | [< 'String k >] -> unregister_abbrev_binding k | [< >] -> config_failwith ("Expected a key string after keyword \"unbind_abbrev\"") end | [< 'Kwd "unbind_integer" >] -> begin match line_stream with parser | [< 'String k >] -> unregister_intedit_binding k | [< >] -> config_failwith ("Expected a key string after keyword \"unbind_integer\"") end | [< 'Kwd "unbind_variable" >] -> begin match line_stream with parser | [< 'String k >] -> unregister_varedit_binding k | [< >] -> config_failwith ("Expected a key string after keyword \"unbind_variable\"") end | [< 'Kwd "autobind" >] -> begin match line_stream with parser | [< 'String k >] -> let key, key_string = decode_single_key_string k in autobind_keys_list := (key, key_string, None, 1) :: !autobind_keys_list | [< 'Ident "\\" >] -> begin match line_stream with parser | [< 'Int octal_int >] -> begin try let octal_digits = "0o" ^ (string_of_int octal_int) in let key, key_string = decode_single_key_string octal_digits in autobind_keys_list := (key, key_string, None, 1) :: !autobind_keys_list with (Failure "int_of_string") -> config_failwith "Expected octal digits after \"\\\"" end | [< >] -> config_failwith "Expected octal digits after \"\\\"" end | [< >] -> config_failwith "Expected a key string after keyword \"bind\"" end | [< 'Kwd "macro" >] -> begin match line_stream with parser | [< 'String key >] -> begin match line_stream with parser | [< 'String generated_keys >] -> register_macro key generated_keys | [< >] -> config_failwith ("Expected a key string after \"macro \"" ^ key ^ "\"") end | [< >] -> config_failwith "Expected a key string after keyword \"macro\"" end | [< 'Kwd "abbrev" >] -> begin match line_stream with parser | [< 'String abbr >] -> begin match line_stream with parser | [< 'Ident command_str >] -> let command = operation_of_string command_str in register_abbrev abbr command | [< >] -> config_failwith ("Expected a command name after \"abbrev \"" ^ abbr ^ "\"") end | [< >] -> config_failwith ("Expected an abbreviation string after \"abbrev\"") end | [< 'Kwd "unabbrev" >] -> begin match line_stream with parser | [< 'String abbr >] -> unregister_abbrev abbr | [< >] -> config_failwith ("Expected an abbreviation string after \"unabbrev\"") end | [< 'Kwd "set" >] -> begin match line_stream with parser | [< 'Ident "datadir" >] -> begin match line_stream with parser | [< 'Ident "=" >] -> begin match line_stream with parser | [< 'String dir >] -> datadir := dir | [< >] -> config_failwith ("Expected a directory string after " ^ "\"set datadir = \"") end | [< >] -> config_failwith ("Expected \"=\" after \"set datadir\"") end | [< 'Ident "editor" >] -> begin match line_stream with parser | [< 'Ident "=" >] -> begin match line_stream with parser | [< 'String executable >] -> ( (* Printf.fprintf stderr "using editor \"%s\"\n" executable; *) editor := executable) | [< >] -> config_failwith ("Expected an executable filename string after " ^ "\"set editor = \"") end | [< >] -> config_failwith ("Expected \"=\" after \"set editor\"") end | [< 'Ident "hide_help" >] -> begin match line_stream with parser | [< 'Ident "=" >] -> begin match line_stream with parser | [< 'String setting >] -> if setting = "true" then hide_help := true else if setting = "false" then hide_help := false else config_failwith ("Expected a boolean argument after " ^ "\"set hide_help = \"") | [< >] -> config_failwith ("Expected a boolean argument after " ^ "\"set hide_help = \"") end | [< >] -> config_failwith ("Expected \"=\" after \"set hide_help\"") end | [< 'Ident "conserve_memory" >] -> begin match line_stream with parser | [< 'Ident "=" >] -> begin match line_stream with parser | [< 'String setting >] -> if setting = "true" then conserve_memory := true else if setting = "false" then conserve_memory := false else config_failwith ("Expected a boolean argument after " ^ "\"set conserve_memory = \"") | [< >] -> config_failwith ("Expected a boolean argument after " ^ "\"set conserve_memory = \"") end | [< >] -> config_failwith ("Expected \"=\" after \"set conserve_memory\"") end | [< >] -> config_failwith ("Unmatched variable name after \"set\"") end | [< 'Kwd "base_unit" >] -> begin match line_stream with parser | [< 'String base_u; 'String prefix_s >] -> begin try let prefix = Units.prefix_of_string prefix_s in unit_table := Units.add_base_unit base_u prefix !unit_table with Not_found -> config_failwith ("Expected an SI prefix string (or null string) after: base_unit \"" ^ base_u ^ "\"") end | [< >] -> config_failwith ("Expected a unit string and prefix string after \"base_unit\"") end | [< 'Kwd "unit" >] -> begin match line_stream with parser | [< 'String unit_str; 'String unit_def_str >] -> begin try let unit_def = Units.unit_def_of_string unit_def_str !unit_table in unit_table := Units.add_unit unit_str unit_def !unit_table with Units.Units_error s -> config_failwith ("Illegal unit definition: unit \"" ^ unit_str ^ "\" \"" ^ unit_def_str ^ "\"; " ^ s) end | [< >] -> config_failwith ("Expected a unit string and definition after \"unit\"") end | [< 'Kwd "constant" >] -> begin match line_stream with parser | [< 'String const_str; 'String unit_def_str >] -> begin try let unit_def = Units.unit_def_of_string unit_def_str !unit_table in register_constant const_str unit_def with Units.Units_error s -> config_failwith ("Illegal constant definition: constant \"" ^ const_str ^ "\" \"" ^ unit_def_str ^ "\"; " ^ s) end | [< >] -> config_failwith ("Expected a constant name and definition after \"constant\"") end | [< 'Kwd "#" >] -> () | [< >] -> config_failwith "Expected a keyword at start of line";; (* obtain a valid autobinding array, eliminating duplicate keys *) let generate_autobind_array () = let candidates = Array.of_list (List.rev !autobind_keys_list) in let temp_arr = Array.make (Array.length candidates) (0, "", None, 0) in let pointer = ref 0 in for i = 0 to pred (Array.length candidates) do let (c_k, c_ss, c_bound_f, c_age) = candidates.(i) in let matched = ref false in for j = 0 to !pointer do let (t_k, t_ss, t_bound_f, t_age) = temp_arr.(j) in if c_k = t_k then matched := true else () done; if not !matched then begin temp_arr.(!pointer) <- candidates.(i); pointer := succ !pointer end else () done; autobind_keys := Array.sub temp_arr 0 !pointer (* compare a set of autobindings saved to disk to the set loaded from the * orpierc file. If the autobindings match and the hashtbl abbreviations * are the same, then use the saved version. *) let validate_saved_autobindings saved_autobind = if Array.length !autobind_keys = Array.length saved_autobind then let is_valid = ref true in for i = 0 to pred (Array.length saved_autobind) do let (s_key, s_key_str, s_bound_f, s_age) = saved_autobind.(i) and (n_key, n_key_str, n_bound_f, n_age) = !autobind_keys.(i) in if s_key = n_key then begin try begin match s_bound_f with |None -> () |Some op -> let _ = abbrev_of_operation op in () end with Not_found -> (* if the function has no associated abbreviation, then consider * the saved autobindings to be flawed *) is_valid := false end else (* if the autobindings are different from the saved set, then * consider the saved set to be flawed. *) is_valid := false done; if !is_valid then begin autobind_keys := saved_autobind; for i = 0 to pred (Array.length !autobind_keys) do let (n_key, n_key_str, n_bound_f, n_age) = !autobind_keys.(i) in match n_bound_f with |None -> () |Some op -> register_binding_internal n_key n_key_str op done end else () else () (* try opening the rc file, first looking at $HOME/.orpierc, * then looking at $PREFIX/etc/orpierc *) let open_rcfile rcfile_op = match rcfile_op with |None -> let home_rcfile = let homedir = Sys.getenv "HOME" in homedir ^ "/.orpierc" in let rcfile_fullpath = (* expand out any occurrences of ${prefix} that autoconf * decides to insert *) let prefix_regex = Str.regexp "\\${prefix}" in let expanded_sysconfdir = Str.global_replace prefix_regex Install.prefix Install.sysconfdir in Utility.join_path expanded_sysconfdir "orpierc" in begin try (open_in home_rcfile, home_rcfile) with Sys_error error_str -> begin try (open_in rcfile_fullpath, rcfile_fullpath) with Sys_error error_str -> failwith ("Could not open configuration file \"" ^ home_rcfile ^ "\" or \"" ^ rcfile_fullpath ^ "\" .") end end |Some file -> try (Utility.expand_open_in_ascii file, file) with Sys_error error_str -> config_failwith ("Could not open configuration file \"" ^ file ^ "\".") let rec process_rcfile rcfile_op = let line_lexer line = make_lexer ["include"; "bind"; "unbind_function"; "unbind_command"; "unbind_edit"; "unbind_browse"; "unbind_abbrev"; "unbind_integer"; "unbind_variable"; "autobind"; "abbrev"; "unabbrev"; "macro"; "set"; "base_unit"; "unit"; "constant"; "#"] (Stream.of_string line) in let empty_regexp = Str.regexp "^[\t ]*$" in let config_stream, rcfile_filename = open_rcfile rcfile_op in let line_num = ref 0 in try while true do line_num := succ !line_num; let line_string = input_line config_stream in (* Printf.fprintf stderr "read line %2d: %s\n" !line_num line_string; flush stderr; *) if Str.string_match empty_regexp line_string 0 then (* do nothing on an empty line *) () else try let line_stream = line_lexer line_string in parse_line line_stream; (* process any included rcfiles as they are encountered *) begin match !included_rcfiles with |[] -> () |head :: tail -> included_rcfiles := tail; process_rcfile (Some head) end with |Config_failure s -> (let error_str = Printf.sprintf "Syntax error on line %d of \"%s\": %s" !line_num rcfile_filename s in failwith error_str) |Stream.Failure -> failwith (Printf.sprintf "Syntax error on line %d of \"%s\"" !line_num rcfile_filename) done with End_of_file -> begin close_in config_stream; generate_autobind_array () end (* arch-tag: DO_NOT_CHANGE_614115ed-7d1d-4834-bda4-e6cf93ac3fcd *) orpie-release-1.6.0/src/orpie/rpc_calc.ml000066400000000000000000003007351334120314700202650ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* rpc_calc.ml * This file defines Orpie's underlying calculator object. All calculator * functions and commands have a corresponding method in this object. *) open Rpc_stack;; open Gsl_assist;; open Big_int;; type interruptable_args_t = | Gcd_args of big_int * big_int * orpie_data_t * orpie_data_t | Lcm_args of big_int * big_int * big_int * orpie_data_t * orpie_data_t | Fact_args of big_int * big_int * orpie_data_t | Binom_args of big_int * big_int * big_int * big_int * orpie_data_t * orpie_data_t | Perm_args of big_int * big_int * big_int * orpie_data_t * orpie_data_t | NoArgs;; let pi = 3.14159265358979323846;; let c_of_f ff = { Complex.re = ff; Complex.im = 0.0 } class rpc_calc conserve_memory = object(self) val mutable stack = new rpc_stack conserve_memory val mutable backup_stack = new rpc_stack conserve_memory val mutable modes = {angle = Rad; base = Dec; complex = Rect} val mutable variables = Hashtbl.create 10 val mutable interr_args = NoArgs method backup () = backup_stack <- stack#backup () method undo () = stack <- backup_stack method mode_rad () = modes <- {angle = Rad; base = modes.base; complex = modes.complex} method mode_deg () = modes <- {angle = Deg; base = modes.base; complex = modes.complex} method mode_rect () = modes <- {angle = modes.angle; base = modes.base; complex = Rect} method mode_polar () = modes <- {angle = modes.angle; base = modes.base; complex = Polar} method mode_bin () = modes <- {angle = modes.angle; base = Bin; complex = modes.complex} method mode_oct () = modes <- {angle = modes.angle; base = Oct; complex = modes.complex} method mode_dec () = modes <- {angle = modes.angle; base = Dec; complex = modes.complex} method mode_hex () = modes <- {angle = modes.angle; base = Hex; complex = modes.complex} method get_variables () = variables method toggle_angle_mode () = match modes.angle with |Rad -> self#mode_deg () |Deg -> self#mode_rad () method toggle_complex_mode () = match modes.complex with |Rect -> self#mode_polar () |Polar -> self#mode_rect () method cycle_base () = match modes.base with |Bin -> self#mode_oct () |Oct -> self#mode_dec () |Dec -> self#mode_hex () |Hex -> self#mode_bin () method get_state () = (modes, variables, stack#get_state ()) method set_state (m, v, s_op) = begin match s_op with |Some st -> stack#set_state st |None -> () end; modes <- m; variables <- v; self#backup () method abort_computation () = match interr_args with |Gcd_args (a, b, el1, el2) -> stack#push el1; stack#push el2; interr_args <- NoArgs |Lcm_args (coeff, a, b, el1, el2) -> stack#push el1; stack#push el2; interr_args <- NoArgs |Fact_args (num, acc, el) -> stack#push el; interr_args <- NoArgs |Binom_args (n, k, num, denom, el1, el2) -> stack#push el1; stack#push el2; interr_args <- NoArgs |Perm_args (n, term, partial, el1, el2) -> stack#push el1; stack#push el2; interr_args <- NoArgs |NoArgs -> () (* all calc functions will need to have arguments checked * and a backup performed, so we handle it with a little wrapper function *) method private check_args (num_args : int) (fn_str : string) (fn : unit -> unit) = if stack#length >= num_args then begin self#backup (); fn () end else if stack#length = 0 then raise (Invalid_argument "empty stack") else raise (Invalid_argument ("insufficient arguments for " ^ fn_str)) method add () = self#check_args 2 "addition" self#internal_add method private internal_add () = Add.add stack self#evaln method sub () = self#check_args 2 "subtraction" self#internal_sub method private internal_sub () = Sub.sub stack self#evaln method mult () = self#check_args 2 "multiplication" self#internal_mult method private internal_mult () = Mult.mult stack self#evaln method div () = self#check_args 2 "division" self#internal_div method private internal_div () = Div.div stack self#evaln method inv () = self#check_args 1 "inv" self#internal_inv method private internal_inv () = Inv.inv stack self#evaln method pow () = self#check_args 2 "pow" self#internal_pow method private internal_pow () = Pow.pow stack self#evaln method get_modes () = modes method get_stack_size () = stack#length method dup () = self#check_args 1 "dup" self#internal_dup (* Warning: dup() creates multiple references to the same object. * Therefore all operations need to leave the original stack elements * unaltered. *) method private internal_dup () = stack#dup () method neg () = self#check_args 1 "neg" self#internal_neg method private internal_neg () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> stack#push (RpcInt (minus_big_int el)) |RpcFloatUnit (el, uu) -> stack#push (RpcFloatUnit (~-. el, uu)) |RpcComplexUnit (el, uu) -> stack#push (RpcComplexUnit (Complex.neg el, uu)) |RpcFloatMatrixUnit (el, uu) -> let copy = Gsl.Matrix.copy el in (Gsl.Matrix.scale copy (-1.0); stack#push (RpcFloatMatrixUnit (copy, uu))) |RpcComplexMatrixUnit (el, uu) -> let copy = Gsl.Matrix_complex.copy el in (Gsl.Matrix_complex.scale copy {Complex.re=(-1.0); Complex.im=0.0}; stack#push (RpcComplexMatrixUnit (copy, uu))) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) method sq () = self#check_args 1 "sq" self#internal_sq method private internal_sq () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> stack#push (RpcInt (mult_big_int el el)) |RpcFloatUnit (el, uu) -> stack#push (RpcFloatUnit (el *. el, Units.pow uu 2.)) |RpcComplexUnit (el, uu) -> stack#push (RpcComplexUnit (Complex.mul el el, Units.pow uu 2.)) |RpcFloatMatrixUnit (el, uu) -> let n, m = (Gsl.Matrix.dims el) in if n = m then let result = Gsl.Matrix.create n m in (Gsl.Blas.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:1.0 ~a:el ~b:el ~beta:0.0 ~c:result; stack#push (RpcFloatMatrixUnit (result, Units.pow uu 2.))) else (stack#push gen_el; raise (Invalid_argument "matrix is non-square")) |RpcComplexMatrixUnit (el, uu) -> let n, m = (Gsl.Matrix_complex.dims el) in if m = n then let result = Gsl.Matrix_complex.create n m in Gsl.Blas.Complex.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:Complex.one ~a:el ~b:el ~beta:Complex.zero ~c:result; stack#push (RpcComplexMatrixUnit (result, Units.pow uu 2.)) else (stack#push gen_el; raise (Invalid_argument "matrix is non-square")) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) method sqrt () = self#check_args 1 "sqrt" self#internal_sqrt method private internal_sqrt () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcFloatUnit (el, uu) -> if el < 0.0 then let cc = c_of_f el in stack#push (RpcComplexUnit (Complex.sqrt cc, Units.pow uu 0.5)) else stack#push (RpcFloatUnit (sqrt el, Units.pow uu 0.5)) |RpcComplexUnit (el, uu) -> stack#push (RpcComplexUnit (Complex.sqrt el, Units.pow uu 0.5)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method abs () = self#check_args 1 "abs" self#internal_abs method private internal_abs () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> stack#push (RpcInt (abs_big_int el)) |RpcFloatUnit (el, uu) -> stack#push (RpcFloatUnit (abs_float el, uu)) |RpcComplexUnit (el, uu) -> stack#push (RpcFloatUnit (Gsl.Gsl_complex.abs el, uu)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method arg () = self#check_args 1 "arg" self#internal_arg method private internal_arg () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcComplexUnit (el, uu) -> begin match modes.angle with |Rad -> let (f, u) = funit_of_float (Gsl.Gsl_complex.arg el) in stack#push (RpcFloatUnit (f, u)) |Deg -> let (f, u) = funit_of_float (180.0 /. pi *. (Gsl.Gsl_complex.arg el)) in stack#push (RpcFloatUnit (f, u)) end |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method exp () = self#check_args 1 "exp" self#internal_exp method private internal_exp () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float (exp (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot exponentiate dimensioned value" end else let (f, u) = funit_of_float (exp el) in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot exponentiate dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.exp el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method ln () = self#check_args 1 "ln" self#internal_ln method private internal_ln () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float (log (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute logarithm of dimensioned value" end else if el >= 0.0 then begin let (f, u) = funit_of_float (log el) in stack#push (RpcFloatUnit (f, u)) end else let c_arg = c_of_f el in let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.log c_arg) in stack#push (RpcComplexUnit (c, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute logarithm of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.log el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method ten_pow_x () = self#check_args 1 "10_x" self#internal_ten_pow_x method private internal_ten_pow_x () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float (10.0 ** (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot exponentiate dimensioned value" end else let (f, u) = funit_of_float (10.0 ** el) in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot exponentiate dimensioned value" end else let (c, u) = cunit_of_cpx (Complex.pow (cmpx_of_float 10.0) el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method log10 () = self#check_args 1 "log10" self#internal_log10 method private internal_log10 () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float (log10 (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute logarithm of dimensioned value" end else if el >= 0.0 then begin let (f, u) = funit_of_float (log10 el) in stack#push (RpcFloatUnit (f, u)) end else let c_arg = c_of_f el in let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.log10 c_arg) in stack#push (RpcComplexUnit (c, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute logarithm of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.log10 el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method conj () = self#check_args 1 "conj" self#internal_conj method private internal_conj () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> stack#push (RpcInt el) |RpcFloatUnit (el, uu) -> stack#push (RpcFloatUnit (el, uu)) |RpcComplexUnit (el, uu) -> stack#push (RpcComplexUnit (Gsl.Gsl_complex.conjugate el, uu)) |RpcFloatMatrixUnit (el, uu) -> stack#push (RpcFloatMatrixUnit (el, uu)) |RpcComplexMatrixUnit (el, uu) -> (* element-by-element conjugation *) let rows, cols = Gsl.Matrix_complex.dims el and arr = Gsl.Matrix_complex.to_array el in let conj_arr = Array.map Gsl.Gsl_complex.conjugate arr in let conj_mat = Gsl.Matrix_complex.of_array conj_arr rows cols in stack#push (RpcComplexMatrixUnit (conj_mat, uu)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) method sin () = self#check_args 1 "sin" self#internal_sin method private internal_sin () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float begin match modes.angle with |Rad -> sin (float_of_big_int el) |Deg -> sin (pi /. 180.0 *. (float_of_big_int el)) end in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute sine of dimensioned value" end else let (f, u) = funit_of_float begin match modes.angle with |Rad -> sin el |Deg -> sin (pi /. 180.0 *. el) end in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute sine of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.sin el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method cos () = self#check_args 1 "cos" self#internal_cos method private internal_cos () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float begin match modes.angle with |Rad -> cos (float_of_big_int el) |Deg -> cos (pi /. 180.0 *. (float_of_big_int el)) end in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute cosine of dimensioned value" end else let (f, u) = funit_of_float begin match modes.angle with |Rad -> cos el |Deg -> cos (pi /. 180.0 *. el) end in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute cosine of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.cos el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method tan () = self#check_args 1 "tan" self#internal_tan method private internal_tan () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float begin match modes.angle with |Rad -> tan (float_of_big_int el) |Deg -> tan (pi /. 180.0 *. (float_of_big_int el)) end in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute tangent of dimensioned value" end else let (f, u) = funit_of_float begin match modes.angle with |Rad -> tan el |Deg -> tan (pi /. 180.0 *. el) end in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute tangent of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.tan el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method asin () = self#check_args 1 "asin" self#internal_asin method private internal_asin () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float begin match modes.angle with |Rad -> asin (float_of_big_int el) |Deg -> 180.0 /. pi *. asin (float_of_big_int el) end in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute arcsine of dimensioned value" end else let (f, u) = funit_of_float begin match modes.angle with |Rad -> asin el |Deg -> 180.0 /. pi *. asin el end in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute arcsine of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.arcsin el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method acos () = self#check_args 1 "acos" self#internal_acos method private internal_acos () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float begin match modes.angle with |Rad -> acos (float_of_big_int el) |Deg -> 180.0 /. pi *. acos (float_of_big_int el) end in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute arccos of dimensioned value" end else let (f, u) = funit_of_float begin match modes.angle with |Rad -> acos el |Deg -> 180.0 /. pi *. acos el end in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute arccos of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.arccos el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method atan () = self#check_args 1 "atan" self#internal_atan method private internal_atan () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float begin match modes.angle with |Rad -> atan (float_of_big_int el) |Deg -> 180.0 /. pi *. atan (float_of_big_int el) end in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute arctan of dimensioned value" end else let (f, u) = funit_of_float begin match modes.angle with |Rad -> atan el |Deg -> 180.0 /. pi *. atan el end in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute arctan of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.arctan el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method sinh () = self#check_args 1 "sinh" self#internal_sinh method private internal_sinh () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float (sinh (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute sinh of dimensioned value" end else let (f, u) = funit_of_float (sinh el) in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute sinh of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.sinh el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method cosh () = self#check_args 1 "cosh" self#internal_cosh method private internal_cosh () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float (cosh (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute cosh of dimensioned value" end else let (f, u) = funit_of_float (cosh el) in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute cosh of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.cosh el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method tanh () = self#check_args 1 "tanh" self#internal_tanh method private internal_tanh () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float (tanh (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute tanh of dimensioned value" end else let (f, u) = funit_of_float (tanh el) in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute tanh of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.tanh el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method asinh () = self#check_args 1 "asinh" self#internal_asinh method private internal_asinh () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float (Gsl.Math.asinh (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute asinh of dimensioned value" end else let (f, u) = funit_of_float (Gsl.Math.asinh el) in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute asinh of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.arcsinh el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method acosh () = self#check_args 1 "acosh" self#internal_acosh method private internal_acosh () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float (Gsl.Math.acosh (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute acosh of dimensioned value" end else let (f, u) = funit_of_float (Gsl.Math.acosh el) in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute acosh of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.arccosh el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method atanh () = self#check_args 1 "atanh" self#internal_atanh method private internal_atanh () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float (Gsl.Math.atanh (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute atanh of dimensioned value" end else let (f, u) = funit_of_float (Gsl.Math.atanh el) in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute atanh of dimensioned value" end else let (c, u) = cunit_of_cpx (Gsl.Gsl_complex.arctanh el) in stack#push (RpcComplexUnit (c, u)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method re () = self#check_args 1 "re" self#internal_re (* real part of complex (or complex matrix) *) method private internal_re () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> stack#push gen_el |RpcFloatUnit (el, uu) -> stack#push gen_el |RpcComplexUnit (el, uu) -> stack#push (RpcFloatUnit (el.Complex.re, uu)) |RpcFloatMatrixUnit (el, uu) -> stack#push gen_el |RpcComplexMatrixUnit (el, uu) -> let n, m = Gsl.Matrix_complex.dims el and carr = Gsl.Matrix_complex.to_array el in let farr = Array.make (n * m) 0.0 in for i = 0 to pred (n * m) do farr.(i) <- carr.(i).Complex.re done; stack#push (RpcFloatMatrixUnit (Gsl.Matrix.of_array farr n m, uu)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) method im () = self#check_args 1 "im" self#internal_im (* imaginary part of complex (or complex matrix) *) method private internal_im () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> stack#push (RpcInt zero_big_int) |RpcFloatUnit (el, uu) -> stack#push (RpcFloatUnit (0.0, uu)) |RpcComplexUnit (el, uu) -> stack#push (RpcFloatUnit (el.Complex.im, uu)) |RpcFloatMatrixUnit (el, uu) -> let n, m = Gsl.Matrix.dims el in let farr = Array.make (n * m) 0.0 in stack#push (RpcFloatMatrixUnit (Gsl.Matrix.of_array farr n m, uu)) |RpcComplexMatrixUnit (el, uu) -> let n, m = Gsl.Matrix_complex.dims el and carr = Gsl.Matrix_complex.to_array el in let farr = Array.make (n * m) 0.0 in for i = 0 to pred (n * m) do farr.(i) <- carr.(i).Complex.im done; stack#push (RpcFloatMatrixUnit (Gsl.Matrix.of_array farr n m, uu)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) method gamma () = self#check_args 1 "gamma" self#internal_gamma (* Euler gamma function *) method private internal_gamma () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> begin try let (f, u) = funit_of_float (Gsl.Sf.gamma (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) with Gsl.Error.Gsl_exn (err, errstr) -> (stack#push gen_el; raise (Invalid_argument errstr)) end |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute gamma of dimensioned value" end else begin try let (f, u) = funit_of_float (Gsl.Sf.gamma el) in stack#push (RpcFloatUnit (f, u)) with Gsl.Error.Gsl_exn (err, errstr) -> (stack#push gen_el; raise (Invalid_argument errstr)) end |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method lngamma () = self#check_args 1 "lngamma" self#internal_lngamma (* log_e of Euler gamma function *) method private internal_lngamma () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> begin try let (f, u) = funit_of_float (Gsl.Sf.lngamma (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) with Gsl.Error.Gsl_exn (err, errstr) -> (stack#push gen_el; raise (Invalid_argument errstr)) end |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute lngamma of dimensioned value" end else begin try let (f, u) = funit_of_float (Gsl.Sf.lngamma el) in stack#push (RpcFloatUnit (f, u)) with Gsl.Error.Gsl_exn (err, errstr) -> (stack#push gen_el; raise (Invalid_argument errstr)) end |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method erf () = self#check_args 1 "erf" self#internal_erf (* error function *) method private internal_erf () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> begin try let (f, u) = funit_of_float (Gsl.Sf.erf (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) with Gsl.Error.Gsl_exn (err, errstr) -> (stack#push gen_el; raise (Invalid_argument errstr)) end |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute error function of dimensioned value" end else begin try let (f, u) = funit_of_float (Gsl.Sf.erf el) in stack#push (RpcFloatUnit (f, u)) with Gsl.Error.Gsl_exn (err, errstr) -> (stack#push gen_el; raise (Invalid_argument errstr)) end |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) method erfc () = self#check_args 1 "erfc" self#internal_erfc (* complementary error function *) method private internal_erfc () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> begin try let (f, u) = funit_of_float (Gsl.Sf.erfc (float_of_big_int el)) in stack#push (RpcFloatUnit (f, u)) with Gsl.Error.Gsl_exn (err, errstr) -> (stack#push gen_el; raise (Invalid_argument errstr)) end |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute erfc of dimensioned value" end else begin try let (f, u) = funit_of_float (Gsl.Sf.erfc el) in stack#push (RpcFloatUnit (f, u)) with Gsl.Error.Gsl_exn (err, errstr) -> (stack#push gen_el; raise (Invalid_argument errstr)) end |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) (* factorial * calls gamma function for float arguments, and jumps * to an interruptible exact implementation for integer * arguments. * This function is designed to be called multiple times * until it returns true. If computation is aborted, the interface * should call abort_computation() to clean up. *) method fact () = match interr_args with |Fact_args (num, acc, el) -> if eq_big_int num zero_big_int then begin stack#push (RpcInt acc); interr_args <- NoArgs; true end else begin let next_num = pred_big_int num and next_acc = mult_big_int acc num in interr_args <- Fact_args (next_num, next_acc, el); false end |NoArgs -> if stack#length > 0 then begin self#backup (); self#evaln 1; let gen_el = stack#pop () in begin match gen_el with |RpcInt el -> if sign_big_int el >= 0 then begin interr_args <- Fact_args (el, unit_big_int, gen_el); false end else begin stack#push gen_el; raise (Invalid_argument "integer factorial requires non-negative argument") end |RpcFloatUnit (el, uu) -> if uu <> Units.empty_unit then begin stack#push gen_el; raise_invalid "cannot compute factorial of dimensioned value" end else begin try let (f, u) = funit_of_float (Gsl.Sf.gamma (el +. 1.0)) in stack#push (RpcFloatUnit (f, u)); true with Gsl.Error.Gsl_exn (err, errstr) -> (stack#push gen_el; raise (Invalid_argument errstr)) end |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "invalid argument")) end end else raise (Invalid_argument "empty stack") |_ -> (* shouldn't hit this point if interface is well-behaved *) self#abort_computation (); false method transpose () = self#check_args 1 "transpose" self#internal_transpose (* matrix transpose *) method private internal_transpose () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcFloatMatrixUnit (el, uu) -> let n, m = (Gsl.Matrix.dims el) in let trans_mat = Gsl.Matrix.create m n in Gsl.Matrix.transpose trans_mat el; stack#push (RpcFloatMatrixUnit (trans_mat, uu)) |RpcComplexMatrixUnit (el, uu) -> let n, m = (Gsl.Matrix_complex.dims el) in let trans_mat = Gsl.Matrix_complex.create m n in Gsl.Matrix_complex.transpose trans_mat el; stack#push (RpcComplexMatrixUnit (trans_mat, uu)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "transpose requires a matrix argument")) method mod_int () = self#check_args 2 "mod" self#internal_mod_int (* mod (remainder) *) method private internal_mod_int () = self#evaln 2; let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in match gen_el1 with |RpcInt el1 -> begin match gen_el2 with |RpcInt el2 -> if eq_big_int el2 zero_big_int then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "division by zero") end else stack#push (RpcInt (mod_big_int el1 el2)) |RpcFloatUnit (ff2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "cannot compute mod of dimensioned values") end else if (abs_float ff2) < 1e9 then let bi_el2 = big_int_of_int (int_of_float ff2) in if eq_big_int bi_el2 zero_big_int then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "division by zero") end else stack#push (RpcInt (mod_big_int el1 bi_el2)) else begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "real argument is too large to convert to integer") end |RpcVariable s -> stack#push gen_el1; stack#push gen_el2; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "mod can only be applied to arguments of type integer or real")) end |RpcFloatUnit (ff1, uu1) -> if uu1 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "cannot compute mod of dimensioned values") end else if (abs_float ff1) < 1e9 then begin let bi_el1 = big_int_of_int (int_of_float ff1) in begin match gen_el2 with |RpcInt el2 -> if eq_big_int el2 zero_big_int then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "division by zero") end else stack#push (RpcInt (mod_big_int bi_el1 el2)) |RpcFloatUnit (ff2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "cannot compute mod of dimensioned values") end else if (abs_float ff2) < 1e9 then let bi_el2 = big_int_of_int (int_of_float ff2) in if eq_big_int bi_el2 zero_big_int then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "division by zero") end else stack#push (RpcInt (mod_big_int bi_el1 bi_el2)) else begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "real argument is too large to convert to integer") end |RpcVariable s -> stack#push gen_el1; stack#push gen_el2; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "mod can only be applied to arguments of type integer or real")) end end else begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "real argument is too large to convert to integer") end |RpcVariable s -> stack#push gen_el1; stack#push gen_el2; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "mod can only be applied to arguments of type integer or real")) method floor () = self#check_args 1 "floor" self#internal_floor (* floor function *) method private internal_floor () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcFloatUnit (el, uu) -> stack#push (RpcFloatUnit (floor el, uu)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "floor can only be applied to real data")) method ceiling () = self#check_args 1 "ceiling" self#internal_ceiling (* ceiling function *) method private internal_ceiling () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcFloatUnit (el, uu) -> stack#push (RpcFloatUnit (ceil el, uu)) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "ceiling can only be applied to real data")) method to_int () = self#check_args 1 "toint" self#internal_to_int (* coerce to an integer type *) method private internal_to_int () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> stack#push gen_el |RpcFloatUnit (ff, uu) -> if (abs_float ff) < 1e9 then stack#push (RpcInt (big_int_of_int (int_of_float ff))) else (stack#push gen_el; raise (Invalid_argument "value is too large to convert to integer")) |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "to_int can only be applied to real data")) method to_float () = self#check_args 1 "toreal" self#internal_to_float (* coerce to a floating-point type *) method private internal_to_float () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcInt el -> let (f, u) = funit_of_float (float_of_big_int el) in stack#push (RpcFloatUnit (f, u)) |RpcFloatMatrixUnit (el, uu) -> let n, m = Gsl.Matrix.dims el in if n = 1 && m = 1 then stack#push (RpcFloatUnit (el.{0, 0}, uu)) else begin stack#push gen_el; raise_invalid "matrix argument of to_float must be 1x1" end |RpcVariable s -> stack#push gen_el; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el; raise (Invalid_argument "to_float can only be applied to integer data")) method solve_linear () = self#check_args 2 "solvelin" self#internal_solve_linear (* solve a linear system Ax = b, with input nxn matrix A and output nx1 * matrix b *) method private internal_solve_linear () = Solvelin.solve_linear stack self#evaln method enter_pi () = self#backup (); let (f, u) = funit_of_float pi in stack#push (RpcFloatUnit (f, u)) method get_display_line line_num = stack#get_display_string line_num modes method get_fullscreen_display line_num = stack#get_fullscreen_display_string line_num modes (* fill in the display string lookup table *) method launch_fill_in_thread () = stack#launch_fill_in_thread () method drop () = self#check_args 1 "drop" self#internal_drop method private internal_drop () = let _ = stack#pop () in () method swap () = self#check_args 2 "swap" self#internal_swap method private internal_swap () = stack#swap () method clear () = self#backup (); for i = 1 to stack#length do let _ = stack#pop () in () done method push (v : orpie_data_t) = self#backup (); stack#push v method echo el_num = if el_num <= stack#length then stack#echo el_num else raise (Invalid_argument "cannot echo nonexistant element") method rolldown i = stack#rolldown i method rollup i = stack#rollup i method delete i = stack#delete i method deleteN i = stack#deleteN i method keep i = stack#keep i method keepN i = stack#keepN i method enter_int i = stack#push (RpcInt i) method enter_float f = stack#push (RpcFloatUnit (f, Units.empty_unit)) method enter_cmpx c = stack#push (RpcComplexUnit (c, Units.empty_unit)) method enter_fmat fm uu = stack#push (RpcFloatMatrixUnit (fm, uu)) method enter_cmat cm uu = stack#push (RpcComplexMatrixUnit (cm, uu)) method enter_const cc uu = stack#push (RpcFloatUnit (cc, uu)) (* evaluate last n variables of the stack (internal use only) *) method private evaln (num : int) = (* grab the last n stack elements into a list *) let rec grab_elements el_lst n = if n > 0 then let next_el = stack#pop () in grab_elements (next_el :: el_lst) (pred n) else el_lst in (* eval the list elements one-by-one; if there * is a lookup failure, push everything back on the stack. *) let rec eval_elements el_lst = match el_lst with |[] -> () |head :: tail -> begin match head with |RpcVariable s -> begin try let data = Hashtbl.find variables s in stack#push data; eval_elements tail with |Not_found -> let err_msg = Printf.sprintf "variable \"%s\" is not bound" s in List.iter stack#push el_lst; raise (Invalid_argument err_msg) end |_ -> stack#push head; eval_elements tail end in let raw_elements = grab_elements [] num in eval_elements raw_elements method eval () = if stack#length > 0 then begin (* the extra push and pop is necessary to be able to back up the * stack *only* when the eval() changes it *) let gen_el = stack#pop () in match gen_el with |RpcVariable s -> stack#push gen_el; self#backup (); self#evaln 1 |_ -> stack#push gen_el end else raise (Invalid_argument "empty stack") method store () = self#check_args 2 "store" self#internal_store (* store in a variable *) method private internal_store () = let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in match gen_el2 with |RpcVariable s -> begin match gen_el1 with |RpcVariable dummy -> stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "cannot store variables inside variables") |_ -> Hashtbl.remove variables s; Hashtbl.add variables s gen_el1 end |_ -> stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "cannot store inside non-variable") method purge () = self#check_args 1 "purge" self#internal_purge (* clear a variable *) method private internal_purge () = let gen_el = stack#pop () in match gen_el with |RpcVariable s -> Hashtbl.remove variables s |_ -> stack#push gen_el; raise (Invalid_argument "only variables can be purged") (* greatest common divisor * This is an interruptible computation, and should be * called multiple times until it returns true. * If computation is aborted, the interface should call * abort_computation() to clean up. *) method gcd () = match interr_args with |Gcd_args (a, b, el1, el2) -> if eq_big_int b zero_big_int then begin stack#push (RpcInt a); interr_args <- NoArgs; true end else begin let a_mod_b = mod_big_int a b in interr_args <- Gcd_args (b, a_mod_b, el1, el2); false end |NoArgs -> if stack#length > 1 then begin self#backup (); self#evaln 2; let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in begin match gen_el1 with |RpcInt a -> begin match gen_el2 with |RpcInt b -> let abs_a = abs_big_int a and abs_b = abs_big_int b in interr_args <- Gcd_args (abs_a, abs_b, gen_el1, gen_el2); false |RpcFloatUnit (ff2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "cannot compute gcd of dimensioned values") end else if (abs_float ff2) < 1e9 then begin let abs_a = abs_big_int a and abs_b = abs_big_int (big_int_of_int (int_of_float ff2)) in interr_args <- Gcd_args (abs_a, abs_b, gen_el1, gen_el2); false end else begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "real argument is too large to convert to integer") end |RpcVariable s -> stack#push gen_el1; stack#push gen_el2; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "gcd requires integer or real arguments") end |RpcFloatUnit (ff1, uu1) -> if uu1 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "cannot compute gcd of dimensioned values") end else if (abs_float ff1) < 1e9 then begin let abs_a = abs_big_int (big_int_of_int (int_of_float ff1)) in begin match gen_el2 with |RpcInt b -> let abs_b = abs_big_int b in interr_args <- Gcd_args (abs_a, abs_b, gen_el1, gen_el2); false |RpcFloatUnit (ff2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "cannot compute gcd of dimensioned values") end else if (abs_float ff2) < 1e9 then begin let abs_b = abs_big_int (big_int_of_int (int_of_float ff2)) in interr_args <- Gcd_args (abs_a, abs_b, gen_el1, gen_el2); false end else begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "real argument is too large to convert to integer") end |RpcVariable s -> stack#push gen_el1; stack#push gen_el2; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "gcd requires integer or real arguments") end end else begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "real argument is too large to convert to integer") end |RpcVariable s -> stack#push gen_el1; stack#push gen_el2; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "gcd requires integer or real arguments") end end else raise (Invalid_argument "insufficient arguments for gcd") |_ -> (* shouldn't hit this point if interface is well-behaved *) self#abort_computation (); false (* least common multiple * This is an interruptible computation, and should be * called multiple times until it returns true. * If computation is aborted, the interface should call * abort_computation() to clean up. *) method lcm () = match interr_args with |Lcm_args (coeff, a, b, el1, el2) -> if eq_big_int b zero_big_int then begin let result = div_big_int coeff a in stack#push (RpcInt result); interr_args <- NoArgs; true end else begin let a_mod_b = mod_big_int a b in interr_args <- Lcm_args (coeff, b, a_mod_b, el1, el2); false end |NoArgs -> if stack#length > 1 then begin self#backup (); self#evaln 2; let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in begin match gen_el1 with |RpcInt a -> begin match gen_el2 with |RpcInt b -> let coeff = mult_big_int a b and abs_a = abs_big_int a and abs_b = abs_big_int b in interr_args <- Lcm_args (coeff, abs_a, abs_b, gen_el1, gen_el2); false |RpcFloatUnit (ff2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "cannot compute lcm of dimensioned values") end else if (abs_float ff2) < 1e9 then begin let bi_b = big_int_of_int (int_of_float ff2) in let abs_a = abs_big_int a and abs_b = abs_big_int bi_b in let coeff = mult_big_int a bi_b in interr_args <- Lcm_args (coeff, abs_a, abs_b, gen_el1, gen_el2); false end else begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "real argument is too large to convert to integer") end |RpcVariable s -> stack#push gen_el1; stack#push gen_el2; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "lcm requires integer or real arguments") end |RpcFloatUnit (ff1, uu1) -> if uu1 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "cannot compute lcm of dimensioned values") end else if (abs_float ff1) < 1e9 then begin let bi_a = big_int_of_int (int_of_float ff1) in let abs_a = abs_big_int bi_a in begin match gen_el2 with |RpcInt b -> let coeff = mult_big_int bi_a b and abs_b = abs_big_int b in interr_args <- Lcm_args (coeff, abs_a, abs_b, gen_el1, gen_el2); false |RpcFloatUnit (ff2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "cannot compute lcm of dimensioned values") end else if (abs_float ff2) < 1e9 then begin let bi_b = big_int_of_int (int_of_float ff2) in let abs_b = abs_big_int bi_b in let coeff = mult_big_int bi_a bi_b in interr_args <- Lcm_args (coeff, abs_a, abs_b, gen_el1, gen_el2); false end else begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "real argument is too large to convert to integer") end |RpcVariable s -> stack#push gen_el1; stack#push gen_el2; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "lcm requires integer or real arguments") end end else begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "real argument is too large to convert to integer") end |RpcVariable s -> stack#push gen_el1; stack#push gen_el2; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "lcm requires integer or real arguments") end end else raise (Invalid_argument "insufficient arguments for lcm") |_ -> (* shouldn't hit this point if interface is well-behaved *) self#abort_computation (); false (* binomial coefficient * For a float argument, this is computed using lngamma in order to avoid * overflow. For an integer argument, jump to an interruptible * exact arithmetic value. *) method binom () = match interr_args with |Binom_args (n, k, num, denom, el1, el2) -> if eq_big_int k zero_big_int then begin let result = div_big_int num denom in stack#push (RpcInt result); interr_args <- NoArgs; true end else begin let nmk = sub_big_int n k in let new_num = mult_big_int num (succ_big_int nmk) in let new_denom = mult_big_int denom k in interr_args <- Binom_args (n, (pred_big_int k), new_num, new_denom, el1, el2); false end |NoArgs -> if stack#length > 1 then begin self#backup (); self#evaln 2; let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in begin match gen_el1 with |RpcInt el1 -> begin match gen_el2 with |RpcInt el2 -> if sign_big_int el1 >= 0 && sign_big_int el2 >= 0 then if ge_big_int el1 el2 then (* save a little computation via a binomial identity *) let nmk = sub_big_int el1 el2 in if lt_big_int nmk el2 then begin interr_args <- Binom_args (el1, nmk, unit_big_int, unit_big_int, gen_el1, gen_el2); false end else begin interr_args <- Binom_args (el1, el2, unit_big_int, unit_big_int, gen_el1, gen_el2); false end else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "first argument to binom must be >= second argument")) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "integer binom requires nonnegative arguments")) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "binom requires either two integer or two real arguments")) end |RpcFloatUnit (el1, uu1) -> begin match gen_el2 with |RpcFloatUnit (el2, uu2) -> if uu1 <> Units.empty_unit || uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "cannot compute binom of dimensioned values" end else begin try let log_coeff = (Gsl.Sf.lngamma (el1 +. 1.0)) -. (Gsl.Sf.lngamma (el2 +. 1.0)) -. (Gsl.Sf.lngamma (el1 -. el2 +. 1.0)) in let (f, u) = funit_of_float (exp log_coeff) in stack#push (RpcFloatUnit (f, u)); true with Gsl.Error.Gsl_exn (err, errstr) -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument errstr)) end |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "binom requires either two integer or two real arguments")) end |RpcVariable s -> stack#push gen_el1; stack#push gen_el2; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "binom can only be applied to real or integer arguments")) end end else raise (Invalid_argument "insufficient arguments for binom") |_ -> (* shouldn't hit this point if interface is well-behaved *) self#abort_computation (); false (* # of permutations of subsets of a population * For a float argument, this is computed using lngamma in order to avoid * overflow. For an integer argument, jump to an interruptible * exact arithmetic value. *) method permutations () = match interr_args with |Perm_args (n, term, partial, el1, el2) -> if eq_big_int n term then begin stack#push (RpcInt partial); interr_args <- NoArgs; true end else begin let new_partial = mult_big_int n partial in interr_args <- Perm_args ((pred_big_int n), term, new_partial, el1, el2); false end |NoArgs -> if stack#length > 1 then begin self#backup (); self#evaln 2; let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in begin match gen_el1 with |RpcInt el1 -> begin match gen_el2 with |RpcInt el2 -> if sign_big_int el1 >= 0 && sign_big_int el2 >= 0 then if ge_big_int el1 el2 then let nmk = sub_big_int el1 el2 in interr_args <- Perm_args (el1, nmk, unit_big_int, gen_el1, gen_el2); false else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "first argument to perm must be >= second argument")) else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "integer perm requires nonnegative arguments")) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "perm requires either two integer or two real arguments")) end |RpcFloatUnit (el1, uu1) -> begin match gen_el2 with |RpcFloatUnit (el2, uu2) -> if uu1 <> Units.empty_unit || uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "cannot compute permutations of dimensioned values" end else begin try let log_perm = (Gsl.Sf.lngamma (el1 +. 1.0)) -. (Gsl.Sf.lngamma (el1 -. el2 +. 1.0)) in let (f, u) = funit_of_float (exp log_perm) in stack#push (RpcFloatUnit (f, u)); true with Gsl.Error.Gsl_exn (err, errstr) -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument errstr)) end |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "perm requires either two integer or two real arguments")) end |RpcVariable s -> stack#push gen_el1; stack#push gen_el2; let err_msg = Printf.sprintf "variable \"%s\" has not been evaluated" s in raise (Invalid_argument err_msg) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "perm can only be applied to real or integer arguments")) end end else raise (Invalid_argument "insufficient arguments for perm") |_ -> (* shouldn't hit this point if interface is well-behaved *) self#abort_computation (); false method total () = self#check_args 1 "total" self#internal_total (* single-variable statistics: total *) method private internal_total () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcFloatMatrixUnit (mat, uu) -> (* multiply on the left by a row of ones *) let n, m = Gsl.Matrix.dims mat in let ones_arr = Array.make n 1.0 in let ones = Gsl.Matrix.of_array ones_arr 1 n in let result = Gsl.Matrix.create 1 m in Gsl.Blas.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:1.0 ~a:ones ~b:mat ~beta:0.0 ~c:result; stack#push (RpcFloatMatrixUnit (result, uu)) |_ -> stack#push gen_el; raise (Invalid_argument "total can only be applied to real matrices") method mean () = self#check_args 1 "mean" self#internal_mean (* single-variable statistics: sample mean *) method private internal_mean () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcFloatMatrixUnit (mat, uu) -> (* multiply on the left by a row of ones, divided by n *) let n, m = Gsl.Matrix.dims mat in let ones_arr = Array.make n (1.0 /. (float_of_int n)) in let ones = Gsl.Matrix.of_array ones_arr 1 n in let result = Gsl.Matrix.create 1 m in Gsl.Blas.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:1.0 ~a:ones ~b:mat ~beta:0.0 ~c:result; stack#push (RpcFloatMatrixUnit (result, uu)) |_ -> stack#push gen_el; raise (Invalid_argument "total can only be applied to real matrices") method sum_squares () = self#check_args 1 "sumsq" self#internal_sum_squares (* single-variable statistics: sum of squares *) method private internal_sum_squares () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcFloatMatrixUnit (mat, uu) -> let n, m = Gsl.Matrix.dims mat in let result = Gsl.Matrix.create 1 m in for col = 0 to pred m do result.{0, col} <- 0.0; for row = 0 to pred n do let squared_el = mat.{row, col} *. mat.{row, col} in result.{0, col} <- result.{0, col} +. squared_el done done; stack#push (RpcFloatMatrixUnit (result, Units.mult uu uu)) |_ -> stack#push gen_el; raise (Invalid_argument "sumsq can only be applied to real matrices") method variance_unbiased () = self#check_args 1 "var" self#internal_variance_unbiased (* single-variable statistics: bias-corrected sample variance *) method private internal_variance_unbiased () = self#evaln 1; let gen_el = stack#peek 1 in match gen_el with |RpcFloatMatrixUnit (mat, uu) -> let n, m = Gsl.Matrix.dims mat in if n >= 2 then begin self#internal_variance_biased (); let n_over_nm1 = (float_of_int n) /. (float_of_int (pred n)) in let (f, u) = funit_of_float n_over_nm1 in stack#push (RpcFloatUnit (f, u)); self#internal_mult () end else raise (Invalid_argument "insufficient matrix rows for unbiased sample variance") |_ -> raise (Invalid_argument "varbias can only be applied to real matrices") method variance_biased () = self#check_args 1 "varbias" self#internal_variance_biased (* single-variable statistics: sample variance (biased) *) method private internal_variance_biased () = self#evaln 1; let gen_el = stack#peek 1 in match gen_el with |RpcFloatMatrixUnit (mat, uu) -> let n, m = Gsl.Matrix.dims mat in let float_n = float_of_int n in (* computes variance as E[X^2] - E[X]^2 *) self#internal_dup (); self#internal_sum_squares (); let (f, u) = funit_of_float float_n in stack#push (RpcFloatUnit (f, u)); self#internal_div (); self#internal_swap (); self#internal_mean (); self#internal_sum_squares (); self#internal_sub () |_ -> raise (Invalid_argument "var can only be applied to real matrices") method standard_deviation_unbiased () = self#check_args 1 "stdev" self#internal_standard_deviation_unbiased (* single-variable statistics: unbiased sample standard deviation *) method private internal_standard_deviation_unbiased () = self#internal_variance_unbiased (); let gen_el = stack#pop () in match gen_el with |RpcFloatMatrixUnit (mat, uu) -> let n, m = Gsl.Matrix.dims mat in let result = Gsl.Matrix.create 1 m in for col = 0 to pred m do result.{0, col} <- sqrt mat.{0, col} done; stack#push (RpcFloatMatrixUnit (result, Units.pow uu 0.5)) |_ -> () method standard_deviation_biased () = self#check_args 1 "stdevbias" self#internal_standard_deviation_biased (* single-variable statistics: unbiased sample standard deviation *) method private internal_standard_deviation_biased () = self#internal_variance_biased (); let gen_el = stack#pop () in match gen_el with |RpcFloatMatrixUnit (mat, uu) -> let n, m = Gsl.Matrix.dims mat in let result = Gsl.Matrix.create 1 m in for col = 0 to pred m do result.{0, col} <- sqrt mat.{0, col} done; stack#push (RpcFloatMatrixUnit (result, Units.pow uu 0.5)) |_ -> () method minimum () = self#check_args 1 "min" self#internal_minimum (* single-variable statistics: minimum of set *) method private internal_minimum () = self#min_or_max true () method maximum () = self#check_args 1 "max" self#internal_maximum (* single-variable statistics: maximum of set *) method private internal_maximum () = self#min_or_max false () method private min_or_max operation_is_min () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcFloatMatrixUnit (mat, uu) -> let n, m = Gsl.Matrix.dims mat in let result = Gsl.Matrix.create 1 m in for col = 0 to pred m do result.{0, col} <- mat.{0, col}; for row = 1 to pred n do if operation_is_min then if mat.{row, col} < result.{0, col} then result.{0, col} <- mat.{row, col} else () else if mat.{row, col} > result.{0, col} then result.{0, col} <- mat.{row, col} else () done done; stack#push (RpcFloatMatrixUnit (result, uu)) |_ -> stack#push gen_el; raise (Invalid_argument "min can only be applied to real matrices") method upper_tail_prob_normal () = self#check_args 3 "utpn" self#internal_upper_tail_prob_normal method private internal_upper_tail_prob_normal () = self#evaln 3; let gen_el3 = stack#pop () in let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in let get_float_args gen_el = match gen_el with |RpcInt i_el -> funit_of_float (float_of_big_int i_el) |RpcFloatUnit (el, uu) -> (el, uu) |_ -> stack#push gen_el1; stack#push gen_el2; stack#push gen_el3; raise (Invalid_argument "utpn requires real scalar arguments") in let (mean_orig, mean_units) = get_float_args gen_el1 and (var_orig, var_units) = get_float_args gen_el2 and (cutoff, cutoff_units) = get_float_args gen_el3 in try (* check that units are consistent *) let mean = mean_orig *. (Units.conversion_factor mean_units cutoff_units !Rcfile.unit_table) in let var = var_orig *. (Units.conversion_factor var_units cutoff_units !Rcfile.unit_table) in if var <= 0.0 then begin stack#push gen_el1; stack#push gen_el2; stack#push gen_el3; raise (Invalid_argument "variance argument to utpn must be positive") end else begin let arg = (cutoff -. mean) /. (sqrt (2.0 *. var)) in let (f, u) = funit_of_float arg in stack#push (RpcFloatUnit (f, u)); self#internal_erfc (); stack#push (RpcFloatUnit (0.5, cutoff_units)); self#internal_mult () end with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; stack#push gen_el3; raise_invalid s (* random float between 0 and 1 *) method rand () = self#backup (); let (f, u) = funit_of_float (Random.float 1.0) in stack#push (RpcFloatUnit (f, u)) (* standardize units *) method standardize_units () = self#check_args 1 "ustand" self#internal_standardize_units method private internal_standardize_units () = self#evaln 1; let gen_el = stack#pop () in let get_std u = Units.standardize_units u !Rcfile.unit_table in match gen_el with |RpcFloatUnit (el, uu) -> let std = get_std uu in stack#push (RpcFloatUnit (el *. std.Units.coeff, std.Units.comp_units)) |RpcComplexUnit (el, uu) -> let std = get_std uu in let c_coeff = c_of_f std.Units.coeff in stack#push (RpcComplexUnit (Complex.mul el c_coeff, std.Units.comp_units)) |RpcFloatMatrixUnit (el, uu) -> let std = get_std uu in let result = Gsl.Matrix.copy el in Gsl.Matrix.scale result std.Units.coeff; stack#push (RpcFloatMatrixUnit (result, std.Units.comp_units)) |RpcComplexMatrixUnit (el, uu) -> let std = get_std uu in let c_coeff = c_of_f std.Units.coeff in let result = Gsl.Matrix_complex.copy el in Gsl.Matrix_complex.scale result c_coeff; stack#push (RpcComplexMatrixUnit (result, std.Units.comp_units)) |_ -> stack#push gen_el (* obtain the magnitude of a dimensioned value *) method unit_value () = self#check_args 1 "uvalue" self#internal_unit_value method private internal_unit_value () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcFloatUnit (el, uu) -> stack#push (RpcFloatUnit (el, Units.empty_unit)) |RpcComplexUnit (el, uu) -> stack#push (RpcComplexUnit (el, Units.empty_unit)) |RpcFloatMatrixUnit (el, uu) -> stack#push (RpcFloatMatrixUnit (el, Units.empty_unit)) |RpcComplexMatrixUnit (el, uu) -> stack#push (RpcComplexMatrixUnit (el, Units.empty_unit)) |_ -> stack#push gen_el (* obtain the magnitude of a dimensioned value *) method convert_units () = self#check_args 2 "uconvert" self#internal_convert_units method private internal_convert_units () = self#evaln 1; let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in match gen_el2 with |RpcFloatUnit (el2, uu2) -> begin match gen_el1 with |RpcFloatUnit (el1, uu1) -> begin try let conv = Units.conversion_factor uu1 uu2 !Rcfile.unit_table in stack#push (RpcFloatUnit (el1 *. conv, uu2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s end |RpcComplexUnit (el1, uu1) -> begin try let c_conv = { Complex.re = Units.conversion_factor uu1 uu2 !Rcfile.unit_table; Complex.im = 0.0 } in stack#push (RpcComplexUnit (Complex.mul el1 c_conv, uu2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s end |RpcFloatMatrixUnit (el1, uu1) -> begin try let conv = Units.conversion_factor uu1 uu2 !Rcfile.unit_table in let result = Gsl.Matrix.copy el1 in Gsl.Matrix.scale result conv; stack#push (RpcFloatMatrixUnit (result, uu2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s end |RpcComplexMatrixUnit (el1, uu1) -> begin try let conv = { Complex.re = Units.conversion_factor uu1 uu2 !Rcfile.unit_table; Complex.im = 0.0 } in let result = Gsl.Matrix_complex.copy el1 in Gsl.Matrix_complex.scale result conv; stack#push (RpcComplexMatrixUnit (result, uu2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s end |_ -> stack#push gen_el1; stack#push gen_el2; raise_invalid "cannot convert units for this data type" end |_ -> stack#push gen_el1; stack#push gen_el2; raise_invalid "unit conversion target must be real-valued" (* trace of a matrix *) method trace () = self#check_args 1 "trace" self#internal_trace method private internal_trace () = self#evaln 1; let gen_el = stack#pop () in match gen_el with |RpcFloatMatrixUnit (el, uu) -> let n, m = Gsl.Matrix.dims el in if n = m then begin let result = ref 0.0 in for i = 0 to pred n do result := !result +. el.{i, i} done; stack#push (RpcFloatUnit (!result, uu)) end else begin stack#push gen_el; raise_invalid "argument of trace must be a square matrix" end |RpcComplexMatrixUnit (el, uu) -> let n, m = Gsl.Matrix_complex.dims el in if n = m then begin let result = ref Complex.zero in for i = 0 to pred n do result := Complex.add !result el.{i, i} done; stack#push (RpcComplexUnit (!result, uu)) end else begin stack#push gen_el; raise_invalid "argument of trace must be a square matrix" end |_ -> stack#push gen_el; raise_invalid "argument of trace must be a square matrix" (* method print_stack () = let print_el line_num el = Printf.printf "%2d: %s\n" line_num el in for i = stack#length downto 1 do print_el i (stack#get_display_line i modes) done *) end;; (* arch-tag: DO_NOT_CHANGE_548916d4-da42-49b4-8941-c0d42306f1b7 *) orpie-release-1.6.0/src/orpie/rpc_stack.ml000066400000000000000000001123631334120314700204660ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* rpc_stack.ml -- implementation of internal calculator stack that holds * multiple data types * * The stack is implemented as a dynamically-allocated array. This approach * enables non-standard stack operations such as random access and cyclic rotation of * elements. * * Each stack element contains both data and string (option) representations * of the data. There can be multiple string representations, corresponding to * line-oriented and fullscreen displays, four different bases, two * different angle modes, and two different complex representations. The * string representations are created immediately when needed; if not * immediately needed, they will eventually be precomputed and filled in in by a * background thread (unless conserve_memory = false). * * Note: arguably, the rendering functions belong with the interface code * rather than with the stack code. I may make this change at some * point, to better abstract the interface away from the underlying * calculator object. *) open Big_int_str open Printf exception Stack_error of string (* orpie_data_t values are returned by pop(), but the values * on the stack are in stack_data_t format *) type orpie_data_t = | RpcInt of Big_int.big_int | RpcFloatUnit of float * Units.unit_set_t | RpcComplexUnit of Complex.t * Units.unit_set_t | RpcFloatMatrixUnit of Gsl.Matrix.matrix * Units.unit_set_t | RpcComplexMatrixUnit of Gsl.Matrix_complex.matrix * Units.unit_set_t | RpcVariable of string type stack_int_string_t = {mutable i_bin_line : string option; mutable i_oct_line : string option; mutable i_dec_line : string option; mutable i_hex_line : string option; mutable i_bin_fs : string option; mutable i_oct_fs : string option; mutable i_dec_fs : string option; mutable i_hex_fs : string option} type stack_float_unit_string_t = {mutable fu : string option} type stack_cmpx_unit_string_t = {mutable c_rect : string option; mutable c_pol_rad : string option; mutable c_pol_deg : string option} type stack_fmat_unit_string_t = {mutable fmat_line : string option; mutable fmat_fs : string option} type stack_cmat_string_t = {mutable cmat_rect_line : string option; mutable cmat_pol_rad_line : string option; mutable cmat_pol_deg_line : string option; mutable cmat_rect_fs : string option; mutable cmat_pol_rad_fs : string option; mutable cmat_pol_deg_fs : string option} type stack_var_string_t = {mutable v_line : string option; mutable v_fs : string option} (* internal storage format of stack elements *) type stack_data_t = | StackInt of Big_int.big_int * stack_int_string_t | StackFloatUnit of float * Units.unit_set_t * stack_float_unit_string_t | StackComplexUnit of Complex.t * Units.unit_set_t * stack_cmpx_unit_string_t | StackFloatMatrixUnit of Gsl.Matrix.matrix * Units.unit_set_t * stack_fmat_unit_string_t | StackComplexMatrixUnit of Gsl.Matrix_complex.matrix * Units.unit_set_t * stack_cmat_string_t | StackVariable of string * stack_var_string_t let raise_invalid s = raise (Invalid_argument s) let orpie_data_of_stack_data (sd : stack_data_t) = match sd with |StackInt (ii, _) -> RpcInt ii |StackFloatUnit (ff, uu, _) -> RpcFloatUnit (ff, uu) |StackComplexUnit (cc, uu, _) -> RpcComplexUnit (cc, uu) |StackFloatMatrixUnit (fm, uu, _) -> RpcFloatMatrixUnit (fm, uu) |StackComplexMatrixUnit (cm, uu, _) -> RpcComplexMatrixUnit (cm, uu) |StackVariable (vv, _) -> RpcVariable vv let stack_data_of_orpie_data (od : orpie_data_t) = match od with |RpcInt ii -> StackInt (ii, {i_bin_line = None; i_oct_line = None; i_dec_line = None; i_hex_line = None; i_bin_fs = None; i_oct_fs = None; i_dec_fs = None; i_hex_fs = None}) |RpcFloatUnit (ff, uu) -> StackFloatUnit (ff, uu, {fu = None}) |RpcComplexUnit (cc, uu) -> StackComplexUnit (cc, uu, {c_rect = None; c_pol_rad = None; c_pol_deg = None}) |RpcFloatMatrixUnit (fm, uu) -> StackFloatMatrixUnit (fm, uu, {fmat_line = None; fmat_fs = None}) |RpcComplexMatrixUnit (cm, uu) -> StackComplexMatrixUnit (cm, uu, {cmat_rect_line = None; cmat_pol_rad_line = None; cmat_pol_deg_line = None; cmat_rect_fs = None; cmat_pol_rad_fs = None; cmat_pol_deg_fs = None}) |RpcVariable vv -> StackVariable (vv, {v_line = None; v_fs = None}) let funit_of_float (ff : float) : float * Units.unit_set_t = (ff, Units.empty_unit) let cunit_of_cpx cc = (cc, Units.empty_unit) let unorm uu = (1.0, uu) let c_of_f ff = {Complex.re = ff; Complex.im = 0.0} let has_units uu = uu <> Units.empty_unit type display_mode_t = | Line | Fullscreen type angle_mode = | Rad | Deg type base_mode = | Bin | Oct | Hex | Dec type complex_mode = | Rect | Polar type calculator_modes = {angle : angle_mode; base : base_mode; complex : complex_mode} let size_inc = 100 let pi = 3.14159265358979323846 class rpc_stack conserve_memory_in = object(self) val mutable len = 0 val mutable stack = let (f0, u0) = funit_of_float 0.0 in Array.make size_inc (stack_data_of_orpie_data (RpcFloatUnit (f0, u0))) val conserve_memory = conserve_memory_in val render_stack = Stack.create () method length = len method get_state () = (stack, len) method set_state (s, l) = stack <- s; len <- l method backup () = let b_stack = Array.copy stack in {< stack = b_stack >} method private expand_size () = (* allocate a new stack if necessary *) if len >= Array.length stack then begin let new_stack = Array.make ((Array.length stack) + size_inc) (stack_data_of_orpie_data (RpcFloatUnit (0.0, Units.empty_unit))) in Array.blit stack 0 new_stack 0 (Array.length stack); stack <- new_stack end else () method push (v : orpie_data_t) = self#expand_size (); let new_el = stack_data_of_orpie_data v in stack.(len) <- new_el; len <- len + 1; if conserve_memory then () else Stack.push new_el render_stack method pop () = (* compact stack memory by size_inc whenever we have 2 * size_inc * elements free *) if len < (Array.length stack) - 2 * size_inc then let new_stack = Array.sub stack 0 ((Array.length stack) - size_inc) in stack <- new_stack else (); let pop_result = if len > 0 then begin len <- len - 1; orpie_data_of_stack_data stack.(len) end else raise (Stack_error "cannot pop empty stack"); in pop_result (* duplicate the top stack element *) method dup () = self#expand_size (); if len > 0 then begin stack.(len) <- stack.(pred len); len <- succ len end else raise (Stack_error "cannot dup with empty stack") (* swap the top two stack elements *) method swap () = if len > 1 then begin let temp = ref stack.(pred len) in stack.(pred len) <- stack.(len - 2); stack.(len - 2) <- !temp end else raise (Stack_error "cannot swap with less than two elements") (* copy a stack element to the top of the stack *) method echo el_num = self#expand_size (); if el_num <= len then begin let actual_el_num = len - el_num in stack.(len) <- stack.(actual_el_num); len <- succ len end else raise (Invalid_argument "cannot echo nonexistant element") (* cyclically roll all stack elements downward (i.e. towards the top * of the stack), starting below element number 'num' (inclusive). *) method rolldown num = if num <= len then let temp = stack.(pred len) in for i = pred len downto len - num + 1 do stack.(i) <- stack.(pred i) done; stack.(len - num) <- temp else raise (Stack_error "insufficient stack elements"); (* cyclically roll all stack elements upward (i.e. away from the top * of the stack), starting below element number 'num' (inclusive). *) method rollup num = if num <= len then let temp = stack.(len - num) in for i = len - num to len - 2 do stack.(i) <- stack.(succ i) done; stack.(pred len) <- temp else raise (Stack_error "insufficient stack elements"); (* delete a particular element *) method delete num = if num <= len then (for i = (len - num) to len do stack.(i) <- stack.(succ i) done; len <- (pred len)) else raise (Stack_error "insufficient stack elements"); (* delete all elements below level N *) method deleteN num = if num <= len then len <- len - num else raise (Stack_error "insufficient stack elements"); (* keep only a particular stack element *) method keep num = if num <= len then (stack.(0) <- stack.(len - num); len <- 1) else raise (Stack_error "insufficient stack elements"); (* keep all elements below the selected (inclusive) *) method keepN num = if num <= len then begin for i = 0 to num - 1 do stack.(i) <- stack.(i + len - num) done; len <- num end else raise (Stack_error "insufficient stack elements"); (* return a particular stack element without removing it from the stack *) (* element 1 points to the top of the stack *) method peek el_num = let peek_result = if el_num <= len then let actual_el_num = len - el_num in orpie_data_of_stack_data stack.(actual_el_num) else let s = Printf.sprintf "cannot access nonexistant stack element %d" el_num in raise (Stack_error s); in peek_result method private get_display_string_wrap disp_mode line_num calc_modes = if line_num > 0 then if line_num <= len then (* this is the actual index into the array *) let index = len - line_num in self#lookup_or_create_string disp_mode calc_modes index else (* line_num > len *) "" else (* line_num <= 0 *) raise (Stack_error ("cannot display nonexistent stack element " ^ (string_of_int line_num))) (* lookup (or create) a line-oriented display string *) method get_display_string line_num calc_modes = self#get_display_string_wrap Line line_num calc_modes (* lookup (or create) a fullscreen-oriented display string *) method get_fullscreen_display_string line_num calc_modes = self#get_display_string_wrap Fullscreen line_num calc_modes (* perform a table lookup to obtain the string representation of * the desired stack element. If the table lookup fails, then create * the representation. *) method private lookup_or_create_string disp_mode calc_modes index = let stack_el = stack.(index) in let lookup_result = begin match stack_el with |StackInt (ii, ii_str) -> let lookup_int_str record = begin match record with |None -> self#create_int_string disp_mode calc_modes ii ii_str |Some ss -> ss end in begin match disp_mode with |Line -> begin match calc_modes.base with |Bin -> lookup_int_str ii_str.i_bin_line |Oct -> lookup_int_str ii_str.i_oct_line |Dec -> lookup_int_str ii_str.i_dec_line |Hex -> lookup_int_str ii_str.i_hex_line end |Fullscreen -> begin match calc_modes.base with |Bin -> lookup_int_str ii_str.i_bin_fs |Oct -> lookup_int_str ii_str.i_oct_fs |Dec -> lookup_int_str ii_str.i_dec_fs |Hex -> lookup_int_str ii_str.i_hex_fs end end |StackFloatUnit (ff, uu, fu_str) -> begin match fu_str.fu with |None -> self#create_float_unit_string ff uu fu_str |Some ss -> ss end |StackComplexUnit (cc, uu, cc_str) -> let lookup_cmpx_str record = begin match record with |None -> self#create_cmpx_unit_string calc_modes cc uu cc_str |Some ss -> ss end in begin match calc_modes.complex with |Rect -> lookup_cmpx_str cc_str.c_rect |Polar -> begin match calc_modes.angle with |Rad -> lookup_cmpx_str cc_str.c_pol_rad |Deg -> lookup_cmpx_str cc_str.c_pol_deg end end |StackFloatMatrixUnit (fm, uu, fm_str) -> begin match disp_mode with |Line -> begin match fm_str.fmat_line with |None -> self#create_fmat_unit_string disp_mode fm uu fm_str |Some ss -> ss end |Fullscreen -> begin match fm_str.fmat_fs with |None -> self#create_fmat_unit_string disp_mode fm uu fm_str |Some ss -> ss end end |StackComplexMatrixUnit (cm, uu, cm_str) -> let lookup_cmat_str record = begin match record with |None -> self#create_cmat_unit_string disp_mode calc_modes cm uu cm_str |Some ss -> ss end in begin match disp_mode with |Line -> begin match calc_modes.complex with |Rect -> lookup_cmat_str cm_str.cmat_rect_line |Polar -> begin match calc_modes.angle with |Rad -> lookup_cmat_str cm_str.cmat_pol_rad_line |Deg -> lookup_cmat_str cm_str.cmat_pol_deg_line end end |Fullscreen -> begin match calc_modes.complex with |Rect -> lookup_cmat_str cm_str.cmat_rect_fs |Polar -> begin match calc_modes.angle with |Rad -> lookup_cmat_str cm_str.cmat_pol_rad_fs |Deg -> lookup_cmat_str cm_str.cmat_pol_deg_fs end end end |StackVariable (vv, vv_str) -> begin match disp_mode with |Line -> begin match vv_str.v_line with |None -> self#create_var_string disp_mode vv vv_str |Some ss -> ss end |Fullscreen -> begin match vv_str.v_fs with |None -> self#create_var_string disp_mode vv vv_str |Some ss -> ss end end end; in lookup_result (* render all string representations of a particular stack element. *) method private render_all_strings stack_el = match stack_el with |StackInt (ii, ii_str) -> let lookup_int_str d_mode c_mode record = begin match record with |None -> let _ = self#create_int_string d_mode c_mode ii ii_str in () |Some ss -> () end in lookup_int_str Line {angle = Rad; base = Dec; complex = Rect} ii_str.i_dec_line; lookup_int_str Line {angle = Rad; base = Bin; complex = Rect} ii_str.i_bin_line; lookup_int_str Line {angle = Rad; base = Oct; complex = Rect} ii_str.i_oct_line; lookup_int_str Line {angle = Rad; base = Hex; complex = Rect} ii_str.i_hex_line (* the remaining integer strings will get filled in as * side-effects of the previous *) |StackFloatUnit (ff, uu, fu_str) -> begin match fu_str.fu with |None -> let _ = self#create_float_unit_string ff uu fu_str in () |Some ss -> () end |StackComplexUnit (cc, uu, cc_str) -> let lookup_cmpx_str c_mode record = begin match record with |None -> let _ = self#create_cmpx_unit_string c_mode cc uu cc_str in () |Some ss -> () end in lookup_cmpx_str {angle = Rad; base = Dec; complex = Rect} cc_str.c_rect; lookup_cmpx_str {angle = Rad; base = Dec; complex = Polar} cc_str.c_pol_rad; lookup_cmpx_str {angle = Deg; base = Dec; complex = Polar} cc_str.c_pol_deg |StackFloatMatrixUnit (fm, uu, fm_str) -> begin match fm_str.fmat_line with |None -> let _ = self#create_fmat_unit_string Line fm uu fm_str in () |Some ss -> () end; begin match fm_str.fmat_fs with |None -> let _ = self#create_fmat_unit_string Fullscreen fm uu fm_str in () |Some ss -> () end |StackComplexMatrixUnit (cm, uu, cm_str) -> let lookup_cmat_str d_mode c_mode record = begin match record with |None -> let _ = self#create_cmat_unit_string d_mode c_mode cm uu cm_str in () |Some ss -> () end in lookup_cmat_str Line {angle = Rad; base = Dec; complex = Rect} cm_str.cmat_rect_line; lookup_cmat_str Line {angle = Rad; base = Dec; complex = Polar} cm_str.cmat_pol_rad_line; lookup_cmat_str Line {angle = Deg; base = Dec; complex = Polar} cm_str.cmat_pol_deg_line; lookup_cmat_str Fullscreen {angle = Rad; base = Dec; complex = Rect} cm_str.cmat_rect_fs; lookup_cmat_str Fullscreen {angle = Rad; base = Dec; complex = Polar} cm_str.cmat_pol_rad_fs; lookup_cmat_str Fullscreen {angle = Deg; base = Dec; complex = Polar} cm_str.cmat_pol_deg_fs |StackVariable (vv, vv_str) -> begin match vv_str.v_line with |None -> let _ = self#create_var_string Line vv vv_str in () |Some ss -> () end (* fullscreen is filled in as side-effect of previous *) (* generate the string representation for an integer, taking into * account the desired display mode and base. Fullscreen and line * representations are computed concurrently because they share * most of the computation. *) method private create_int_string disp_mode calc_modes ii ii_str = match calc_modes.base with |Bin -> let s = string_of_big_int_base ii 2 in let line = "# " ^ s ^ "`b" and fs = "#" ^ s ^ "`b" in if conserve_memory then () else begin ii_str.i_bin_line <- Some line; ii_str.i_bin_fs <- Some fs end; begin match disp_mode with |Line -> line |Fullscreen -> fs end |Oct -> let s = string_of_big_int_base ii 8 in let line = "# " ^ s ^ "`o" and fs = "#" ^ s ^ "`o" in if conserve_memory then () else begin ii_str.i_oct_line <- Some line; ii_str.i_oct_fs <- Some fs end; begin match disp_mode with |Line -> line |Fullscreen -> fs end |Hex -> let s = string_of_big_int_base ii 16 in let line = "# " ^ s ^ "`h" and fs = "#" ^ s ^ "`h" in if conserve_memory then () else begin ii_str.i_hex_line <- Some line; ii_str.i_hex_fs <- Some fs end; begin match disp_mode with |Line -> line |Fullscreen -> fs end |Dec -> let s = string_of_big_int_base_gen ii 10 in let line = "# " ^ s ^ "`d" and fs = "#" ^ s ^ "`d" in if conserve_memory then () else begin ii_str.i_dec_line <- Some line; ii_str.i_dec_fs <- Some fs end; begin match disp_mode with |Line -> line |Fullscreen -> fs end (* generate a string representation for a floating-point value with a unit *) method private create_float_unit_string ff uu fu_str = let s = if uu <> Units.empty_unit then sprintf "%.15g_%s" ff (Units.string_of_units uu) else sprintf "%.15g" ff in if conserve_memory then () else fu_str.fu <- Some s; s (* generate a string representation for a complex value, taking * into account the representation mode and angle mode of the calc *) method private create_cmpx_unit_string calc_modes cc uu cc_str = let append_units ss = if uu <> Units.empty_unit then ss ^ "_" ^ (Units.string_of_units uu) else ss in match calc_modes.complex with |Rect -> let s = append_units (sprintf "(%.15g, %.15g)" cc.Complex.re cc.Complex.im) in if conserve_memory then () else cc_str.c_rect <- Some s; s |Polar -> let r = sqrt (cc.Complex.re *. cc.Complex.re +. cc.Complex.im *. cc.Complex.im) and theta = atan2 cc.Complex.im cc.Complex.re in begin match calc_modes.angle with |Rad -> let s = append_units (sprintf "(%.15g <%.15g)" r theta) in if conserve_memory then () else cc_str.c_pol_rad <- Some s; s |Deg -> let s = append_units (sprintf "(%.15g <%.15g)" r (180.0 /. pi *. theta)) in if conserve_memory then () else cc_str.c_pol_deg <- Some s; s end (* generate a string representation for a floating-point matrix, * taking into account the display mode. *) method private create_fmat_unit_string disp_mode fm uu fm_str = let append_units ss = if has_units uu then ss ^ "_" ^ Units.string_of_units uu else ss in match disp_mode with |Line -> let s = (* looks like [[ a11, a12 ][ a21, a22 ]] *) let rows, cols = (Gsl.Matrix.dims fm) in let initial_string = "[" in let line = ref initial_string in for n = 0 to rows - 1 do line := !line ^ "[ "; for m = 0 to cols - 2 do line := !line ^ (sprintf "%.15g, " fm.{n, m}) done; line := !line ^ (sprintf "%.15g ]" fm.{n, cols-1}) done; line := !line ^ "]"; !line in let ss = append_units s in if conserve_memory then () else fm_str.fmat_line <- Some ss; ss |Fullscreen -> let s = (* looks like [[ a11, a12 ] * [ a21, a22 ]] * and the columns are aligned. *) let rows, cols = (Gsl.Matrix.dims fm) in (* first get the maximum field width for each column *) let max_width = Array.make cols 0 in for m = 0 to pred cols do for n = 0 to pred rows do let dummy_string = sprintf "%-20.15g" fm.{n, m} in let ds_len = String.length dummy_string in if ds_len > max_width.(m) then max_width.(m) <- ds_len else () done done; (* now use the maximum field widths to align the columns * during string creation *) let initial_string = "[" in let line = ref initial_string in for n = 0 to rows - 1 do line := !line ^ "[ "; for m = 0 to cols - 2 do line := !line ^ (sprintf "%*.15g, " max_width.(m) fm.{n, m}) done; line := !line ^ (sprintf "%*.15g ]" max_width.(cols-1) fm.{n, cols-1}); if n < pred rows then line := !line ^ "\n " else () done; line := !line ^ "]"; !line in let ss = append_units s in if conserve_memory then () else fm_str.fmat_fs <- Some ss; ss (* generate a string representation for a complex matrix, * taking into account the display mode. *) method private create_cmat_unit_string disp_mode calc_modes cm uu cm_str = let append_units ss = if uu <> Units.empty_unit then ss ^ "_" ^ Units.string_of_units uu else ss in match disp_mode with |Line -> let s = (* looks like [[ (a11re, a11im), (a12re, a12im) ][ (a21re, a21im), (a22re, a22im) ] *) let rows, cols = (Gsl.Matrix_complex.dims cm) in let initial_string = "[" in let line = ref initial_string in for n = 0 to rows - 1 do line := !line ^ "[ "; for m = 0 to cols - 2 do match calc_modes.complex with |Rect -> line := !line ^ (sprintf "(%.15g, %.15g), " cm.{n, m}.Complex.re cm.{n, m}.Complex.im) |Polar -> let rr = cm.{n, m}.Complex.re and ii = cm.{n, m}.Complex.im in let r = sqrt (rr *. rr +. ii *. ii) and theta = atan2 ii rr in begin match calc_modes.angle with |Rad -> line := !line ^ (sprintf "(%.15g <%.15g), " r theta) |Deg -> line := !line ^ (sprintf "(%.15g <%.15g), " r (180.0 /. pi *. theta)) end done; match calc_modes.complex with |Rect -> line := !line ^ (sprintf "(%.15g, %.15g) ]" cm.{n, cols-1}.Complex.re cm.{n, cols-1}.Complex.im) |Polar -> let rr = cm.{n, cols-1}.Complex.re and ii = cm.{n, cols-1}.Complex.im in let r = sqrt (rr *. rr +. ii *. ii) and theta = atan2 ii rr in begin match calc_modes.angle with |Rad -> line := !line ^ (sprintf "(%.15g <%.15g) ]" r theta) |Deg -> line := !line ^ (sprintf "(%.15g <%.15g) ]" r (180.0 /. pi *. theta)) end done; line := !line ^ "]"; !line in let ss = append_units s in if conserve_memory then () else begin match calc_modes.complex with |Rect -> cm_str.cmat_rect_line <- Some ss; |Polar -> begin match calc_modes.angle with |Rad -> cm_str.cmat_pol_rad_line <- Some ss |Deg -> cm_str.cmat_pol_deg_line <- Some ss end end; ss |Fullscreen -> let s = (* looks like [[ (a11re, a11im), (a12re, a12im) ] * [ (a21re, a21im), (a22re, a22im) ] * with properly aligned columns *) let rows, cols = (Gsl.Matrix_complex.dims cm) in (* first get the maximum field width for each column *) let max_width = Array.make_matrix cols 2 0 in for m = 0 to pred cols do for n = 0 to pred rows do match calc_modes.complex with |Rect -> let dummy_re = sprintf "%-20.15g" cm.{n, m}.Complex.re in let dr_len = String.length dummy_re in if dr_len > max_width.(m).(0) then max_width.(m).(0) <- dr_len else (); let dummy_im = sprintf "%-20.15g" cm.{n, m}.Complex.im in let di_len = String.length dummy_im in if di_len > max_width.(m).(1) then max_width.(m).(1) <- di_len else () |Polar -> let rr = cm.{n, m}.Complex.re and ii = cm.{n, m}.Complex.im in let r = sqrt (rr *. rr +. ii *. ii) and theta = atan2 ii rr in let dummy_r = sprintf "%-20.15g" r in let r_len = String.length dummy_r in if r_len > max_width.(m).(0) then max_width.(m).(0) <- r_len else (); let dummy_theta = match calc_modes.angle with |Rad -> sprintf "%-20.15g" theta |Deg -> sprintf "%-20.15g" (180.0 /. pi *. theta) in let theta_len = String.length dummy_theta in if theta_len > max_width.(m).(1) then max_width.(m).(1) <- theta_len else (); done done; (* now use the maximum field widths to align the columns * during string creation *) let initial_string = "[" in let line = ref initial_string in for n = 0 to rows - 1 do line := !line ^ "[ "; for m = 0 to cols - 2 do match calc_modes.complex with |Rect -> line := !line ^ (sprintf "(%*.15g, %*.15g), " max_width.(m).(0) cm.{n, m}.Complex.re max_width.(m).(1) cm.{n, m}.Complex.im) |Polar -> begin let rr = cm.{n, m}.Complex.re and ii = cm.{n, m}.Complex.im in let r = sqrt (rr *. rr +. ii *. ii) and theta = atan2 ii rr in match calc_modes.angle with |Rad -> line := !line ^ (sprintf "(%*.15g <%*.15g), " max_width.(m).(0) r max_width.(m).(1) theta) |Deg -> line := !line ^ (sprintf "(%*.15g <%*.15g), " max_width.(m).(0) r max_width.(m).(1) (180.0 /. pi *. theta)) end done; begin match calc_modes.complex with |Rect -> line := !line ^ (sprintf "(%*.15g, %*.15g) ]" max_width.(cols-1).(0) cm.{n, cols-1}.Complex.re max_width.(cols-1).(1) cm.{n, cols-1}.Complex.im) |Polar -> begin let rr = cm.{n, cols-1}.Complex.re and ii = cm.{n, cols-1}.Complex.im in let r = sqrt (rr *. rr +. ii *. ii) and theta = atan2 ii rr in match calc_modes.angle with |Rad -> line := !line ^ (sprintf "(%*.15g <%*.15g) ]" max_width.(cols-1).(0) r max_width.(cols-1).(1) theta) |Deg -> line := !line ^ (sprintf "(%*.15g <%*.15g) ]" max_width.(cols-1).(0) r max_width.(cols-1).(1) (180.0 /. pi *. theta)) end end; if n < pred rows then line := !line ^ "\n " else () done; line := !line ^ "]"; !line in let ss = append_units s in if conserve_memory then () else begin match calc_modes.complex with |Rect -> cm_str.cmat_rect_fs <- Some ss; |Polar -> begin match calc_modes.angle with |Rad -> cm_str.cmat_pol_rad_fs <- Some ss |Deg -> cm_str.cmat_pol_deg_fs <- Some ss end end; ss (* generate a string representation for a complex matrix, * taking into account the display mode. *) method private create_var_string disp_mode vv vv_str = let line = "@ " ^ vv and fs = "@" ^ vv in if conserve_memory then () else begin vv_str.v_line <- Some line; vv_str.v_fs <- Some fs end; match disp_mode with |Line -> line |Fullscreen -> fs method launch_fill_in_thread () = let _ = Thread.create self#fill_in_all_strings () in () (* fill in any unknown string representations from the stack *) method private fill_in_all_strings () = try while true do let unrendered_el = Stack.pop render_stack in self#render_all_strings unrendered_el done with Stack.Empty -> () end (* arch-tag: DO_NOT_CHANGE_59b80e87-dfde-4203-a7a2-8e1f95813151 *) orpie-release-1.6.0/src/orpie/solvelin.ml000066400000000000000000000142221334120314700203430ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) open Rpc_stack (* solve a linear system Ax = b, with input nxn matrix A and output nx1 * matrix b *) let solve_linear (stack : rpc_stack) (evaln : int -> unit) = evaln 2; let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in match gen_el1 with |RpcFloatMatrixUnit (el1, u1) -> begin match gen_el2 with |RpcFloatMatrixUnit (el2, u2) -> let n1, m1 = Gsl.Matrix.dims el1 in if n1 <> m1 then (stack#push gen_el2; stack#push gen_el1; raise (Invalid_argument "multiplier matrix must be square")) else let n2, m2 = Gsl.Matrix.dims el2 in if m2 <> 1 then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "resultant matrix must be a column") end else if n2 <> m1 then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument ("dimensions of multiplier and " ^ "resultant matrices do not match")) end else begin let b = Gsl.Matrix.to_array el2 in let x = Gsl.Linalg.solve_LU (`M el1) (`A b) in let x_mat = Gsl.Matrix.of_array x m1 1 in stack#push (RpcFloatMatrixUnit (x_mat, Units.div u2 u1)) end |RpcComplexMatrixUnit (el2, u2) -> let n1, m1 = Gsl.Matrix.dims el1 in if n1 <> m1 then (stack#push gen_el2; stack#push gen_el1; raise (Invalid_argument "multiplier matrix must be square")) else let n2, m2 = Gsl.Matrix_complex.dims el2 in if m2 <> 1 then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "resultant matrix must be a column") end else if n2 <> m1 then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument ("dimensions of multiplier and" ^ "resultant matrices do not match")) end else begin let a_cpx = Gsl_assist.cmat_of_fmat el1 in let b_arr = Gsl.Matrix_complex.to_array el2 in let b_vec = Gsl.Vector_complex.of_array b_arr in let x = Gsl_assist.solve_complex_LU (`CM a_cpx) b_vec in let x_mat = Gsl.Matrix_complex.of_complex_array x m1 1 in stack#push (RpcComplexMatrixUnit (x_mat, Units.div u2 u1)) end |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "both arguments of solve_linear must be matrices")) end |RpcComplexMatrixUnit (el1, u1) -> begin match gen_el2 with |RpcFloatMatrixUnit (el2, u2) -> let n1, m1 = Gsl.Matrix_complex.dims el1 in if n1 <> m1 then begin stack#push gen_el2; stack#push gen_el1; raise (Invalid_argument "multiplier matrix must be square") end else let n2, m2 = Gsl.Matrix.dims el2 in if m2 <> 1 then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "resultant matrix must be a column") end else if n2 <> m1 then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument ("dimensions of multiplier and" ^ "resultant matrices do not match")) end else begin let b_cpx = Gsl_assist.cmat_of_fmat el2 in let b_arr = Gsl.Matrix_complex.to_array b_cpx in let b_vec = Gsl.Vector_complex.of_array b_arr in let x = Gsl_assist.solve_complex_LU (`CM el1) b_vec in let x_mat = Gsl.Matrix_complex.of_complex_array x m1 1 in stack#push (RpcComplexMatrixUnit (x_mat, Units.div u2 u1)) end |RpcComplexMatrixUnit (el2, u2) -> let n1, m1 = Gsl.Matrix_complex.dims el1 in if n1 <> m1 then begin stack#push gen_el2; stack#push gen_el1; raise (Invalid_argument "multiplier matrix must be square") end else let n2, m2 = Gsl.Matrix_complex.dims el2 in if m2 <> 1 then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "resultant matrix must be a column") end else if n2 <> m1 then begin stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument ("dimensions of multiplier and" ^ "resultant matrices do not match")) end else begin let b_arr = Gsl.Matrix_complex.to_array el2 in let b_vec = Gsl.Vector_complex.of_array b_arr in let x = Gsl_assist.solve_complex_LU (`CM el1) b_vec in let x_mat = Gsl.Matrix_complex.of_complex_array x m1 1 in stack#push (RpcComplexMatrixUnit (x_mat, Units.div u2 u1)) end |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "both arguments of solve_linear must be matrices")) end |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "both arguments of solve_linear must be matrices")) (* arch-tag: DO_NOT_CHANGE_c11268a8-d37a-4573-98db-e985cc338e38 *) orpie-release-1.6.0/src/orpie/statefile.ml000066400000000000000000000104231334120314700204670ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* statefile.ml * This file contains code for saving and loading the calculator * state. *) open Rpc_stack (* save to a datafile using the Marshal module *) let save_state ((modes : calculator_modes), (variables : (string, orpie_data_t) Hashtbl.t), ((data_stack : stack_data_t array), (data_len : int))) = try let version_file = Utility.join_path !(Rcfile.datadir) "version" in let version_channel = Utility.open_or_create_out_bin version_file in output_string version_channel Version.version; close_out version_channel; let save_file = Utility.join_path !(Rcfile.datadir) "calc_state" in let save_channel = Utility.open_or_create_out_bin save_file in Marshal.to_channel save_channel (modes, variables, !Rcfile.autobind_keys, data_stack, data_len) []; close_out save_channel with |Sys_error ss -> raise (Invalid_argument "can't open data file for writing") |Failure ff -> raise (Invalid_argument "can't serialize calculator data to file") (* load from a datafile using the Marshal module *) let load_state () = try (* check whether the version file exists *) let version_file = Utility.join_path !(Rcfile.datadir) "version" in if Sys.file_exists (Utility.expand_file version_file) then begin (* if it does exist, try loading it *) let version_channel = Utility.expand_open_in_ascii version_file in let ver_string = input_line version_channel in close_in version_channel; (* if the version strings match, then assume it's okay to use * Marshal. *) if ver_string = Version.version then begin (* check whether the state file exists *) let datafile = Utility.join_path !(Rcfile.datadir) "calc_state" in if Sys.file_exists (Utility.expand_file datafile) then begin (* if it does exist, try loading it *) let load_channel = Utility.expand_open_in_bin datafile in let data_modes, data_variables, data_autobind_keys, data_stack, data_len = (Marshal.from_channel load_channel : calculator_modes * ((string, orpie_data_t) Hashtbl.t) * (int * string * Operations.operation_t option * int) array * (stack_data_t array) * int) in close_in load_channel; Rcfile.validate_saved_autobindings data_autobind_keys; (data_modes, data_variables, Some (data_stack, data_len)) end else (* if the datafile is missing, do nothing as it will be * created later *) ({angle = Rad; base = Dec; complex = Rect}, Hashtbl.create 20, None) end else (* if the version strings don't match, don't try loading anything *) ({angle = Rad; base = Dec; complex = Rect}, Hashtbl.create 20, None) end else (* if the version file does not exist, don't try loading anything *) ({angle = Rad; base = Dec; complex = Rect}, Hashtbl.create 20, None) with (* this gets raised if, for example, we don't have read permission * on the state data file *) |Sys_error ss -> raise (Invalid_argument "can't open calculator state data file") (* this shouldn't happen unless the data file gets corrupted. *) |Failure ff -> raise (Invalid_argument "can't deserialize calculator data from file") (* arch-tag: DO_NOT_CHANGE_00dfe125-d9a3-4eca-842d-2a7e29cd29d0 *) orpie-release-1.6.0/src/orpie/sub.ml000066400000000000000000000214041334120314700173010ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) open Rpc_stack open Gsl_assist open Big_int let sub (stack : rpc_stack) (evaln : int -> unit) = evaln 2; let gen_el2 = stack#pop () in let gen_el1 = stack#pop () in match gen_el1 with |RpcInt el1 -> ( match gen_el2 with |RpcInt el2 -> stack#push (RpcInt (sub_big_int el1 el2)) |RpcFloatUnit (el2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "inconsistent units" end else let (f, u) = funit_of_float ((float_of_big_int el1) -. el2) in stack#push (RpcFloatUnit (f, u)) |RpcComplexUnit (el2, uu2) -> if uu2 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "inconsistent units" end else let c_el1 = cmpx_of_int el1 in let (c, u) = cunit_of_cpx (Complex.sub c_el1 el2) in stack#push (RpcComplexUnit (c, u)) |_ -> (* if the elements are incompatible, we have to put them back on the stack *) (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for subtraction")) ) |RpcFloatUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> if uu1 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "inconsistent units" end else let (f, u) = funit_of_float (el1 -. float_of_big_int el2) in stack#push (RpcFloatUnit (f, u)) |RpcFloatUnit (el2, uu2) -> begin try let conv = Units.conversion_factor uu1 uu2 !Rcfile.unit_table in stack#push (RpcFloatUnit (conv *. el1 -. el2, uu2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s end |RpcComplexUnit (el2, uu2) -> begin try let c_el1 = c_of_f (el1 *. (Units.conversion_factor uu1 uu2 !Rcfile.unit_table)) in stack#push (RpcComplexUnit (Complex.sub c_el1 el2, uu2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s end |_ -> (* if the elements are incompatible, we have to put them back on the stack *) (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for subtraction")) ) |RpcComplexUnit (el1, uu1) -> ( match gen_el2 with |RpcInt el2 -> if uu1 <> Units.empty_unit then begin stack#push gen_el1; stack#push gen_el2; raise_invalid "inconsistent units" end else let c_el2 = cmpx_of_int el2 in let (c, u) = cunit_of_cpx (Complex.sub el1 c_el2) in stack#push (RpcComplexUnit (c, u)) |RpcFloatUnit (el2, uu2) -> begin try let conv = c_of_f (Units.conversion_factor uu1 uu2 !Rcfile.unit_table) in let c_el1 = Complex.mul el1 conv in let c_el2 = c_of_f el2 in stack#push (RpcComplexUnit (Complex.sub c_el1 c_el2, uu2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s end |RpcComplexUnit (el2, uu2) -> begin try let conv = c_of_f (Units.conversion_factor uu1 uu2 !Rcfile.unit_table) in let c_el1 = Complex.mul el1 conv in stack#push (RpcComplexUnit (Complex.sub c_el1 el2, uu2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s end |_ -> (* if the elements are incompatible, we have to put them back on the stack *) (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for subtraction")) ) |RpcFloatMatrixUnit (el1, u1) -> ( match gen_el2 with |RpcFloatMatrixUnit (el2, u2) -> let dim1 = (Gsl.Matrix.dims el1) and dim2 = (Gsl.Matrix.dims el2) in if dim1 = dim2 then try let conv = Units.conversion_factor u1 u2 !Rcfile.unit_table in let result = Gsl.Matrix.copy el1 in Gsl.Matrix.scale result conv; Gsl.Matrix.sub result el2; stack#push (RpcFloatMatrixUnit (result, u2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for subtraction")) |RpcComplexMatrixUnit (el2, u2) -> let dim1 = (Gsl.Matrix.dims el1) and dim2 = (Gsl.Matrix_complex.dims el2) in if dim1 = dim2 then try let conv = c_of_f (Units.conversion_factor u1 u2 !Rcfile.unit_table) in let c_el1 = cmat_of_fmat el1 in Gsl.Matrix_complex.scale c_el1 conv; Gsl.Matrix_complex.sub c_el1 el2; stack#push (RpcComplexMatrixUnit (c_el1, u2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for subtraction")) |_ -> (* if the elements are incompatible, we have to put them back on the stack *) (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for subtraction")) ) |RpcComplexMatrixUnit (el1, u1) -> ( match gen_el2 with |RpcFloatMatrixUnit (el2, u2) -> let dim1 = (Gsl.Matrix_complex.dims el1) and dim2 = (Gsl.Matrix.dims el2) in if dim1 = dim2 then try let conv = c_of_f (Units.conversion_factor u1 u2 !Rcfile.unit_table) in let c_el2 = cmat_of_fmat el2 in let result = Gsl.Matrix_complex.copy el1 in Gsl.Matrix_complex.scale result conv; Gsl.Matrix_complex.sub result c_el2; stack#push (RpcComplexMatrixUnit (result, u2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for subtraction")) |RpcComplexMatrixUnit (el2, u2) -> let dim1 = (Gsl.Matrix_complex.dims el1) and dim2 = (Gsl.Matrix_complex.dims el2) in if dim1 = dim2 then try let conv = c_of_f (Units.conversion_factor u1 u2 !Rcfile.unit_table) in let result = Gsl.Matrix_complex.copy el1 in Gsl.Matrix_complex.scale result conv; Gsl.Matrix_complex.sub result el2; stack#push (RpcComplexMatrixUnit (result, u2)) with Units.Units_error s -> stack#push gen_el1; stack#push gen_el2; raise_invalid s else (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible matrix dimensions for subtraction")) |_ -> (* if the elements are incompatible, we have to put them back on the stack *) (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for subtraction")) ) |_ -> (stack#push gen_el1; stack#push gen_el2; raise (Invalid_argument "incompatible types for subtraction")) (* arch-tag: DO_NOT_CHANGE_f9044e6f-03c7-465a-b8ab-87cf65a0bc37 *) orpie-release-1.6.0/src/orpie/txtin_lexer.mll000066400000000000000000000042171334120314700212340ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* txtin_lexer.mll * * Orpie can handle input of data from a textfile created by an external editor. * editor_lexer.mll generates a lexer that tokenizes this data. *) { open Txtin_parser } (* space, tab, CR, linefeed, vertical tab *) let whitespace = [' ' '\010' '\013' '\009' '\012'] let digit = ['0'-'9'] let hex_digit = ['0'-'9' 'a'-'f' 'A'-'F'] let base_ident = ['b' 'o' 'd' 'h'] let sign = ['-' '+'] let variable_char = ['a'-'z' 'A'-'Z' '0'-'9' '-' '_'] let units = ['a'-'z' 'A'-'Z' '0'-'9' '.' '-' '*' '/' '^'] rule token = parse whitespace+ {token lexbuf} | '#' sign? hex_digit+ '`' base_ident { let s = Lexing.lexeme lexbuf in let int_str = String.sub s 1 (String.length s - 1) in INTEGER int_str} | '@' variable_char+ { let s = Lexing.lexeme lexbuf in let var_str = String.sub s 1 (String.length s - 1) in VARIABLE var_str} | ((sign? digit+ ('.' digit*)?) | (sign? digit* '.' digit+)) ('e' sign? digit+)? { FLOAT (Lexing.lexeme lexbuf)} | '_' units* { UNITS (Lexing.lexeme lexbuf)} | '(' { BEGINCOMPLEX } | ')' { ENDCOMPLEX } | ',' { SEPARATOR } | '<' { ANGLE } | '[' { BEGINMATRIX } | ']' { ENDMATRIX } | eof { EOF} (* arch-tag: DO_NOT_CHANGE_d4979b04-40d1-47e1-aab6-a04c0ded49ff *) orpie-release-1.6.0/src/orpie/txtin_parser.mly000066400000000000000000000255321334120314700214310ustar00rootroot00000000000000%{ (* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* txtinr_parser.mly * * Orpie can handle input of data from a textfile created by an external editor. * editor_parser.mly generates a parser that works in conjunction with * editor_lexer.mll to enter this data on the stack. *) let pi = 3.14159265358979323846;; type f_or_c = | F of float | C of Complex.t (* decode a matrix of type CF and decide * whether it has float elements or complex elements, * then create the appropriate orpie_data_t type. *) let decode_float_complex_matrix mat u_str = let num_rows = Array.length mat and num_cols = Array.length mat.(0) in let flt_array = Array.make_matrix num_rows num_cols 0.0 and cpx_array = Array.make_matrix num_rows num_cols Complex.zero in let has_complex = ref false in for i = 0 to pred num_rows do if Array.length mat.(i) != num_cols then raise (Utility.Txtin_error "inconsistent number of columns in input matrix") else begin for j = 0 to pred num_cols do begin match mat.(i).(j) with |F el -> flt_array.(i).(j) <- el; cpx_array.(i).(j) <- {Complex.re = el; Complex.im = 0.0} |C el -> has_complex := true; cpx_array.(i).(j) <- el end done; end done; if !has_complex then Rpc_stack.RpcComplexMatrixUnit (Gsl.Matrix_complex.of_arrays cpx_array, Units.units_of_string (Str.string_after u_str 1) !Rcfile.unit_table) else Rpc_stack.RpcFloatMatrixUnit (Gsl.Matrix.of_arrays flt_array, Units.units_of_string (Str.string_after u_str 1) !Rcfile.unit_table) let rect_of_polar_rad r theta = let real = r *. (cos theta) and imag = r *. (sin theta) in {Complex.re = real; Complex.im = imag} let rect_of_polar_deg r theta = let rad_theta = theta /. 180.0 *. pi in rect_of_polar_rad r rad_theta (* convert an integer string to an RpcInt *) let decode_integer i_str = let int_str = i_str in let str_len = String.length int_str in let digits = Str.string_before int_str (str_len - 2) in let int_val = let base_char = int_str.[pred str_len] in if base_char = 'b' then Big_int_str.big_int_of_string_base digits 2 else if base_char = 'o' then Big_int_str.big_int_of_string_base digits 8 else if base_char = 'd' then Big_int_str.big_int_of_string_base digits 10 else if base_char = 'h' then Big_int_str.big_int_of_string_base digits 16 else let base_string = String.make 1 base_char in raise (Utility.Txtin_error ("illegal base character " ^ base_string)) in Rpc_stack.RpcInt int_val (* convert a floating point string and a unit string to an RpcFloatUnit *) let decode_float_units f_str u_str = Rpc_stack.RpcFloatUnit ((float_of_string f_str), Units.units_of_string (Str.string_after u_str 1) !Rcfile.unit_table) (* convert a cartesian complex number string and a unit string to an * RpcComplexUnit *) let decode_complex_rect_units re_str im_str u_str = let f1 = float_of_string re_str and f2 = float_of_string im_str in Rpc_stack.RpcComplexUnit ({Complex.re = f1; Complex.im = f2}, Units.units_of_string (Str.string_after u_str 1) !Rcfile.unit_table) (* convert a polar representation complex number string to an * RpcComplex. The rect_of_polar argument should take care of * any necessary degrees/radians conversion. *) let decode_complex_polar_units rect_of_polar mag_str ang_str u_str = let mag = float_of_string mag_str and ang = float_of_string ang_str in Rpc_stack.RpcComplexUnit ((rect_of_polar mag ang), Units.units_of_string (Str.string_after u_str 1) !Rcfile.unit_table) (* convert a polar representation complex number string to an * RpcComplex. Assumes radian representation of the angle. *) let decode_complex_polar_rad_units mag_str ang_str u_str = decode_complex_polar_units rect_of_polar_rad mag_str ang_str u_str (* convert a polar representation complex number string to an * RpcComplex. Assumes degree representation of the angle. *) let decode_complex_polar_deg_units mag_str ang_str u_str = decode_complex_polar_units rect_of_polar_deg mag_str ang_str u_str (* convert a matrix to an RpcFloatMatrixUnit or an RpcComplexMatrix. *) let decode_matrix_units mat_rows u_str = (* matrix_rows is a list of rows, each of which * is a list of elements; create a 2d array * from these lists, and generate the appropriate * orpie_data_t from the 2d array. *) let num_rows = List.length mat_rows in let num_cols = List.length (List.hd mat_rows) in let mat = Array.make_matrix num_rows num_cols (F 0.0) in let temp_arr = Array.of_list (List.rev mat_rows) in for i = 0 to pred num_rows do mat.(i) <- Array.of_list (List.rev temp_arr.(i)) done; (* create a float array or a complex array, depending * on whether or not any complex types are present * in this array. *) decode_float_complex_matrix mat u_str (* convert a variable string to an RpcVariable *) let decode_variable v_str = Rpc_stack.RpcVariable v_str %} /* declarations */ %token INTEGER %token FLOAT %token VARIABLE %token UNITS %token BEGINCOMPLEX %token ENDCOMPLEX %token SEPARATOR %token ANGLE %token BEGINMATRIX %token ENDMATRIX %token EOF /* parse the input under the assumption that angles * are provided using radian measure */ %start decode_data_rad %type decode_data_rad /* parse the input under the assumption that angles * are provided using degree measure */ %start decode_data_deg %type decode_data_deg %% /* rules */ /**************************************** * ANGLES PROVIDED USING RADIAN MEASURE * ****************************************/ decode_data_rad: datalist_rad EOF { List.rev $1 } ; datalist_rad: datalist_rad datagroup_rad { $2 :: $1 } | /* empty */ { [] } ; datagroup_rad: data_rad { $1 } ; data_rad: INTEGER { decode_integer $1 } | VARIABLE { decode_variable $1} | FLOAT UNITS { decode_float_units $1 $2 } | FLOAT { decode_float_units $1 "_" } | BEGINCOMPLEX FLOAT SEPARATOR FLOAT ENDCOMPLEX UNITS { decode_complex_rect_units $2 $4 $6 } | BEGINCOMPLEX FLOAT SEPARATOR FLOAT ENDCOMPLEX { decode_complex_rect_units $2 $4 "_" } | BEGINCOMPLEX FLOAT ANGLE FLOAT ENDCOMPLEX UNITS { decode_complex_polar_rad_units $2 $4 $6 } | BEGINCOMPLEX FLOAT ANGLE FLOAT ENDCOMPLEX { decode_complex_polar_rad_units $2 $4 "_" } | BEGINMATRIX matrix_rows_rad ENDMATRIX UNITS { decode_matrix_units $2 $4 } | BEGINMATRIX matrix_rows_rad ENDMATRIX { decode_matrix_units $2 "_" } ; matrix_rows_rad: matrix_rows_rad BEGINMATRIX matrix_row_elements_rad ENDMATRIX {$3 :: $1} | /* empty */ {[]} ; matrix_row_elements_rad: matrix_row_elements_rad SEPARATOR FLOAT { (F (float_of_string $3)) :: $1 } | matrix_row_elements_rad SEPARATOR BEGINCOMPLEX FLOAT SEPARATOR FLOAT ENDCOMPLEX { let f1 = float_of_string $4 and f2 = float_of_string $6 in (C {Complex.re = f1; Complex.im = f2}) :: $1 } | matrix_row_elements_rad SEPARATOR BEGINCOMPLEX FLOAT ANGLE FLOAT ENDCOMPLEX { let r = float_of_string $4 and th = float_of_string $6 in (C (rect_of_polar_rad r th)) :: $1 } | FLOAT { (F (float_of_string $1)) :: [] } | BEGINCOMPLEX FLOAT SEPARATOR FLOAT ENDCOMPLEX { let f1 = float_of_string $2 and f2 = float_of_string $4 in (C {Complex.re = f1; Complex.im = f2}) :: [] } | BEGINCOMPLEX FLOAT ANGLE FLOAT ENDCOMPLEX { let r = float_of_string $2 and th = float_of_string $4 in (C (rect_of_polar_rad r th)) :: [] } ; /**************************************** * ANGLES PROVIDED USING DEGREE MEASURE * ****************************************/ decode_data_deg: datalist_deg EOF { List.rev $1 } ; datalist_deg: datalist_deg datagroup_deg { $2 :: $1 } | /* empty */ { [] } ; datagroup_deg: data_deg { $1 } ; data_deg: INTEGER { decode_integer $1 } | VARIABLE { decode_variable $1} | FLOAT UNITS { decode_float_units $1 $2 } | FLOAT { decode_float_units $1 "_" } | BEGINCOMPLEX FLOAT SEPARATOR FLOAT ENDCOMPLEX UNITS { decode_complex_rect_units $2 $4 $6 } | BEGINCOMPLEX FLOAT SEPARATOR FLOAT ENDCOMPLEX { decode_complex_rect_units $2 $4 "_" } | BEGINCOMPLEX FLOAT ANGLE FLOAT ENDCOMPLEX UNITS { decode_complex_polar_deg_units $2 $4 $6 } | BEGINCOMPLEX FLOAT ANGLE FLOAT ENDCOMPLEX { decode_complex_polar_deg_units $2 $4 "_" } | BEGINMATRIX matrix_rows_deg ENDMATRIX UNITS { decode_matrix_units $2 $4 } | BEGINMATRIX matrix_rows_deg ENDMATRIX { decode_matrix_units $2 "_" } ; matrix_rows_deg: matrix_rows_deg BEGINMATRIX matrix_row_elements_deg ENDMATRIX {$3 :: $1} | /* empty */ {[]} ; matrix_row_elements_deg: matrix_row_elements_deg SEPARATOR FLOAT { (F (float_of_string $3)) :: $1 } | matrix_row_elements_deg SEPARATOR BEGINCOMPLEX FLOAT SEPARATOR FLOAT ENDCOMPLEX { let f1 = float_of_string $4 and f2 = float_of_string $6 in (C {Complex.re = f1; Complex.im = f2}) :: $1 } | matrix_row_elements_deg SEPARATOR BEGINCOMPLEX FLOAT ANGLE FLOAT ENDCOMPLEX { let r = float_of_string $4 and th = float_of_string $6 in (C (rect_of_polar_deg r th)) :: $1 } | FLOAT { (F (float_of_string $1)) :: [] } | BEGINCOMPLEX FLOAT SEPARATOR FLOAT ENDCOMPLEX { let f1 = float_of_string $2 and f2 = float_of_string $4 in (C {Complex.re = f1; Complex.im = f2}) :: [] } | BEGINCOMPLEX FLOAT ANGLE FLOAT ENDCOMPLEX { let r = float_of_string $2 and th = float_of_string $4 in (C (rect_of_polar_deg r th)) :: [] } ; /* arch-tag: DO_NOT_CHANGE_c65a2550-f00d-40f1-b51f-f5d654257785 */ orpie-release-1.6.0/src/orpie/units.ml000066400000000000000000000412421334120314700176540ustar00rootroot00000000000000(* ocaml-units -- a module for handling standard operations on * physical units * * Copyright (C) 2007 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Please send feedback, bug reports, patches, etc. to * . *) exception Units_error of string let units_failwith ss = raise (Units_error ss) (* SI prefixes. *) type prefix_t = | NoPrefix | Yocto | Zepto | Atto | Femto | Pico | Nano | Micro | Milli | Centi | Deci | Deka | Hecto | Kilo | Mega | Giga | Tera | Peta | Exa | Zetta | Yotta (* A unit is viewed as a composite of one or more base units. The Map structure * works well for this purpose; the string representation of a unit is the key, * and this key maps to the (float) power of that unit. This data structure * allows nice lookup semantics when multiplying units together, etc. *) module SMap = Map.Make (String) (* Any number of units can be attached to a numeric value. We call this a * "unit set", and it is a mapping from the string representations of the * units to the float powers which they are raised to. *) type unit_set_t = float SMap.t (* A unit *definition* is given in terms of a coefficient and a set of * component units. *) type unit_def_t = { coeff : float; comp_units : unit_set_t } (* A *representation* of a unit involves both the prefix and the string * that identifies the base unit. (e.g. Kilo and "g") *) type unit_rep_t = { prefix : prefix_t; base : string } (* The table of base units maps string representations of units to the preferred * SI prefix. Typically most base units will prefer NoPrefix, but (for example) * the SI standard prefers Kilo for the unit 'g'. *) type base_unit_table_t = prefix_t SMap.t (* The unit definition table maps string representations of units to * sets of base units. *) type unit_def_table_t = unit_def_t SMap.t type unit_table_t = { base_table : base_unit_table_t; def_table : unit_def_table_t } let empty_unit = SMap.empty let empty_unit_table = { base_table = SMap.empty; def_table = SMap.empty } (* Multiply a single unit by a set of units, collecting like terms. *) let mult_aux (unit_str : string) (unit_pow : float) (unit_set : unit_set_t) = let existing_pow = if SMap.mem unit_str unit_set then SMap.find unit_str unit_set else 0.0 in let new_pow = existing_pow +. unit_pow in if new_pow <> 0.0 then SMap.add unit_str new_pow unit_set else (* if unit powers happen to cancel exactly, remove them completely *) SMap.remove unit_str unit_set (* Create a new base unit with a preferred SI prefix. *) let add_base_unit (unit_str : string) (preferred_prefix : prefix_t) (known_units : unit_table_t) : unit_table_t = if SMap.mem unit_str known_units.base_table then units_failwith ("base unit \"" ^ unit_str ^ "\" already declared") else (* We also enter this base unit into the main unit definion table * as an A->A mapping, so that base units can be "expanded" just * like any other unit. (Base units expand to themselves.) *) let unit_def = { coeff = 1.0; comp_units = SMap.add unit_str 1.0 SMap.empty } in { base_table = SMap.add unit_str preferred_prefix known_units.base_table; def_table = SMap.add unit_str unit_def known_units.def_table } let prefix_string_table = Hashtbl.create 25;; Hashtbl.add prefix_string_table "y" Yocto;; Hashtbl.add prefix_string_table "z" Zepto;; Hashtbl.add prefix_string_table "a" Atto;; Hashtbl.add prefix_string_table "f" Femto;; Hashtbl.add prefix_string_table "p" Pico;; Hashtbl.add prefix_string_table "n" Nano;; Hashtbl.add prefix_string_table "u" Micro;; Hashtbl.add prefix_string_table "m" Milli;; Hashtbl.add prefix_string_table "c" Centi;; Hashtbl.add prefix_string_table "d" Deci;; Hashtbl.add prefix_string_table "da" Deka;; Hashtbl.add prefix_string_table "h" Hecto;; Hashtbl.add prefix_string_table "k" Kilo;; Hashtbl.add prefix_string_table "M" Mega;; Hashtbl.add prefix_string_table "G" Giga;; Hashtbl.add prefix_string_table "T" Tera;; Hashtbl.add prefix_string_table "P" Peta;; Hashtbl.add prefix_string_table "E" Exa;; Hashtbl.add prefix_string_table "Z" Zetta;; Hashtbl.add prefix_string_table "Y" Yotta;; Hashtbl.add prefix_string_table "" NoPrefix;; let prefix_of_string s = Hashtbl.find prefix_string_table s;; let prefix_list = ["y"; "z"; "a"; "f"; "p"; "n"; "u"; "m"; "c"; "da"; "d"; "h"; "k"; "M"; "G"; "T"; "P"; "E"; "Z"; "Y"];; let prefix_value (pre : prefix_t) = match pre with | NoPrefix -> 1.0 | Yocto -> 1e-24 | Zepto -> 1e-21 | Atto -> 1e-18 | Femto -> 1e-15 | Pico -> 1e-12 | Nano -> 1e-9 | Micro -> 1e-6 | Milli -> 1e-3 | Centi -> 0.01 | Deci -> 0.1 | Deka -> 10.0 | Hecto -> 100.0 | Kilo -> 1e3 | Mega -> 1e6 | Giga -> 1e9 | Tera -> 1e12 | Peta -> 1e15 | Exa -> 1e18 | Zetta -> 1e21 | Yotta -> 1e24 let string_of_prefix (pre : prefix_t) = match pre with | NoPrefix -> "" | Yocto -> "y" | Zepto -> "z" | Atto -> "a" | Femto -> "f" | Pico -> "p" | Nano -> "n" | Micro -> "u" | Milli -> "m" | Centi -> "c" | Deci -> "d" | Deka -> "da" | Hecto -> "h" | Kilo -> "k" | Mega -> "M" | Giga -> "G" | Tera -> "T" | Peta -> "P" | Exa -> "E" | Zetta -> "Z" | Yotta -> "Y" (* Is 'pre' a prefix of 'word'? *) let is_prefix pre word = if String.length word >= String.length pre then pre = (String.sub word 0 (String.length pre)) else false (* Given a string like "kg", try to parse it as the representation of a unit * with prefix. *) let unit_rep_of_string (ss : string) (known_units : unit_table_t) : unit_rep_t = let rec test_prefixes plist = match plist with | [] -> raise Not_found | head :: tail -> if is_prefix head ss then let suffix = Str.string_after ss (String.length head) in if SMap.mem suffix known_units.def_table then let si_prefix = Hashtbl.find prefix_string_table head in { prefix = si_prefix; base = suffix } else test_prefixes tail else test_prefixes tail in (* Look first for matches with no prefix, so we can catch * units like "mmHg" *) if SMap.mem ss known_units.def_table then { prefix = NoPrefix; base = ss } else test_prefixes prefix_list (* Given a leading coefficient and a set of unit definitions (mappings to base * units) considered to be multiplied together, compute an equivalent singular * unit definition (collecting like terms). * * In effect, this is doing an operation like * (3)*(5_m/s)*(10_m^2)*(0.5_g) -> 75_m^3*g/s . *) let collect (terms : float * (unit_def_t SMap.t)) : unit_def_t = let (leading_coeff, uncollected) = terms in let collect_single (_ : string) (unit_def : unit_def_t) (collection : unit_def_t) = { coeff = collection.coeff *. unit_def.coeff; comp_units = SMap.fold mult_aux unit_def.comp_units collection.comp_units } in SMap.fold collect_single uncollected {coeff = leading_coeff; comp_units = SMap.empty} (* Given a unit definition (mapping to other known units), expand * out all the known units in terms of base units. The leading * coefficient is prepended. * * In effect, this is doing an operation like * 0.1_N*Hz^2 -> (100)*(1_g*m/s^2)*(1_s^-2) . *) let expand (unit_def : unit_def_t) (known_units : unit_table_t) : (float * (unit_def_t SMap.t)) = let expand_single (unit_rep_str : string) (unit_pow : float) = let unit_rep = unit_rep_of_string unit_rep_str known_units in let base_def = SMap.find unit_rep.base known_units.def_table in (* The powers of all the base units need to be multiplied * by the power of this unit, and the coefficient needs * to be exponentiated. *) let prefix_base_coeff = (prefix_value unit_rep.prefix) *. base_def.coeff in let exponentiate base_unit_pow = base_unit_pow *. unit_pow in { coeff = prefix_base_coeff ** unit_pow; comp_units = SMap.map exponentiate base_def.comp_units } in let base_expansion = SMap.mapi expand_single unit_def.comp_units in (unit_def.coeff, base_expansion) (* Add a unit definition to the table. The definition is immediately * recast in terms of base units only, and is stored in this form. *) let add_unit (unit_str : string) (unit_def : unit_def_t) (known_units : unit_table_t) : unit_table_t = if SMap.mem unit_str known_units.def_table then units_failwith ("unit \"" ^ unit_str ^ "\" already declared") else begin try let unit_base_def = collect (expand unit_def known_units) in {known_units with def_table = SMap.add unit_str unit_base_def known_units.def_table} with Not_found -> units_failwith ("unit \"" ^ unit_str ^ "\" depends on an undefined unit") end (* Is this string a known unit (possibly with a prefix)? *) let is_known_unit (ss : string) (known_units : unit_table_t) = try let _ = unit_rep_of_string ss known_units in true with _ -> false (* Convert a string into an appropriate set of units. Units should be * multiplied with '*', divided with '/', and raised to powers with '^'. * So the following would be a valid example: "kg^2*m/s^2/h*ft^-2". *) let units_of_string (ss : string) (known_units : unit_table_t) : unit_set_t = let mult_regex = Str.regexp "\\*" and div_regex = Str.regexp "/" and pow_regex = Str.regexp "\\^" in (* Given a string like "mm^3" parse it into a unit representation * and floating-point power. *) let unit_of_term (tt : string) : (string * float) = match Str.split pow_regex tt with | [] -> units_failwith "empty power split in unit_of_string()" | unit_str :: [] -> if String.contains tt '^' then units_failwith "illegal unit exponentiation syntax" else if is_known_unit unit_str known_units then (unit_str, 1.0) else units_failwith ("unrecognized unit \"" ^ unit_str ^ "\"") | unit_str :: pow_str :: [] -> let pow = try float_of_string pow_str with _ -> units_failwith ("illegal unit power: \"" ^ pow_str ^ "\"") in if is_known_unit unit_str known_units then (unit_str, pow) else units_failwith ("unrecognized unit \"" ^ unit_str ^ "\"") | _ -> units_failwith ("too many exponentiations in unit term \"" ^ tt ^ "\"") in let rec build_unit_set mlist set = match mlist with | [] -> set | head :: tail -> let div_list = Str.split div_regex head in if List.length div_list = 0 then units_failwith "empty unit string" else if List.length div_list = 1 && String.contains head '/' then units_failwith "illegal unit division syntax" else (* the tail of div_list consists of terms which followed division * operators, so we negate the exponents before multiplying out * these terms *) let mult_inverse set div_str = let (div_unit_str, div_unit_pow) = unit_of_term div_str in mult_aux div_unit_str (~-. div_unit_pow) set in let set_with_inverses = List.fold_left mult_inverse set (List.tl div_list) in (* the head of div_list is multiplied, not divided, because it * preceded a division operator *) let (mult_unit_str, mult_unit_pow) = unit_of_term (List.hd div_list) in let next_set = mult_aux mult_unit_str mult_unit_pow set_with_inverses in build_unit_set tail next_set in let mult_terms = Str.split mult_regex ss in build_unit_set mult_terms SMap.empty (* Generate a string representation of a set of units. * FIXME: this will generate an alphabetical unit ordering. * That's probably a little too simplistic. *) let string_of_units (u : unit_set_t) : string = let gen_string unit_str unit_pow str_list = if unit_pow <> 0.0 then let str_rep = if unit_pow <> 1.0 then Printf.sprintf "%s^%g" unit_str unit_pow else unit_str in str_rep :: str_list else str_list in let str_list_rev = SMap.fold gen_string u [] in String.concat "*" (List.rev str_list_rev) (* Convert a string into an appropriate unit definition. Units are specified * as above, but a float coefficient is prepended like so: * "2.3_N*m^2". This can also handle a pure numeric constant. *) let unit_def_of_string (ss : string) (known_units : unit_table_t) : unit_def_t = let split_regex = Str.regexp "_" in let split_str = Str.split split_regex ss in begin try if List.length split_str <> 2 then let c = float_of_string ss in { coeff = c; comp_units = empty_unit } else let float_str = List.hd split_str and units_str = List.hd (List.tl split_str) in let c = float_of_string float_str and u = units_of_string units_str known_units in { coeff = c; comp_units = u } with Failure "float_of_string" -> units_failwith ("unrecognized format for unit definition \"" ^ ss ^ "\"") end let string_of_unit_def (unit_def : unit_def_t) : string = Printf.sprintf "%g_%s" unit_def.coeff (string_of_units unit_def.comp_units) (* Print out the tables of known units. *) let dump_table (known_units : unit_table_t) = let print_base_entry base_unit_str preferred_prefix = Printf.printf "%s (prefix \"%s\")\n" base_unit_str (string_of_prefix preferred_prefix) in let print_entry unit_str unit_def = Printf.printf "%s -> %g_%s\n" unit_str unit_def.coeff (string_of_units unit_def.comp_units) in Printf.printf "BASE UNITS:\n--------------------------\n"; SMap.iter print_base_entry known_units.base_table; Printf.printf "\nUNIT DEFINITIONS:\n--------------------------\n"; SMap.iter print_entry known_units.def_table (* Refactor a set of units in terms of base units. *) let standardize_units (u : unit_set_t) (known_units : unit_table_t) : unit_def_t = let units = {coeff = 1.0; comp_units = u} in let base_units = collect (expand units known_units) in (* base_units doesn't have any prefixes, so we need to add those in *) let add_prefix unit_str unit_pow unit_total = let prefix = SMap.find unit_str known_units.base_table in let prefix_str = string_of_prefix prefix in let total_prefix_value = (prefix_value prefix) ** unit_pow in { coeff = unit_total.coeff /. total_prefix_value; comp_units = SMap.add (prefix_str ^ unit_str) unit_pow unit_total.comp_units } in SMap.fold add_prefix base_units.comp_units {coeff = base_units.coeff; comp_units = SMap.empty} (* conversion_factor a b t * computes the conversion factor 'f' such that a == f*b. * Raises an exception if the units are dimensionally incompatible. *) let conversion_factor (a : unit_set_t) (b : unit_set_t) (known_units : unit_table_t) : float = let a_std = standardize_units a known_units and b_std = standardize_units b known_units in if a_std.comp_units <> b_std.comp_units then units_failwith "units are dimensionally incompatible" else a_std.coeff /. b_std.coeff (* Raise a set of units to a power. *) let pow (u : unit_set_t) (p : float) : unit_set_t = let f x = x *. p in SMap.map f u (* Multiply two unit sets together. *) let mult (a : unit_set_t) (b : unit_set_t) = SMap.fold mult_aux a b (* Divide two unit sets (a/b). *) let div (a : unit_set_t) (b : unit_set_t) = let div_aux unit_str unit_pow unit_set = mult_aux unit_str (~-. unit_pow) unit_set in SMap.fold div_aux b a orpie-release-1.6.0/src/orpie/utility.ml000066400000000000000000000132711334120314700202160ustar00rootroot00000000000000(* Orpie -- a fullscreen RPN calculator for the console * Copyright (C) 2003-2004, 2005, 2006-2007, 2010, 2018 Paul Pelzl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Please send bug reports, patches, etc. to Paul Pelzl at * . *) (* utility.ml * * miscellaneous helper functions that don't really fit elsewhere *) (* for some reason this is unrecognized if I leave it in txtin_parser.mly *) exception Txtin_error of string (* Word wrap a string to a width of 'cols', placing 'num_spaces' spaces between * the words. Breaks lines on whitespace; if a word is too long, it will be * broken and hyphenated. Return value is a list of strings. *) let wordwrap_nspace s cols num_spaces = let space_str = String.make num_spaces ' ' in if String.length s <= cols then s :: [] else let rec process_words (word_list : string list) (wrapped_list : string list) = match word_list with |word :: word_tail -> begin match wrapped_list with |line :: line_tail -> let new_line = line ^ space_str ^ word in if String.length new_line <= cols then process_words word_tail (new_line :: line_tail) else if String.length word <= cols then process_words word_tail (word :: wrapped_list) else let len = String.length word in let partial_word = (String.sub word 0 (cols-1)) ^ "-" and remainder_word = "-" ^ (String.sub word (cols-1) (len-cols+1)) in process_words (remainder_word :: word_tail) (partial_word :: wrapped_list) |[] -> if String.length word <= cols then process_words word_tail (word :: wrapped_list) else let len = String.length word in let partial_word = (String.sub word 0 (cols-1)) ^ "-" and remainder_word = "-" ^ (String.sub word (cols-1) (len-cols+1)) in process_words (remainder_word :: word_tail) (partial_word :: wrapped_list) end |[] -> wrapped_list in let words = Str.split (Str.regexp "[ \t]+") s in List.rev (process_words words []) (* wordwrap with single space *) let wordwrap s cols = wordwrap_nspace s cols 1 (* append a file to a directory, with the proper number * of slashes *) let join_path dirname filename = let dir_last = dirname.[String.length dirname - 1] and file_first = filename.[0] in if dir_last = '/' && file_first = '/' then dirname ^ (Str.string_after filename 1) else if dir_last <> '/' && file_first <> '/' then dirname ^ "/" ^ filename else dirname ^ filename (* If the filename starts with "~", substitute $HOME *) let expand_file filename = if Str.string_before filename 2 = "~/" then let homedir = Sys.getenv "HOME" in homedir ^ Str.string_after filename 1 else filename (* Do whatever is necessary to open up a file for writing. If it already exists, * open it as-is. If it does not exist, make sure that all prefix directories * do exist, then open a new file. *) let open_or_create_out_gen is_binary filename = let exp_file = expand_file filename in (* Test whether the file exists *) if Sys.file_exists exp_file then if is_binary then open_out_bin exp_file else open_out exp_file else (* Check whether all directories exist *) let dir_path = Filename.dirname exp_file in let dir_list = Str.split (Str.regexp "/+") dir_path in (* if necessary, add the first "/" to the first directory *) let slash_dir_list = if not (Filename.is_relative dir_path) then ("/" ^ (List.hd dir_list)) :: (List.tl dir_list) else dir_list in let rec make_directories d_list = match d_list with | [] -> () | d :: tail -> begin try Sys.chdir d with Sys_error _ -> begin let _ = Sys.command ("mkdir " ^ d) in Sys.chdir d end end; make_directories tail in make_directories slash_dir_list; if is_binary then open_out_bin (Filename.basename exp_file) else open_out (Filename.basename exp_file) let open_or_create_out_bin filename = open_or_create_out_gen true filename let open_or_create_out_ascii filename = open_or_create_out_gen false filename (* open a filename, with tilde expansion *) let expand_open_in_gen is_binary filename = (* If the filename starts with "~", substitute $HOME *) if is_binary then open_in_bin (expand_file filename) else open_in (expand_file filename) let expand_open_in_bin filename = expand_open_in_gen true filename let expand_open_in_ascii filename = expand_open_in_gen false filename (* arch-tag: DO_NOT_CHANGE_a87790db-2dd0-496c-9620-ed968f3253fd *)